'use client'; import { useEffect, useMemo, useState } from 'react'; import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; import { AlertTriangle, Anchor, CheckCircle2, FileText, Loader2, Lock, Ship, Wrench, } from 'lucide-react'; import { toast } from 'sonner'; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Checkbox } from '@/components/ui/checkbox'; import { apiFetch } from '@/lib/api/client'; interface RestoreReversal { id: string; kind: string; refId: string; label: string; reason: string; } interface LockedReversal extends RestoreReversal { lockReason: string; } interface RestoreDossier { client: { id: string; fullName: string; portId: string }; autoReversible: RestoreReversal[]; reversibleWithPrompt: RestoreReversal[]; locked: LockedReversal[]; } interface Props { open: boolean; onOpenChange: (next: boolean) => void; clientId: string; clientName: string; onSuccess?: () => void; } function iconFor(kind: string) { if (kind.startsWith('berth_')) return ; if (kind.startsWith('yacht_')) return ; if (kind.startsWith('documenso_')) return ; return ; } export function SmartRestoreDialog({ open, onOpenChange, clientId, clientName, onSuccess }: Props) { const qc = useQueryClient(); const dossierQuery = useQuery({ queryKey: ['client-restore-dossier', clientId], queryFn: () => apiFetch<{ data: RestoreDossier }>(`/api/v1/clients/${clientId}/restore-dossier`), enabled: open, }); const dossier = dossierQuery.data?.data; const [selected, setSelected] = useState>({}); useEffect(() => { if (!open || !dossier) return; setSelected({}); }, [open, dossier]); const restoreMutation = useMutation({ mutationFn: () => { const applyReversals = Object.entries(selected) .filter(([, v]) => v) .map(([k]) => k); return apiFetch<{ data: { autoReversed: number; promptedReversed: number; lockedSkipped: number }; }>(`/api/v1/clients/${clientId}/restore`, { method: 'POST', body: { applyReversals }, }); }, onSuccess: (res) => { const d = res.data; const parts: string[] = []; if (d.autoReversed > 0) parts.push(`${d.autoReversed} auto-reversed`); if (d.promptedReversed > 0) parts.push(`${d.promptedReversed} re-applied`); if (d.lockedSkipped > 0) parts.push(`${d.lockedSkipped} locked`); toast.success(`${clientName} restored${parts.length > 0 ? ` (${parts.join(', ')})` : ''}.`); qc.invalidateQueries({ queryKey: ['clients'] }); qc.invalidateQueries({ queryKey: ['clients', clientId] }); qc.invalidateQueries({ queryKey: ['berths'] }); qc.invalidateQueries({ queryKey: ['interests'] }); onOpenChange(false); onSuccess?.(); }, onError: (err: unknown) => { toast.error(err instanceof Error ? err.message : 'Restore failed'); }, }); const nothingToShow = useMemo( () => !!dossier && dossier.autoReversible.length === 0 && dossier.reversibleWithPrompt.length === 0 && dossier.locked.length === 0, [dossier], ); return ( Restore {clientName} Review what was changed at archive time and decide which changes to undo. The client record itself will always be un-archived. {dossierQuery.isLoading ? (
Loading restore dossier…
) : dossierQuery.error || !dossier ? (
Failed to load dossier:{' '} {dossierQuery.error instanceof Error ? dossierQuery.error.message : 'unknown error'}
) : nothingToShow ? (
No tracked archive changes to review. The client will simply be un-archived.
) : (
{dossier.autoReversible.length > 0 && ( Auto-reversed ( {dossier.autoReversible.length}) {dossier.autoReversible.map((r) => (
{iconFor(r.kind)} {r.label} — {r.reason}
))}
)} {dossier.reversibleWithPrompt.length > 0 && ( Opt-in to undo ( {dossier.reversibleWithPrompt.length}) {dossier.reversibleWithPrompt.map((r) => ( ))} )} {dossier.locked.length > 0 && ( Cannot be undone ({dossier.locked.length}) {dossier.locked.map((r) => (
{iconFor(r.kind)} {r.label} — {r.reason}.{' '} {r.lockReason}
))}
)}
)}
); }