import { useState } from "react"; import { Link } from "react-router"; import type { LedgerViewRow } from "../../../ledger/view"; import type { Decision, ProjectDetailView, SnoozableIsc } from "../../detail"; import { Badge } from "../components/badge"; import { Button } from "../components/button"; import { Separator } from "../components/separator"; import { clock } from "../format"; import { Empty, Panel, Pending } from "../frame"; import { useLoaded } from "../lib/api"; import { cn } from "../lib/cn"; import { setIsc, setSnooze } from "../lib/write"; const ISC_MARK: Record = { open: "", done: "✓", retired: "~", }; const SNOOZE_DAYS = 7; function outcomeVariant(outcome: string) { if (outcome === "applied") return "neutral" as const; if (outcome === "failed") return "outline" as const; return "alarm" as const; } /** Retired is a decision, not a checkbox — only open and done toggle here. */ function Criteria({ slug, iscs, onChanged, }: { slug: string; iscs: SnoozableIsc[]; onChanged: () => void; }) { const [error, setError] = useState(null); if (iscs.length === 0) return No criteria written yet.; const toggle = (isc: SnoozableIsc) => { void setIsc(slug, isc.id, isc.status === "open" ? "done" : "open").then((failure) => { setError(failure); if (!failure) onChanged(); }); }; return ( <> {error && (

{error}

)} ); } function Decisions({ decisions }: { decisions: Decision[] }) { if (decisions.length === 0) return Nothing recorded.; return (
{decisions.map((d) => (
{d.date ?? "—"} {d.text} {d.why && — {d.why}}
))}
); } function Runtimes({ runtimes }: { runtimes: Record }) { const entries = Object.entries(runtimes); if (entries.length === 0) return No recorded actions in the window.; const max = Math.max(1, ...entries.map(([, n]) => n)); return (
{entries.map(([runtime, n]) => (
{runtime} {n}
))}
); } function RecentActions({ rows }: { rows: LedgerViewRow[] }) { if (rows.length === 0) return Nothing recorded yet.; return (
{rows.map((r) => (
{clock(r.ts).slice(5)} {r.tool}{" "} {r.target} {r.outcome}
))}
); } function Head({ d }: { d: ProjectDetailView }) { const open = d.iscs.filter((i) => i.status === "open").length; return ( <>

{d.slug} {d.status}

{d.goal || d.purpose}

{d.remote ?? "no remote on record"}
{d.path ?? "not checked out here"}
{open} open · serves {d.serves ?? "nothing on record"} {d.servesBy && ` · ${d.servesBy}`}
); } export function ProjectDetail({ slug }: { slug: string }) { const [nonce, setNonce] = useState(0); const view = useLoaded( `/api/project?slug=${encodeURIComponent(slug)}&v=${nonce}` ); if (view.state !== "ready") return ; const d = view.data; const reload = () => setNonce((n) => n + 1); return (
{d.next.length === 0 ? ( nothing queued ) : (
    {d.next.map((n) => (
  1. {n}
  2. ))}
)}
{d.blockers.length === 0 ? ( none ) : (
{d.blockers.map((b) => (

{b}

))}
)}
); }