import { useQuery } from '@tanstack/react-query' import { useFlag, useParams } from 'common' import dayjs from 'dayjs' import { BarChart2, ChevronRight, ExternalLink, Telescope } from 'lucide-react' import Link from 'next/link' import { useRouter } from 'next/router' import { AiIconAnimation, Button, Tooltip, TooltipContent, TooltipTrigger } from 'ui' import { Chart, ChartActions, ChartCard, ChartContent, ChartEmptyState, ChartHeader, ChartLoadingState, ChartMetric, ChartTitle, } from 'ui-patterns/Chart' import { PageSection, PageSectionContent, PageSectionMeta, PageSectionSummary, PageSectionTitle, } from 'ui-patterns/PageSection' import { StatusCode } from 'ui-patterns/StatusCode' import { AuthErrorCodeRow, fetchTopAuthErrorCodes, fetchTopResponseErrors, parseAuthErrorCodes, parseResponseErrors, ResponseErrorRow, } from './OverviewErrors.constants' import { formatMetricChange, formatMetricValue } from './OverviewMetrics.utils' import { OverviewTable } from './OverviewTable' import { AuthMetricsResponse, calculatePercentageChange, calculatePercentagePointChange, getApiSuccessRates, getAuthSuccessRates, getMetricValues, } from './OverviewUsage.constants' import { SIDEBAR_KEYS } from '@/components/layouts/ProjectLayout/LayoutSidebar/LayoutSidebarProvider' import { AlertError } from '@/components/ui/AlertError' import { ErrorCodeTooltip } from '@/components/ui/ErrorCodeTooltip/ErrorCodeTooltip' import { Service } from '@/data/graphql/graphql' import { useAiAssistantStateSnapshot } from '@/state/ai-assistant-state' import { useSidebarManagerSnapshot } from '@/state/sidebar-manager-state' const StatCard = ({ title, current, change, loading, suffix = '', href, tooltip, }: { title: string current: number | null change: number | null loading: boolean suffix?: string href?: string tooltip?: string }) => { const router = useRouter() const actions = [ { label: 'Go to Auth Report', icon: , onClick: href ? () => router.push(href) : undefined, }, ] return ( ) } const LogsLink = ({ href }: { href: string }) => ( Go to Logs ) interface OverviewMetricsProps { metrics?: AuthMetricsResponse isLoading: boolean error: unknown } export const OverviewMetrics = ({ metrics, isLoading, error }: OverviewMetricsProps) => { const { ref } = useParams() const useOtel = useFlag('otelLegacyLogs') const endDate = dayjs().toISOString() const startDate = dayjs().subtract(24, 'hour').toISOString() const aiSnap = useAiAssistantStateSnapshot() const { openSidebar } = useSidebarManagerSnapshot() const { current: activeUsersCurrent, previous: activeUsersPrevious } = getMetricValues( metrics, 'activeUsers' ) const { current: signUpsCurrent, previous: signUpsPrevious } = getMetricValues( metrics, 'signUpCount' ) const activeUsersChange = calculatePercentageChange(activeUsersCurrent, activeUsersPrevious) const signUpsChange = calculatePercentageChange(signUpsCurrent, signUpsPrevious) const { current: apiSuccessRateCurrent, previous: apiSuccessRatePrevious } = getApiSuccessRates(metrics) const { current: authSuccessRateCurrent, previous: authSuccessRatePrevious } = getAuthSuccessRates(metrics) const apiSuccessRateChange = calculatePercentagePointChange( apiSuccessRateCurrent, apiSuccessRatePrevious ) const authSuccessRateChange = calculatePercentagePointChange( authSuccessRateCurrent, authSuccessRatePrevious ) const { data: respErrData, isPending: isLoadingResp, isError: isResponseError, error: responseError, } = useQuery({ queryKey: ['auth-overview', ref, 'top-response-errors', { otel: useOtel }], queryFn: () => fetchTopResponseErrors(ref as string, useOtel), enabled: !!ref, }) const { data: codeErrData, isPending: isLoadingCodes, isError: isCodeError, error: codeError, } = useQuery({ queryKey: ['auth-overview', ref, 'top-auth-error-codes', { otel: useOtel }], queryFn: () => fetchTopAuthErrorCodes(ref as string, useOtel), enabled: !!ref, }) const responseErrors: ResponseErrorRow[] = Array.isArray(respErrData?.result) ? parseResponseErrors(respErrData.result) : [] const errorCodes: AuthErrorCodeRow[] = Array.isArray(codeErrData?.result) ? parseAuthErrorCodes(codeErrData.result) : [] const errorCodesActions = [ { label: 'Ask Assistant about Error Codes', icon: , onClick: () => { openSidebar(SIDEBAR_KEYS.AI_ASSISTANT) aiSnap.newChat({ name: 'Auth Help', initialInput: `Can you explain to me what the authentication error codes mean?`, }) }, }, ] return ( <> {!!error && ( )}
Usage Go to observability
Monitoring
Auth API Errors
} emptyState={
} title="No data to show" description="It may take up to 24 hours for data to refresh" />
} loadingState={
} > isLoading={isLoadingResp} data={responseErrors} columns={[ { key: 'request', header: 'Request', className: 'w-auto pr-0!', render: (row) => { return }, }, { key: 'path', header: 'Path', className: 'w-full', render: (row) => ( {row.path} ), }, { key: 'count', header: 'Count', className: 'text-right shrink-0 ml-auto justify-end', render: (row) => (
{row.count}
), }, ]} /> Auth Server Errors } emptyState={
} title="No data to show" description="It may take up to 24 hours for data to refresh" />
} loadingState={
} > isLoading={isLoadingCodes} data={errorCodes} columns={[ { key: 'error_code', header: 'Error code', className: 'w-full', render: (row) => ( {row.error_code} ), }, { key: 'count', header: 'Count', className: 'text-right', render: (row) => (
{row.count}
), }, ]} />
) }