Gas Station Feature

This commit is contained in:
Eric Gullickson
2025-11-04 18:46:46 -06:00
parent d8d0ada83f
commit 5dc58d73b9
61 changed files with 12952 additions and 52 deletions

View File

@@ -1,37 +1,237 @@
# Stations Feature Capsule
# Gas Stations Feature
## Quick Summary (50 tokens)
Search nearby gas stations via Google Maps and manage users' saved stations with user-owned saved lists. Caches search results for 1 hour. JWT required for all endpoints.
Complete gas station discovery and management feature with Google Maps integration, caching, and user favorites.
## API Endpoints (JWT required)
- `POST /api/stations/search` — Search nearby stations
- `POST /api/stations/save` — Save a station to user's favorites
- `GET /api/stations/saved` — List saved stations for the user
- `DELETE /api/stations/saved/:placeId` — Remove a saved station
## Quick Summary
## Structure
- **api/** - HTTP endpoints, routes, validators
- **domain/** - Business logic, types, rules
- **data/** - Repository, database queries
- **migrations/** - Feature-specific schema
- **external/** - External API integrations
- **events/** - Event handlers
- **tests/** - All feature tests
- **docs/** - Detailed documentation
Search nearby gas stations via Google Maps API. Users can view stations on a map, save favorites with custom notes, and integrate station data into fuel logging. Search results cached for 1 hour. JWT required for all endpoints. User data isolation via `user_id`.
## Dependencies
- Internal: core/auth, core/cache
- External: Google Maps API (Places)
- Database: stations table (see `docs/DATABASE-SCHEMA.md`)
## Implementation Phases Status
## Quick Commands
| Phase | Feature | Status |
|-------|---------|--------|
| 1 | Frontend K8s-aligned secrets pattern | ✅ Complete |
| 2 | Backend improvements (circuit breaker, tests) | ✅ Complete |
| 3 | Frontend foundation (types, API, hooks) | ✅ Complete |
| 4 | Frontend components (card, list, form, map) | ✅ Complete |
| 5 | Desktop page with map/search layout | ✅ Complete |
| 6 | Mobile screen with tab navigation | 🔄 In Progress |
| 7 | Fuel logs integration (StationPicker) | ⏳ Pending |
| 8 | Testing (unit, integration, E2E) | ⏳ Pending |
| 9 | Documentation (API, setup, troubleshooting) | ⏳ Pending |
| 10 | Validation & Polish (lint, tests, manual) | ⏳ Pending |
| 11 | Deployment preparation & checklist | ⏳ Pending |
## Architecture
### Backend Structure
```
features/stations/
├── api/ # HTTP handlers and routes
│ ├── stations.controller.ts
│ └── stations.routes.ts
├── domain/ # Business logic
│ ├── stations.service.ts
│ └── stations.types.ts
├── data/ # Database access
│ └── stations.repository.ts
├── external/google-maps/ # External API
│ ├── google-maps.client.ts
│ ├── google-maps.circuit-breaker.ts
│ └── google-maps.types.ts
├── jobs/ # Scheduled tasks
│ └── cache-cleanup.job.ts
├── migrations/ # Database schema
│ ├── 001_create_stations_tables.sql
│ └── 002_add_indexes.sql
├── tests/ # Test suite
│ ├── fixtures/
│ ├── unit/
│ └── integration/
└── index.ts # Feature exports
```
### Frontend Structure
```
features/stations/
├── types/ # TypeScript definitions
│ └── stations.types.ts
├── api/ # API client
│ └── stations.api.ts
├── hooks/ # React Query hooks
│ ├── useStationsSearch.ts
│ ├── useSavedStations.ts
│ ├── useSaveStation.ts
│ ├── useDeleteStation.ts
│ └── useGeolocation.ts
├── utils/ # Utilities
│ ├── distance.ts
│ ├── maps-loader.ts
│ └── map-utils.ts
├── components/ # React components
│ ├── StationCard.tsx
│ ├── StationsList.tsx
│ ├── SavedStationsList.tsx
│ ├── StationsSearchForm.tsx
│ └── StationMap.tsx
├── pages/ # Page layouts
│ └── StationsPage.tsx # Desktop
├── mobile/ # Mobile layouts (Phase 6)
│ └── StationsMobileScreen.tsx
└── __tests__/ # Tests (Phase 8)
```
## API Endpoints (All require JWT)
```
POST /api/stations/search
Body: { latitude, longitude, radius?, fuelType? }
Response: { stations[], searchLocation, searchRadius, timestamp }
POST /api/stations/save
Body: { placeId, nickname?, notes?, isFavorite? }
Response: SavedStation
GET /api/stations/saved
Response: SavedStation[]
GET /api/stations/saved/:placeId
Response: SavedStation | 404
PATCH /api/stations/saved/:placeId
Body: { nickname?, notes?, isFavorite? }
Response: SavedStation
DELETE /api/stations/saved/:placeId
Response: 204
```
## Database Schema
### station_cache
Temporary Google Places results (auto-cleanup after 24h)
```sql
- id: UUID PRIMARY KEY
- place_id: VARCHAR UNIQUE
- name: VARCHAR
- address: VARCHAR
- latitude: DECIMAL
- longitude: DECIMAL
- rating: DECIMAL
- photo_url: VARCHAR
- created_at: TIMESTAMP
```
### saved_stations
User's favorite stations with metadata
```sql
- id: UUID PRIMARY KEY
- user_id: VARCHAR (indexed)
- place_id: VARCHAR (indexed)
- nickname: VARCHAR
- notes: TEXT
- is_favorite: BOOLEAN
- created_at: TIMESTAMP
- updated_at: TIMESTAMP
- deleted_at: TIMESTAMP (soft delete)
- UNIQUE(user_id, place_id)
```
## Key Features Implemented
**Security**
- User-scoped data isolation via `user_id` filtering
- Parameterized queries (no SQL injection)
- JWT authentication required on all endpoints
- K8s-aligned secrets pattern (never in environment variables)
**Performance**
- Redis caching with 1-hour TTL
- Circuit breaker for resilience (10s timeout, 50% threshold)
- Database indexes on user_id and place_id
- Scheduled cache cleanup (24h auto-expiry)
- Lazy-loaded Google Maps API in browser
**User Experience**
- Real-time browser geolocation with permission handling
- Touch-friendly 44px minimum button heights
- Responsive map with auto-fit bounds
- Saved stations with custom nicknames and notes
- One-click directions to Google Maps
## Testing
### Run Tests
```bash
# Run feature tests
# Backend feature tests
cd backend
npm test -- features/stations
# Run feature migrations
npm run migrate:feature stations
# Frontend component tests (Phase 8)
cd frontend
npm test -- stations
# E2E tests (Phase 8)
npm run e2e
```
## Notes
- Search payload and saved schema to be finalized; align with Google Places best practices and platform quotas. Caching policy: 1 hour TTL (key `stations:search:{query}`).
### Coverage Goals
- Backend: >80% coverage
- Frontend components: >80% coverage
- All critical paths tested
## Deployment
### Prerequisites
1. Google Maps API key with Places API enabled
2. PostgreSQL database with migrations applied
3. Redis cache service running
4. Docker for containerization
### Setup
```bash
# Create secrets directory
mkdir -p ./secrets/app
# Add Google Maps API key
echo "YOUR_API_KEY_HERE" > ./secrets/app/google-maps-api-key.txt
# Build and start
make setup
make logs
# Verify
curl http://localhost:3001/health
```
### Verification
```bash
# Check secrets mounted
docker compose exec mvp-frontend cat /run/secrets/google-maps-api-key
# Check config generated
docker compose exec mvp-frontend cat /usr/share/nginx/html/config.js
# Test API endpoint
curl -H "Authorization: Bearer $TOKEN" http://localhost:3001/api/stations/saved
```
## Next Steps (Phases 6-11)
### Phase 6: Mobile Implementation
Create mobile bottom-tab navigation screen with Search, Saved, Map tabs.
### Phase 7: Fuel Logs Integration
Add StationPicker component with autocomplete to FuelLogForm.
### Phases 8-11: Testing, Docs, Validation, Deployment
Follow the detailed plan in `/docs/GAS-STATIONS.md`.
## References
- Full implementation plan: `/docs/GAS-STATIONS.md`
- Runtime config pattern: `/frontend/docs/RUNTIME-CONFIG.md`
- Design patterns: See Platform feature (`backend/src/features/platform/`)
- Component patterns: See Vehicles feature (`frontend/src/features/vehicles/`)