Files
sp80/.planning/phases/01-foundation-dashboard/01-04-PLAN.md

358 lines
11 KiB
Markdown

---
phase: 01-foundation-dashboard
plan: 04
type: execute
wave: 3
depends_on:
- 01-01
- 01-02
- 01-03
files_modified:
- src/app/components/Sidebar.tsx
- src/app/components/LoginIndicator.tsx
- src/app/components/Navigation.tsx
- src/app/routes.ts
- vite.config.ts
- .bundlesize.json
- src/app/components/__tests__/Sidebar.test.tsx
- src/app/components/__tests__/LoginIndicator.test.tsx
autonomous: true
requirements:
- DASH-07
- UI-02
- UI-03
- UI-04
- UI-05
must_haves:
truths:
- Sidebar navigation has Settings, Calibration, Flash Memory links
- Login indicator shows logged-in state
- Bundle size is under 170KB (target) / 200KB (limit)
- All routes defined and working
- Touch-friendly navigation (44px+ targets)
artifacts:
- path: src/app/components/Sidebar.tsx
provides: Navigation sidebar
exports: Sidebar
min_lines: 50
- path: src/app/components/LoginIndicator.tsx
provides: Login status indicator
exports: LoginIndicator
- path: src/app/routes.ts
provides: Route definitions
exports: router
- path: .bundlesize.json
provides: Bundle size budget config
key_links:
- from: Sidebar
to: routes.ts
via: Link components
- from: LoginIndicator
to: Header
via: Header composition
- from: routes.ts
to: RainfallView
via: Route configuration
---
<objective>
Complete the dashboard with navigation, login indicator, route configuration, and verify performance budget (<170KB bundle size).
Purpose: Navigation provides access to other sections (Settings, Calibration, Flash Memory). Login indicator shows authentication status. Performance verification ensures Pi Zero 2 W compatibility.
Output: Working navigation, login indicator, routes configured, bundle size verified under budget.
</objective>
<execution_context>
@./.opencode/get-shit-done/workflows/execute-plan.md
@./.opencode/get-shit-done/templates/summary.md
</execution_context>
<context>
@.planning/phases/01-foundation-dashboard/01-CONTEXT.md
@.planning/phases/01-foundation-dashboard/01-RESEARCH.md
@sample_interface/src/app/components/Sidebar.tsx
@sample_interface/src/app/routes.ts
## Key Implementation Details
**Navigation Items:**
- Dashboard (home)
- Settings (with sub-items)
- Calibration
- Flash Memory
**Sidebar Design:**
- Fixed width (64px icons-only or 240px expanded)
- Touch-friendly items (44px+ height)
- Active state highlighting
- Collapsible on mobile
**Login Indicator:**
- Green dot/icon when logged in
- Gray/red when logged out
- Text label: "Logged In" / "Logged Out"
- Small and compact for header
**Routes:**
- `/` → Dashboard (RainfallView)
- `/utility/*` → Settings (placeholder for Phase 2)
- `/calibration` → Calibration (placeholder)
- `/flash-memory` → Flash Memory (placeholder)
**Bundle Size Budget:**
- Target: <170KB gzipped initial JS
- Limit: <200KB gzipped
- Tools: bundlesize, rollup-plugin-analyzer
</context>
<tasks>
<task type="auto" tdd="true">
<name>task 1: Create LoginIndicator component</name>
<files>src/app/components/LoginIndicator.tsx, src/app/components/__tests__/LoginIndicator.test.tsx</files>
<behavior>
- Shows green status when isLoggedIn=true
- Shows gray/red status when isLoggedIn=false
- Compact size for header placement
- Accessible (aria-label)
</behavior>
<action>
1. Create src/app/components/LoginIndicator.tsx:
- Props: isLoggedIn (boolean), className (optional)
- Use shadcn/ui Badge or custom indicator
- Logged in: Green dot + "Logged In" text
- Logged out: Gray dot + "Login" text
- Compact: h-6, text-xs
- Use Lucide User icon
- Add aria-label for accessibility
2. Create tests:
- Shows correct state for logged in/out
- Has accessible label
- Accepts custom className
</action>
<verify>
<automated>npm test -- --run LoginIndicator 2>&1 | grep -E "(PASS|FAIL|✓|✗)"</automated>
</verify>
<done>LoginIndicator with accessible states created</done>
</task>
<task type="auto" tdd="true">
<name>task 2: Update Sidebar navigation</name>
<files>src/app/components/Sidebar.tsx, src/app/components/__tests__/Sidebar.test.tsx</files>
<behavior>
- Shows Dashboard, Settings, Calibration, Flash Memory links
- Touch-friendly items (44px+ height)
- Active state for current route
- Collapsible sections
</behavior>
<action>
1. Update src/app/components/Sidebar.tsx:
- Import NavLink from react-router
- Define navigation items array:
* Dashboard (Home icon) → /
* Settings (Settings icon) → /utility
* Calibration (Gauge icon) → /calibration
* Flash Memory (HardDrive icon) → /flash-memory
- Map items to NavLink components
- Item height: min-h-[44px]
- Active state: bg-accent class
- Icon + label layout
- Collapsible on mobile with hamburger
2. Create tests:
- Renders all navigation items
- Items have correct links
- Touch target size adequate
- Active state applied correctly
</action>
<verify>
<automated>npm test -- --run Sidebar 2>&1 | grep -E "(PASS|FAIL|✓|✗)"</automated>
</verify>
<done>Sidebar with 4 navigation items created</done>
</task>
<task type="auto">
<name>task 3: Configure routes for all views</name>
<files>src/app/routes.ts, src/app/components/views/SettingsView.tsx, src/app/components/views/CalibrationView.tsx, src/app/components/views/FlashMemoryView.tsx</files>
<behavior>
- All routes defined in routes.ts
- Settings, Calibration, Flash Memory have placeholder views
- Routes use lazy loading where appropriate
- Navigation between routes works
</behavior>
<action>
1. Create placeholder views:
- src/app/components/views/SettingsView.tsx: "Settings - Phase 2" placeholder
- src/app/components/views/CalibrationView.tsx: "Calibration - Phase 3" placeholder
- src/app/components/views/FlashMemoryView.tsx: "Flash Memory - Phase 3" placeholder
2. Update src/app/routes.ts:
- Import all views
- Define routes array:
* path: "/", element: RainfallView
* path: "/utility/*", element: SettingsView
* path: "/calibration", element: CalibrationView
* path: "/flash-memory", element: FlashMemoryView
- Use createBrowserRouter
- Add error boundary for 404s
</action>
<verify>
<automated>grep -n "SettingsView\|CalibrationView\|FlashMemoryView" src/app/routes.ts 2>/dev/null | head -10</automated>
</verify>
<done>Routes configured for all 4 main sections</done>
</task>
<task type="auto">
<name>task 4: Integrate LoginIndicator into Header</name>
<files>src/app/components/Header.tsx</files>
<behavior>
- Header displays LoginIndicator
- Gets login state from sensor store
- Compact placement in header row
</behavior>
<action>
1. Update src/app/components/Header.tsx:
- Import LoginIndicator
- Get isLoggedIn from sensor store (or mock for now)
- Place LoginIndicator in header layout
- Ensure proper spacing and alignment
2. Test integration:
- Login indicator visible in header
- Updates when login state changes
</action>
<verify>
<automated>grep -n "LoginIndicator" src/app/components/Header.tsx 2>/dev/null | head -5</automated>
</verify>
<done>LoginIndicator integrated into Header</done>
</task>
<task type="auto">
<name>task 5: Configure bundle size checking</name>
<files>.bundlesize.json, vite.config.ts, package.json</files>
<behavior>
- Bundle size limit configured (170KB target, 200KB max)
- Build fails if bundle exceeds limit
- Size checking integrated in CI
</behavior>
<action>
1. Create .bundlesize.json:
```json
{
"files": [
{
"path": "dist/assets/*.js",
"maxSize": "200kb",
"compression": "gzip"
}
]
}
```
2. Update package.json scripts:
- "build:check": "npm run build && bundlesize"
- Add bundlesize as devDependency
3. Update vite.config.ts for optimization:
- Manual chunks for vendor code
- Drop console in production
- Minification settings
4. Install bundlesize: `pnpm add -D bundlesize`
</action>
<verify>
<automated>test -f .bundlesize.json && echo "Config exists" || echo "Config missing"</automated>
</verify>
<done>Bundle size checking configured</done>
</task>
<task type="auto">
<name>task 6: Build and verify bundle size</name>
<files>dist/</files>
<behavior>
- Production build succeeds
- Bundle size under 200KB limit
- Ideally under 170KB target
- No build errors or warnings
</behavior>
<action>
1. Run production build:
- `npm run build`
- Check for errors
2. Analyze bundle size:
- `npm run build:check` or check dist/ folder
- Look at main JS file size
- Use rollup-plugin-visualizer if needed
3. If over budget, investigate:
- Run `npx vite-bundle-visualizer`
- Identify large dependencies
- Consider code splitting
4. Document actual size in SUMMARY
</action>
<verify>
<automated>ls -lh dist/assets/*.js 2>/dev/null | awk '{print $5, $9}' | head -5</automated>
</verify>
<done>Bundle built and size verified (target: <170KB, limit: <200KB)</done>
</task>
<task type="auto">
<name>task 7: Final integration test</name>
<files>src/app/App.tsx</files>
<behavior>
- Full app renders without errors
- Navigation works between routes
- Dashboard shows all data
- No console errors
</behavior>
<action>
1. Verify App.tsx integration:
- RouterProvider with routes
- DashboardLayout with Header and Sidebar
- Outlet for route content
2. Run all tests:
- `npm test` — should pass
3. Verify no TypeScript errors:
- `npx tsc --noEmit`
4. Check for console errors:
- Review any warnings in test output
</action>
<verify>
<automated>npm test 2>&1 | tail -5</automated>
</verify>
<done>Full integration verified, all tests passing</done>
</task>
</tasks>
<verification>
After completing all tasks:
1. Run `npm test` — all tests should pass
2. Run `npm run build` — should succeed
3. Verify bundle size: Check dist/ folder, should be <200KB
4. Check routes: `grep -r "path:" src/app/routes.ts | wc -l` should be 4+
5. Verify navigation: `grep -r "NavLink\|Link" src/app/components/Sidebar.tsx`
6. Check LoginIndicator in Header: `grep "LoginIndicator" src/app/components/Header.tsx`
</verification>
<success_criteria>
- LoginIndicator component shows logged-in state
- Sidebar has navigation to Settings, Calibration, Flash Memory
- All routes configured and working
- Bundle size under 200KB (target <170KB)
- No TypeScript errors
- All tests passing
- Full app integrates without errors
</success_criteria>
<output>
After completion, create `.planning/phases/01-foundation-dashboard/01-04-SUMMARY.md`
</output>