Gas Station Feature
This commit is contained in:
@@ -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/`)
|
||||
|
||||
Reference in New Issue
Block a user