1119 lines
40 KiB
Markdown
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)**
|