From ec93835ae5ac3fa6df052be017086ae4df628b6a Mon Sep 17 00:00:00 2001 From: Lukas May Date: Wed, 4 Feb 2026 21:03:44 +0100 Subject: [PATCH] feat(17-02): create InitiativeCard component - Renders initiative name, StatusBadge, ProgressBar, and phase count - Fetches phase stats per-card via trpc.listPhases (self-contained) - Spawn Architect dropdown with discuss/breakdown modes - More actions menu with edit/duplicate/archive/delete - Responsive: stacks vertically on mobile, hides phase count text - Card clickable with stopPropagation on action buttons --- .../web/src/components/InitiativeCard.tsx | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 packages/web/src/components/InitiativeCard.tsx diff --git a/packages/web/src/components/InitiativeCard.tsx b/packages/web/src/components/InitiativeCard.tsx new file mode 100644 index 0000000..d10c5eb --- /dev/null +++ b/packages/web/src/components/InitiativeCard.tsx @@ -0,0 +1,118 @@ +import { MoreHorizontal, Eye, Bot } from "lucide-react"; +import type { Initiative } from "@codewalk-district/shared"; +import { Card } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + DropdownMenuSeparator, +} from "@/components/ui/dropdown-menu"; +import { StatusBadge } from "@/components/StatusBadge"; +import { ProgressBar } from "@/components/ProgressBar"; +import { trpc } from "@/lib/trpc"; + +interface InitiativeCardProps { + initiative: Initiative; + onView: () => void; + onSpawnArchitect: (mode: "discuss" | "breakdown") => void; + onDelete: () => void; +} + +export function InitiativeCard({ + initiative, + onView, + onSpawnArchitect, + onDelete, +}: InitiativeCardProps) { + // Each card fetches its own phase stats (N+1 acceptable for v1 small counts) + const phasesQuery = trpc.listPhases.useQuery({ + initiativeId: initiative.id, + }); + + const phases = phasesQuery.data ?? []; + const completedCount = phases.filter((p) => p.status === "completed").length; + const totalCount = phases.length; + + return ( + +
+ {/* Left: Initiative name */} +
+ {initiative.name} +
+ + {/* Middle: Status + Progress + Phase count */} +
+ + + + {completedCount}/{totalCount} phases + +
+ + {/* Right: Action buttons */} +
e.stopPropagation()} + > + + + {/* Spawn Architect Dropdown */} + + + + + + onSpawnArchitect("discuss")} + > + Discuss + + onSpawnArchitect("breakdown")} + > + Breakdown + + + + + {/* More Actions Menu */} + + + + + + Edit + Duplicate + Archive + + + Delete + + + +
+
+
+ ); +}