Files
motovaultpro/docs/GAS-STATION-AGENTS.md
Eric Gullickson 2cc9cc5f9f Gas Station Prep
2025-11-03 14:18:25 -06:00

1119 lines
40 KiB
Markdown

# Gas Stations Feature - Parallel Agent Execution Plan
## Overview
This document contains agent definition prompts for implementing the Gas Stations feature using parallel execution. The backend API is complete; this plan focuses on frontend implementation, backend improvements, and integration.
### Key Context
- Backend API: Fully implemented at `backend/src/features/stations/`
- Frontend: Completely missing (needs full implementation)
- Database: Schema ready, needs migration execution
- Secrets: Google Maps API key exists in `secrets/app/google-maps-api-key.txt`
- Pattern: Follow K8s-aligned runtime secrets (not build-time env vars)
- Reference: Use `frontend/src/features/vehicles/` as implementation pattern
### Investigation Summary
- Backend has 4 complete API endpoints (search, save, get saved, delete)
- Database migration ready: `001_create_stations_tables.sql`
- External integration: Google Maps Places API client implemented
- No frontend code exists yet (placeholder route only)
- No tests exist (empty directories)
- No circuit breaker or cache cleanup implemented
## Execution Phases
```
Phase 1 (Parallel - Infrastructure):
├─ Agent 1: Frontend-Secrets-Agent
└─ Agent 2: Backend-Reliability-Agent
Phase 2 (Foundation):
└─ Agent 3: Frontend-Foundation-Agent (depends on Agent 1)
Phase 3 (Parallel - Components):
├─ Agent 4: Components-Agent (depends on Agent 3)
└─ Agent 5: Map-Visualization-Agent (depends on Agent 3)
Phase 4 (Parallel - Pages):
├─ Agent 6: Desktop-Mobile-Agent (depends on Agents 4, 5)
└─ Agent 7: Integration-Agent (depends on Agents 4, 5)
Phase 5 (Testing):
└─ Agent 8: Testing-Documentation-Agent (depends on all previous)
```
---
## Agent 1: Frontend-Secrets-Agent
### Scope
Implement K8s-aligned runtime secrets pattern for frontend to load Google Maps API key from `/run/secrets/` at container startup.
### Prerequisites
- None (can start immediately)
### Responsibilities
1. **Create Frontend Config Loader Script**
- File: `frontend/scripts/load-config.sh`
- Reads `/run/secrets/google-maps-api-key`
- Generates `/usr/share/nginx/html/config.js` with: `window.CONFIG = { googleMapsApiKey: 'KEY_VALUE' }`
- Fallback to empty string if secret not found
- Logs success/failure for debugging
2. **Update Frontend Dockerfile**
- File: `frontend/Dockerfile`
- Copy `scripts/load-config.sh` into image
- Make script executable
- Add `SECRETS_DIR` environment variable (default: `/run/secrets`)
- Update CMD to: `["sh", "-c", "/app/load-config.sh && nginx -g 'daemon off;'"]`
3. **Update index.html**
- File: `frontend/index.html`
- Add `<script src="/config.js"></script>` before app bundle script tag
- Ensure config loads synchronously before React initializes
4. **Create Frontend Config Types**
- File: `frontend/src/core/config/config.types.ts`
- Interface: `RuntimeConfig { googleMapsApiKey: string }`
- Extend Window interface: `declare global { interface Window { CONFIG: RuntimeConfig } }`
- Export typed accessor: `export function getConfig(): RuntimeConfig`
- Runtime validation: throw if required values missing
5. **Update docker-compose.yml**
- File: `docker-compose.yml`
- Add to `mvp-frontend` service volumes:
```yaml
- ./secrets/app/google-maps-api-key.txt:/run/secrets/google-maps-api-key:ro
```
- Add environment variable: `SECRETS_DIR: /run/secrets`
6. **Create Documentation**
- File: `frontend/docs/RUNTIME-CONFIG.md`
- Explain runtime config pattern vs build-time
- How to add new runtime secrets
- K8s deployment notes
- Local development setup
### Success Criteria
- Frontend container starts successfully
- `/usr/share/nginx/html/config.js` exists and contains API key
- `getConfig()` returns typed RuntimeConfig object
- No TypeScript errors
- Linters pass
### Handoff Notes
Provides `getConfig().googleMapsApiKey` for Agent 5 (Map-Visualization-Agent) to use when loading Google Maps JavaScript API.
---
## Agent 2: Backend-Reliability-Agent
### Scope
Enhance backend reliability with circuit breaker, comprehensive tests, and cache cleanup job.
### Prerequisites
- None (can start immediately)
### Responsibilities
1. **Add Circuit Breaker Pattern**
- Install package: Add `"opossum": "^8.1.4"` to `backend/package.json`
- File: `backend/src/features/stations/external/google-maps/google-maps.circuit-breaker.ts`
- Create circuit breaker factory function
- Configuration: 10s timeout, 50% error threshold, 30s reset timeout
- Update: `backend/src/features/stations/external/google-maps/google-maps.client.ts`
- Wrap `searchNearby()` method with circuit breaker
- Follow pattern from `backend/src/features/platform/` (reference implementation)
- Add circuit breaker events logging (open, close, halfOpen)
2. **Backend Unit Tests**
- File: `backend/src/features/stations/tests/unit/stations.service.test.ts`
- Test searchStations() with valid coordinates
- Test saveStation() with user_id isolation
- Test getSavedStations() returns only user's stations
- Test deleteSavedStation() removes correct station
- File: `backend/src/features/stations/tests/unit/stations.repository.test.ts`
- Test database queries with mock connection
- Test SQL injection protection
- Test constraint violations (duplicate saves)
- File: `backend/src/features/stations/tests/unit/google-maps.client.test.ts`
- Mock Google Maps API responses
- Test distance calculation (Haversine formula)
- Test photo URL generation
- Test error handling
3. **Backend Integration Tests**
- File: `backend/src/features/stations/tests/integration/stations.api.test.ts`
- Test all 4 endpoints with real database
- Test JWT authentication requirement
- Test request validation (Zod schemas)
- Test error responses (400, 401, 500)
- File: `backend/src/features/stations/tests/integration/saved-stations.flow.test.ts`
- Test complete workflow: search → save → retrieve → delete
- Test duplicate save handling
- Test user isolation (user A can't see user B's stations)
4. **Test Fixtures**
- File: `backend/src/features/stations/tests/fixtures/mock-stations.ts`
- Export sample Station objects (5-10 examples)
- Include various ratings, distances, prices
- File: `backend/src/features/stations/tests/fixtures/mock-google-response.ts`
- Mock Google Places API responses
- Include success and error cases
- File: `backend/src/features/stations/tests/fixtures/test-coordinates.ts`
- Sample coordinates for major cities
- Edge cases (equator, poles, date line)
5. **Cache Cleanup Job**
- File: `backend/src/features/stations/jobs/cache-cleanup.job.ts`
- Scheduled job using cron (daily at 2 AM)
- Delete `station_cache` entries older than 24 hours
- Log metrics: rows deleted, execution time
- Error handling and retry logic
- Update: `backend/src/app.ts`
- Import and register cache cleanup job
- Add to scheduler (if exists) or create simple interval
6. **Run Tests**
- Execute: `cd backend && npm test -- features/stations`
- Verify 100% pass rate
- Check coverage (aim for >80%)
### Success Criteria
- Circuit breaker implemented and tested
- All unit tests pass
- All integration tests pass
- Test coverage >80%
- Cache cleanup job registered
- Linters pass
- No TypeScript errors
### Handoff Notes
Backend is now production-ready with reliability patterns. Tests provide safety net for future changes.
---
## Agent 3: Frontend-Foundation-Agent
### Scope
Create frontend foundation: types, API client, and React Query hooks for stations feature.
### Prerequisites
- **Depends on Agent 1** (needs `getConfig()` function)
### Responsibilities
1. **Type Definitions**
- File: `frontend/src/features/stations/types/stations.types.ts`
- Interface `Station`:
- placeId: string
- name: string
- address: string
- location: { latitude: number; longitude: number }
- rating?: number
- distance: number (meters)
- photoUrl?: string
- prices?: { fuel_type: string; price: number }[]
- Interface `SavedStation` extends Station:
- userId: string
- nickname?: string
- notes?: string
- isFavorite: boolean
- savedAt: string
- Interface `SearchRequest`:
- latitude: number
- longitude: number
- radius?: number (meters, default 5000)
- fuelType?: string
- Interface `SearchResponse`:
- stations: Station[]
- searchLocation: { latitude: number; longitude: number }
- Interface `SaveStationData`:
- placeId: string
- nickname?: string
- notes?: string
- isFavorite?: boolean
2. **API Client**
- File: `frontend/src/features/stations/api/stations.api.ts`
- Function `searchStations(request: SearchRequest): Promise<Station[]>`
- POST /api/stations/search
- Include JWT in Authorization header
- Return stations array
- Function `saveStation(data: SaveStationData): Promise<SavedStation>`
- POST /api/stations/save
- Include JWT in Authorization header
- Return saved station with full details
- Function `getSavedStations(): Promise<SavedStation[]>`
- GET /api/stations/saved
- Include JWT in Authorization header
- Return array of user's saved stations
- Function `deleteSavedStation(placeId: string): Promise<void>`
- DELETE /api/stations/saved/:placeId
- Include JWT in Authorization header
- Return nothing (204 No Content)
- Use axios instance from `frontend/src/core/api/client.ts`
- Proper error handling with try-catch
- Add request/response logging for debugging
3. **React Query Hooks**
- File: `frontend/src/features/stations/hooks/useStationsSearch.ts`
- Export `useStationsSearch()` mutation hook
- Accepts SearchRequest, returns Station[]
- Don't cache by default (location-specific queries)
- Loading/error states
- File: `frontend/src/features/stations/hooks/useSavedStations.ts`
- Export `useSavedStations()` query hook
- Query key: `['stations', 'saved']`
- Auto-refetch on window focus
- Stale time: 5 minutes
- Loading/error states
- File: `frontend/src/features/stations/hooks/useSaveStation.ts`
- Export `useSaveStation()` mutation hook
- Accepts SaveStationData
- Optimistic update: add to saved list immediately
- On success: invalidate `['stations', 'saved']` query
- On error: rollback optimistic update
- File: `frontend/src/features/stations/hooks/useDeleteStation.ts`
- Export `useDeleteStation()` mutation hook
- Accepts placeId string
- Optimistic update: remove from saved list
- On success: invalidate `['stations', 'saved']` query
- On error: rollback optimistic update
- File: `frontend/src/features/stations/hooks/useGeolocation.ts`
- Export `useGeolocation()` hook
- Uses browser Geolocation API
- Returns: `{ position, loading, error, requestLocation }`
- Handle permission states: prompt, granted, denied
- Error handling: timeout, unavailable, permission denied
- Options: enableHighAccuracy, timeout, maximumAge
4. **Utilities**
- File: `frontend/src/features/stations/utils/distance.ts`
- Export `formatDistance(meters: number): string`
- Convert meters to miles with proper formatting
- Examples: "0.3 mi", "1.2 mi", "5.7 mi"
- File: `frontend/src/features/stations/utils/location.ts`
- Export `getCurrentPosition(): Promise<GeolocationPosition>`
- Wrapper around navigator.geolocation.getCurrentPosition
- Promisified API
- Error handling helpers
### Success Criteria
- All type interfaces defined with proper TypeScript types
- API client functions work (test with curl against backend)
- All React Query hooks created with proper caching
- Geolocation hook handles all permission states
- No TypeScript errors
- Linters pass
- Utilities properly format data
### Handoff Notes
Foundation is complete. Agent 4 and Agent 5 can now build components and map visualization using these types, API client, and hooks.
---
## Agent 4: Components-Agent
### Scope
Build all React components for stations feature (cards, lists, forms).
### Prerequisites
- **Depends on Agent 3** (needs types, hooks, API client)
### Responsibilities
1. **StationCard Component**
- File: `frontend/src/features/stations/components/StationCard.tsx`
- Props:
- station: Station
- isSaved?: boolean
- onSave?: (placeId: string) => void
- onDelete?: (placeId: string) => void
- onSelect?: (station: Station) => void
- UI Elements:
- Material-UI Card component
- Station name (Typography variant="h6")
- Address (Typography variant="body2", color="text.secondary")
- Distance chip (Chip with LocationOnIcon)
- Rating stars (Rating component, if rating exists)
- Price display (Typography, if prices available)
- Photo thumbnail (CardMedia, if photoUrl exists)
- Save/Unsave IconButton (BookmarkIcon / BookmarkBorderIcon)
- Directions link (Button with OpenInNewIcon, opens Google Maps)
- Styling:
- Responsive: full width on mobile, fixed width on desktop
- Min height 44px for touch targets
- Hover effects on desktop
- Loading state while saving/deleting
- Follow pattern: `frontend/src/features/vehicles/components/VehicleCard.tsx`
2. **StationsList Component**
- File: `frontend/src/features/stations/components/StationsList.tsx`
- Props:
- stations: Station[]
- loading?: boolean
- error?: Error | null
- onSaveStation?: (placeId: string) => void
- savedStationIds?: Set<string>
- UI Elements:
- Grid container (responsive)
- Layout: 1 col mobile, 2 cols tablet, 3 cols desktop
- Loading state: Skeleton components (3-6 skeletons)
- Empty state: "No stations found. Try adjusting your search."
- Error state: Alert with retry option
- Map stations to StationCard components
- Styling:
- Gap between cards: 16px
- Smooth loading transitions
- Follow pattern: `frontend/src/features/vehicles/components/VehiclesList.tsx`
3. **SavedStationsList Component**
- File: `frontend/src/features/stations/components/SavedStationsList.tsx`
- Props:
- savedStations: SavedStation[]
- loading?: boolean
- error?: Error | null
- onDelete: (placeId: string) => void
- onSelect?: (station: SavedStation) => void
- currentLocation?: { latitude: number; longitude: number }
- UI Elements:
- List container (vertical)
- Each item shows: nickname || name, address, distance from current location
- Notes preview (truncated to 50 chars)
- Favorite icon if isFavorite
- Delete IconButton with confirmation dialog
- Loading state: Linear progress bar
- Empty state: "No saved stations yet. Save stations from search results."
- Error state: Alert with error message
- Styling:
- List item min height: 64px
- Dividers between items
- Delete button always visible (not just on hover for mobile)
- Follow pattern: Material-UI List component examples
4. **StationsSearchForm Component**
- File: `frontend/src/features/stations/components/StationsSearchForm.tsx`
- Props:
- onSearch: (request: SearchRequest) => void
- loading?: boolean
- UI Elements:
- Form container (Card or Paper)
- "Use Current Location" Button (uses useGeolocation hook)
- OR divider
- Manual lat/lng inputs (TextField type="number")
- Radius slider (Slider: 1-25 miles, default 5, step 1)
- Fuel type select (optional, Select component)
- Search LoadingButton (shows loading state during search)
- Geolocation status indicator (permission state)
- Error display for geolocation failures
- Validation:
- Require either current location OR manual lat/lng
- Validate latitude: -90 to 90
- Validate longitude: -180 to 180
- Behavior:
- Click "Use Current Location" → request permission → populate hidden lat/lng
- Manual input disables current location
- Convert miles to meters for API (1 mile = 1609.34 meters)
- Styling:
- Compact form layout
- Responsive: stack vertically on mobile
- Clear visual feedback for loading states
- Follow pattern: `frontend/src/features/fuel-logs/components/FuelLogForm.tsx`
5. **Create Feature Index**
- File: `frontend/src/features/stations/components/index.ts`
- Export all components for easy importing
### Success Criteria
- All 4 components render without errors
- Components follow Material-UI design patterns
- Mobile responsive (test at 375px width)
- Touch targets minimum 44px
- Loading and error states work
- TypeScript types are correct
- Linters pass
- Components match vehicles feature pattern
### Handoff Notes
Core components are ready. Agent 6 can now build pages using these components. Agent 7 can integrate with map visualization.
---
## Agent 5: Map-Visualization-Agent
### Scope
Implement Google Maps integration and StationMap component for visualizing stations on a map.
### Prerequisites
- **Depends on Agent 1** (needs `getConfig().googleMapsApiKey`)
- **Depends on Agent 3** (needs Station types)
### Responsibilities
1. **Google Maps API Loader**
- File: `frontend/src/features/stations/utils/maps-loader.ts`
- Function: `loadGoogleMapsAPI(): Promise<typeof google>`
- Load Google Maps JavaScript API dynamically
- Use runtime config: `getConfig().googleMapsApiKey`
- Include libraries: `libraries=places,geometry`
- Singleton pattern: only load once, return cached promise
- Error handling: throw if API key missing or script fails
- TypeScript: Use `@types/google.maps` for types
2. **Map Utilities**
- File: `frontend/src/features/stations/utils/map-utils.ts`
- Function: `createStationMarker(station: Station, map: google.maps.Map, isSaved: boolean): google.maps.Marker`
- Create marker at station location
- Icon: blue pin for normal, gold star for saved
- Title: station name
- Clickable: true
- Function: `createInfoWindow(station: Station, isSaved: boolean, onSave: () => void, onDirections: () => void): google.maps.InfoWindow`
- Create info window with station details
- Content: name, address, rating, distance
- Action buttons: Save/Saved, Directions
- HTML string with proper escaping
- Function: `fitBoundsToMarkers(map: google.maps.Map, markers: google.maps.Marker[]): void`
- Calculate bounds containing all markers
- Fit map to bounds with padding
- Handle single marker case (zoom to 14)
- Function: `calculateCenter(stations: Station[]): google.maps.LatLng`
- Calculate geometric center of stations
- Return LatLng object
3. **StationMap Component**
- File: `frontend/src/features/stations/components/StationMap.tsx`
- Props:
- stations: Station[]
- savedStationIds?: Set<string>
- center?: { latitude: number; longitude: number }
- zoom?: number
- currentLocation?: { latitude: number; longitude: number }
- onStationClick?: (station: Station) => void
- onSaveStation?: (placeId: string) => void
- State:
- map: google.maps.Map | null
- markers: google.maps.Marker[]
- infoWindow: google.maps.InfoWindow | null
- Behavior:
- Load Google Maps API on mount (useEffect)
- Initialize map in div container
- Create markers for all stations
- Current location marker (red pin, if provided)
- Click marker → open info window
- Click save button → call onSaveStation
- Click directions → open Google Maps in new tab
- Auto-fit bounds to show all markers
- Clean up markers on unmount
- Styling:
- Container: width 100%, height configurable (default 500px)
- Responsive: 300px height on mobile, 500px on desktop
- Map controls: zoom, pan, fullscreen
- Custom map styles (optional: subtle styling)
- Map Options:
- mapTypeControl: false
- streetViewControl: false
- fullscreenControl: true
- zoomControl: true
- Error Handling:
- Show error message if API fails to load
- Fallback: display stations in list if map unavailable
4. **TypeScript Types**
- Ensure `@types/google.maps` is in `frontend/package.json`
- Add if missing: `"@types/google.maps": "^3.54.0"`
5. **Testing**
- Test map loads with valid API key
- Test markers render for each station
- Test info windows open on marker click
- Test current location marker (red pin)
- Test directions link opens correct URL
- Test error handling (invalid API key)
### Success Criteria
- Google Maps API loads successfully from runtime config
- StationMap component renders map
- Markers appear for all stations
- Color coding works (blue vs gold for saved)
- Info windows display correct station details
- Directions links work
- Current location marker shows if provided
- Responsive sizing works
- No TypeScript errors
- Linters pass
### Handoff Notes
Map visualization is complete. Agent 6 can integrate StationMap into desktop and mobile pages.
---
## Agent 6: Desktop-Mobile-Agent
### Scope
Build desktop StationsPage and mobile StationsMobileScreen, update routing.
### Prerequisites
- **Depends on Agent 4** (needs all components)
- **Depends on Agent 5** (needs StationMap)
### Responsibilities
1. **Desktop StationsPage**
- File: `frontend/src/features/stations/pages/StationsPage.tsx`
- Layout:
- Container: Full width, Grid layout
- Left column (60%): StationMap (full height 600px)
- Right column (40%):
- StationsSearchForm at top
- Tabs component: "Search Results" | "Saved Stations"
- Tab 1 content: StationsList (scrollable)
- Tab 2 content: SavedStationsList (scrollable)
- State:
- searchResults: Station[] (from useStationsSearch)
- selectedStation: Station | null (for map focus)
- currentLocation: Position | null (from useGeolocation)
- activeTab: 0 | 1
- Hooks:
- useStationsSearch() for searching
- useSavedStations() for saved list
- useSaveStation() for saving
- useDeleteStation() for deleting
- useGeolocation() for current location
- Behavior:
- On search submit → call searchStations mutation
- On search success → update map with results
- On save station → add to saved list (optimistic)
- On delete station → remove from saved list (optimistic)
- Click station card → highlight on map (zoom to marker)
- Map marker click → highlight card and scroll into view
- Responsive:
- Breakpoint <960px: Stack vertically (map on top)
- Mobile view: Map 300px height, full width
- Loading States:
- Show loading spinner during search
- Skeleton loaders for saved stations
- Error Handling:
- Display error alerts for API failures
- Retry buttons where appropriate
- Follow pattern: `frontend/src/features/vehicles/pages/VehiclesPage.tsx`
2. **Mobile StationsScreen**
- File: `frontend/src/features/stations/pages/StationsMobileScreen.tsx`
- Layout:
- BottomNavigation with 3 tabs: "Search", "Saved", "Map"
- Tab content area (full screen minus bottom nav)
- Tab 0 (Search):
- StationsSearchForm (compact mode, vertical layout)
- StationsList (vertical scroll, full width)
- Pull-to-refresh support (optional)
- Tab 1 (Saved):
- SavedStationsList (full screen)
- Swipe-to-delete gestures (optional enhancement)
- Empty state with illustration
- Tab 2 (Map):
- StationMap (full screen, 100vh minus nav)
- Floating action button (FAB) to return to search tab
- Current location button (FAB)
- State:
- activeTab: 0 | 1 | 2
- Same data state as desktop page
- Hooks:
- Same as desktop page
- Behavior:
- Tab switching persists scroll position
- Search → auto-switch to results view
- Save station → show snackbar confirmation
- Map marker click → open bottom sheet with station details
- Bottom Sheet:
- SwipeableDrawer component
- Shows station details
- Save/Delete buttons
- Directions link
- Touch Targets:
- All buttons minimum 44px
- Adequate spacing between interactive elements
- Safe Areas:
- Handle notched devices (safe-area-inset)
- Bottom navigation respects safe area
- Follow pattern: `frontend/src/features/vehicles/pages/VehiclesMobileScreen.tsx`
3. **Update Desktop Routing**
- File: `frontend/src/App.tsx` (around line 556)
- Remove: `<Route path="/stations" element={<div>Stations (TODO)</div>} />`
- Add: `<Route path="/stations" element={<StationsPage />} />`
- Import: `import { StationsPage } from './features/stations/pages/StationsPage'`
- Verify route is inside ProtectedRoute wrapper (requires auth)
4. **Update Mobile Routing**
- File: `frontend/src/App.tsx` (mobile routes section)
- Add: `<Route path="/m/stations" element={<StationsMobileScreen />} />`
- Import: `import { StationsMobileScreen } from './features/stations/pages/StationsMobileScreen'`
- Update mobile navigation items:
- Add stations icon and route to bottom nav
- Icon: PlaceRoundedIcon (already used in Layout.tsx)
- Ensure onClick navigates to `/m/stations`
5. **Feature Index**
- File: `frontend/src/features/stations/index.ts`
- Export StationsPage and StationsMobileScreen
- Export all components for external use
6. **Manual Testing**
- Desktop: Navigate to /stations, test all functionality
- Mobile: Navigate to /m/stations, test all tabs
- Test on real mobile device or Chrome DevTools mobile emulation
- Verify responsive breakpoints work
- Verify touch targets are adequate (44px minimum)
### Success Criteria
- Desktop page renders and functions completely
- Mobile screen renders with all 3 tabs
- Routing works for both desktop and mobile
- All components integrate properly
- Search, save, delete operations work
- Map updates when data changes
- Mobile touch targets meet 44px requirement
- Responsive design works at all breakpoints
- No TypeScript errors
- Linters pass
### Handoff Notes
Desktop and mobile UIs are complete and fully functional. Users can now search, save, and view stations on both platforms.
---
## Agent 7: Integration-Agent
### Scope
Integrate stations feature with fuel logs using a station picker component.
### Prerequisites
- **Depends on Agent 4** (needs StationCard and search functionality)
- **Depends on Agent 5** (needs map utilities)
### Responsibilities
1. **StationPicker Component**
- File: `frontend/src/features/fuel-logs/components/StationPicker.tsx`
- Props:
- value: string (station name)
- onChange: (stationName: string) => void
- userLocation?: { latitude: number; longitude: number }
- label?: string
- error?: boolean
- helperText?: string
- UI Elements:
- Autocomplete component (Material-UI)
- TextField with LocationOnIcon adornment
- Dropdown shows: saved stations (group 1) + nearby stations (group 2)
- Option rendering:
- Primary text: station name
- Secondary text: distance + address
- Bookmark icon if saved
- FreeSolo mode: allow manual text entry
- Loading indicator while fetching stations
- Behavior:
- On focus → fetch saved stations immediately
- If userLocation provided → fetch nearby stations (5 mile radius)
- Show saved stations first (grouped, labeled "Saved Stations")
- Show nearby stations second (grouped, labeled "Nearby Stations")
- On select → call onChange with station name
- On manual input → call onChange with entered text
- Debounce: 300ms delay for search queries
- Data:
- Use useSavedStations() hook
- Use useStationsSearch() hook with userLocation
- Combine results: saved + nearby
- De-duplicate by placeId
- Error Handling:
- If geolocation fails → only show saved stations
- If API fails → fallback to manual text input
- Display error message in helper text
- Follow pattern: Material-UI Autocomplete with grouping
2. **Update FuelLogForm**
- File: `frontend/src/features/fuel-logs/components/FuelLogForm.tsx`
- Find: Current location input field (likely TextField or LocationInput component)
- Replace with: StationPicker component
- Props to pass:
- value: formData.stationName (or equivalent)
- onChange: (name) => setFormData({ ...formData, stationName: name })
- userLocation: from useGeolocation hook
- label: "Gas Station"
- error: Boolean(errors.stationName)
- helperText: errors.stationName
- Integration:
- Add useGeolocation hook to FuelLogForm
- Pass current position to StationPicker
- Maintain existing form validation
- Ensure backward compatibility (text input still works)
3. **Enhance Fuel Log Display (Optional)**
- File: `frontend/src/features/fuel-logs/components/FuelLogCard.tsx`
- If stationName is present → make it clickable
- On click → navigate to /stations with search for that name
- OR: Show station details in dialog
- Add LocationOnIcon next to station name
- Follow existing card styling
4. **Add Geolocation to Fuel Logs**
- Update FuelLogForm to request location permission on mount
- Store current location in component state
- Pass to StationPicker for nearby stations
- Show permission request UI if needed
- Handle permission denied gracefully (picker still works with saved stations)
5. **Testing**
- Test StationPicker in isolation (render component)
- Test selecting saved station
- Test selecting nearby station
- Test manual text entry (freeSolo)
- Test integration in FuelLogForm
- Test submitting fuel log with station
- Test geolocation permission flow
### Success Criteria
- StationPicker component renders and functions
- Saved stations appear in dropdown
- Nearby stations appear in dropdown (if location available)
- Manual text entry works (fallback)
- FuelLogForm integrates StationPicker successfully
- Submitting fuel log with station works
- Backward compatible (existing fuel logs still display)
- Geolocation permission handled gracefully
- No TypeScript errors
- Linters pass
### Handoff Notes
Fuel logs now integrate with stations feature. Users can select stations from saved or nearby lists when logging fuel, improving data consistency and user experience.
---
## Agent 8: Testing-Documentation-Agent
### Scope
Create comprehensive tests for all new code and complete documentation.
### Prerequisites
- **Depends on all previous agents** (needs complete implementation)
### Responsibilities
1. **Backend Tests Verification**
- Run: `cd backend && npm test -- features/stations`
- Verify all tests pass (from Agent 2)
- If failures: debug and fix
- Check coverage: `npm test -- --coverage features/stations`
- Ensure coverage >80%
2. **Frontend Component Tests**
- File: `frontend/src/features/stations/__tests__/components/StationCard.test.tsx`
- Test rendering with all props
- Test save button click
- Test delete button click
- Test directions link
- File: `frontend/src/features/stations/__tests__/components/StationsList.test.tsx`
- Test rendering with stations array
- Test loading state (skeletons)
- Test empty state
- Test error state
- File: `frontend/src/features/stations/__tests__/components/SavedStationsList.test.tsx`
- Test rendering saved stations
- Test delete action
- Test empty state
- File: `frontend/src/features/stations/__tests__/components/StationsSearchForm.test.tsx`
- Test form submission
- Test validation (lat/lng ranges)
- Test current location button
- Test manual input
- File: `frontend/src/features/stations/__tests__/components/StationMap.test.tsx`
- Mock Google Maps API
- Test map initialization
- Test marker rendering
- Test info window
- Use React Testing Library
- Mock hooks with jest.mock()
3. **Frontend API Tests**
- File: `frontend/src/features/stations/api/__tests__/stations.api.test.ts`
- Test searchStations() - mock axios
- Test saveStation() - mock axios
- Test getSavedStations() - mock axios
- Test deleteSavedStation() - mock axios
- Test error handling (network errors, 401, 500)
- Verify request payloads
- Verify response parsing
4. **Frontend Hook Tests**
- File: `frontend/src/features/stations/hooks/__tests__/useStationsSearch.test.ts`
- Test mutation flow
- Test loading state
- Test success state
- Test error state
- File: `frontend/src/features/stations/hooks/__tests__/useSavedStations.test.ts`
- Test query flow
- Test cache behavior
- Test refetch logic
- File: `frontend/src/features/stations/hooks/__tests__/useSaveStation.test.ts`
- Test mutation with optimistic update
- Test cache invalidation
- Test rollback on error
- File: `frontend/src/features/stations/hooks/__tests__/useDeleteStation.test.ts`
- Test mutation with optimistic update
- Test cache invalidation
- Test rollback on error
- Use @testing-library/react-hooks
- Mock React Query client
5. **Frontend Page Tests**
- File: `frontend/src/features/stations/__tests__/pages/StationsPage.test.tsx`
- Test page renders
- Test search workflow
- Test save station workflow
- Test tab switching
- Test map interaction
- File: `frontend/src/features/stations/__tests__/pages/StationsMobileScreen.test.tsx`
- Test all 3 tabs render
- Test tab navigation
- Test search on mobile
- Test saved list on mobile
6. **Integration Component Tests**
- File: `frontend/src/features/fuel-logs/__tests__/components/StationPicker.test.tsx`
- Test rendering
- Test saved stations load
- Test nearby stations load (with geolocation)
- Test manual text entry
- Test selection callback
- Mock useStationsSearch and useSavedStations
7. **E2E Tests (Optional but Recommended)**
- File: `frontend/cypress/e2e/stations.cy.ts` (or similar framework)
- Test flows:
- Login → Navigate to Stations
- Search for nearby stations
- Save a station
- View saved stations
- Delete a station
- Navigate to Fuel Logs
- Select station from picker
- Submit fuel log
- Verify station name appears in log
- Use real backend in test environment
- Seed test data if needed
8. **Run All Tests**
- Backend: `cd backend && npm test`
- Frontend: `cd frontend && npm test`
- Verify 100% pass rate
- Fix any failures
9. **Backend Documentation**
- File: `backend/src/features/stations/docs/ARCHITECTURE.md`
- System design overview
- Data flow diagrams (text-based)
- External dependencies (Google Maps Places API)
- Caching strategy (Redis + PostgreSQL)
- Database schema with relationships
- Circuit breaker pattern explanation
- File: `backend/src/features/stations/docs/API.md`
- All endpoint documentation
- Request/response examples with curl commands
- Authentication requirements (JWT)
- Rate limits and quotas
- Error response formats
- File: `backend/src/features/stations/docs/TESTING.md`
- How to run tests
- Test database setup
- Writing new tests
- Coverage goals (>80%)
- CI/CD integration
- File: `backend/src/features/stations/docs/GOOGLE-MAPS-SETUP.md`
- Google Cloud Console setup
- Create project and enable Places API
- API key creation and restrictions
- Quota management and monitoring
- Cost estimation ($10-50/month typical)
- Security best practices (restrict by IP, referrer)
10. **Frontend Documentation**
- File: `frontend/src/features/stations/README.md`
- Feature overview
- Component hierarchy diagram (text-based)
- Hook usage examples
- Adding new functionality guide
- Runtime config pattern explanation
- Testing guide
- Integration with other features
- Update: `frontend/docs/RUNTIME-CONFIG.md` (created by Agent 1)
- Ensure complete and accurate
11. **Update Main Documentation**
- File: `docs/README.md`
- Add "Gas Stations" to features list
- Brief description: "Search and save gas stations using Google Maps"
- Link to backend docs: `backend/src/features/stations/docs/`
- Link to frontend docs: `frontend/src/features/stations/README.md`
- File: `backend/src/features/stations/README.md`
- Update with complete feature overview
- Configuration requirements
- API endpoints summary
- Testing instructions
- Deployment notes
12. **Create User Guide (Optional)**
- File: `docs/USER-GUIDE-STATIONS.md`
- How to search for stations
- How to save favorites
- How to use in fuel logs
- Screenshots (if possible)
- Troubleshooting common issues
### Success Criteria
- All backend tests pass (100%)
- All frontend tests pass (100%)
- Test coverage >80% for new code
- All documentation files created
- Documentation is clear and accurate
- Code examples in docs work
- Main README updated
- No TypeScript errors
- Linters pass
### Handoff Notes
Testing and documentation complete. Feature is production-ready. All code is tested, documented, and follows project standards.
---
## Final Validation Checklist
After all agents complete, run final validation:
### Build & Deploy
```bash
make rebuild # Rebuild all containers
make logs # Watch for errors
make migrate # Run database migrations
make test # Run all tests
```
### Linting
```bash
cd backend && npm run lint && npm run type-check
cd frontend && npm run lint && npm run type-check
```
### Manual Testing
- [ ] Desktop: Search stations at https://motovaultpro.com/stations
- [ ] Desktop: Save a station
- [ ] Desktop: View saved stations
- [ ] Desktop: Delete a station
- [ ] Desktop: Map visualization works
- [ ] Mobile: Navigate to stations (/m/stations)
- [ ] Mobile: All 3 tabs work
- [ ] Mobile: Touch targets are 44px minimum
- [ ] Fuel Logs: Station picker works
- [ ] Fuel Logs: Submit log with station
- [ ] Cross-browser: Chrome, Safari, Firefox
### Performance
- [ ] Map loads in <2 seconds
- [ ] API responses <500ms
- [ ] Redis caching works (check logs)
- [ ] No memory leaks (React DevTools)
### Security
- [ ] All endpoints require JWT auth
- [ ] User data isolation works (can't see other users' saved stations)
- [ ] SQL injection protected (prepared statements)
- [ ] API key not exposed to client (runtime config)
### Documentation
- [ ] All docs created and accurate
- [ ] Code examples work
- [ ] Main README updated
- [ ] Google Maps setup guide complete
### Standards (per CLAUDE.md)
- [ ] All linters pass with zero issues
- [ ] All tests pass
- [ ] Feature works end-to-end on desktop
- [ ] Feature works end-to-end on mobile
- [ ] Old code deleted (no placeholder routes)
- [ ] No emojis in code or docs
- [ ] Meaningful variable names used
---
## Execution Commands
To execute agents in parallel using Claude Code:
### Phase 1 (Parallel)
```bash
# Terminal 1
claude-code "Execute Agent 1: Frontend-Secrets-Agent as defined in GAS-STATION-AGENTS.md"
# Terminal 2
claude-code "Execute Agent 2: Backend-Reliability-Agent as defined in GAS-STATION-AGENTS.md"
```
### Phase 2
```bash
# Wait for Phase 1 to complete
claude-code "Execute Agent 3: Frontend-Foundation-Agent as defined in GAS-STATION-AGENTS.md"
```
### Phase 3 (Parallel)
```bash
# Terminal 1
claude-code "Execute Agent 4: Components-Agent as defined in GAS-STATION-AGENTS.md"
# Terminal 2
claude-code "Execute Agent 5: Map-Visualization-Agent as defined in GAS-STATION-AGENTS.md"
```
### Phase 4 (Parallel)
```bash
# Terminal 1
claude-code "Execute Agent 6: Desktop-Mobile-Agent as defined in GAS-STATION-AGENTS.md"
# Terminal 2
claude-code "Execute Agent 7: Integration-Agent as defined in GAS-STATION-AGENTS.md"
```
### Phase 5
```bash
# Wait for Phase 4 to complete
claude-code "Execute Agent 8: Testing-Documentation-Agent as defined in GAS-STATION-AGENTS.md"
```
---
## Notes
- Each agent is designed to be autonomous and complete its scope independently
- Agents include clear prerequisites and dependencies
- Follow CLAUDE.md standards: no emojis, meaningful names, delete old code
- All implementations follow K8s-aligned patterns (runtime secrets, not build-time)
- Reference implementation: vehicles feature
- Testing is mandatory (per CLAUDE.md: must be 100% green)
- Mobile + Desktop requirement: all features work on both platforms
---
## Estimated Total Time
- Phase 1: 8 hours (parallel: 8 hours)
- Phase 2: 4 hours
- Phase 3: 8 hours (parallel: 8 hours)
- Phase 4: 8 hours (parallel: 8 hours)
- Phase 5: 12 hours
**Total: 40 hours elapsed (with parallelization)**
**Total: 48 hours of work (across all agents)**
**Savings from parallelization: 8 hours (17% faster)**