feat: add Filament admin panel with migrations and design spec
- Add AdminPanelProvider mounting panel at `/admin` with `is_admin` auth guard - Add `is_admin` boolean column to users table - Add brent_prices and price_predictions tables with appropriate indexes - Add comprehensive admin design spec covering resources, dashboard, navigation, and build order - Configure default panel with amber primary color and standard middleware stack - Add compiled Filament assets (actions.js, app.css)
This commit is contained in:
@@ -392,22 +392,55 @@ FUEL_FINDER_CLIENT_SECRET=
|
||||
FUEL_FINDER_BASE_URL=https://api.fuel-finder.service.gov.uk
|
||||
```
|
||||
|
||||
## Postcodes.io — postcode → lat/lng
|
||||
## Postcodes.io — location resolution
|
||||
|
||||
- URL: `https://api.postcodes.io/postcodes/{postcode}`
|
||||
- Free, no API key required
|
||||
- Called once on user registration / when postcode changes
|
||||
- Store resolved `lat` + `lng` on `users` table
|
||||
- Cache postcode lookups for 30 days (postcodes rarely change coordinates)
|
||||
- Handled by `PostcodeService::resolve(string $query): ?LocationResult`
|
||||
- Returns `LocationResult` DTO with `query`, `displayName`, `lat`, `lng`
|
||||
- Results cached for 30 days — cache key `postcode:{normalised_input}`
|
||||
- Failed lookups are NOT cached — retried on next request
|
||||
- Input is auto-detected:
|
||||
|
||||
## FRED API (St. Louis Fed) — Brent crude direction
|
||||
| Input type | Example | Endpoint |
|
||||
|---|---|---|
|
||||
| Full postcode | `SW1A 1AA` | `GET /postcodes/{postcode}` |
|
||||
| Outcode (district) | `PE7` | `GET /outcodes/{outcode}` |
|
||||
| Place / city name | `Manchester` | `GET /places?q={query}&limit=1` |
|
||||
|
||||
- Series: `DCOILBRENTEU` (daily Brent spot price)
|
||||
- URL: `https://api.stlouisfed.org/fred/series/observations?series_id=DCOILBRENTEU&api_key={key}&sort_order=desc&limit=10&file_type=json`
|
||||
- Free API key required — stored as `FRED_API_KEY` in .env
|
||||
- Fetched once daily via scheduler at 7am
|
||||
- Stored in `brent_prices` table: `(date DATE, price_usd DECIMAL(8,2))`
|
||||
- Only the 5-day trend direction is used by the scoring engine
|
||||
**Anonymous search flow:** user types a postcode/city → `PostcodeService::resolve()` → lat/lng stored in a JSON cookie (30 days) alongside the query string. On return visits, cookie lat/lng is used directly — postcodes.io is only called when the search term changes.
|
||||
|
||||
**Registered users:** postcode resolved once on registration, lat/lng stored on `users` table — not re-resolved unless postcode changes.
|
||||
|
||||
## FRED API (St. Louis Fed) — Brent crude prices
|
||||
|
||||
- Series: `DCOILBRENTEU` (daily Brent spot price, USD/barrel)
|
||||
- Endpoint: `GET https://api.stlouisfed.org/fred/series/observations`
|
||||
- Params: `series_id=DCOILBRENTEU`, `sort_order=desc`, `limit=30`, `file_type=json`
|
||||
- Free API key required — stored as `FRED_API_KEY` in `.env`
|
||||
- Handled by `OilPriceService::fetchBrentPrices()`
|
||||
- Fetched daily at 7am via `oil:predict --fetch` scheduler command
|
||||
- FRED uses `"."` as a placeholder for non-trading days (weekends/holidays) — filtered out before insert
|
||||
- Stored in `brent_prices` table, upserted on `date` primary key
|
||||
|
||||
## Anthropic API — oil price direction prediction
|
||||
|
||||
- Endpoint: `POST https://api.anthropic.com/v1/messages`
|
||||
- Model: `claude-haiku-4-5-20251001` (configurable via `ANTHROPIC_MODEL` in `.env`)
|
||||
- Key stored as `ANTHROPIC_API_KEY` in `.env`
|
||||
- Handled by `OilPriceService::generateLlmPrediction()`
|
||||
- Called once daily after FRED fetch — sends last 30 days of Brent prices + pre-computed EWMA context
|
||||
- Response must be JSON: `{"direction": "rising|falling|flat", "confidence": 0-85, "reasoning": "..."}`
|
||||
- Model sometimes wraps JSON in markdown code fences — these are stripped before `json_decode`
|
||||
- Confidence is capped at 85 regardless of what the model returns
|
||||
- On any failure (API error, malformed JSON, invalid direction) → falls back to EWMA silently
|
||||
- Result stored in `price_predictions` table with `source = 'llm'`
|
||||
|
||||
**EWMA fallback (`OilPriceService::generateEwmaPrediction()`):**
|
||||
- Compares 3-day EWMA vs 7-day EWMA on chronological Brent price data
|
||||
- Threshold: ±1.5% change → rising/falling; below → flat
|
||||
- Confidence capped at 65 (simpler model)
|
||||
- Used when: no `ANTHROPIC_API_KEY` set, or LLM call fails
|
||||
- Result stored in `price_predictions` table with `source = 'ewma'`
|
||||
|
||||
## OneSignal — push notifications
|
||||
|
||||
|
||||
Reference in New Issue
Block a user