import { useQuery } from '@tanstack/react-query' import dayjs from 'dayjs' import { ChevronRight, ExternalLink, HelpCircle, Telescope } from 'lucide-react' import Link from 'next/link' import { useRouter } from 'next/router' import { useParams } from 'common' import { getStatusLevel } from 'components/interfaces/UnifiedLogs/UnifiedLogs.utils' import AlertError from 'components/ui/AlertError' import { ButtonTooltip } from 'components/ui/ButtonTooltip' import { DataTableColumnStatusCode } from 'components/ui/DataTable/DataTableColumn/DataTableColumnStatusCode' import { Card, CardContent, CardHeader, CardTitle, cn, Skeleton, Tooltip, TooltipContent, TooltipTrigger, } from 'ui' import { PageSection, PageSectionContent, PageSectionMeta, PageSectionSummary, PageSectionTitle, } from 'ui-patterns/PageSection' import { AuthErrorCodeRow, fetchTopAuthErrorCodes, fetchTopResponseErrors, ResponseErrorRow, } from './OverviewErrors.constants' import { OverviewTable } from './OverviewTable' import { AuthMetricsResponse, calculatePercentageChange, getApiSuccessRates, getAuthSuccessRates, getChangeColor, getMetricValues, } from './OverviewUsage.constants' const StatCard = ({ title, current, previous, loading, suffix = '', invert = false, href, tooltip, }: { title: string current: number previous: number loading: boolean suffix?: string invert?: boolean href?: string tooltip?: string }) => { const router = useRouter() const isZeroChange = previous === 0 const changeColor = isZeroChange ? 'text-foreground-lighter' : invert ? previous >= 0 ? 'text-destructive' : 'text-brand' : getChangeColor(previous) const formattedCurrent = suffix === 'ms' ? current.toFixed(2) : suffix === '%' ? current.toFixed(1) : Math.round(current).toLocaleString() const signChar = previous > 0 ? '+' : previous < 0 ? '-' : '' return ( {title} {tooltip && (

{tooltip}

)}
} className="w-6 h-6 absolute right-4 top-3" onClick={() => router.push(href || '')} tooltip={{ content: { side: 'top', text: 'Go to Auth Report', }, }} />
{loading ? (
) : (

{`${formattedCurrent}${suffix}`}

{`${signChar}${Math.abs(previous).toFixed(1)}%`}
)}
) } const LogsLink = ({ href }: { href: string }) => ( Go to logs ) function isResponseErrorRow(row: unknown): row is ResponseErrorRow { if (!row || typeof row !== 'object') return false const r = row as Record return ( typeof r.method === 'string' && typeof r.path === 'string' && typeof r.status_code === 'number' && typeof r.count === 'number' ) } function isAuthErrorCodeRow(row: unknown): row is AuthErrorCodeRow { if (!row || typeof row !== 'object') return false const r = row as Record return typeof r.error_code === 'string' && typeof r.count === 'number' } interface OverviewMetricsProps { metrics?: AuthMetricsResponse isLoading: boolean error: unknown } export const OverviewMetrics = ({ metrics, isLoading, error }: OverviewMetricsProps) => { const { ref } = useParams() const endDate = dayjs().toISOString() const startDate = dayjs().subtract(24, 'hour').toISOString() 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 = calculatePercentageChange( apiSuccessRateCurrent, apiSuccessRatePrevious ) const authSuccessRateChange = calculatePercentageChange( authSuccessRateCurrent, authSuccessRatePrevious ) const { data: respErrData, isPending: isLoadingResp } = useQuery({ queryKey: ['auth-overview', ref, 'top-response-errors'], queryFn: () => fetchTopResponseErrors(ref as string), enabled: !!ref, }) const { data: codeErrData, isPending: isLoadingCodes } = useQuery({ queryKey: ['auth-overview', ref, 'top-auth-error-codes'], queryFn: () => fetchTopAuthErrorCodes(ref as string), enabled: !!ref, }) const responseErrors: ResponseErrorRow[] = Array.isArray(respErrData?.result) ? (respErrData?.result as unknown[]).filter(isResponseErrorRow) : [] const errorCodes: AuthErrorCodeRow[] = Array.isArray(codeErrData?.result) ? (codeErrData?.result as unknown[]).filter(isAuthErrorCodeRow) : [] return ( <> {!!error && ( )}
Usage Go to observability
Monitoring
0 ? 'pb-4' : 'pb-0')}> Auth API Errors isLoading={isLoadingResp} data={responseErrors} columns={[ { key: 'request', header: 'Request', className: 'w-[60px]', render: (row) => ( {row.method} ), }, { key: 'status_code', header: 'Status', className: 'w-[60px]', render: (row) => ( ), }, { key: 'path', header: 'Path', className: 'flex-shrink-0 w-52', render: (row) => (
{row.path}
), }, { key: 'count', header: 'Count', className: 'text-right flex-shrink-0 ml-auto justify-end', render: (row) => (
{row.count}
), }, { key: 'actions', header: '', className: 'w-6', render: (row) => (
), }, ]} />
0 ? 'pb-4' : 'pb-0')}> Auth Server Errors 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}
), }, { key: 'actions', header: '', className: 'text-right', render: (row) => (
), }, ]} />
) }