- New `scheduled_scans` table with daily/weekly/monthly frequencies
- asyncio background scheduler loop checks for due schedules every 60s
- 6 REST endpoints: CRUD + toggle enabled + run-now
- `scheduled_scan_id` FK added to scans table; migrated automatically
- Frontend: Schedules page (list + create form), Schedules nav link,
"Scheduled" badge on ScanDetails when scan was triggered by a schedule
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the non-functional title attribute with a small Info icon
next to the IATA code badge. Hovering shows a dark tooltip with the
full airport name and city. Only rendered when useful name data exists.
Clicking the icon stops propagation so it doesn't expand the flights row.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Backend:
- DELETE /api/v1/scans/{id} — 204 on success, 404 if missing,
409 if pending/running; CASCADE removes routes and flights
Frontend (api.ts):
- scanApi.delete(id)
Frontend (ScanDetails.tsx):
- Re-run button: derives window_months from stored dates, detects
country vs airports mode via comma in scan.country, creates new
scan and navigates to it; disabled while scan is active
- Delete button: inline two-step confirm (no modal), navigates to
dashboard on success; disabled while scan is active
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- docker-compose.dev.yml: backend on 8000, frontend (Vite) on 5173
- Backend mounts source files + uvicorn --reload for hot reload
- Frontend uses node:20-alpine, mounts ./frontend, runs npm run dev --host
- vite.config.ts: proxy target reads from API_TARGET env var
(defaults to localhost:8000 for plain npm run dev,
set to http://backend:8000 by docker-compose.dev.yml)
Usage: docker compose -f docker-compose.dev.yml up
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Each date row now shows e.g. "WED 2026-04-01" — the 3-letter weekday
prefix is rendered in muted monospace before the date string.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Auto-refresh toggle (RefreshCw icon, animates when active, polls every 5s)
- Horizontal filter bar: level select (140px) + search input + Clear button
- Clear button only rendered when level or search is active
- Level badges: INFO=blue, WARNING=amber, ERROR=red, CRITICAL=dark red, DEBUG=grey
- Row background tints: ERROR=#FFF5F5, WARNING=#FFFBF0, CRITICAL=#FFF0F0
- Message text in font-mono, metadata line with · separators
- Right-aligned timestamp: time on first line, date below
- Skeleton loading (8× SkeletonTableRow) while fetching
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>