import { ArrowRightLeft } from "lucide-react"; import { useMemo, useRef, useState } from "react"; import type { ListSnapshotsResponse } from "~/client/api-client"; import { ByteSize } from "~/client/components/bytes-size"; import { Card, CardContent } from "~/client/components/ui/card"; import { Button } from "~/client/components/ui/button"; import { useTimeFormat } from "~/client/lib/datetime"; import { cn } from "~/client/lib/utils"; import { RetentionCategoryBadges } from "~/client/components/retention-category-badges"; export type SnapshotTimelineSortOrder = "asc" | "desc"; export const SNAPSHOT_TIMELINE_SORT_ORDER_COOKIE_NAME = "snapshot_timeline_sort_order"; const SNAPSHOT_TIMELINE_SORT_ORDER_COOKIE_MAX_AGE = 60 * 60 * 24 * 365; const getSortedSnapshots = (snapshots: ListSnapshotsResponse, sortOrder: SnapshotTimelineSortOrder) => { return [...snapshots].sort((snapshotA, snapshotB) => { return sortOrder === "desc" ? snapshotB.time - snapshotA.time : snapshotA.time - snapshotB.time; }); }; const getSnapshotRange = (snapshots: ListSnapshotsResponse) => { if (snapshots.length === 0) { return null; } return snapshots.reduce( (range, snapshot) => ({ oldest: snapshot.time < range.oldest.time ? snapshot : range.oldest, newest: snapshot.time > range.newest.time ? snapshot : range.newest, }), { oldest: snapshots[0], newest: snapshots[0] }, ); }; interface Props { snapshots: ListSnapshotsResponse; snapshotId?: string; loading?: boolean; error?: string; initialSortOrder?: SnapshotTimelineSortOrder; onSnapshotSelect: (snapshotId: string) => void; } export const SnapshotTimeline = (props: Props) => { const { snapshots, snapshotId, loading, onSnapshotSelect, error, initialSortOrder = "asc" } = props; const selectedRef = useRef(null); const { formatDateWithMonth, formatShortDate, formatTime } = useTimeFormat(); const [sortOrder, setSortOrder] = useState(initialSortOrder); const sortedSnapshots = useMemo(() => getSortedSnapshots(snapshots, sortOrder), [snapshots, sortOrder]); const snapshotRange = useMemo(() => getSnapshotRange(snapshots), [snapshots]); const sortOrderButtonLabel = "Toggle snapshot sort order"; const handleToggleSortOrder = () => { setSortOrder((currentSortOrder) => { const nextSortOrder = currentSortOrder === "asc" ? "desc" : "asc"; document.cookie = `${SNAPSHOT_TIMELINE_SORT_ORDER_COOKIE_NAME}=${nextSortOrder}; path=/; max-age=${SNAPSHOT_TIMELINE_SORT_ORDER_COOKIE_MAX_AGE}`; return nextSortOrder; }); }; if (error) { return (

{error}

); } if (loading) { return (

Loading snapshots...

); } if (snapshots.length === 0) { return (

No snapshots available

); } return (
Snapshots
{sortedSnapshots.map((snapshot) => { const date = new Date(snapshot.time); const isSelected = snapshotId === snapshot.short_id; return ( ); })}
{snapshots.length} snapshots {snapshotRange && ( {formatDateWithMonth(snapshotRange.oldest.time)} -  {formatDateWithMonth(snapshotRange.newest.time)} )}
); };