storage.tsx203 lines · main
| 1 | import dayjs from 'dayjs' |
| 2 | import { ArrowRight, ExternalLinkIcon, RefreshCw } from 'lucide-react' |
| 3 | import Link from 'next/link' |
| 4 | |
| 5 | import { |
| 6 | NetworkTrafficRenderer, |
| 7 | ResponseSpeedChartRenderer, |
| 8 | TopApiRoutesRenderer, |
| 9 | TotalRequestsChartRenderer, |
| 10 | } from '@/components/interfaces/Reports/renderers/ApiRenderers' |
| 11 | import { |
| 12 | CacheHitRateChartRenderer, |
| 13 | TopCacheMissesRenderer, |
| 14 | } from '@/components/interfaces/Reports/renderers/StorageRenderers' |
| 15 | import ReportFilterBar from '@/components/interfaces/Reports/ReportFilterBar' |
| 16 | import ReportHeader from '@/components/interfaces/Reports/ReportHeader' |
| 17 | import ReportPadding from '@/components/interfaces/Reports/ReportPadding' |
| 18 | import { REPORT_DATERANGE_HELPER_LABELS } from '@/components/interfaces/Reports/Reports.constants' |
| 19 | import ReportStickyNav from '@/components/interfaces/Reports/ReportStickyNav' |
| 20 | import ReportWidget from '@/components/interfaces/Reports/ReportWidget' |
| 21 | import { |
| 22 | DatePickerValue, |
| 23 | LogsDatePicker, |
| 24 | } from '@/components/interfaces/Settings/Logs/Logs.DatePickers' |
| 25 | import UpgradePrompt from '@/components/interfaces/Settings/Logs/UpgradePrompt' |
| 26 | import DefaultLayout from '@/components/layouts/DefaultLayout' |
| 27 | import ObservabilityLayout from '@/components/layouts/ObservabilityLayout/ObservabilityLayout' |
| 28 | import { ButtonTooltip } from '@/components/ui/ButtonTooltip' |
| 29 | import { ObservabilityLink } from '@/components/ui/ObservabilityLink' |
| 30 | import { useStorageReport } from '@/data/reports/storage-report-query' |
| 31 | import { useRefreshHandler, useReportDateRange } from '@/hooks/misc/useReportDateRange' |
| 32 | import { DOCS_URL } from '@/lib/constants' |
| 33 | import type { NextPageWithLayout } from '@/types' |
| 34 | |
| 35 | export const StorageReport: NextPageWithLayout = () => { |
| 36 | const report = useStorageReport() |
| 37 | |
| 38 | const { |
| 39 | data, |
| 40 | error, |
| 41 | filters, |
| 42 | isLoading, |
| 43 | params, |
| 44 | mergeParams, |
| 45 | removeFilters, |
| 46 | addFilter, |
| 47 | refresh, |
| 48 | } = report |
| 49 | |
| 50 | const { |
| 51 | datePickerHelpers, |
| 52 | datePickerValue, |
| 53 | handleDatePickerChange: handleDatePickerChangeFromHook, |
| 54 | showUpgradePrompt, |
| 55 | setShowUpgradePrompt, |
| 56 | selectedDateRange, |
| 57 | } = useReportDateRange(REPORT_DATERANGE_HELPER_LABELS.LAST_60_MINUTES) |
| 58 | |
| 59 | const handleDatepickerChange = (vals: DatePickerValue) => { |
| 60 | const promptShown = handleDatePickerChangeFromHook(vals) |
| 61 | if (!promptShown) { |
| 62 | mergeParams({ |
| 63 | iso_timestamp_start: vals.from ?? '', |
| 64 | iso_timestamp_end: vals.to ?? '', |
| 65 | }) |
| 66 | } |
| 67 | } |
| 68 | |
| 69 | const onRefreshReport = useRefreshHandler( |
| 70 | datePickerValue, |
| 71 | datePickerHelpers, |
| 72 | handleDatepickerChange, |
| 73 | refresh |
| 74 | ) |
| 75 | |
| 76 | return ( |
| 77 | <ReportPadding> |
| 78 | <ReportHeader title="Storage" showDatabaseSelector={false} /> |
| 79 | <ReportStickyNav |
| 80 | content={ |
| 81 | <div className="flex flex-col gap-2"> |
| 82 | <div className="flex items-center gap-2"> |
| 83 | <ButtonTooltip |
| 84 | type="default" |
| 85 | disabled={report.isLoading} |
| 86 | icon={<RefreshCw className={report.isLoading ? 'animate-spin' : ''} />} |
| 87 | className="w-7" |
| 88 | tooltip={{ content: { side: 'bottom', text: 'Refresh report' } }} |
| 89 | onClick={onRefreshReport} |
| 90 | /> |
| 91 | <LogsDatePicker |
| 92 | onSubmit={handleDatepickerChange} |
| 93 | value={datePickerValue} |
| 94 | helpers={datePickerHelpers} |
| 95 | /> |
| 96 | {selectedDateRange && ( |
| 97 | <div className="flex items-center gap-x-2 text-xs"> |
| 98 | <p className="text-foreground-light"> |
| 99 | {dayjs(selectedDateRange.period_start.date).format('MMM D, h:mma')} |
| 100 | </p> |
| 101 | <p className="text-foreground-light"> |
| 102 | <ArrowRight size={12} /> |
| 103 | </p> |
| 104 | <p className="text-foreground-light"> |
| 105 | {dayjs(selectedDateRange.period_end.date).format('MMM D, h:mma')} |
| 106 | </p> |
| 107 | </div> |
| 108 | )} |
| 109 | <UpgradePrompt |
| 110 | show={showUpgradePrompt} |
| 111 | setShowUpgradePrompt={setShowUpgradePrompt} |
| 112 | title="Report date range" |
| 113 | description="Report data can be stored for a maximum of 3 months depending on the plan that your project is on." |
| 114 | source="storageReportDateRange" |
| 115 | /> |
| 116 | </div> |
| 117 | <ReportFilterBar |
| 118 | onRemoveFilters={removeFilters} |
| 119 | onDatepickerChange={handleDatepickerChange} |
| 120 | datepickerFrom={params.totalRequests.iso_timestamp_start} |
| 121 | datepickerTo={params.totalRequests.iso_timestamp_end} |
| 122 | onAddFilter={addFilter} |
| 123 | isLoading={isLoading} |
| 124 | filters={filters} |
| 125 | selectedProduct="storage" |
| 126 | hideDatepicker={true} |
| 127 | datepickerHelpers={datePickerHelpers} |
| 128 | initialDatePickerValue={datePickerValue} |
| 129 | className="w-full" |
| 130 | showDatabaseSelector={false} |
| 131 | /> |
| 132 | </div> |
| 133 | } |
| 134 | > |
| 135 | <div className="mt-8 flex flex-col gap-4"> |
| 136 | <ReportWidget |
| 137 | isLoading={isLoading} |
| 138 | params={params.totalRequests} |
| 139 | title="Total Requests" |
| 140 | data={data.totalRequests || []} |
| 141 | error={error.totalRequest} |
| 142 | renderer={TotalRequestsChartRenderer} |
| 143 | append={TopApiRoutesRenderer} |
| 144 | appendProps={{ data: data.topRoutes || [], params: params.topRoutes }} |
| 145 | /> |
| 146 | <ReportWidget |
| 147 | isLoading={isLoading} |
| 148 | params={params.responseSpeed} |
| 149 | title="Response Speed" |
| 150 | tooltip="Average response speed of a request (in ms)" |
| 151 | data={data.responseSpeed || []} |
| 152 | error={error.responseSpeed} |
| 153 | renderer={ResponseSpeedChartRenderer} |
| 154 | appendProps={{ data: data.topSlowRoutes || [], params: params.topSlowRoutes }} |
| 155 | append={TopApiRoutesRenderer} |
| 156 | /> |
| 157 | <ReportWidget |
| 158 | isLoading={isLoading} |
| 159 | params={params.networkTraffic} |
| 160 | error={error.networkTraffic} |
| 161 | title="Network Traffic" |
| 162 | tooltip="Ingress and egress of requests and responses respectively" |
| 163 | data={data.networkTraffic || []} |
| 164 | renderer={NetworkTrafficRenderer} |
| 165 | /> |
| 166 | |
| 167 | <ReportWidget |
| 168 | isLoading={isLoading} |
| 169 | params={params.cacheHitRate} |
| 170 | title="Request Caching" |
| 171 | tooltip={ |
| 172 | <div> |
| 173 | The number of storage requests that are cached at the edge level. A higher number of |
| 174 | hits is better.{' '} |
| 175 | <span className="flex items-center gap-1 text-foreground-lighter"> |
| 176 | <Link href={`${DOCS_URL}/guides/storage/cdn/fundamentals`} target="_blank"> |
| 177 | Read More |
| 178 | </Link> |
| 179 | <ExternalLinkIcon className="w-3 h-3" /> |
| 180 | </span> |
| 181 | </div> |
| 182 | } |
| 183 | data={data.cacheHitRate || []} |
| 184 | renderer={CacheHitRateChartRenderer} |
| 185 | append={TopCacheMissesRenderer} |
| 186 | appendProps={{ data: data.topCacheMisses || [] }} |
| 187 | /> |
| 188 | </div> |
| 189 | </ReportStickyNav> |
| 190 | <div className="py-8"> |
| 191 | <ObservabilityLink /> |
| 192 | </div> |
| 193 | </ReportPadding> |
| 194 | ) |
| 195 | } |
| 196 | |
| 197 | StorageReport.getLayout = (page) => ( |
| 198 | <DefaultLayout> |
| 199 | <ObservabilityLayout title="Storage">{page}</ObservabilityLayout> |
| 200 | </DefaultLayout> |
| 201 | ) |
| 202 | |
| 203 | export default StorageReport |