import { useParams } from 'common' import Link from 'next/link' import { useRouter } from 'next/router' import { useMemo, useState } from 'react' import { Card, CardContent, CardHeader, CardTitle, cn, Loading } from 'ui' import { LogsBarChart } from 'ui-patterns/LogsBarChart' import { Row } from 'ui-patterns/Row' import { buildSortedServiceCards, computeSuccessAndNonSuccessRates, getBucketLogRange, isServiceDisabled, type ChartIntervalKey, type LogsBarChartDatum, } from './ProjectUsage.metrics' import { useUnifiedLogsPreview } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { useServiceHealthMetrics } from '@/components/interfaces/Observability/useServiceHealthMetrics' 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 { useCheckEntitlements } from '@/hooks/misc/useCheckEntitlements' import { useIsDataApiEnabled } from '@/hooks/misc/useIsDataApiEnabled' import { useIsFeatureEnabled } from '@/hooks/misc/useIsFeatureEnabled' import { useSelectedOrganizationQuery } from '@/hooks/misc/useSelectedOrganization' import { useTrack } from '@/lib/telemetry/track' // Services the homepage shows; matches the telemetry event types. type HomeServiceKey = 'db' | 'functions' | 'auth' | 'storage' | 'realtime' | 'data_api' const isChartIntervalKey = (value: string): value is ChartIntervalKey => value === '1hr' || value === '1day' || value === '7day' type ServiceEntry = { key: HomeServiceKey title: string href?: string route: string logType: UnifiedLogType enabled: boolean } export const ProjectUsageSectionDeltas = () => { 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 { isEnabled: dataApiEnabled } = useIsDataApiEnabled({ projectRef }) const { isEnabled: isUnifiedLogsEnabled } = useUnifiedLogsPreview() const { getEntitlementMax } = useCheckEntitlements('log.retention_days') const retentionDays = getEntitlementMax() const [userInterval, setUserInterval] = useState(undefined) const interval: ChartIntervalKey = userInterval ?? (retentionDays !== undefined && retentionDays < 7 ? '1hr' : '1day') const selectedInterval = CHART_INTERVALS.find((i) => i.key === interval) || CHART_INTERVALS[1] const datetimeFormat = selectedInterval.format || 'MMM D, ha' const { services: serviceData, isLoading } = useServiceHealthMetrics( projectRef ?? '', interval, 0 ) const serviceBase: ServiceEntry[] = useMemo( () => [ { key: 'db', title: 'Postgres', href: `/project/${projectRef}/editor`, route: '/logs/postgres-logs', logType: 'postgres', enabled: true, }, { key: 'functions', title: 'Edge Functions', href: `/project/${projectRef}/functions`, route: '/logs/edge-functions-logs', logType: 'edge function', enabled: true, }, { key: 'auth', title: 'Auth', href: `/project/${projectRef}/auth/users`, route: '/logs/auth-logs', logType: 'auth', enabled: authEnabled, }, { key: 'storage', title: 'Storage', href: `/project/${projectRef}/storage/buckets`, route: '/logs/storage-logs', logType: 'storage', enabled: storageEnabled, }, { key: 'realtime', title: 'Realtime', href: `/project/${projectRef}/realtime/inspector`, route: '/logs/realtime-logs', logType: 'realtime', enabled: true, }, { key: 'data_api', title: 'API Gateway', href: `/project/${projectRef}/integrations/data_api/overview`, route: '/logs/edge-logs', logType: 'edge', enabled: dataApiEnabled, }, ], [projectRef, authEnabled, storageEnabled, dataApiEnabled] ) const services = useMemo( () => buildSortedServiceCards(serviceBase, serviceData), [serviceBase, serviceData] ) const totalRequests = services.reduce((sum, s) => sum + s.total, 0) const totalErrors = services.reduce((sum, s) => sum + s.err, 0) const totalWarnings = services.reduce((sum, s) => sum + s.warn, 0) const { successRate } = computeSuccessAndNonSuccessRates( totalRequests, totalWarnings, totalErrors ) const handleBarClick = (service: ServiceEntry) => (datum: LogsBarChartDatum) => { if (!datum?.timestamp) return const { start, end } = getBucketLogRange(datum.timestamp, interval) if (isUnifiedLogsEnabled) { router.push( buildUnifiedLogsUrl({ projectRef: projectRef!, logType: service.logType, start, end }) ) } else { // Logs explorer reads the range from `its`/`ite` (iso_timestamp_start/end // only set the label). const queryParams = new URLSearchParams({ its: start, ite: end }) router.push(`/project/${projectRef}${service.route}?${queryParams.toString()}`) } track('home_project_usage_chart_clicked', { service_type: service.key, bar_timestamp: datum.timestamp, }) } return (
{totalRequests.toLocaleString()} Total Requests
{successRate.toFixed(1)}% Success Rate
{ if (isChartIntervalKey(next)) setUserInterval(next) }} organizationSlug={organization?.slug} dropdownAlign="end" tooltipSide="left" />
{services.map((s) => { const disabled = isServiceDisabled(s.total, isLoading) return (
{s.href && !disabled ? ( { track('home_project_usage_service_clicked', { service_type: s.key, total_requests: s.total, error_count: s.err, }) }} > {s.title} ) : ( s.title )} {s.total.toLocaleString()}
Warnings
{s.warn.toLocaleString()}
Errors
{s.err.toLocaleString()}
) : ( ) } /> ) })}
) }