diff --git a/src/app/p/[clientSlug]/[projectSlug]/c/[cardId]/page.tsx b/src/app/p/[clientSlug]/[projectSlug]/c/[cardId]/page.tsx index 9a5ad43..3f6666d 100644 --- a/src/app/p/[clientSlug]/[projectSlug]/c/[cardId]/page.tsx +++ b/src/app/p/[clientSlug]/[projectSlug]/c/[cardId]/page.tsx @@ -6,6 +6,7 @@ import { AppShell } from "@/components/shell/app-shell"; import { TopBar } from "@/components/shell/top-bar"; import { CardHeader } from "@/components/card/card-header"; import { CardActions } from "@/components/card/card-actions"; +import { CardBrief } from "@/components/card/card-brief"; import { CardNarrative } from "@/components/card/card-narrative"; import { CommitHistory } from "@/components/card/commit-history"; import { SubtaskList } from "@/components/card/subtask-list"; @@ -66,6 +67,12 @@ export default async function CardPage({ projectSlug={projectSlug} /> + + diff --git a/src/components/card/card-brief.tsx b/src/components/card/card-brief.tsx new file mode 100644 index 0000000..5b8f1b4 --- /dev/null +++ b/src/components/card/card-brief.tsx @@ -0,0 +1,111 @@ +"use client"; + +import { useState } from "react"; +import { MessageSquare } from "lucide-react"; + +import { Pill } from "@/components/ui/pill"; + +interface StoredBrief { + title?: string; + summary?: string; + subtasks?: string[]; + openQuestions?: string[]; +} + +interface StoredTurn { + question?: string; + answer?: string; +} + +const BAND_LABEL: Record<"small" | "medium" | "large", string> = { + small: "Looks small", + medium: "Looks medium", + large: "Looks large", +}; + +/** + * What the client asked for, as they asked for it. + * + * Shown to both roles. The developer needs it to build the right thing; the + * client needs to see their request recorded faithfully. The transcript is + * behind a toggle — it is the evidence, not the summary. + * + * Everything is rendered as text. This is client-authored content that passed + * through a model, which is the least trustworthy string in the product. + */ +export function CardBrief({ + brief, + transcript, + sizeBand, +}: { + brief: unknown; + transcript: unknown; + sizeBand: string; +}) { + const [showTranscript, setShowTranscript] = useState(false); + + const parsed = (brief ?? {}) as StoredBrief; + const turns = Array.isArray(transcript) ? (transcript as StoredTurn[]) : []; + + const openQuestions = Array.isArray(parsed.openQuestions) ? parsed.openQuestions : []; + + if (!parsed.summary && turns.length === 0) return null; + + const band = BAND_LABEL[sizeBand as keyof typeof BAND_LABEL]; + + return ( +
+
+

+ What was asked for +

+ {band && {band}} + {turns.length > 0 && ( + + )} +
+ + {parsed.summary && ( +

+ {parsed.summary} +

+ )} + + {openQuestions.length > 0 && ( +
+

+ Still to confirm +

+
    + {openQuestions.map((question, index) => ( +
  • + {question} +
  • + ))} +
+
+ )} + + {showTranscript && turns.length > 0 && ( +
    + {turns.map((turn, index) => ( +
  1. +

    {turn.question}

    +

    + {turn.answer} +

    +
  2. + ))} +
+ )} +
+ ); +}