import { Check, ExternalLink, Eye } from 'lucide-react' import { useRouter } from 'next/router' import { useMemo } from 'react' import { Button, Card } from 'ui' import { PageSection, PageSectionAside, PageSectionContent, PageSectionMeta, PageSectionSummary, PageSectionTitle, } from 'ui-patterns/PageSection' import { getEdgeFunctionLogsUrl, getLegacyEdgeFunctionLogsUrl } from './EdgeFunctionOverview.utils' import { getSinceLastDeployInvocationCount, getSinceLastDeployInvocationCountSql, getSinceLastDeployInvocationPhrase, getSinceLastDeployLogRange, } from './EdgeFunctionRecentErrors.utils' import { EdgeFunctionRecentErrorsActions } from './EdgeFunctionRecentErrorsActions' import { useUnifiedLogsPreview } from '@/components/interfaces/App/FeaturePreview/FeaturePreviewContext' import { EmbeddedLogsTable } from '@/components/interfaces/UnifiedLogs/components/EmbeddedLogsTable' import type { ColumnSchema } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.schema' import { getUniqueLogRows, loadUnifiedLogsSearchParams, toQuerySearchParams, } from '@/components/interfaces/UnifiedLogs/UnifiedLogs.utils' import { useUnifiedLogsCountQuery } from '@/data/logs/unified-logs-count-query' import { useUnifiedLogsInfiniteQuery } from '@/data/logs/unified-logs-infinite-query' import { useLogsQuery } from '@/hooks/analytics/useLogsQuery' // Error invocations (5xx) and runtime errors, as the Logs tab's level filter defines them const ERROR_LOG_FILTERS = ['level:eq:error'] // The overview previews the latest errors; the rest are a click away in the Logs tab const MAX_VISIBLE_ERRORS = 5 interface EdgeFunctionRecentErrorsProps { functionId?: string functionSlug?: string projectRef?: string updatedAt?: string | number } export const EdgeFunctionRecentErrors = ({ functionId, functionSlug, projectRef, updatedAt, }: EdgeFunctionRecentErrorsProps) => { const router = useRouter() const { isEnabled: isUnifiedLogsEnabled } = useUnifiedLogsPreview() const { isoTimestampStart, isoTimestampEnd } = useMemo( () => getSinceLastDeployLogRange(updatedAt), [updatedAt] ) // Built from the same URL params the Logs tab reads, so both run the same queries const search = useMemo( () => toQuerySearchParams( { ...loadUnifiedLogsSearchParams(new URLSearchParams()), filter: ERROR_LOG_FILTERS, date: isoTimestampStart && isoTimestampEnd ? [new Date(isoTimestampStart), new Date(isoTimestampEnd)] : null, }, functionId ? { functionId } : undefined ), [functionId, isoTimestampStart, isoTimestampEnd] ) const isQueryEnabled = Boolean(projectRef && functionId && isoTimestampStart) const { data, error, isError, isPending, isFetching } = useUnifiedLogsInfiniteQuery( { projectRef, search }, { enabled: isQueryEnabled } ) const { data: counts } = useUnifiedLogsCountQuery( { projectRef, search }, { enabled: isQueryEnabled } ) const { logData: sinceLastDeployInvocationCountRows, error: sinceLastDeployInvocationCountError, } = useLogsQuery({ projectRef: projectRef!, initialParams: { sql: getSinceLastDeployInvocationCountSql(functionId), iso_timestamp_start: isoTimestampStart, iso_timestamp_end: isoTimestampEnd, }, enabled: isQueryEnabled, options: { useOtel: true }, }) const sinceLastDeployInvocationCount = getSinceLastDeployInvocationCount( sinceLastDeployInvocationCountRows ) const fetchedRows = useMemo(() => getUniqueLogRows(data?.pages), [data?.pages]) const rows = useMemo(() => fetchedRows.slice(0, MAX_VISIBLE_ERRORS), [fetchedRows]) // Falls back to the fetched page until the count query resolves const totalErrorCount = counts?.totalRowCount ?? fetchedRows.length const hiddenErrorCount = Math.max(totalErrorCount - rows.length, 0) // Without unified logs, invocations and runtime output live on separate tabs const getLogsUrl = (row?: ColumnSchema) => { const target = { projectRef: projectRef ?? '', functionSlug: functionSlug ?? '', start: isoTimestampStart, end: isoTimestampEnd, logId: row?.id, } if (isUnifiedLogsEnabled) { return getEdgeFunctionLogsUrl({ ...target, filters: ERROR_LOG_FILTERS }) } const tab = row?.log_type === 'edge function' ? 'invocations' : 'logs' return getLegacyEdgeFunctionLogsUrl({ ...target, tab }) } const handleOpenLogs = () => router.push(getLogsUrl()) const handleOpenLog = (logId: string) => { router.push(getLogsUrl(rows.find((row) => row.id === logId))) } const isLoading = !functionId || (isQueryEnabled && isPending) const hasNoErrors = !isLoading && !isError && rows.length === 0 const hasInvocationCount = !!isoTimestampStart && !sinceLastDeployInvocationCountError return ( Errors in the last 24h {hasNoErrors && (
{hasInvocationCount && sinceLastDeployInvocationCount > 0 && (
)} {!hasNoErrors && ( 0 && ( // Sized to its label so the Button's press-scale stays centered on the text
) } />
)}
) }