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

5.7 KiB
Raw Blame History

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)