/** * @file SnapshotSettings.tsx * @description Settings → Snapshots: every frozen session (`GET * /api/snapshots`), each with Open, Copy link and a confirmed Delete — the * buttons and the same actions on a right-click menu. Snapshots survive Clear * Data, so this is the one place they go away. * @author Nguyễn Ngọc Trí Vĩ */ import { useCallback, useEffect, useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useTranslation } from "react-i18next"; import { ExternalLink, ShieldCheck, Trash2 } from "lucide-react"; import { api } from "../../lib/api"; import { formatDateTime } from "../../lib/format"; import type { SessionSnapshotInfo } from "../../lib/types"; import { ConfirmModal } from "../ConfirmModal"; import { ContextMenu, MenuItem, MenuSeparator, useContextMenu } from "../ContextMenu"; import { CopyButton } from "../event-views/primitives"; import { snapshotUrl } from "../SnapshotButton"; import { SettingsSection } from "./SettingsRow"; export function SnapshotSettings() { const { t } = useTranslation("settings"); const [items, setItems] = useState(null); const [error, setError] = useState(null); const [deleting, setDeleting] = useState(null); const [busy, setBusy] = useState(false); const ctx = useContextMenu(); const navigate = useNavigate(); const load = useCallback(() => { api.snapshots .list() .then((r) => setItems(r.snapshots)) .catch((err) => setError(err instanceof Error ? err.message : String(err))); }, []); useEffect(load, [load]); const remove = async () => { if (!deleting) return; setBusy(true); try { await api.snapshots.remove(deleting.id); setItems((xs) => xs?.filter((x) => x.id !== deleting.id) ?? null); } catch (err) { setError(err instanceof Error ? err.message : String(err)); } finally { setBusy(false); setDeleting(null); } }; const pick = (fn: (s: SessionSnapshotInfo) => void) => () => { const s = ctx.menu?.item; ctx.close(); if (s) fn(s); }; return ( {error && (

{error}

)} {items?.length === 0 &&

{t("snapshots.empty")}

} {items && items.length > 0 && (
    {items.map((s) => (
  • ctx.open(e, s)} className="flex items-center gap-3 py-2.5" >

    {s.session_name || s.session_id.slice(0, 8)}

    {formatDateTime(s.created_at)} · {t("snapshots.events", { n: s.event_count ?? 0 })} {s.redacted && ( {t("snapshots.redacted")} )}

  • ))}
)} {ctx.menu && ( navigate(`/snapshot/${s.token}`))}> {t("snapshots.open")} void navigator.clipboard?.writeText(snapshotUrl(s.token)))} > {t("snapshots.copyLink")} {t("snapshots.delete")} )} void remove()} onCancel={() => setDeleting(null)} />
); }