From feaed41454934ad3adc51653111909a4949fcb79 Mon Sep 17 00:00:00 2001 From: admin Date: Fri, 13 Mar 2026 06:23:52 +0800 Subject: [PATCH] docs(01-foundation-dashboard): create phase 1 plans --- .../01-foundation-dashboard/01-01-PLAN.md | 286 ++++++++++++++ .../01-foundation-dashboard/01-02-PLAN.md | 271 +++++++++++++ .../01-foundation-dashboard/01-03-PLAN.md | 287 ++++++++++++++ .../01-foundation-dashboard/01-04-PLAN.md | 357 ++++++++++++++++++ .../01-foundation-dashboard/01-CONTEXT.md | 104 +++++ .../01-foundation-dashboard/01-RESEARCH.md | 176 +++++++++ .../01-foundation-dashboard/01-VALIDATION.md | 90 +++++ 7 files changed, 1571 insertions(+) create mode 100644 .planning/phases/01-foundation-dashboard/01-01-PLAN.md create mode 100644 .planning/phases/01-foundation-dashboard/01-02-PLAN.md create mode 100644 .planning/phases/01-foundation-dashboard/01-03-PLAN.md create mode 100644 .planning/phases/01-foundation-dashboard/01-04-PLAN.md create mode 100644 .planning/phases/01-foundation-dashboard/01-CONTEXT.md create mode 100644 .planning/phases/01-foundation-dashboard/01-RESEARCH.md create mode 100644 .planning/phases/01-foundation-dashboard/01-VALIDATION.md diff --git a/.planning/phases/01-foundation-dashboard/01-01-PLAN.md b/.planning/phases/01-foundation-dashboard/01-01-PLAN.md new file mode 100644 index 000000000..d0da35dbe --- /dev/null +++ b/.planning/phases/01-foundation-dashboard/01-01-PLAN.md @@ -0,0 +1,286 @@ +--- +phase: 01-foundation-dashboard +plan: 01 +type: execute +wave: 1 +depends_on: [] +files_modified: + - src/app/stores/sensorStore.ts + - src/app/api/client.ts + - src/app/App.tsx + - vitest.config.ts + - src/test/setup.ts + - package.json +autonomous: true +requirements: + - DASH-01 + - DASH-02 + - DASH-03 + - DASH-04 + - DASH-05 + - DASH-06 + - DASH-07 + - UI-04 +must_haves: + truths: + - Zustand store exists with sensor data types + - API client with mock fallback works + - Port-based mode detection works (8080=kiosk, 9090=remote) + - Test infrastructure is ready + artifacts: + - path: src/app/stores/sensorStore.ts + provides: Zustand store for sensor data + exports: useSensorStore, SensorState + - path: src/app/api/client.ts + provides: API client with mock fallback + exports: fetchSensorData, mockSensorData + - path: src/app/App.tsx + provides: Mode detection and app root + min_lines: 30 + - path: vitest.config.ts + provides: Test configuration + key_links: + - from: sensorStore + to: client.ts + via: fetchSensorData updates store + - from: App.tsx + to: sensorStore + via: Initializes data polling +--- + + +Create the foundational infrastructure for the RTU dashboard: Zustand state management, API client with mock fallback, port-based mode detection, and test infrastructure (Wave 0). + +Purpose: This infrastructure enables all subsequent dashboard features. Without these foundations, individual components cannot share data or communicate with the backend. + +Output: Working store, API client, mode detection, and test setup that subsequent plans build upon. + + + +@./.opencode/get-shit-done/workflows/execute-plan.md +@./.opencode/get-shit-done/templates/summary.md + + + +@.planning/phases/01-foundation-dashboard/01-CONTEXT.md +@.planning/phases/01-foundation-dashboard/01-RESEARCH.md +@.planning/ROADMAP.md +@.planning/REQUIREMENTS.md +@sample_interface/src/app/App.tsx + +## Key Implementation Details + +**Mode Detection:** +- Port 8080 → kiosk mode (fixed 1024x600) +- Port 9090 → remote mode (responsive Full HD) +- Use `window.location.port` for detection + +**Sensor Data Structure:** +```typescript +interface SensorData { + rainfall: { + today: number; + hourly: number; + monthlyAcc: number; + yearlyAcc: number; + }; + voltage: { + solar: number; + battery: number; + batteryStatus: 'HIGH' | 'LOW'; + }; + station: { + id: string; + version: string; + }; + communication: { + asu: number; + dBm: number; + percentage: number; + }; + timestamp: string; +} +``` + +**Polling Strategy:** +- Default 5 second interval +- Configurable via settings +- Pause when document.hidden +- Use AbortController for cleanup + + + + + + task 1: Create Wave 0 test infrastructure + vitest.config.ts, src/test/setup.ts, package.json + + - Test config extends Vite config + - Setup file provides test utilities + - Test command works: `npm test` + + + 1. Create vitest.config.ts extending vite.config.ts: + - Use @vitejs/plugin-react + - Configure test environment as jsdom + - Set setupFiles to src/test/setup.ts + + 2. Create src/test/setup.ts: + - Import @testing-library/jest-dom matchers + - Configure cleanup after each test + - Add mock for window.matchMedia if needed + + 3. Update package.json scripts: + - "test": "vitest run" + - "test:watch": "vitest" + - Add devDependencies: vitest, @testing-library/react, @testing-library/jest-dom, jsdom + + 4. Run `pnpm install` to add dependencies + + + npm test -- --run 2>&1 | head -20 + + Test infrastructure ready, `npm test` runs successfully + + + + task 2: Create Zustand sensor store + src/app/stores/sensorStore.ts, src/app/stores/__tests__/sensorStore.test.ts + + - Store initializes with default/mock data + - setSensorData updates all fields + - Store can be subscribed to + - Selectors work for derived data + + + 1. Install zustand: `pnpm add zustand` + + 2. Create src/app/stores/sensorStore.ts: + - Define SensorData interface per context + - Create store with initial state + - Export useSensorStore hook + - Add actions: setSensorData, updatePollingInterval + - Include selector for last update timestamp + + 3. Create tests verifying: + - Store initializes with defaults + - setSensorData updates state + - Subscribers receive updates + + + npm test -- --run stores/sensorStore 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + Zustand store created with SensorData interface, tests pass + + + + task 3: Create API client with mock fallback + src/app/api/client.ts, src/app/api/__tests__/client.test.ts + + - fetchSensorData returns SensorData + - On API failure, returns mock data + - Uses AbortController for cancellation + - Logs errors but doesn't throw + + + 1. Create src/app/api/client.ts: + - Define API endpoint URL (configurable) + - Create fetchSensorData function with AbortSignal + - Create mockSensorData generator with realistic values + - Implement fallback: try API → catch → return mock + - Add request timeout (5s) + + 2. Create tests verifying: + - Returns data on successful fetch + - Returns mock on network error + - Respects AbortController + - Mock data has valid structure + + + npm test -- --run api/client 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + API client with mock fallback created, tests pass + + + + task 4: Implement port-based mode detection + src/app/App.tsx, src/app/hooks/useDisplayMode.ts + + - Port 8080 → returns 'kiosk' + - Port 9090 → returns 'remote' + - Other ports → defaults to 'kiosk' + - Mode available via hook + + + 1. Create src/app/hooks/useDisplayMode.ts: + - Read window.location.port + - Return 'kiosk' for 8080, 'remote' for 9090 + - Default to 'kiosk' for other ports + - Memoize result (port won't change) + + 2. Update src/app/App.tsx: + - Import useDisplayMode hook + - Pass mode to DashboardLayout + - Initialize data polling on mount + - Clean up polling on unmount (useEffect cleanup) + - Add data-attr or class based on mode for CSS targeting + + + grep -n "useDisplayMode\|window.location.port\|displayMode" src/app/App.tsx src/app/hooks/useDisplayMode.ts 2>/dev/null | head -20 + + Mode detection implemented, App.tsx uses display mode hook + + + + task 5: Set up data polling with cleanup + src/app/App.tsx, src/app/hooks/useSensorPolling.ts + + - Polls every 5 seconds by default + - Pauses when document.hidden + - Resumes when visible again + - Properly cleans up on unmount + + + 1. Create src/app/hooks/useSensorPolling.ts: + - Accept polling interval (ms) parameter, default 5000 + - Use setInterval for polling + - Listen for visibilitychange event + - Pause interval when document.hidden + - Resume when visible + - Use AbortController per fetch + - Return cleanup function + + 2. Update App.tsx: + - Call useSensorPolling in component + - Pass polling interval from settings (or default) + - Ensure cleanup happens on unmount + + + grep -n "useSensorPolling\|setInterval\|visibilitychange\|AbortController" src/app/App.tsx src/app/hooks/useSensorPolling.ts 2>/dev/null | head -20 + + Polling implemented with visibility awareness and cleanup + + + + + +After completing all tasks: +1. Run `npm test` — all tests should pass +2. Check store exists: `ls src/app/stores/sensorStore.ts` +3. Check API client exists: `ls src/app/api/client.ts` +4. Check mode detection: `grep "useDisplayMode" src/app/App.tsx` +5. Verify no console errors on build: `npm run build 2>&1 | grep -i error || echo "Build clean"` + + + +- Zustand store with SensorData interface exists and is tested +- API client with mock fallback exists and is tested +- Port-based mode detection works (8080=kiosk, 9090=remote) +- Data polling with cleanup and visibility awareness implemented +- Test infrastructure ready (`npm test` runs) +- All files committed + + + +After completion, create `.planning/phases/01-foundation-dashboard/01-01-SUMMARY.md` + diff --git a/.planning/phases/01-foundation-dashboard/01-02-PLAN.md b/.planning/phases/01-foundation-dashboard/01-02-PLAN.md new file mode 100644 index 000000000..8ffa2bd8e --- /dev/null +++ b/.planning/phases/01-foundation-dashboard/01-02-PLAN.md @@ -0,0 +1,271 @@ +--- +phase: 01-foundation-dashboard +plan: 02 +type: execute +wave: 1 +depends_on: + - 01-01 +files_modified: + - src/app/components/Header.tsx + - src/app/components/VoltageDisplay.tsx + - src/app/components/BatteryStatus.tsx + - src/app/components/__tests__/Header.test.tsx + - src/app/components/__tests__/VoltageDisplay.test.tsx + - src/app/components/__tests__/BatteryStatus.test.tsx +autonomous: true +requirements: + - DASH-02 + - DASH-03 + - DASH-04 + - DASH-07 + - UI-01 + - UI-02 +must_haves: + truths: + - Header displays all status information + - Voltage displays show solar and battery readings + - Battery status shows HIGH/LOW indicator with color + - All touch targets are minimum 44px + - Header is compact for 1024x600 display + artifacts: + - path: src/app/components/Header.tsx + provides: Main header with status info + exports: Header + min_lines: 80 + - path: src/app/components/VoltageDisplay.tsx + provides: Solar/battery voltage display + exports: VoltageDisplay + - path: src/app/components/BatteryStatus.tsx + provides: Battery status indicator + exports: BatteryStatus + key_links: + - from: Header + to: sensorStore + via: useSensorStore hook + - from: BatteryStatus + to: VoltageDisplay + via: Status color based on voltage level +--- + + +Create the Header component and voltage monitoring components with modern styling, touch-friendly sizing, and status indicators. + +Purpose: The header is the primary information display area, showing station status, voltage, and login state. These components are used across all views. + +Output: Header.tsx, VoltageDisplay.tsx, BatteryStatus.tsx components with tests, all meeting touch target requirements. + + + +@./.opencode/get-shit-done/workflows/execute-plan.md +@./.opencode/get-shit-done/templates/summary.md + + + +@.planning/phases/01-foundation-dashboard/01-CONTEXT.md +@.planning/phases/01-foundation-dashboard/01-01-SUMMARY.md +@sample_interface/src/app/components/Header.tsx + +## Key Implementation Details + +**Header Layout (compact for 1024x600):** +``` +[TCK Logo] [Station ID] [Version] [Solar: XX.XV] [Battery: XX.XV] [Status] + [HH:MM:SS] [YYYY-MM-DD] [ASU: XX] [Login: ●] +``` + +**Voltage Display:** +- Format: XX.XV (one decimal place) +- Solar voltage: Always shown +- Battery voltage: With status indicator + +**Battery Status:** +- HIGH (≥12.0V): Green indicator +- LOW (<12.0V): Red/orange indicator +- Threshold: 12.0V configurable + +**Touch Targets:** +- All interactive elements: minimum 44px height +- Prefer 56px for primary actions +- Use padding not margin for hit areas + +**Design Tokens:** +- Use existing Tailwind classes +- shadcn/ui Badge for status indicators +- Lucide icons for visual elements + + + + + + task 1: Create VoltageDisplay component + src/app/components/VoltageDisplay.tsx, src/app/components/__tests__/VoltageDisplay.test.tsx + + - Displays voltage with one decimal place + - Shows label (Solar/Battery) + - Updates when sensor data changes + - Touch-friendly container (44px min) + + + 1. Create src/app/components/VoltageDisplay.tsx: + - Props: label (string), voltage (number), className (optional) + - Format voltage to XX.XV + - Use flex layout with gap-2 + - Min-height 44px for touch + - Use text-sm for compact display + - Add voltage icon from Lucide (Zap or Battery) + + 2. Create tests: + - Renders label and formatted voltage + - Updates on prop change + - Has minimum touch target size + + + npm test -- --run VoltageDisplay 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + VoltageDisplay component created with tests passing + + + + task 2: Create BatteryStatus component + src/app/components/BatteryStatus.tsx, src/app/components/__tests__/BatteryStatus.test.tsx + + - Shows battery voltage with status badge + - HIGH status (≥12V): green badge + - LOW status (<12V): red/orange badge + - Status text visible: HIGH or LOW + + + 1. Create src/app/components/BatteryStatus.tsx: + - Props: voltage (number), threshold (optional, default 12.0) + - Calculate status based on threshold + - Use shadcn/ui Badge component + - Green variant for HIGH, destructive for LOW + - Display "XX.XV - HIGH/LOW" format + - Touch-friendly (44px min) + + 2. Create tests: + - Shows HIGH for voltage ≥12.0 + - Shows LOW for voltage <12.0 + - Uses custom threshold if provided + - Updates when voltage changes + + + npm test -- --run BatteryStatus 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + BatteryStatus component created with color-coded indicators + + + + task 3: Modernize Header component + src/app/components/Header.tsx, src/app/components/__tests__/Header.test.tsx + + - Displays TCK logo from /logo/ + - Shows station ID and version + - Shows current time (HH:MM:SS) updating every second + - Shows current date (YYYY-MM-DD) + - Shows solar voltage + - Shows battery voltage with status + - Shows communication ASU + - Shows login status indicator + - All in compact layout for 1024x600 + + + 1. Update/modernize src/app/components/Header.tsx: + - Import useSensorStore from stores + - Import VoltageDisplay and BatteryStatus + - Use useEffect for clock updates (1s interval) + - Layout: Logo | Station Info | Time/Date | Voltages | Comm | Login + - Use flexbox with justify-between + - Compact padding (py-2 px-4) + - Use text-sm throughout + - Login indicator: green dot when logged in + - Cleanup clock interval on unmount + + 2. Create tests: + - Renders all required elements + - Time updates every second + - Uses sensor store data + - Has proper structure + + + npm test -- --run Header 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + Header component displays all status info, tests pass + + + + task 4: Verify touch target sizing + src/app/components/Header.tsx, src/app/components/VoltageDisplay.tsx, src/app/components/BatteryStatus.tsx + + - All interactive elements ≥44px + - Verified via CSS inspection + - Document any exceptions + + + 1. Review all components for touch target compliance: + - Check min-height on containers + - Check padding on clickable elements + - Ensure no margins-only hit areas + + 2. Add explicit classes where needed: + - `min-h-[44px]` for touch targets + - `flex items-center` for vertical centering + + 3. Run visual verification: + - DevTools element picker + - Verify minimum dimensions + + + grep -n "min-h-\[44px\]\|min-h-11\|h-11\|h-\[44px\]" src/app/components/Header.tsx src/app/components/VoltageDisplay.tsx src/app/components/BatteryStatus.tsx 2>/dev/null | wc -l + + All components have minimum 44px touch targets + + + + task 5: Update DashboardLayout to use new Header + src/app/components/DashboardLayout.tsx + + - DashboardLayout imports and renders new Header + - Layout works with Sidebar + - Content area scrollable + + + 1. Update src/app/components/DashboardLayout.tsx: + - Import new Header component + - Place Header above main content area + - Ensure flex layout works correctly + - Test with both kiosk and remote modes + + 2. Verify integration: + - Header displays correctly + - Sidebar navigation still works + - Main content has proper padding + + + grep -n "import.*Header\|/dev/null | head -5 + + DashboardLayout integrates new Header component + + + + + +After completing all tasks: +1. Run `npm test` — all tests should pass +2. Verify components exist: `ls src/app/components/Header.tsx src/app/components/VoltageDisplay.tsx src/app/components/BatteryStatus.tsx` +3. Check touch targets: `grep -r "min-h-" src/app/components/*.tsx | grep -E "(Header|Voltage|Battery)"` +4. Verify Header integration: `grep " + + +- VoltageDisplay component shows formatted voltage (XX.XV) +- BatteryStatus component shows color-coded HIGH/LOW status +- Header displays all required info: Logo, Station ID, Version, Time, Date, Solar, Battery, Comm, Login +- All touch targets minimum 44px +- Components tested and passing +- DashboardLayout uses new Header + + + +After completion, create `.planning/phases/01-foundation-dashboard/01-02-SUMMARY.md` + diff --git a/.planning/phases/01-foundation-dashboard/01-03-PLAN.md b/.planning/phases/01-foundation-dashboard/01-03-PLAN.md new file mode 100644 index 000000000..84795bc67 --- /dev/null +++ b/.planning/phases/01-foundation-dashboard/01-03-PLAN.md @@ -0,0 +1,287 @@ +--- +phase: 01-foundation-dashboard +plan: 03 +type: execute +wave: 2 +depends_on: + - 01-01 + - 01-02 +files_modified: + - src/app/components/views/RainfallView.tsx + - src/app/components/RainfallCard.tsx + - src/app/components/StationInfo.tsx + - src/app/components/ClockDisplay.tsx + - src/app/components/CommStatus.tsx + - src/app/components/__tests__/RainfallView.test.tsx + - src/app/components/__tests__/RainfallCard.test.tsx +autonomous: true +requirements: + - DASH-01 + - DASH-05 + - DASH-06 + - DASH-04 + - UI-01 + - UI-02 + - UI-03 +must_haves: + truths: + - Dashboard shows 4 rainfall cards (Today/Hourly/MAR/Yearly) + - Cards update in real-time from sensor store + - Clock displays HH:MM:SS with seconds + - Comm status shows ASU/dBm/percentage + - Layout adapts to kiosk (1024x600) and remote (Full HD) modes + artifacts: + - path: src/app/components/views/RainfallView.tsx + provides: Main dashboard view + exports: RainfallView + min_lines: 60 + - path: src/app/components/RainfallCard.tsx + provides: Individual rainfall metric card + exports: RainfallCard + - path: src/app/components/ClockDisplay.tsx + provides: Real-time clock component + exports: ClockDisplay + key_links: + - from: RainfallView + to: sensorStore + via: useSensorStore hook + - from: RainfallCard + to: RainfallView + via: Props passing rainfall data + - from: ClockDisplay + to: sensorStore + via: Optional sync with store timestamp +--- + + +Create the main dashboard view with rainfall cards, clock display, communication status, and dual-mode responsive layout. + +Purpose: This is the primary user interface where operators view rainfall data and station status. Must work on both 7" touchscreen (1024x600) and Full HD remote displays. + +Output: RainfallView as main dashboard, RainfallCard components, ClockDisplay, and CommStatus with responsive layouts. + + + +@./.opencode/get-shit-done/workflows/execute-plan.md +@./.opencode/get-shit-done/templates/summary.md + + + +@.planning/phases/01-foundation-dashboard/01-CONTEXT.md +@.planning/phases/01-foundation-dashboard/01-01-SUMMARY.md +@.planning/phases/01-foundation-dashboard/01-02-SUMMARY.md +@sample_interface/src/app/components/views/RainfallView.tsx + +## Key Implementation Details + +**Rainfall Metrics (4 cards):** +- Today: Current day's rainfall in mm +- Hourly: Current hour's rainfall in mm +- MAR Acc: Monthly accumulation in mm +- Yearly Acc: Yearly accumulation in mm + +**Card Design:** +- Color-coded: Today (blue), Hourly (cyan), MAR (green), Yearly (purple) +- Large value display: XX.X mm +- Label below value +- Compact for grid layout +- Touch-friendly (44px+ tap area) + +**Grid Layout:** +- Kiosk (1024x600): 2x2 grid, compact cards +- Remote (Full HD): 4x1 or 2x2 with larger cards +- Use CSS Grid with responsive breakpoints + +**Clock Display:** +- Format: HH:MM:SS (24-hour) +- Updates every second +- Also shown in Header (this is secondary/alternative) + +**Comm Status:** +- ASU: Signal strength (0-31) +- dBm: Signal power +- Percentage: Derived from ASU + + + + + + task 1: Create RainfallCard component + src/app/components/RainfallCard.tsx, src/app/components/__tests__/RainfallCard.test.tsx + + - Displays rainfall value in mm (XX.X format) + - Shows label (Today/Hourly/MAR Acc/Yearly Acc) + - Color-coded border or background + - Large readable text + - Updates when value changes + + + 1. Create src/app/components/RainfallCard.tsx: + - Props: label (string), value (number), variant (today|hourly|monthly|yearly) + - Format value to one decimal place + - Color variants: today=blue, hourly=cyan, monthly=green, yearly=purple + - Use shadcn/ui Card component as base + - Min-height 100px for touch targets + - Value text: text-3xl font-bold + - Label text: text-sm text-muted-foreground + - Add rain icon from Lucide (CloudRain) + + 2. Create tests: + - Renders label and formatted value + - Applies correct color variant + - Updates on value change + - Has minimum dimensions + + + npm test -- --run RainfallCard 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + RainfallCard component with 4 color variants created + + + + task 2: Create ClockDisplay component + src/app/components/ClockDisplay.tsx, src/app/components/__tests__/ClockDisplay.test.tsx + + - Displays HH:MM:SS in 24-hour format + - Updates every second + - Shows date (YYYY-MM-DD) below or beside + - Cleans up interval on unmount + + + 1. Create src/app/components/ClockDisplay.tsx: + - Use useState for current time + - Use useEffect with setInterval (1000ms) + - Format with date-fns or native Intl + - Display: Time large, date smaller + - Optional: Sync with sensor store timestamp + - Cleanup interval in useEffect return + - Touch-friendly container + + 2. Create tests: + - Renders time in correct format + - Renders date in correct format + - Cleanup works on unmount (no memory leaks) + + + npm test -- --run ClockDisplay 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + ClockDisplay with real-time updates created + + + + task 3: Create CommStatus component + src/app/components/CommStatus.tsx, src/app/components/__tests__/CommStatus.test.tsx + + - Shows ASU (0-31), dBm, percentage + - Visual indicator for signal strength + - Updates from sensor store + - Compact for header placement + + + 1. Create src/app/components/CommStatus.tsx: + - Props: asu (number), dBm (number), percentage (number) + - Calculate percentage from ASU if not provided: (asu/31)*100 + - Visual indicator: signal bars or progress + - Color: green (good), yellow (fair), red (poor) + - Compact display: icon + brief text + - Use Lucide Signal icon + + 2. Create tests: + - Displays all three values + - Calculates percentage correctly + - Shows appropriate color indicator + + + npm test -- --run CommStatus 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + CommStatus component with signal visualization + + + + task 4: Create RainfallView dashboard + src/app/components/views/RainfallView.tsx, src/app/components/__tests__/RainfallView.test.tsx + + - Displays 4 RainfallCards in grid + - Shows optional ClockDisplay + - Shows CommStatus + - Uses sensor store data + - Responsive layout for both modes + + + 1. Create/update src/app/components/views/RainfallView.tsx: + - Import useSensorStore + - Import RainfallCard, ClockDisplay, CommStatus + - Get rainfall data from store + - Grid layout: 2 cols on mobile/kiosk, 4 cols on desktop/remote + - Use CSS Grid: grid-cols-2 lg:grid-cols-4 + - Gap: gap-4 + - Padding: p-4 + - Add section title: "Rainfall Monitor" + - Show last update timestamp + - Responsive font sizes + + 2. Create tests: + - Renders all 4 rainfall cards + - Passes correct data to each card + - Has responsive grid layout + - Updates when store changes + + + npm test -- --run RainfallView 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + RainfallView dashboard with responsive grid created + + + + task 5: Implement dual-mode responsive layout + src/app/components/views/RainfallView.tsx, src/styles/responsive.css + + - Port 8080: Compact layout for 1024x600 + - Port 9090: Expanded layout for Full HD + - Cards resize appropriately + - Font sizes scale with mode + + + 1. Update RainfallView with mode-aware styling: + - Import useDisplayMode from hooks + - Apply conditional classes based on mode + - Kiosk: More compact padding, smaller gaps + - Remote: Larger spacing, bigger fonts + + 2. Create src/styles/responsive.css if needed: + - Define mode-specific utilities + - Or use Tailwind classes inline + + 3. Test responsive behavior: + - Verify grid adapts to viewport + - Cards maintain touch target size + - No horizontal scroll at 1024px + + + grep -n "useDisplayMode\|kiosk\|remote" src/app/components/views/RainfallView.tsx 2>/dev/null | head -10 + + Dual-mode layout responds to port number + + + + + +After completing all tasks: +1. Run `npm test` — all tests should pass +2. Verify dashboard exists: `ls src/app/components/views/RainfallView.tsx` +3. Check responsive classes: `grep -E "grid-cols-|lg:grid-cols" src/app/components/views/RainfallView.tsx` +4. Verify sensor store connection: `grep "useSensorStore" src/app/components/views/RainfallView.tsx` + + + +- RainfallCard component with 4 color variants exists +- RainfallView displays 4 cards in responsive grid +- ClockDisplay shows HH:MM:SS updating every second +- CommStatus shows ASU/dBm with visual indicator +- Layout adapts to kiosk (1024x600) and remote (Full HD) modes +- All components tested and passing + + + +After completion, create `.planning/phases/01-foundation-dashboard/01-03-SUMMARY.md` + diff --git a/.planning/phases/01-foundation-dashboard/01-04-PLAN.md b/.planning/phases/01-foundation-dashboard/01-04-PLAN.md new file mode 100644 index 000000000..c050c197e --- /dev/null +++ b/.planning/phases/01-foundation-dashboard/01-04-PLAN.md @@ -0,0 +1,357 @@ +--- +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 +--- + + +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. + + + +@./.opencode/get-shit-done/workflows/execute-plan.md +@./.opencode/get-shit-done/templates/summary.md + + + +@.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 + + + + + + task 1: Create LoginIndicator component + src/app/components/LoginIndicator.tsx, src/app/components/__tests__/LoginIndicator.test.tsx + + - Shows green status when isLoggedIn=true + - Shows gray/red status when isLoggedIn=false + - Compact size for header placement + - Accessible (aria-label) + + + 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 + + + npm test -- --run LoginIndicator 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + LoginIndicator with accessible states created + + + + task 2: Update Sidebar navigation + src/app/components/Sidebar.tsx, src/app/components/__tests__/Sidebar.test.tsx + + - Shows Dashboard, Settings, Calibration, Flash Memory links + - Touch-friendly items (44px+ height) + - Active state for current route + - Collapsible sections + + + 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 + + + npm test -- --run Sidebar 2>&1 | grep -E "(PASS|FAIL|✓|✗)" + + Sidebar with 4 navigation items created + + + + task 3: Configure routes for all views + src/app/routes.ts, src/app/components/views/SettingsView.tsx, src/app/components/views/CalibrationView.tsx, src/app/components/views/FlashMemoryView.tsx + + - All routes defined in routes.ts + - Settings, Calibration, Flash Memory have placeholder views + - Routes use lazy loading where appropriate + - Navigation between routes works + + + 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 + + + grep -n "SettingsView\|CalibrationView\|FlashMemoryView" src/app/routes.ts 2>/dev/null | head -10 + + Routes configured for all 4 main sections + + + + task 4: Integrate LoginIndicator into Header + src/app/components/Header.tsx + + - Header displays LoginIndicator + - Gets login state from sensor store + - Compact placement in header row + + + 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 + + + grep -n "LoginIndicator" src/app/components/Header.tsx 2>/dev/null | head -5 + + LoginIndicator integrated into Header + + + + task 5: Configure bundle size checking + .bundlesize.json, vite.config.ts, package.json + + - Bundle size limit configured (170KB target, 200KB max) + - Build fails if bundle exceeds limit + - Size checking integrated in CI + + + 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` + + + test -f .bundlesize.json && echo "Config exists" || echo "Config missing" + + Bundle size checking configured + + + + task 6: Build and verify bundle size + dist/ + + - Production build succeeds + - Bundle size under 200KB limit + - Ideally under 170KB target + - No build errors or warnings + + + 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 + + + ls -lh dist/assets/*.js 2>/dev/null | awk '{print $5, $9}' | head -5 + + Bundle built and size verified (target: <170KB, limit: <200KB) + + + + task 7: Final integration test + src/app/App.tsx + + - Full app renders without errors + - Navigation works between routes + - Dashboard shows all data + - No console errors + + + 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 + + + npm test 2>&1 | tail -5 + + Full integration verified, all tests passing + + + + + +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` + + + +- 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 + + + +After completion, create `.planning/phases/01-foundation-dashboard/01-04-SUMMARY.md` + diff --git a/.planning/phases/01-foundation-dashboard/01-CONTEXT.md b/.planning/phases/01-foundation-dashboard/01-CONTEXT.md new file mode 100644 index 000000000..404f0892c --- /dev/null +++ b/.planning/phases/01-foundation-dashboard/01-CONTEXT.md @@ -0,0 +1,104 @@ +# Phase 1 Context - Foundation & Dashboard + +**Phase:** 1 +**Created:** 2026-03-13 + +--- + +## Decisions Made + +### Dashboard Layout + +- **Layout**: Keep current 3-panel layout (Sidebar | Header + Content) +- **Header content**: All in header - Logo, Time, Date, Station ID, Comm Status, Version, Login Status, Solar Voltage, Battery Voltage +- **Logo**: Use TCK logo from `/logo/` directory +- **Rainfall cards**: Grid layout with color-coded cards (Today/Hourly/MAR/Yearly) + +### Real-time Data Strategy + +- **Approach**: Real API with mock fallback +- **Polling interval**: User adjustable (stored in settings) +- **Default polling**: 5 seconds +- **Implementation**: Create API client with mock data fallback for demo/offline + +### Dual-Mode Display + +- **Detection**: By port number + - Port 8080 → Kiosk mode (1024x600) + - Port 9090 → Full HD remote mode +- **Implementation**: `window.location.port` detection in app root +- **Remote mode**: Expanded dashboard with larger fonts and more information visible + +### State Management + +- **Library**: Zustand (~1.1KB, research-recommended) +- **Sensor data**: Global state in Zustand store +- **Settings persistence**: localStorage + sync to backend when online +- **Architecture**: Offline-first - settings persist locally, sync in background + +--- + +## Code Context + +### Existing Assets + +**Routes** (`sample_interface/src/app/routes.ts`): +- Dashboard at `/` (RainfallView) +- Settings under `/utility/*` +- Calibration at `/calibration` +- Flash Memory at `/flash-memory` + +**Components**: +- `DashboardLayout.tsx` - Main layout wrapper +- `Header.tsx` - Header component (needs modernization) +- `Sidebar.tsx` - Navigation sidebar +- UI components in `components/ui/` - shadcn/ui components available + +**Guidelines**: `sample_interface/guidelines/Guidelines.md` - Full UI specification + +### Key Files to Modify + +- `src/app/App.tsx` - Add mode detection (port-based) +- `src/app/components/Header.tsx` - Modernize for Phase 1 +- `src/app/components/views/RainfallView.tsx` - Main dashboard view +- New: `src/app/stores/sensorStore.ts` - Zustand store for sensor data +- New: `src/app/api/client.ts` - API client with mock fallback + +### Dependencies to Add + +- `zustand` - State management (research recommended) + +--- + +## Prior Decisions (from PROJECT.md) + +- React/TypeScript stack +- shadcn/ui components +- Tailwind CSS +- Two-port architecture (8080/9090) +- <200KB bundle target for Pi Zero 2 W + +--- + +## Success Criteria + +1. User sees real-time rainfall data (Today, Hourly, MAR Acc, Yearly Acc) updating automatically +2. User sees solar and battery voltage with visual status indicators +3. User sees station ID, version info, current time/date, and communication status +4. User sees login status indicator on dashboard +5. Dashboard loads in <2 seconds on Pi Zero 2 W +6. All touch targets are minimum 44px (preferably 56px) +7. Dashboard displays correctly in both 1024x600 (kiosk) and Full HD (remote) modes +8. Navigation menu provides access to Settings, Calibration, and Flash Memory sections + +--- + +## Out of Scope + +- CSV processing (Phase 3) +- Network stack implementation (Phase 3) +- Full settings implementation (Phase 2) + +--- + +*Context for Phase 1 planning - decisions that guide research and planning* diff --git a/.planning/phases/01-foundation-dashboard/01-RESEARCH.md b/.planning/phases/01-foundation-dashboard/01-RESEARCH.md new file mode 100644 index 000000000..37b1b1c40 --- /dev/null +++ b/.planning/phases/01-foundation-dashboard/01-RESEARCH.md @@ -0,0 +1,176 @@ +# Phase 1: Foundation & Dashboard - Research + +**Phase:** 1 +**Researched:** 2026-03-13 +**Confidence:** HIGH + +--- + +## Research Question + +What do we need to know to PLAN Phase 1 well? Phase 1 delivers a modern, performant dashboard for rainfall monitoring on Raspberry Pi Zero 2 W hardware. + +--- + +## Key Findings + +### Dashboard Requirements (from CONTEXT.md + ROADMAP) + +**Must Display:** +1. Real-time rainfall data (Today, Hourly, MAR Acc, Yearly Acc) — DASH-01 +2. Solar voltage reading — DASH-02 +3. Battery voltage with HIGH/LOW status indicator — DASH-03 +4. Station ID and version info — DASH-04 +5. Real-time clock (HH:MM:SS) and date (YYYY-MM-DD) — DASH-05 +6. Communication status (ASU/dBm/percentage) — DASH-06 +7. Login status indicator — DASH-07 + +**UI Requirements:** +- Modern, compact design optimized for 1024x600 — UI-01 +- Touch-friendly (44px+ touch targets) — UI-02 +- Responsive layout for Full HD remote access — UI-03 +- <200KB JS bundle for Pi Zero 2 W — UI-04 +- Navigation menu to Settings, Calibration, Flash Memory — UI-05 + +### Implementation Decisions (Locked) + +**Layout:** +- 3-panel layout: Sidebar | Header + Content +- Header contains: Logo, Time, Date, Station ID, Comm Status, Version, Login Status, Solar Voltage, Battery Voltage +- Rainfall cards in grid with color-coding + +**Dual-Mode Display:** +- Port 8080 = Kiosk mode (1024x600) +- Port 9090 = Full HD remote mode +- Detection via `window.location.port` + +**State Management:** +- Zustand (~1.1KB) for sensor data +- localStorage + backend sync for settings persistence +- Offline-first architecture + +**Real-time Data:** +- Polling-based (default 5s, user adjustable) +- API client with mock fallback for demo/offline + +### Technology Stack (from existing research) + +**Confirmed Stack:** +- React 19.2.4 — 15-20% smaller than v18, required for shadcn/ui +- Vite 8.0.0 — Fast HMR, superior tree-shaking +- Tailwind CSS 4.2.0 — Zero-runtime CSS +- React Router 7.x — Type-safe routing +- Zustand 5.0.11 — ~1.1KB state management +- shadcn/ui — Tree-shakeable components + +**Bundle Budget:** <170KB initial JS (200KB limit with buffer) + +### Critical Pitfalls to Avoid + +1. **Performance constraints** — Pi Zero 2 W has 1GHz quad-core, 512MB RAM +2. **Layout thrashing** — Use React.memo, debounce updates +3. **Unbounded data** — Circular buffers for historical data +4. **Kiosk anti-patterns** — 44px+ touch targets, no hover-only states +5. **Memory leaks** — Proper useEffect cleanup +6. **Blocking operations** — Keep CSV/network for Phase 3 + +### File Structure (from existing codebase) + +**Routes:** +- `/` — Dashboard (RainfallView) +- `/utility/*` — Settings +- `/calibration` — Calibration +- `/flash-memory` — Flash Memory + +**Components to Create/Modify:** +- `App.tsx` — Add mode detection +- `Header.tsx` — Modernize with all status info +- `RainfallView.tsx` — Main dashboard view +- `stores/sensorStore.ts` — New Zustand store +- `api/client.ts` — API client with mock fallback + +### Dependencies to Add + +- `zustand` — State management + +--- + +## Validation Architecture + +### Testable Behaviors + +1. **Dashboard displays all required data fields** + - Rainfall metrics (Today, Hourly, MAR, Yearly) + - Voltage readings (Solar, Battery with status) + - Station info (ID, Version) + - Time/Date display + - Communication status + - Login indicator + +2. **Touch targets meet minimum size** + - All interactive elements ≥44px + - Preferably 56px for primary actions + +3. **Dual-mode display works** + - Port 8080 renders kiosk layout + - Port 9090 renders expanded layout + +4. **Performance budget met** + - Initial JS <170KB gzipped + - LCP <2s on Pi Zero 2 W + +5. **Data updates in real-time** + - Polling interval configurable + - Updates visible without refresh + +6. **Navigation accessible** + - Sidebar shows Settings, Calibration, Flash Memory links + - Touch-friendly navigation + +### Observable Outputs + +| Artifact | Location | Verification | +|----------|----------|--------------| +| Dashboard component | `src/app/components/views/RainfallView.tsx` | Renders all 7 data fields | +| Header component | `src/app/components/Header.tsx` | Shows status indicators | +| Sensor store | `src/app/stores/sensorStore.ts` | Zustand store with mock data | +| API client | `src/app/api/client.ts` | Fetch with fallback | +| Mode detection | `src/app/App.tsx` | Port-based routing | +| Bundle size | Build output | <170KB initial JS | + +### Key Connections + +- Sensor store → Dashboard view (data flow) +- API client → Sensor store (update trigger) +- Mode detection → Layout components (responsive behavior) +- Header → Sensor store (status indicators) + +--- + +## Standard Stack Recommendations + +### What to Use +- React 19 + Vite 8 + Tailwind 4 (established) +- Zustand for state (research-validated) +- React Router 7 for navigation +- shadcn/ui Card, Badge, Separator components +- Lucide React for icons + +### What NOT to Use +- Redux Toolkit (too heavy) +- TanStack Query (overkill for polling) +- Material UI (large CSS-in-JS) +- Real-time libraries (WebSocket/SSE not needed for 5s polling) + +--- + +## Gaps Requiring Decisions + +1. **API endpoint contracts** — Coordinate with Python backend team for exact data format +2. **Color scheme for status indicators** — Use existing TCK brand colors? +3. **Historical chart implementation** — Recharts is already in dependencies +4. **Polling interval bounds** — Min/max acceptable values? + +--- + +*Research synthesis for Phase 1 planning. Based on project CONTEXT.md, ROADMAP.md, and existing codebase research.* diff --git a/.planning/phases/01-foundation-dashboard/01-VALIDATION.md b/.planning/phases/01-foundation-dashboard/01-VALIDATION.md new file mode 100644 index 000000000..e123783f5 --- /dev/null +++ b/.planning/phases/01-foundation-dashboard/01-VALIDATION.md @@ -0,0 +1,90 @@ +--- +phase: 1 +slug: foundation-dashboard +status: draft +nyquist_compliant: false +wave_0_complete: false +created: 2026-03-13 +--- + +# Phase 1 — Validation Strategy + +> Validation contract for Foundation & Dashboard phase. Focus on component rendering, bundle size, and touch-friendly interactions. + +--- + +## Test Infrastructure + +| Property | Value | +|----------|-------| +| **Framework** | Vitest (bundled with Vite) | +| **Config file** | `vitest.config.ts` — Wave 0 creates if missing | +| **Quick run command** | `npm test` or `pnpm test` | +| **Full suite command** | `npm run test:coverage` | +| **Estimated runtime** | ~10 seconds (unit tests only) | + +--- + +## Sampling Rate + +- **After every task commit:** Run `npm test` +- **After every plan wave:** Run `npm run test:coverage` +- **Before `/gsd-verify-work`:** Full suite must be green +- **Max feedback latency:** 15 seconds + +--- + +## Per-task Verification Map + +| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status | +|---------|------|------|-------------|-----------|-------------------|-------------|--------| +| 1-01-01 | 01 | 1 | UI-01 | component | `npm test -- Header.test.tsx` | ❌ W0 | ⬜ pending | +| 1-01-02 | 01 | 1 | DASH-01 | component | `npm test -- RainfallView.test.tsx` | ❌ W0 | ⬜ pending | +| 1-02-01 | 02 | 1 | DASH-02 | component | `npm test -- VoltageDisplay.test.tsx` | ❌ W0 | ⬜ pending | +| 1-02-02 | 02 | 1 | DASH-03 | component | `npm test -- BatteryStatus.test.tsx` | ❌ W0 | ⬜ pending | +| 1-03-01 | 03 | 2 | DASH-04 | component | `npm test -- StationInfo.test.tsx` | ❌ W0 | ⬜ pending | +| 1-03-02 | 03 | 2 | DASH-05 | component | `npm test -- ClockDisplay.test.tsx` | ❌ W0 | ⬜ pending | +| 1-03-03 | 03 | 2 | DASH-06 | component | `npm test -- CommStatus.test.tsx` | ❌ W0 | ⬜ pending | +| 1-04-01 | 04 | 2 | DASH-07 | component | `npm test -- LoginIndicator.test.tsx` | ❌ W0 | ⬜ pending | +| 1-04-02 | 04 | 2 | UI-02 | e2e/visual | Manual verification | N/A | ⬜ pending | +| 1-04-03 | 04 | 2 | UI-03 | e2e/visual | Manual verification | N/A | ⬜ pending | +| 1-04-04 | 04 | 3 | UI-04 | build | `npm run build && bundlesize` | ❌ W0 | ⬜ pending | +| 1-04-05 | 04 | 3 | UI-05 | component | `npm test -- Navigation.test.tsx` | ❌ W0 | ⬜ pending | + +*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky* + +--- + +## Wave 0 Requirements + +- [ ] `src/app/components/__tests__/Header.test.tsx` — Header component tests +- [ ] `src/app/components/__tests__/RainfallView.test.tsx` — Dashboard view tests +- [ ] `src/app/stores/__tests__/sensorStore.test.ts` — Zustand store tests +- [ ] `vitest.config.ts` — Vitest configuration +- [ ] `src/test/setup.ts` — Test utilities and mocks +- [ ] `.bundlesize.json` — Bundle size budget configuration + +--- + +## Manual-Only Verifications + +| Behavior | Requirement | Why Manual | Test Instructions | +|----------|-------------|------------|-------------------| +| Touch target sizing | UI-02 | Visual measurement | Verify all buttons ≥44px using DevTools inspector | +| Kiosk mode rendering | UI-03 | Device-specific | Test on 1024x600 display, verify no horizontal scroll | +| Remote mode rendering | UI-03 | Device-specific | Test on Full HD display, verify layout expands | +| Pi Zero performance | UI-04 | Hardware-specific | Deploy to Pi, measure LCP with Chrome DevTools | +| Real-time data updates | DASH-01 | Time-based | Verify polling updates display every 5 seconds | + +--- + +## Validation Sign-Off + +- [ ] All tasks have `` verify or Wave 0 dependencies +- [ ] Sampling continuity: no 3 consecutive tasks without automated verify +- [ ] Wave 0 covers all MISSING references +- [ ] No watch-mode flags +- [ ] Feedback latency < 15s +- [ ] `nyquist_compliant: true` set in frontmatter + +**Approval:** pending 2026-03-13