Files
fuel-price/docs/superpowers/specs/2026-04-04-filament-admin-design.md
Ovidiu U d5fb7f85bd
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.3) (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
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)
2026-04-04 13:40:56 +01:00

161 lines
5.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Filament Admin Panel — Design Spec
**Date:** 2026-04-04
**Stack:** Laravel 11, Filament v5, Livewire v4
**Scope:** Admin panel for FuelAlert — internal tooling only, no user-facing exposure
---
## 1. Access & Auth
- Filament panel mounted at `/admin`
- Protected by `is_admin` boolean on `users` table
- Migration adds `is_admin TINYINT(1) DEFAULT 0` to `users`
- `AdminPanelProvider` uses `->authGuard('web')` (same guard as the main app — no separate admin users table)
- `canAccessPanel()` check: `$user->is_admin === true`
- `uovidiu@sent.com` seeded as admin via a dedicated `AdminSeeder` (idempotent — safe to re-run)
- No self-registration on the admin panel — access is granted only via the `is_admin` flag
---
## 2. Dashboard Page
Default landing page. Four `StatsOverviewWidget` cards:
| Stat | Source | Alert |
|---|---|---|
| Total users | `users` count | — |
| Stations in DB | `stations` count + `max(last_seen_at)` | — |
| Latest oil prediction | Most recent `price_predictions` row — direction, confidence, source | Yellow if > 24h old |
| API errors (24h) | `api_logs` where `status_code >= 400` or `error IS NOT NULL`, last 24h | Red if > 0 |
No charts on the dashboard — keep it scannable. Charts live inside individual resources.
---
## 3. Resources
### 3.1 Users (`UserResource`)
**Purpose:** View all registered users, manage admin flag, correct postcodes.
**Table columns:** name, email, postcode, is_admin (badge), created_at
**Filters:** is_admin toggle
**Actions:**
- Edit: `is_admin` toggle, `postcode` text field (no other fields editable from admin)
- Delete: allowed (hard delete, with confirmation modal)
**Notes:**
- No subscription tier column yet — add when Cashier is integrated
- No impersonation in v1 — add later when user dashboard exists
---
### 3.2 API Logs (`ApiLogResource`)
**Purpose:** Primary debugging tool — see every outbound HTTP call made by the scheduler.
**Table columns:** service (badge), method, url (truncated), status_code (colour-coded), duration_ms, error (truncated), created_at
**Filters:** service (select: `fuel_finder`, `fred`, `anthropic`), errors only (toggle: where error IS NOT NULL or status >= 400), date range
**Default sort:** created_at DESC
**Actions:** View (modal showing full url, full error, request/response if available) — no edit, no delete
**Colour coding for status_code:**
- 2xx → success (green)
- 4xx → warning (yellow)
- 5xx / null → danger (red)
---
### 3.3 Oil Predictions (`OilPredictionResource`)
**Purpose:** Verify the daily `oil:predict` job is running and producing sensible output.
**Table columns:** predicted_for (date), source (badge: LLM / EWMA), direction (badge: rising/falling/flat with colour), confidence (progress bar 0100), reasoning (truncated), generated_at
**Filters:** source, direction, date range
**Default sort:** predicted_for DESC
**Actions:**
- View (modal showing full reasoning text)
- **Run prediction now** (page-level action): executes `php artisan oil:predict` via `Artisan::call()`, shows success/failure notification
**No edit/delete** — predictions are immutable audit records.
---
### 3.4 Brent Prices (`BrentPriceResource`)
**Purpose:** Verify FRED fetch is populating data correctly.
**Table columns:** date, price_usd
**Default sort:** date DESC
**Actions:** none
Simple read-only table. No filters needed — data is always chronological. Include a line chart widget (`BrentPriceChartWidget`) showing the last 30 days using Filament's built-in chart widget.
---
### 3.5 Stations (`StationResource`)
**Purpose:** Browse the ~14,500 UK stations; verify supermarket tagging and closure flags.
**Table columns:** trading_name, brand_name, postcode, city, is_supermarket (badge), is_motorway_service_station (badge), temporary_closure (badge), last_seen_at
**Filters:** is_supermarket, is_motorway_service_station, temporary_closure, permanent_closure, postcode prefix (text search)
**Search:** trading_name, brand_name, postcode
**Default sort:** last_seen_at DESC
**Actions:**
- View (modal with full address, amenities, opening_times, fuel_types JSON)
- **Trigger full poll** (page-level action): dispatches a queued `PollFuelPricesJob` (wraps `fuel:poll --full`) rather than calling Artisan synchronously — full station refresh on 14,500 records would exceed HTTP timeout. Shows "Poll dispatched to queue" notification immediately; result visible in API Logs once complete.
**No edit** — station data is owned by Fuel Finder API, overwritten on each poll.
---
## 4. Resources Planned But Not Built Yet
These will be added once the underlying tables/services exist:
| Resource | Depends on |
|---|---|
| Alerts Log | `alerts` table + `NotificationDispatchService` |
| Scoring Results | `scoring_results` table + `AlertScoringService` |
| Subscriptions | Cashier integration + `subscriptions` table |
---
## 5. Navigation Groups
```
Dashboard (no group)
├── Users
Data
├── Stations
├── Brent Prices
├── Oil Predictions
System
├── API Logs
```
---
## 6. Build Order
1. Migration — add `is_admin` to `users`
2. `AdminSeeder` — seed `uovidiu@sent.com` as admin
3. `AdminPanelProvider` — mount panel, configure auth
4. `ApiLogResource` — highest immediate value
5. `UserResource` — manage admin flag
6. `OilPredictionResource` + run-prediction action
7. `BrentPriceResource` + chart widget
8. `StationResource` + full-poll action
9. Dashboard widgets (last — depends on all resources being stable)
---
## 7. Out of Scope (v1)
- Role-based permissions beyond `is_admin` — single admin user is sufficient
- Activity log / audit trail of admin actions
- Dark mode customisation
- Custom Filament theme — use default
- Two-factor auth on admin panel — covered by app-level 2FA (Fortify)