diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/RefineModal/steps/2_SelectEvaluationResults/SelectableEvaluationResultsTable.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/RefineModal/steps/2_SelectEvaluationResults/SelectableEvaluationResultsTable.tsx index 4afc8a371..16bd6c53b 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/RefineModal/steps/2_SelectEvaluationResults/SelectableEvaluationResultsTable.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/_components/DocumentEditor/Editor/RefineModal/steps/2_SelectEvaluationResults/SelectableEvaluationResultsTable.tsx @@ -18,8 +18,8 @@ import { TableRow, Text, } from '@latitude-data/web-ui' -import { relativeTime } from '$/app/_lib/formatUtils' import { LogicTablePaginationFooterWithoutCount } from '$/components/TablePaginationFooter/TablePaginationFooterWithoutCount' +import { relativeTime } from '$/lib/relativeTime' import { EvaluationResultByDocument } from '$/stores/evaluationResultsByDocumentContent' export const ResultCellContent = ({ diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultsTable.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultsTable.tsx index e8905ee8c..5f510812a 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultsTable.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultsTable.tsx @@ -22,10 +22,11 @@ import { useCurrentCommit, useCurrentProject, } from '@latitude-data/web-ui' -import { formatCostInMillicents, relativeTime } from '$/app/_lib/formatUtils' +import { formatCostInMillicents } from '$/app/_lib/formatUtils' import { getRunErrorFromErrorable } from '$/app/(private)/_lib/getRunErrorFromErrorable' import { useCurrentDocument } from '$/app/providers/DocumentProvider' import { LinkableTablePaginationFooter } from '$/components/TablePaginationFooter' +import { relativeTime } from '$/lib/relativeTime' import { ROUTES } from '$/services/routes' import useEvaluationResultsPagination from '$/stores/useEvaluationResultsCount' import { useSearchParams } from 'next/navigation' diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx index eaa47d8c4..1df00dfcc 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogsTable.tsx @@ -16,14 +16,11 @@ import { useCurrentCommit, useCurrentProject, } from '@latitude-data/web-ui' -import { - formatCostInMillicents, - formatDuration, - relativeTime, -} from '$/app/_lib/formatUtils' +import { formatCostInMillicents, formatDuration } from '$/app/_lib/formatUtils' import { getRunErrorFromErrorable } from '$/app/(private)/_lib/getRunErrorFromErrorable' import { useCurrentDocument } from '$/app/providers/DocumentProvider' import { LinkableTablePaginationFooter } from '$/components/TablePaginationFooter' +import { relativeTime } from '$/lib/relativeTime' import { ROUTES } from '$/services/routes' import useDocumentLogsPagination from '$/stores/useDocumentLogsPagination' import { useSearchParams } from 'next/navigation' diff --git a/apps/web/src/app/_lib/formatUtils.ts b/apps/web/src/app/_lib/formatUtils.ts index d14ad8686..7f841680c 100644 --- a/apps/web/src/app/_lib/formatUtils.ts +++ b/apps/web/src/app/_lib/formatUtils.ts @@ -1,19 +1,6 @@ -import { format, formatDistanceToNow, formatRelative } from 'date-fns' - const SECONDS = 1000 // ms const MINUTES = 60 * SECONDS const HOURS = MINUTES * 60 -const DAYS = HOURS * 24 -export function relativeTime(date: Date | null) { - if (date == null) return 'never' - if (!(date instanceof Date)) return '-' // NOTE: This is a dummy defense to avoid crashing on the frontend - - const now = new Date() - const diff = now.getTime() - date.getTime() - if (diff < 1 * HOURS) return formatDistanceToNow(date, { addSuffix: true }) - if (diff < 7 * DAYS) return formatRelative(date, new Date()) - return format(date, 'PPpp') -} export function formatDuration(duration?: number | null) { if (!duration) return '-' diff --git a/apps/web/src/lib/relativeTime.test.ts b/apps/web/src/lib/relativeTime.test.ts new file mode 100644 index 000000000..b8d50233e --- /dev/null +++ b/apps/web/src/lib/relativeTime.test.ts @@ -0,0 +1,52 @@ +import { beforeEach, describe, expect, it, vi } from 'vitest' + +import { relativeTime } from './relativeTime' + +describe('relativeTime', () => { + const SECONDS = 1000 + const MINUTES = 60 * SECONDS + const HOURS = 60 * MINUTES + const DAYS = 24 * HOURS + const YEARS = 365 * DAYS + const NOW = new Date(2000, 6, 31, 12, 0, 0) + + beforeEach(() => { + vi.useFakeTimers() + vi.setSystemTime(NOW) + }) + + it('returns formatted relative time', () => { + expect(relativeTime(new Date(NOW.getTime() - 3 * SECONDS))).toBe( + 'Less than a minute ago', + ) + expect(relativeTime(new Date(NOW.getTime() - 30 * SECONDS))).toBe( + '1 minute ago', + ) + expect(relativeTime(new Date(NOW.getTime() - 30 * MINUTES))).toBe( + '30 minutes ago', + ) + expect(relativeTime(new Date(NOW.getTime() - 59 * MINUTES))).toBe( + 'About 1 hour ago', + ) + expect(relativeTime(new Date(NOW.getTime() - 2 * HOURS))).toBe( + 'Today at 10:00 AM', + ) + expect(relativeTime(new Date(NOW.getTime() - 3 * DAYS))).toBe( + 'Last Friday at 12:00 PM', + ) + expect( + relativeTime(new Date(NOW.getTime() - (6 * DAYS + 12 * HOURS))), + ).toBe('Jul 25, 2000, 12:00:00 AM') + expect(relativeTime(new Date(NOW.getTime() - 7 * DAYS))).toBe( + 'Jul 24, 2000, 12:00:00 PM', + ) + expect(relativeTime(new Date(NOW.getTime() - 1 * YEARS))).toBe( + 'Aug 1, 1999, 12:00:00 PM', + ) + }) + + it('returns "-" when no date is provided', () => { + expect(relativeTime(null)).toBe('-') + expect(relativeTime(undefined)).toBe('-') + }) +}) diff --git a/apps/web/src/lib/relativeTime.ts b/apps/web/src/lib/relativeTime.ts index 2aba064a8..b0e9bd14e 100644 --- a/apps/web/src/lib/relativeTime.ts +++ b/apps/web/src/lib/relativeTime.ts @@ -1,19 +1,29 @@ -import { format, formatDistanceToNow, formatRelative } from 'date-fns' +import { + differenceInDays, + differenceInHours, + format, + formatDistanceToNow, + formatRelative, +} from 'date-fns' -const HOURS = 1000 * 60 * 60 -const DAYS = HOURS * 24 +function capitalize(str: string) { + return str.replace(/^\w/, (c) => c.toUpperCase()) +} -export function relativeTime(date?: Date | null) { - if (date == null) return '-' +export function relativeTime(date: Date | null | undefined) { + if (!date) return '-' + // NOTE: This is a dummy defense to avoid crashing on the frontend + if (!(date instanceof Date)) return '-' const now = new Date() - const diff = now.getTime() - date.getTime() - const capitalize = (str: string) => str.replace(/^\w/, (c) => c.toUpperCase()) - if (diff < 1 * HOURS) { + if (differenceInHours(now, date) < 1) { return capitalize(formatDistanceToNow(date, { addSuffix: true })) } - if (diff < 7 * DAYS) return capitalize(formatRelative(date, new Date())) + + if (differenceInDays(now, date) < 6) { + return capitalize(formatRelative(date, now)) + } return capitalize(format(date, 'PPpp')) }