import { useQuery } from "@tanstack/react-query"; import { X } from "lucide-react"; import { getSessionDocuments } from "../api/sessions"; import type { SessionDocument, SessionSummary } from "../api/types"; import { Button } from "../components/ui/button"; import { ErrorBoundary } from "../components/ErrorBoundary"; import { SqlViewer } from "../viewers/SqlViewer"; import { SchemaLinkingViewer } from "../viewers/SchemaLinkingViewer"; import { MarkdownView } from "../viewers/MarkdownView"; interface Props { session: SessionSummary; onClose: () => void; onResume: (id: string) => void; desktopSplit?: boolean; hidden?: boolean; } function statusLabel(s: SessionSummary): string { if (s.archived) return "Archived"; if (s.status === "finalized") return "Completed"; return `In progress · ${s.status}`; } type MemoryItem = { status: "approved" | "declined"; subject: string; detail: string; rationale: string; }; const HIDDEN_DECISION_TYPES = new Set([ "concept_clarified", "memory_promoted", "memory_promotion_declined", "memory_rejected", "phase_approved", "phase_auto_approved", "table_approved", "table_promoted", "column_promoted", "cte_approved", "sql_approved", "datamart_requested", "datamart_declined", ]); function DocBody({ doc }: { doc: SessionDocument }) { if (doc.format === "sql") return ; if (doc.format === "markdown") return ; if (doc.format === "schema-linking") { try { const linking = JSON.parse(doc.content); return ; } catch { return
{doc.content}
; } } if (doc.format === "memories") { const raw = JSON.parse(doc.content) as unknown; const items = Array.isArray(raw) ? raw.filter((item): item is MemoryItem => { if (!item || typeof item !== "object") return false; const candidate = item as Partial; return ( (candidate.status === "approved" || candidate.status === "declined") && typeof candidate.subject === "string" && typeof candidate.detail === "string" && typeof candidate.rationale === "string" ); }) : []; if (items.length === 0) { return

No memories recorded.

; } return (
    {items.map((item, index) => (
  • {item.status === "approved" ? "Approved" : "Declined"}
    {item.detail && (
    )} {item.rationale && (
    )}
  • ))}
); } if (doc.format === "decisions") { const items = doc.content .split("\n") .filter(Boolean) .map((line) => { try { return JSON.parse(line) as { type?: string; subject?: string; detail?: string }; } catch { return null; } }) .filter((d): d is { type?: string; subject?: string; detail?: string } => ( d !== null && !HIDDEN_DECISION_TYPES.has(d.type ?? "") )); if (items.length === 0) return

No decisions recorded.

; return (
    {items.map((d, i) => (
  • {(d.type ?? "decision").replace(/_/g, " ")} {d.subject && (
    )} {d.detail && (
    )}
  • ))}
); } return ; } /** Semantic tint for a decision-type chip: approvals/promotions read as progress * (green), exclusions as neutral, everything else carries the brand accent. */ function decisionChipClass(type: string): string { const t = type.toLowerCase(); if (t.includes("approved") || t.includes("promoted")) return "bg-[oklch(var(--success)/0.15)] text-[oklch(0.47_0.12_165)]"; if (t.includes("excluded") || t.includes("rejected") || t.includes("reopened")) return "bg-muted text-muted-foreground"; return "bg-[oklch(var(--primary)/0.1)] text-primary"; } export function SessionDocumentsPanel({ session, onClose, onResume, desktopSplit = false, hidden = false }: Props) { const { data: docs = [], isLoading } = useQuery({ queryKey: ["session-documents", session.id], queryFn: () => getSessionDocuments(session.id), }); const resumable = session.status !== "finalized" && !session.archived; return ( ); }