docs(16): create frontend scaffold phase plan
Phase 16: Frontend Scaffold - 5 plans in 3 waves - 4 parallel-capable, 1 sequential with checkpoint - Ready for execution
This commit is contained in:
154
.planning/phases/16-frontend-scaffold/16-01-PLAN.md
Normal file
154
.planning/phases/16-frontend-scaffold/16-01-PLAN.md
Normal file
@@ -0,0 +1,154 @@
|
||||
---
|
||||
phase: 16-frontend-scaffold
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified: [packages/web/package.json, packages/web/vite.config.ts, packages/web/tsconfig.json, packages/web/tsconfig.app.json, packages/web/index.html, packages/web/src/main.tsx, packages/web/src/App.tsx, packages/web/src/index.css, packages/web/postcss.config.js, packages/web/components.json, packages/web/tailwind.config.ts, packages/web/src/lib/utils.ts, package.json, tsconfig.json]
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
Create the Vite + React project with Tailwind CSS and shadcn/ui component library.
|
||||
|
||||
Purpose: Establish the frontend build toolchain and component foundation for all subsequent UI work. This is the bare-bones project that compiles and serves an empty page with styling ready.
|
||||
Output: A working `packages/web/` directory with Vite dev server, Tailwind CSS, and shadcn/ui configured.
|
||||
</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
|
||||
|
||||
# Existing project structure - this is currently a single package
|
||||
@package.json
|
||||
@tsconfig.json
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create Vite + React + TypeScript project</name>
|
||||
<files>packages/web/package.json, packages/web/vite.config.ts, packages/web/tsconfig.json, packages/web/tsconfig.app.json, packages/web/index.html, packages/web/src/main.tsx, packages/web/src/App.tsx, packages/web/src/index.css, package.json, tsconfig.json</files>
|
||||
<action>
|
||||
1. Create `packages/web/` directory.
|
||||
2. Initialize a new Vite project with React + TypeScript template files manually (do NOT use `npm create vite` interactively):
|
||||
- `packages/web/package.json` with name `@codewalk-district/web`, type `module`, scripts: `dev`, `build`, `preview`, `lint`. Dependencies: `react`, `react-dom`. Dev dependencies: `@types/react`, `@types/react-dom`, `@vitejs/plugin-react`, `typescript`, `vite`.
|
||||
- `packages/web/vite.config.ts` with react plugin. Set server proxy: `/trpc` → `http://127.0.0.1:3847/trpc` to avoid CORS issues during development.
|
||||
- `packages/web/tsconfig.json` extending `tsconfig.app.json` with references.
|
||||
- `packages/web/tsconfig.app.json` targeting ES2020, module ESNext, moduleResolution bundler, strict mode, jsx react-jsx, include `src`.
|
||||
- `packages/web/index.html` standard Vite entry pointing to `/src/main.tsx`.
|
||||
- `packages/web/src/main.tsx` rendering `<App />` into `#root`.
|
||||
- `packages/web/src/App.tsx` minimal component returning `<div>Codewalk District</div>`.
|
||||
|
||||
3. Update root `package.json`:
|
||||
- Add `"workspaces": ["packages/*"]` for npm workspaces.
|
||||
- Add script `"dev:web": "npm run dev --workspace=packages/web"`.
|
||||
- Keep all existing scripts and dependencies unchanged.
|
||||
|
||||
4. Run `npm install` from root to link workspaces.
|
||||
|
||||
Do NOT modify any existing source files in `src/`. Only touch root package.json and tsconfig.json for workspace config.
|
||||
</action>
|
||||
<verify>
|
||||
cd packages/web && npx vite build succeeds without errors.
|
||||
npm run dev:web starts dev server (verify it starts, then kill).
|
||||
</verify>
|
||||
<done>Vite dev server starts and builds. Root workspace config links packages/web.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Configure Tailwind CSS and shadcn/ui</name>
|
||||
<files>packages/web/tailwind.config.ts, packages/web/postcss.config.js, packages/web/src/index.css, packages/web/components.json, packages/web/src/lib/utils.ts, packages/web/package.json</files>
|
||||
<action>
|
||||
1. Install Tailwind CSS v3 in packages/web:
|
||||
- Dev dependencies: `tailwindcss`, `postcss`, `autoprefixer`, `@tailwindcss/typography`
|
||||
- `postcss.config.js` with tailwindcss and autoprefixer plugins.
|
||||
- `tailwind.config.ts` with content paths pointing to `./src/**/*.{ts,tsx}`, extend theme as needed.
|
||||
|
||||
2. Update `packages/web/src/index.css` with Tailwind directives:
|
||||
```css
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
```
|
||||
Plus CSS custom properties for shadcn theming (the standard shadcn/ui CSS variables for colors, radius, etc.).
|
||||
|
||||
3. Install shadcn/ui dependencies:
|
||||
- Dependencies: `class-variance-authority`, `clsx`, `tailwind-merge`, `lucide-react`
|
||||
- Dev dependency: `tailwindcss-animate`
|
||||
- Add tailwindcss-animate plugin to tailwind.config.ts
|
||||
|
||||
4. Create `packages/web/components.json` (shadcn/ui config):
|
||||
```json
|
||||
{
|
||||
"$schema": "https://ui.shadcn.com/schema.json",
|
||||
"style": "default",
|
||||
"rsc": false,
|
||||
"tsx": true,
|
||||
"tailwind": {
|
||||
"config": "tailwind.config.ts",
|
||||
"css": "src/index.css",
|
||||
"baseColor": "neutral",
|
||||
"cssVariables": true
|
||||
},
|
||||
"aliases": {
|
||||
"components": "@/components",
|
||||
"utils": "@/lib/utils"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
5. Create `packages/web/src/lib/utils.ts`:
|
||||
```typescript
|
||||
import { type ClassValue, clsx } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
```
|
||||
|
||||
6. Configure path aliases in `vite.config.ts` and `tsconfig.app.json`:
|
||||
- `@/` → `./src/` (standard shadcn convention)
|
||||
- Install `@types/node` as dev dependency for path resolution in vite config.
|
||||
|
||||
7. Update `packages/web/src/App.tsx` to use a Tailwind class (e.g., `<div className="text-2xl font-bold p-8">Codewalk District</div>`) to verify Tailwind works.
|
||||
|
||||
8. Run `npm install` from root after adding dependencies.
|
||||
</action>
|
||||
<verify>
|
||||
npx vite build in packages/web succeeds.
|
||||
The built output includes Tailwind styles (check dist/assets/*.css contains Tailwind resets).
|
||||
</verify>
|
||||
<done>Tailwind CSS compiles. shadcn/ui configured with components.json and utils.ts. Path alias `@/` resolves.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `cd packages/web && npx vite build` succeeds
|
||||
- [ ] Root `npm install` completes without errors
|
||||
- [ ] `npm run dev:web` starts Vite dev server
|
||||
- [ ] Tailwind classes render in built output
|
||||
- [ ] `@/` path alias resolves in imports
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
- Vite + React + TypeScript project builds in packages/web/
|
||||
- Tailwind CSS configured and compiling
|
||||
- shadcn/ui components.json and utils ready
|
||||
- Root npm workspaces configured
|
||||
- Dev server starts with proxy to backend
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/16-frontend-scaffold/16-01-SUMMARY.md`
|
||||
</output>
|
||||
107
.planning/phases/16-frontend-scaffold/16-02-PLAN.md
Normal file
107
.planning/phases/16-frontend-scaffold/16-02-PLAN.md
Normal file
@@ -0,0 +1,107 @@
|
||||
---
|
||||
phase: 16-frontend-scaffold
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified: [packages/shared/package.json, packages/shared/tsconfig.json, packages/shared/src/index.ts, packages/shared/src/trpc.ts, packages/shared/src/types.ts, package.json, src/trpc/router.ts]
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
Export shared types (AppRouter, DB entity types) from a shared package so the frontend can consume them.
|
||||
|
||||
Purpose: The frontend needs AppRouter type for tRPC client type inference, and DB entity types (Initiative, Phase, Task, Agent, Message) for component props. Creating a shared package avoids import path gymnastics between backend and frontend.
|
||||
Output: `packages/shared/` package exporting AppRouter and entity types.
|
||||
</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
|
||||
|
||||
# Existing types to export
|
||||
@src/trpc/router.ts
|
||||
@src/db/schema.ts
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create shared types package</name>
|
||||
<files>packages/shared/package.json, packages/shared/tsconfig.json, packages/shared/src/index.ts, packages/shared/src/trpc.ts, packages/shared/src/types.ts, package.json</files>
|
||||
<action>
|
||||
1. Create `packages/shared/` directory.
|
||||
|
||||
2. Create `packages/shared/package.json`:
|
||||
- name: `@codewalk-district/shared`
|
||||
- type: module
|
||||
- main: `./src/index.ts` (no build step — consumed via TypeScript project references)
|
||||
- types: `./src/index.ts`
|
||||
- exports: `{ ".": "./src/index.ts" }`
|
||||
|
||||
3. Create `packages/shared/tsconfig.json`:
|
||||
- Extends nothing (standalone)
|
||||
- compilerOptions: target ES2022, module NodeNext, moduleResolution NodeNext, strict, declaration true, composite true, outDir dist
|
||||
- include: `["src"]`
|
||||
|
||||
4. Create `packages/shared/src/trpc.ts`:
|
||||
- Re-export the AppRouter type from the backend:
|
||||
```typescript
|
||||
export type { AppRouter } from '../../../src/trpc/router.js';
|
||||
```
|
||||
This uses relative path since we're in the same git repo. The frontend will import `AppRouter` from `@codewalk-district/shared`.
|
||||
|
||||
5. Create `packages/shared/src/types.ts`:
|
||||
- Re-export entity types the frontend needs:
|
||||
```typescript
|
||||
export type { Initiative, Phase, Plan, Task, Agent, Message } from '../../../src/db/schema.js';
|
||||
```
|
||||
These are the Drizzle InferSelectModel types (read-only shapes).
|
||||
|
||||
6. Create `packages/shared/src/index.ts`:
|
||||
- Barrel export:
|
||||
```typescript
|
||||
export type { AppRouter } from './trpc.js';
|
||||
export type { Initiative, Phase, Plan, Task, Agent, Message } from './types.js';
|
||||
```
|
||||
|
||||
7. Add `packages/shared` to root `package.json` workspaces array (should already include `"packages/*"` from Plan 01, but verify).
|
||||
|
||||
8. Run `npm install` from root to link the shared package.
|
||||
|
||||
NOTE: This package exports ONLY types (no runtime code). It exists solely so the frontend can `import type { AppRouter } from '@codewalk-district/shared'` without reaching into backend internals.
|
||||
</action>
|
||||
<verify>
|
||||
cd packages/shared && npx tsc --noEmit succeeds (types resolve correctly).
|
||||
From packages/web, importing `type { AppRouter } from '@codewalk-district/shared'` should resolve (test with a scratch file if needed, or just verify tsc).
|
||||
</verify>
|
||||
<done>AppRouter and entity types importable from @codewalk-district/shared. No build step needed — TypeScript resolves directly.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `packages/shared/` exists with package.json, tsconfig.json, and src/
|
||||
- [ ] TypeScript compiles `packages/shared/` without errors
|
||||
- [ ] AppRouter type re-exported correctly
|
||||
- [ ] Entity types (Initiative, Phase, Task, etc.) re-exported correctly
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
- @codewalk-district/shared package exists and resolves
|
||||
- AppRouter type accessible to frontend consumers
|
||||
- Entity types accessible to frontend consumers
|
||||
- No runtime dependencies — types only
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/16-frontend-scaffold/16-02-SUMMARY.md`
|
||||
</output>
|
||||
168
.planning/phases/16-frontend-scaffold/16-03-PLAN.md
Normal file
168
.planning/phases/16-frontend-scaffold/16-03-PLAN.md
Normal file
@@ -0,0 +1,168 @@
|
||||
---
|
||||
phase: 16-frontend-scaffold
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["16-01", "16-02"]
|
||||
files_modified: [packages/web/package.json, packages/web/src/lib/trpc.ts, packages/web/src/main.tsx, packages/web/tsconfig.app.json]
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
Wire tRPC React Query client to connect the frontend to the existing backend server.
|
||||
|
||||
Purpose: Enable type-safe data fetching from the Codewalk District backend. All subsequent UI screens (dashboard, detail, inbox) will use these tRPC hooks for data.
|
||||
Output: Working tRPC client with React Query provider, connected to backend via Vite dev proxy.
|
||||
</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
|
||||
|
||||
# Backend server runs at 127.0.0.1:3847, tRPC at /trpc/*
|
||||
@src/server/index.ts
|
||||
|
||||
# AppRouter type from shared package
|
||||
@packages/shared/src/trpc.ts
|
||||
|
||||
# Vite project from Plan 01
|
||||
@packages/web/vite.config.ts
|
||||
@packages/web/src/main.tsx
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Install tRPC React Query dependencies</name>
|
||||
<files>packages/web/package.json, packages/web/tsconfig.app.json</files>
|
||||
<action>
|
||||
1. Install in packages/web:
|
||||
- Dependencies: `@trpc/client`, `@trpc/react-query`, `@tanstack/react-query`
|
||||
- Add `@codewalk-district/shared` as a workspace dependency: `"@codewalk-district/shared": "*"`
|
||||
|
||||
2. Update `packages/web/tsconfig.app.json`:
|
||||
- Add project reference to shared package: `"references": [{ "path": "../shared" }]`
|
||||
- Ensure paths can resolve `@codewalk-district/shared` (npm workspaces handles this via node_modules symlink)
|
||||
|
||||
3. Run `npm install` from root.
|
||||
</action>
|
||||
<verify>npm ls @trpc/react-query --workspace=packages/web shows installed version.</verify>
|
||||
<done>tRPC and React Query packages installed. Shared package linked.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Create tRPC client and provider</name>
|
||||
<files>packages/web/src/lib/trpc.ts, packages/web/src/main.tsx</files>
|
||||
<action>
|
||||
1. Create `packages/web/src/lib/trpc.ts`:
|
||||
```typescript
|
||||
import { createTRPCReact } from '@trpc/react-query';
|
||||
import { httpBatchLink } from '@trpc/client';
|
||||
import type { AppRouter } from '@codewalk-district/shared';
|
||||
|
||||
export const trpc = createTRPCReact<AppRouter>();
|
||||
|
||||
export function createTRPCClient() {
|
||||
return trpc.createClient({
|
||||
links: [
|
||||
httpBatchLink({
|
||||
// In dev, Vite proxy forwards /trpc to backend
|
||||
// In prod, same-origin or configured URL
|
||||
url: '/trpc',
|
||||
}),
|
||||
],
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
2. Update `packages/web/src/main.tsx` to wrap App with providers:
|
||||
```typescript
|
||||
import React, { useState } from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
||||
import { trpc, createTRPCClient } from './lib/trpc';
|
||||
import App from './App';
|
||||
import './index.css';
|
||||
|
||||
function Root() {
|
||||
const [queryClient] = useState(() => new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
retry: 1,
|
||||
},
|
||||
},
|
||||
}));
|
||||
const [trpcClient] = useState(createTRPCClient);
|
||||
|
||||
return (
|
||||
<trpc.Provider client={trpcClient} queryClient={queryClient}>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<App />
|
||||
</QueryClientProvider>
|
||||
</trpc.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<Root />
|
||||
</React.StrictMode>
|
||||
);
|
||||
```
|
||||
|
||||
3. Update `packages/web/src/App.tsx` to verify tRPC works:
|
||||
```typescript
|
||||
import { trpc } from './lib/trpc';
|
||||
|
||||
function App() {
|
||||
const health = trpc.health.useQuery();
|
||||
|
||||
return (
|
||||
<div className="p-8">
|
||||
<h1 className="text-2xl font-bold">Codewalk District</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
Server: {health.data?.status ?? 'connecting...'}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
```
|
||||
|
||||
This App component is temporary — Phase 17+ will replace it. It exists to prove the tRPC connection works end-to-end.
|
||||
</action>
|
||||
<verify>
|
||||
cd packages/web && npx vite build succeeds (type-checks tRPC client against AppRouter).
|
||||
</verify>
|
||||
<done>tRPC client created with httpBatchLink. React Query provider wraps app. Build passes with type-safe router inference.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `cd packages/web && npx vite build` succeeds
|
||||
- [ ] `trpc.health.useQuery()` type-checks correctly
|
||||
- [ ] tRPC client configured with `/trpc` URL (proxied by Vite in dev)
|
||||
- [ ] React Query provider configured with sensible defaults
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
- tRPC React Query client wired to AppRouter type
|
||||
- React Query provider wraps application
|
||||
- Build passes with full type inference
|
||||
- Vite proxy configured for /trpc → backend
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/16-frontend-scaffold/16-03-SUMMARY.md`
|
||||
</output>
|
||||
270
.planning/phases/16-frontend-scaffold/16-04-PLAN.md
Normal file
270
.planning/phases/16-frontend-scaffold/16-04-PLAN.md
Normal file
@@ -0,0 +1,270 @@
|
||||
---
|
||||
phase: 16-frontend-scaffold
|
||||
plan: 04
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on: ["16-03"]
|
||||
files_modified: [packages/web/package.json, packages/web/src/App.tsx, packages/web/src/routeTree.gen.ts, packages/web/src/router.tsx, packages/web/src/routes/__root.tsx, packages/web/src/routes/index.tsx, packages/web/src/routes/initiatives/index.tsx, packages/web/src/routes/initiatives/$id.tsx, packages/web/src/routes/inbox.tsx, packages/web/src/layouts/AppLayout.tsx, packages/web/src/components/ui/button.tsx, packages/web/src/components/ui/badge.tsx, packages/web/src/components/ui/card.tsx, packages/web/src/components/ui/dropdown-menu.tsx]
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
Set up TanStack Router with page stubs and the app shell layout (navigation bar) matching the wireframe header.
|
||||
|
||||
Purpose: Establish the navigation structure and persistent layout that all subsequent screens plug into. Phase 17-19 will implement the actual page content; this plan creates the skeleton they slot into. TanStack Router provides fully type-safe routing with validated params and search params.
|
||||
Output: Working router with 3 page stubs (Dashboard, Initiative Detail, Inbox) and persistent nav header.
|
||||
</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
|
||||
|
||||
# Wireframe navigation structure
|
||||
@docs/wireframes/initiative-dashboard.md
|
||||
|
||||
# tRPC client from Plan 03
|
||||
@packages/web/src/lib/trpc.ts
|
||||
@packages/web/src/main.tsx
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Install TanStack Router and add shadcn/ui base components</name>
|
||||
<files>packages/web/package.json, packages/web/src/components/ui/button.tsx, packages/web/src/components/ui/badge.tsx, packages/web/src/components/ui/card.tsx, packages/web/src/components/ui/dropdown-menu.tsx</files>
|
||||
<action>
|
||||
1. Install in packages/web:
|
||||
- Dependencies: `@tanstack/react-router`
|
||||
- Dev dependencies: `@tanstack/router-plugin` (Vite plugin for route generation), `@radix-ui/react-dropdown-menu`, `@radix-ui/react-slot` (shadcn/ui primitives)
|
||||
|
||||
2. Update `packages/web/vite.config.ts` to add the TanStack Router Vite plugin:
|
||||
```typescript
|
||||
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
TanStackRouterVite(),
|
||||
react(),
|
||||
],
|
||||
// ... existing proxy config
|
||||
})
|
||||
```
|
||||
The TanStack Router plugin must come BEFORE the react plugin. It auto-generates the route tree from the file-based routes in `src/routes/`.
|
||||
|
||||
3. Add the 4 most-needed shadcn/ui components. Use `npx shadcn@latest add button badge card dropdown-menu` from packages/web directory. If the CLI doesn't work with this directory structure, manually create the component files following the shadcn/ui source (available at ui.shadcn.com).
|
||||
|
||||
The components go in `packages/web/src/components/ui/`:
|
||||
- `button.tsx` — needed by every page (View, Spawn, Queue, etc.)
|
||||
- `badge.tsx` — needed for StatusBadge component
|
||||
- `card.tsx` — needed for InitiativeCard
|
||||
- `dropdown-menu.tsx` — needed for ActionMenu and SpawnArchitectDropdown
|
||||
|
||||
These are the foundation components. Additional shadcn components will be added in later phases as needed.
|
||||
|
||||
4. Run `npm install` from root.
|
||||
</action>
|
||||
<verify>
|
||||
cd packages/web && npx vite build succeeds.
|
||||
Component files exist in packages/web/src/components/ui/.
|
||||
</verify>
|
||||
<done>TanStack Router and shadcn/ui base components installed.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Create file-based routes, app shell layout, and page stubs</name>
|
||||
<files>packages/web/src/App.tsx, packages/web/src/router.tsx, packages/web/src/routes/__root.tsx, packages/web/src/routes/index.tsx, packages/web/src/routes/initiatives/index.tsx, packages/web/src/routes/initiatives/$id.tsx, packages/web/src/routes/inbox.tsx, packages/web/src/layouts/AppLayout.tsx</files>
|
||||
<action>
|
||||
1. Create file-based route structure in `packages/web/src/routes/`:
|
||||
|
||||
TanStack Router uses file-based routing. The Vite plugin generates `routeTree.gen.ts` automatically from the file structure:
|
||||
|
||||
- `__root.tsx` — Root layout route (wraps all pages with AppLayout)
|
||||
- `index.tsx` — `/` route, redirects to `/initiatives`
|
||||
- `initiatives/index.tsx` — `/initiatives` DashboardPage
|
||||
- `initiatives/$id.tsx` — `/initiatives/$id` InitiativeDetailPage (type-safe param)
|
||||
- `inbox.tsx` — `/inbox` InboxPage
|
||||
|
||||
2. Create `packages/web/src/routes/__root.tsx`:
|
||||
The root route wraps all pages with the AppLayout shell:
|
||||
```typescript
|
||||
import { createRootRoute, Outlet } from '@tanstack/react-router'
|
||||
import { AppLayout } from '../layouts/AppLayout'
|
||||
|
||||
export const Route = createRootRoute({
|
||||
component: () => (
|
||||
<AppLayout>
|
||||
<Outlet />
|
||||
</AppLayout>
|
||||
),
|
||||
notFoundComponent: () => (
|
||||
<div className="p-8 text-center">
|
||||
<h1 className="text-2xl font-bold">Page not found</h1>
|
||||
<p className="text-muted-foreground mt-2">The page you're looking for doesn't exist.</p>
|
||||
</div>
|
||||
),
|
||||
})
|
||||
```
|
||||
|
||||
3. Create `packages/web/src/routes/index.tsx`:
|
||||
Redirect root `/` to `/initiatives`:
|
||||
```typescript
|
||||
import { createFileRoute, redirect } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
beforeLoad: () => {
|
||||
throw redirect({ to: '/initiatives' })
|
||||
},
|
||||
})
|
||||
```
|
||||
|
||||
4. Create `packages/web/src/routes/initiatives/index.tsx`:
|
||||
Dashboard stub:
|
||||
```typescript
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
import { trpc } from '../../lib/trpc'
|
||||
|
||||
export const Route = createFileRoute('/initiatives/')({
|
||||
component: DashboardPage,
|
||||
})
|
||||
|
||||
function DashboardPage() {
|
||||
const health = trpc.health.useQuery()
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Initiative Dashboard</h1>
|
||||
<p className="text-muted-foreground mt-2">
|
||||
Server: {health.data?.status ?? 'connecting...'}
|
||||
</p>
|
||||
<p className="text-muted-foreground mt-1">Content coming in Phase 17</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
5. Create `packages/web/src/routes/initiatives/$id.tsx`:
|
||||
Detail stub with type-safe params:
|
||||
```typescript
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/initiatives/$id')({
|
||||
component: InitiativeDetailPage,
|
||||
})
|
||||
|
||||
function InitiativeDetailPage() {
|
||||
const { id } = Route.useParams()
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Initiative Detail</h1>
|
||||
<p className="text-muted-foreground mt-2">Initiative ID: {id}</p>
|
||||
<p className="text-muted-foreground mt-1">Content coming in Phase 18</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
6. Create `packages/web/src/routes/inbox.tsx`:
|
||||
Inbox stub:
|
||||
```typescript
|
||||
import { createFileRoute } from '@tanstack/react-router'
|
||||
|
||||
export const Route = createFileRoute('/inbox')({
|
||||
component: InboxPage,
|
||||
})
|
||||
|
||||
function InboxPage() {
|
||||
return (
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold">Agent Inbox</h1>
|
||||
<p className="text-muted-foreground mt-1">Content coming in Phase 19</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
7. Create `packages/web/src/layouts/AppLayout.tsx`:
|
||||
The persistent navigation shell from the wireframe header:
|
||||
```
|
||||
CODEWALK DISTRICT [+ New Initiative]
|
||||
Initiatives Agents Tasks Settings
|
||||
```
|
||||
Implementation:
|
||||
- Header with app title "Codewalk District" on the left
|
||||
- "New Initiative" button on the right (placeholder handler for now)
|
||||
- Navigation tabs: Initiatives (links to /initiatives), Inbox (links to /inbox)
|
||||
- Skip "Agents", "Tasks", "Settings" tabs for now — not in Phase 16-19 scope. Include them as disabled/greyed out.
|
||||
- Use `Link` from `@tanstack/react-router` for navigation links
|
||||
- Active nav item highlighted using `activeProps` or `activeOptions` on Link
|
||||
- Children rendered via props.children (passed from root route Outlet)
|
||||
- Minimal Tailwind styling: fixed header, content below with padding
|
||||
|
||||
8. Create `packages/web/src/router.tsx`:
|
||||
```typescript
|
||||
import { createRouter } from '@tanstack/react-router'
|
||||
import { routeTree } from './routeTree.gen'
|
||||
|
||||
export const router = createRouter({ routeTree })
|
||||
|
||||
declare module '@tanstack/react-router' {
|
||||
interface Register {
|
||||
router: typeof router
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
9. Update `packages/web/src/App.tsx`:
|
||||
Replace the temporary health-check component with the router:
|
||||
```typescript
|
||||
import { RouterProvider } from '@tanstack/react-router'
|
||||
import { router } from './router'
|
||||
|
||||
function App() {
|
||||
return <RouterProvider router={router} />
|
||||
}
|
||||
|
||||
export default App
|
||||
```
|
||||
|
||||
10. Run `cd packages/web && npx vite build` — the TanStack Router plugin will generate `routeTree.gen.ts` during the build. The generated file should be committed to git (it's a type-safe route manifest).
|
||||
|
||||
Keep it simple. These are stubs — the real UI comes in Phases 17-19. The goal is a working navigation skeleton with the right route structure.
|
||||
</action>
|
||||
<verify>
|
||||
cd packages/web && npx vite build succeeds.
|
||||
routeTree.gen.ts generated with routes: /, /initiatives, /initiatives/$id, /inbox.
|
||||
AppLayout renders header and child content.
|
||||
</verify>
|
||||
<done>TanStack Router configured with file-based routes. App shell layout renders nav header. Page stubs in place for Phases 17-19 to fill in.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `cd packages/web && npx vite build` succeeds
|
||||
- [ ] Route `/initiatives` renders DashboardPage stub
|
||||
- [ ] Route `/initiatives/some-id` renders InitiativeDetailPage stub with type-safe param
|
||||
- [ ] Route `/inbox` renders InboxPage stub
|
||||
- [ ] AppLayout nav header present on all pages
|
||||
- [ ] Active nav item highlighted
|
||||
- [ ] routeTree.gen.ts generated
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
- TanStack Router configured with file-based routes
|
||||
- Type-safe params on /initiatives/$id route
|
||||
- AppLayout shell matches wireframe header structure
|
||||
- Page stubs ready for Phase 17-19 implementation
|
||||
- Navigation between pages works
|
||||
- Build passes cleanly
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/16-frontend-scaffold/16-04-SUMMARY.md`
|
||||
</output>
|
||||
91
.planning/phases/16-frontend-scaffold/16-05-PLAN.md
Normal file
91
.planning/phases/16-frontend-scaffold/16-05-PLAN.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
phase: 16-frontend-scaffold
|
||||
plan: 05
|
||||
type: execute
|
||||
wave: 3
|
||||
depends_on: ["16-04"]
|
||||
files_modified: [packages/web/src/App.tsx]
|
||||
autonomous: false
|
||||
---
|
||||
|
||||
<objective>
|
||||
Verify the complete frontend scaffold works end-to-end with the running backend.
|
||||
|
||||
Purpose: Confirm that the Vite dev server, tRPC client, TanStack Router, Tailwind, and shadcn/ui all work together before proceeding to build actual UI screens.
|
||||
Output: Verified working scaffold ready for Phase 17 (Initiative Dashboard).
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@~/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@~/.claude/get-shit-done/templates/summary.md
|
||||
@~/.claude/get-shit-done/references/checkpoints.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
|
||||
# All scaffold pieces
|
||||
@packages/web/src/main.tsx
|
||||
@packages/web/src/App.tsx
|
||||
@packages/web/src/router.tsx
|
||||
@packages/web/src/routes/__root.tsx
|
||||
@packages/web/src/layouts/AppLayout.tsx
|
||||
@packages/web/vite.config.ts
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Verify build and type-check</name>
|
||||
<files>packages/web/src/App.tsx</files>
|
||||
<action>
|
||||
1. Run full build from root: `npm run build` (should build the backend). Then `cd packages/web && npx vite build` (should build frontend).
|
||||
2. Run TypeScript type-check on shared package: `cd packages/shared && npx tsc --noEmit`
|
||||
3. Verify no TypeScript errors across the entire project.
|
||||
4. If any errors, fix them.
|
||||
</action>
|
||||
<verify>
|
||||
All three commands succeed with zero errors.
|
||||
</verify>
|
||||
<done>Full project builds cleanly. No type errors.</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<what-built>Complete frontend scaffold with Vite + React + tRPC + Tailwind + shadcn/ui + TanStack Router</what-built>
|
||||
<how-to-verify>
|
||||
1. Start backend: `cw --server` (or however the server starts)
|
||||
2. Start frontend: `npm run dev:web`
|
||||
3. Visit: http://localhost:5173 (Vite default port)
|
||||
4. Verify: Navigation header shows "Codewalk District" with nav tabs
|
||||
5. Click "Initiatives" tab → should show dashboard stub
|
||||
6. Click "Inbox" tab → should show inbox stub
|
||||
7. Verify: Tailwind styling renders (text should be styled, not raw HTML)
|
||||
8. Check browser console: No errors
|
||||
9. If backend is running: DashboardPage should show "Server: ok" from tRPC health check
|
||||
</how-to-verify>
|
||||
<resume-signal>Type "approved" or describe issues to fix</resume-signal>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Full build succeeds (backend + frontend)
|
||||
- [ ] No TypeScript errors
|
||||
- [ ] Visual verification passed
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
|
||||
- Complete scaffold builds and runs
|
||||
- tRPC connection works (health check shows server status)
|
||||
- Navigation works between all routes
|
||||
- Tailwind styling renders correctly
|
||||
- Human verified visual correctness
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/16-frontend-scaffold/16-05-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user