import { useParams } from 'common' import dayjs from 'dayjs' import Link from 'next/link' import { useRouter } from 'next/router' import { useEffect, useMemo, useState } from 'react' import { Card, CardContent, CardHeader, CardTitle, Loading, WarningIcon } from 'ui' import { ChartEmptyState } from 'ui-patterns/Chart' import { LogsBarChart } from 'ui-patterns/LogsBarChart' import { Row } from 'ui-patterns/Row' import { useUnifiedLogsPreview } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { buildUnifiedLogsUrl, type UnifiedLogType, } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.utils' import NoDataPlaceholder from '@/components/ui/Charts/NoDataPlaceholder' import { ChartIntervalDropdown } from '@/components/ui/Logs/ChartIntervalDropdown' import { CHART_INTERVALS } from '@/components/ui/Logs/logs.utils' import { UsageApiCounts, useProjectLogStatsQuery } from '@/data/analytics/project-log-stats-query' import { fillTimeseriesSorted } from '@/hooks/analytics/useFillTimeseriesSorted' import { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { useTrack } from '@/lib/telemetry/track' type LogsBarChartDatum = { timestamp: string error_count: number ok_count: number warning_count: number } type ChartIntervalKey = '1hr' | '1day' | '7day' type ServiceKey = 'db' | 'auth' | 'storage' | 'realtime' type ServiceEntry = { key: ServiceKey title: string href?: string route: string logType: UnifiedLogType enabled: boolean } type ServiceComputed = ServiceEntry & { data: LogsBarChartDatum[] total: number isLoading: boolean error: unknown | null } export const ProjectUsageSection = () => { const router = useRouter() const { ref: projectRef } = useParams() const { data: organization } = useSelectedOrganizationQuery() const track = useTrack() const { projectAuthAll: authEnabled, projectStorageAll: storageEnabled } = useIsFeatureEnabled([ 'project_auth:all', 'project_storage:all', ]) const { getEntitlementMax } = useCheckEntitlements('log.retention_days') const retentionDays = getEntitlementMax() const { isEnabled: isUnifiedLogsEnabled } = useUnifiedLogsPreview() const DEFAULT_INTERVAL: ChartIntervalKey = retentionDays !== undefined && retentionDays < 7 ? '1hr' : '1day' const [interval, setInterval] = useState(DEFAULT_INTERVAL) useEffect(() => { setInterval(retentionDays !== undefined && retentionDays < 7 ? '1hr' : '1day') }, [retentionDays]) const selectedInterval = CHART_INTERVALS.find((i) => i.key === interval) || CHART_INTERVALS[1] const { datetimeFormat } = useMemo(() => { const format = selectedInterval.format || 'MMM D, ha' return { datetimeFormat: format } }, [selectedInterval]) // Use V1 data fetching const { data: filledCharts, isPending: isLoading, error, } = useProjectLogStatsQuery( { projectRef, interval, }, { select: (data) => { // Calculate date range for gap filling const startDateLocal = dayjs().subtract( selectedInterval.startValue, selectedInterval.startUnit as dayjs.ManipulateType ) const endDateLocal = dayjs() return fillTimeseriesSorted({ data: data.result, timestampKey: 'timestamp', valueKey: [ 'total_auth_requests', 'total_rest_requests', 'total_storage_requests', 'total_realtime_requests', ], defaultValue: 0, startDate: startDateLocal.toISOString(), endDate: endDateLocal.toISOString(), minPointsToFill: 5, }) }, refetchOnWindowFocus: false, } ) const serviceBase: ServiceEntry[] = useMemo( () => [ { key: 'db', title: 'Database requests', href: `/project/${projectRef}/editor`, route: '/logs/postgres-logs', logType: 'postgres', enabled: true, }, { key: 'auth', title: 'Auth requests', href: `/project/${projectRef}/auth/users`, route: '/logs/auth-logs', logType: 'auth', enabled: authEnabled, }, { key: 'storage', title: 'Storage requests', href: `/project/${projectRef}/storage/buckets`, route: '/logs/storage-logs', logType: 'storage', enabled: storageEnabled, }, { key: 'realtime', title: 'Realtime requests', href: `/project/${projectRef}/realtime/inspector`, route: '/logs/realtime-logs', logType: 'realtime', enabled: true, }, ], [projectRef, authEnabled, storageEnabled] ) const services: ServiceComputed[] = useMemo( () => serviceBase.map((s) => { // Map service keys to V1 data field names const dataKeyMap: Record = { db: 'total_rest_requests', auth: 'total_auth_requests', storage: 'total_storage_requests', realtime: 'total_realtime_requests', } const dataKey = dataKeyMap[s.key] // Transform V1 data to LogsBarChart format // Since V1 doesn't have error/warning breakdown, we show everything as "ok" const transformedData: LogsBarChartDatum[] = (filledCharts?.data || []).map((item) => ({ timestamp: item.timestamp, error_count: 0, warning_count: 0, ok_count: Number(item[dataKey]) || 0, })) // Calculate total from filled data const total = transformedData.reduce((sum, item) => sum + item.ok_count, 0) return { ...s, data: transformedData, total, isLoading, error: error || filledCharts?.error || null, } }), [serviceBase, filledCharts, isLoading, error] ) const handleBarClick = (service: ServiceEntry) => (datum: LogsBarChartDatum) => { if (!datum?.timestamp) return const datumTimestamp = dayjs(datum.timestamp).toISOString() const start = dayjs(datumTimestamp).subtract(1, 'minute').toISOString() const end = dayjs(datumTimestamp).add(1, 'minute').toISOString() if (isUnifiedLogsEnabled) { router.push( buildUnifiedLogsUrl({ projectRef: projectRef!, logType: service.logType, start, end }) ) } else { const queryParams = new URLSearchParams({ iso_timestamp_start: start, iso_timestamp_end: end, }) router.push(`/project/${projectRef}${service.route}?${queryParams.toString()}`) } track('home_project_usage_chart_clicked', { service_type: service.key, bar_timestamp: datum.timestamp, }) } const enabledServices = services.filter((s) => s.enabled) const totalRequests = enabledServices.reduce((sum, s) => sum + (s.total || 0), 0) return (
{totalRequests.toLocaleString()} Total Requests
setInterval(interval as ChartIntervalKey)} organizationSlug={organization?.slug} dropdownAlign="end" tooltipSide="left" />
{enabledServices.map((s) => (
{s.href ? ( { track('home_project_usage_service_clicked', { service_type: s.key, total_requests: s.total || 0, }) }} > {s.title} ) : ( s.title )} {(s.total || 0).toLocaleString()}
} title="Failed to load project usage" description="Check our Status Page or try again later." /> } EmptyState={ } />
))}
) }