Gas Stations Feature - Complete Implementation Plan (K8s-Aligned Secrets) Overview Implement complete Gas Stations feature with full integration, map visualization, backend improvements, and comprehensive testing. Uses K8s-aligned runtime secrets pattern for frontend Google Maps API key. Phase 1: Frontend Secrets Infrastructure (K8s-Aligned) 1.1 Create Frontend Config Loader Create frontend/scripts/load-config.sh: - Bash script that runs as container entrypoint - Reads /run/secrets/google-maps-api-key - Generates /usr/share/nginx/html/config.js with: window.CONFIG = { googleMapsApiKey: '...' } - Falls back to empty string if secret not found - Logs success/failure for debugging 1.2 Update Frontend Dockerfile Modify frontend/Dockerfile: - Copy load-config.sh into image - Make script executable - Add SECRETS_DIR environment variable (default: /run/secrets) - Update CMD to run script before nginx: ["sh", "-c", "/app/load-config.sh && nginx -g 'daemon off;'"] 1.3 Update Frontend index.html Modify frontend/index.html: - Add before app bundle - Config loads before React app initializes - Add TypeScript types for window.CONFIG 1.4 Create Frontend Config Types Create frontend/src/core/config/config.types.ts: - Interface for window.CONFIG - Export typed accessor: getConfig() - Runtime validation (throw if required values missing) 1.5 Update docker-compose.yml Add to mvp-frontend service: volumes: - ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro environment: SECRETS_DIR: /run/secrets 1.6 Document Pattern Create frontend/docs/RUNTIME-CONFIG.md: - Explain runtime config pattern - How to add new runtime secrets - K8s deployment notes - Local development setup Phase 2: Backend Improvements 2.1 Add Circuit Breaker Pattern - Install opossum package: backend/package.json - Create backend/src/features/stations/external/google-maps/google-maps.circuit-breaker.ts - Update google-maps.client.ts to wrap API calls - Follow platform feature pattern (reference: backend/src/features/platform/) - Config: 10s timeout, 50% threshold, 30s reset 2.2 Backend Unit Tests Create backend/src/features/stations/tests/unit/: - stations.service.test.ts - Business logic, user isolation - stations.repository.test.ts - Database operations, SQL queries - google-maps.client.test.ts - API mocking, distance calculation - Use test fixtures for sample data 2.3 Backend Integration Tests Create backend/src/features/stations/tests/integration/: - stations.api.test.ts - Full endpoint testing with real database - saved-stations.flow.test.ts - Save, retrieve, delete workflow - Use test database (configured in test setup) 2.4 Test Fixtures Create backend/src/features/stations/tests/fixtures/: - mock-stations.ts - Sample station objects - mock-google-response.ts - Mock Google Places API responses - test-coordinates.ts - Test location data 2.5 Cache Cleanup Job Create backend/src/features/stations/jobs/cache-cleanup.job.ts: - Scheduled job (daily at 2 AM) - Delete station_cache entries older than 24 hours - Log cleanup metrics (rows deleted) - Register in main app scheduler Phase 3: Frontend Foundation 3.1 Type Definitions Create frontend/src/features/stations/types/stations.types.ts: - Station interface (id, name, address, location, rating, distance, photoUrl, prices) - SavedStation interface (extends Station, adds nickname, notes, isFavorite) - SearchRequest interface (latitude, longitude, radius?, fuelType?) - SearchResponse interface - MapMarker interface for map pins 3.2 API Client Create frontend/src/features/stations/api/stations.api.ts: - searchStations(request: SearchRequest): Promise - saveStation(placeId: string, data: SaveStationData): Promise - getSavedStations(): Promise - deleteSavedStation(placeId: string): Promise - Use axios with proper error handling - Add request/response logging 3.3 React Query Hooks Create frontend/src/features/stations/hooks/: useStationsSearch.ts: - Mutation hook for search (not cached by default) - Accepts SearchRequest - Returns Station array - Integrates with useGeolocation useSavedStations.ts: - Query hook with React Query caching - Auto-refetch on window focus - Invalidate on save/delete mutations useSaveStation.ts: - Mutation hook - Optimistic updates to saved list - Invalidates saved stations cache on success useDeleteStation.ts: - Mutation hook - Optimistic removal from list - Invalidates cache on success useGeolocation.ts: - Browser Geolocation API wrapper - Permission handling - Error states (permission denied, unavailable, timeout) - Returns current position 3.4 Google Maps Integration Create frontend/src/features/stations/utils/maps-loader.ts: - Load Google Maps JavaScript API dynamically - Use runtime config: getConfig().googleMapsApiKey - Promise-based API - Singleton pattern (load once) - TypeScript types for Google Maps objects Phase 4: Frontend Components 4.1 Core Components Create frontend/src/features/stations/components/: StationCard.tsx: - Material-UI Card component - Props: station, onSave, onDelete, isSaved - Display: name (Typography h6), address (Typography body2), distance (Chip) - Rating stars (Rating component) - Price display (if available) - Photo thumbnail (if available) - Save/Unsave IconButton (BookmarkIcon / BookmarkBorderIcon) - Directions link (opens Google Maps) - Mobile: 44px min height, touch targets - Desktop: hover effects StationsList.tsx: - Container for search results - Props: stations[], loading, error, onSaveStation - Grid layout (responsive: 1 col mobile, 2 cols tablet, 3 cols desktop) - Loading skeleton (Skeleton components) - Empty state: "No stations found. Try adjusting your search." - Error state with retry button SavedStationsList.tsx: - User's favorites display - Props: savedStations[], onDelete, onSelect - List layout (vertical) - Show nickname if set, otherwise station name - Notes preview (truncated) - Distance from current location (optional) - Delete IconButton - Empty state: "No saved stations yet. Save stations from search results." StationsSearchForm.tsx: - Form with search parameters - Current Location button (uses useGeolocation) - Manual lat/lng inputs (number fields) - Radius slider (FormControl, Slider: 1-25 miles, default 5) - Fuel type select (optional filter) - Search button (LoadingButton) - Loading states on button - Validation: require location (current OR manual) - Error display for geolocation failures StationMap.tsx: - Google Maps embed component - Props: stations[], center, zoom, onMarkerClick - Load map using maps-loader utility - Station markers (color-coded: blue=normal, gold=saved) - Current location marker (red pin) - Info windows on marker click (station details + save button) - Directions button in info window - Zoom controls, pan controls - Responsive height (300px mobile, 500px desktop) 4.2 UI Utilities Create frontend/src/features/stations/utils/: distance.ts: - formatDistance(meters: number): string - "1.2 mi" or "0.3 mi" - calculateDistance(lat1, lng1, lat2, lng2): number - Haversine formula location.ts: - getCurrentPosition(): Promise - requestLocationPermission(): Promise - Error handling helpers map-utils.ts: - createStationMarker(station, map, isSaved): google.maps.Marker - createInfoWindow(station): google.maps.InfoWindow - fitBoundsToMarkers(map, markers): void Phase 5: Desktop Implementation 5.1 Desktop Page Create frontend/src/features/stations/pages/StationsPage.tsx: - Layout: Grid container - Left column (60%): StationMap (full height) - Right column (40%): - StationsSearchForm at top - Tabs: "Search Results" | "Saved Stations" - Tab 1: StationsList - Tab 2: SavedStationsList - State management: - searchResults (from search mutation) - savedStations (from query) - selectedStation (for map focus) - Effects: - Load saved stations on mount - Update map when search results change - Mobile breakpoint: Stack vertically (map on top) 5.2 Desktop Routing Update frontend/src/App.tsx (line 556): - Remove: Stations (TODO)} /> - Add: } /> - Import: import { StationsPage } from './features/stations/pages/StationsPage' - Ensure route is inside ProtectedRoute wrapper Phase 6: Mobile Implementation 6.1 Mobile Screen Create frontend/src/features/stations/pages/StationsMobileScreen.tsx: - BottomNavigation with 3 tabs: Search, Saved, Map - Tab 0 (Search): - StationsSearchForm (compact mode) - StationsList (vertical scroll) - Pull-to-refresh support - Tab 1 (Saved): - SavedStationsList (full screen) - Swipe to delete gestures - Tab 2 (Map): - StationMap (full screen, 100vh) - Floating search button (FAB) to go back to search tab - Bottom sheet for station details (SwipeableDrawer) - Touch targets: 44px minimum - Safe area insets for notched devices 6.2 Mobile Routing Update frontend/src/App.tsx: - Add mobile route: } /> - Update mobile navigation items (add stations) - Ensure icon is interactive (onClick navigation) Phase 7: Fuel Logs Integration 7.1 Station Picker Component Create frontend/src/features/fuel-logs/components/StationPicker.tsx: - Autocomplete component (Material-UI) - Props: value, onChange, userLocation? - Options: - Saved stations (shown first, grouped) - Nearby stations (if location available) - Manual text input (freeSolo mode) - Option rendering: - Station name (primary) - Distance + address (secondary) - Bookmark icon if saved - Debounced search (300ms) - Loading indicator while searching - Fallback to text input if API fails 7.2 Update Fuel Log Form Modify frontend/src/features/fuel-logs/components/FuelLogForm.tsx: - Replace LocationInput with StationPicker - Props: pass user location from geolocation - Value binding to stationName field - Maintain backward compatibility (accepts text string) - Show nearby stations on field focus - Optional: "Save this station" checkbox when new station entered 7.3 Integration Features Optional enhancements: - In FuelLogCard, link station name to station details - In StationsPage, show "Recent fuel logs at this station" - Suggest saving station after logging fuel at new location Phase 8: Testing 8.1 Backend Tests Execution cd backend npm test -- features/stations - Verify all unit tests pass - Verify all integration tests pass - Check coverage report (aim for >80%) - Fix any failing tests 8.2 Frontend Component Tests Create frontend/src/features/stations/__tests__/components/: - StationCard.test.tsx - Rendering, save/delete actions - StationsList.test.tsx - List rendering, empty/error states - SavedStationsList.test.tsx - Saved list, delete action - StationsSearchForm.test.tsx - Form validation, submission - StationMap.test.tsx - Map initialization (with mocks) - Use React Testing Library - Mock API calls with MSW (Mock Service Worker) 8.3 Frontend Page Tests Create frontend/src/features/stations/__tests__/pages/: - StationsPage.test.tsx - Desktop page integration - StationsMobileScreen.test.tsx - Mobile navigation, tabs - Test user workflows: search, save, delete - Test error handling and loading states 8.4 API Client Tests Create frontend/src/features/stations/api/__tests__/: - stations.api.test.ts - Test all API methods - Mock axios responses - Test error handling (network, 401, 500) - Verify request payloads 8.5 React Query Hook Tests Create frontend/src/features/stations/hooks/__tests__/: - Test each hook in isolation - Mock React Query - Test loading, success, error states - Test cache invalidation logic - Test optimistic updates 8.6 E2E Tests Create frontend/cypress/e2e/stations.cy.ts (or similar E2E framework): - Test: Search for nearby stations - Test: Save a station to favorites - Test: View saved stations list - Test: Delete a saved station - Test: Use station in fuel log entry - Test: Mobile navigation flow - Test: Map marker click and info window - Use real backend API in test environment Phase 9: Documentation 9.1 Backend Feature Docs Create backend/src/features/stations/docs/: ARCHITECTURE.md: - System design overview - Data flow diagrams - External dependencies (Google Maps API) - Caching strategy - Database schema - Circuit breaker pattern API.md: - All endpoint documentation - Request/response examples (curl commands) - Authentication requirements - Rate limits and quotas - Error responses TESTING.md: - How to run tests - Test database setup - Writing new tests - Coverage goals GOOGLE-MAPS-SETUP.md: - API key creation in Google Cloud Console - Required APIs to enable - Quota management - Cost estimation - Security best practices 9.2 Frontend Feature Docs Create frontend/src/features/stations/README.md: - Feature overview - Component hierarchy - Hook usage examples - Adding new functionality - Runtime config pattern - Testing guide 9.3 Runtime Config Documentation Already created in Phase 1.6: frontend/docs/RUNTIME-CONFIG.md 9.4 Update Main Documentation Update docs/README.md: - Add "Gas Stations" to features list - Link to backend and frontend docs - Mention Google Maps integration Update backend/src/features/stations/README.md: - Complete feature overview - Configuration requirements - API endpoints summary - Testing instructions - Deployment notes Phase 10: Validation & Polish 10.1 Docker Build & Test make rebuild # Rebuild all containers make logs # Watch for errors make migrate # Run database migrations make test # Run all tests Verify: - Frontend config.js is generated at startup - Backend loads Google Maps API key from secret - All containers start without errors - Database migrations create tables - Tests pass in container environment 10.2 Linting & Formatting # Backend cd backend && npm run lint && npm run type-check # Frontend cd frontend && npm run lint && npm run type-check Fix all issues (MUST be 100% green per CLAUDE.md): - Zero TypeScript errors - Zero ESLint warnings - Zero Prettier formatting issues 10.3 Manual Testing - Desktop - Open https://motovaultpro.com/stations in browser - Test geolocation permission flow - Search for stations near current location - Verify map loads with markers - Click marker, verify info window - Save a station - View saved stations list - Delete a saved station - Test manual lat/lng search - Test with no results (remote location) - Test error handling (API failure) 10.4 Manual Testing - Mobile - Open mobile nav, navigate to Stations - Test all 3 tabs (Search, Saved, Map) - Verify 44px touch targets - Test swipe gestures - Test bottom sheet interactions - Verify responsive layout - Test on actual mobile device (if possible) 10.5 Fuel Logs Integration Testing - Navigate to Fuel Logs - Create new fuel log entry - Test StationPicker autocomplete - Select a saved station - Enter new station name - Submit fuel log - Verify station name is saved 10.6 Cross-Browser Testing Test on: - Chrome (latest) - Safari (latest) - Firefox (latest) - Mobile Safari (iOS) - Mobile Chrome (Android) 10.7 Performance Validation - Check map load time (<2s) - Verify API response times (<500ms) - Check Redis cache hits (backend logs) - Monitor Google Maps API quota usage - Verify no memory leaks (React DevTools Profiler) Phase 11: Deployment Preparation 11.1 Configuration Checklist Backend: - Secret file exists: ./secrets/app/google-maps-api-key.txt - Docker compose mounts secret to /run/secrets/google-maps-api-key - Backend config loader reads from SECRETS_DIR Frontend: - Secret file mounted: /run/secrets/google-maps-api-key - Entrypoint script generates /usr/share/nginx/html/config.js - index.html loads config.js before app - App accesses via getConfig().googleMapsApiKey 11.2 Database Migration make migrate Verify: - Migration 001_create_stations_tables.sql runs successfully - Tables created: station_cache, saved_stations - Indexes created on both tables - Test queries work 11.3 Secrets Verification # Verify backend can read secret docker compose exec mvp-backend cat /run/secrets/google-maps-api-key # Verify frontend generates config docker compose exec mvp-frontend cat /usr/share/nginx/html/config.js # Should show: window.CONFIG = { googleMapsApiKey: "..." } 11.4 Health Checks - Verify /health endpoint includes stations feature - Test stations API endpoints with curl: # Get JWT token first TOKEN="your-jwt-token" # Search stations curl -X POST http://localhost:3001/api/stations/search \ -H "Authorization: Bearer $TOKEN" \ -H "Content-Type: application/json" \ -d '{"latitude": 37.7749, "longitude": -122.4194}' # Get saved stations curl http://localhost:3001/api/stations/saved \ -H "Authorization: Bearer $TOKEN" 11.5 Production Readiness Update config/app/production.yml.example: - Document Google Maps API key requirement - Add example configuration Create deployment checklist: - Google Maps API key in secrets - Frontend container mounts secret - Backend container mounts secret - Database migrations run - All tests pass - Linters pass - Manual testing complete - Performance acceptable - Documentation complete Success Criteria (per CLAUDE.md) - All linters pass with zero issues - All tests pass (backend + frontend) - Feature works end-to-end on desktop - Feature works end-to-end on mobile - Fuel logs integration functional - Map visualization working - Google Maps API integration live via runtime secrets - K8s-aligned secrets pattern implemented - Documentation complete - Old code deleted (no orphaned files) K8s Secrets Pattern Summary Backend (already implemented): - Secret mounted: /run/secrets/google-maps-api-key - Read at runtime by config-loader.ts - Never in environment variables or code Frontend (new implementation): - Secret mounted: /run/secrets/google-maps-api-key - Read at container startup by entrypoint script - Injected into config.js served with static files - App loads config at runtime via window.CONFIG - Can be updated by restarting container (no rebuild) This mirrors K8s deployment where: - Secrets are mounted as volumes - Applications read them at runtime - Secrets can be rotated without rebuilding images Estimated Timeline - Phase 1 (Frontend Secrets): 1 day - Phase 2 (Backend): 1 day - Phase 3 (Foundation): 0.5 day - Phase 4 (Components): 1 day - Phase 5 (Desktop): 0.5 day - Phase 6 (Mobile): 0.5 day - Phase 7 (Integration): 0.5 day - Phase 8 (Testing): 1.5 days - Phase 9 (Docs): 0.5 day - Phase 10 (Validation): 0.5 day - Phase 11 (Deployment): 0.5 day Total: 8 days (full implementation with K8s-aligned secrets) Reference Implementations - Secrets pattern: Backend config-loader.ts and docker-compose.yml - Feature structure: frontend/src/features/vehicles/ - Component patterns: Vehicles components - Testing: Vehicles test suite - Mobile + Desktop: Vehicles pages