---
phase: 17-initiative-dashboard
plan: 02
type: execute
wave: 2
depends_on: ["17-01"]
files_modified:
  - packages/web/src/components/InitiativeCard.tsx
  - packages/web/src/components/InitiativeList.tsx
autonomous: true
---

<objective>
Build the InitiativeCard and InitiativeList components that display initiatives with status, progress, and phase counts.

Purpose: Core display components for the dashboard — the card shows a single initiative, the list renders all initiatives with filtering and empty state.
Output: Card and list components ready for page integration.
</objective>

<execution_context>
@~/.claude/get-shit-done/workflows/execute-plan.md
@~/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md

@docs/wireframes/initiative-dashboard.md
@packages/web/src/components/ui/card.tsx
@packages/web/src/components/ui/button.tsx
@packages/web/src/components/ui/dropdown-menu.tsx
@packages/web/src/lib/trpc.ts
@packages/shared/src/types.ts
@packages/web/src/components/StatusBadge.tsx
@packages/web/src/components/ProgressBar.tsx
</context>

<tasks>

<task type="auto">
  <name>Task 1: Create InitiativeCard component</name>
  <files>packages/web/src/components/InitiativeCard.tsx</files>
  <action>
Create an InitiativeCard component rendering a single initiative row.

**Props:**
- `initiative: Initiative` (from shared types — id, name, description, status, createdAt, updatedAt)
- `phaseStats: { completed: number; total: number }` — phase completion counts
- `onView: () => void` — navigate to detail
- `onSpawnArchitect: (mode: 'discuss' | 'breakdown') => void` — spawn architect
- `onDelete: () => void` — delete initiative

**Layout (matching wireframe):**
Use the Card component from shadcn. Single row layout:
- Left: Initiative name (bold, text-base)
- Middle: StatusBadge showing `initiative.status`, ProgressBar showing `phaseStats.completed / phaseStats.total`, text showing `{completed}/{total}` phases
- Right: Action buttons — "View" button (outline variant), "Spawn Architect" dropdown, "..." more actions menu

**Responsive:** On screens <768px, stack elements vertically (use flex-col). Hide phase count text on small screens.

**Click behavior:** The entire card is clickable (calls `onView`). Action buttons use `e.stopPropagation()` to prevent card click.

Use TanStack Router `Link` for the card click (wrap card in Link to `/initiatives/${initiative.id}`), not an onClick handler — this gives proper anchor semantics and hover states.

Actually, use the card as a clickable div with `onClick={onView}` and `cursor-pointer` class — the View button and Link are redundant. Keep the View button for explicit action.

Import StatusBadge from `@/components/StatusBadge` and ProgressBar from `@/components/ProgressBar`.
  </action>
  <verify>npx tsc --noEmit -p packages/web/tsconfig.app.json</verify>
  <done>InitiativeCard renders initiative name, status badge, progress bar, phase count, and action buttons</done>
</task>

<task type="auto">
  <name>Task 2: Create InitiativeList component</name>
  <files>packages/web/src/components/InitiativeList.tsx</files>
  <action>
Create an InitiativeList component that fetches and displays all initiatives.

**Data fetching:**
- Use `trpc.listInitiatives.useQuery()` to fetch initiatives
- For each initiative, use `trpc.listPhases.useQuery({ initiativeId: initiative.id })` to get phase counts
  - Count phases where `status === 'completed'` for completed count
  - Total is `phases.length`
  - Note: This creates N+1 queries. Acceptable for v1 dashboard with small initiative counts. Wrap each query in the card component or use a helper.

Actually, to avoid N+1 in the list, fetch phases per-initiative inside InitiativeCard. Pass the initiative to the card, let the card fetch its own phase stats. This keeps the list component clean and each card self-contained.

**Filter:**
- Accept an optional `statusFilter` prop: `'all' | 'active' | 'completed' | 'archived'`
- Pass to `trpc.listInitiatives.useQuery({ status: filter === 'all' ? undefined : filter })`

**States:**
- Loading: Show skeleton or "Loading initiatives..." text
- Empty (no initiatives): Show empty state from wireframe — "No initiatives yet" with centered message and "New Initiative" CTA button
- Error: Show error message with retry button
- Data: Map initiatives to InitiativeCard components

**Empty state props:**
- `onCreateNew: () => void` — callback for the CTA button

The list renders a vertical stack of InitiativeCards with `space-y-3` gap.

Export: `export function InitiativeList`.
  </action>
  <verify>npx tsc --noEmit -p packages/web/tsconfig.app.json</verify>
  <done>InitiativeList fetches initiatives via tRPC, displays cards with loading/empty/error states, supports status filtering</done>
</task>

</tasks>

<verification>
Before declaring plan complete:
- [ ] `npx tsc --noEmit -p packages/web/tsconfig.app.json` passes
- [ ] InitiativeCard uses StatusBadge and ProgressBar from Plan 01
- [ ] InitiativeList handles loading, empty, error, and populated states
- [ ] No hardcoded data — all from tRPC queries
</verification>

<success_criteria>

- InitiativeCard displays initiative data with all wireframe elements
- InitiativeList fetches and renders initiatives with proper states
- Filter support via status prop
- Empty state matches wireframe design
- TypeScript compiles without errors
  </success_criteria>

<output>
After completion, create `.planning/phases/17-initiative-dashboard/17-02-SUMMARY.md`
</output>
