12 KiB
UX Reference: Maintenance Banner for Dashboards
Feature Branch: 031-maintenance-banner
Created: 2026-05-21
Status: Draft
1. User Persona & Context
-
Who is the user?:
- ETL Operator: System administrator or DevOps engineer running ETL pipelines (Airflow, cron, dbt). Does not interact with the ss-tools UI directly — API only.
- ss-tools Administrator: Platform administrator managing maintenance mode settings through the web UI.
- Dashboard Viewer: Business user viewing dashboards in Superset. Sees the maintenance banner but does not trigger it.
-
What is their goal?:
- Operator: Automatically warn dashboard users that data is being refreshed and may be inaccurate.
- Administrator: Control the maintenance process, manually remove banners if needed, configure exclusions.
- Dashboard viewer: Understand why dashboard data appears incomplete.
-
Context:
- Operator calls REST API from a pipeline (Airflow DAG, shell script, CI/CD).
- Administrator works in the ss-tools web interface (SvelteKit SPA, desktop).
- Dashboard viewer sees the result in Superset (third-party web application).
2. The "Happy Path" Narrative
Maintenance start (Operator → ss-tools → Superset)
An Airflow DAG finishes clearing staging tables and triggers: curl -X POST https://ss-tools/api/maintenance/start -d '{"tables":["raw.sales","raw.inventory"],"start_time":"2026-05-21T22:00:00+03:00","end_time":"2026-05-22T02:00:00+03:00"}'. The API immediately returns {"task_id":"task-abc","maintenance_id":"m-abc123","status":"pending"}. The operator polls GET /api/tasks/task-abc (or receives a WebSocket notification). Within 60 seconds, the task completes, and 12 dashboards in Superset display an orange banner at the very top: "⚠️ Maintenance in progress: 2026-05-21 22:00 — 2026-05-22 02:00 MSK. Data may be incomplete." Dashboard viewers see the warning immediately upon opening the page.
Maintenance end (Operator → ss-tools → Superset)
The ETL pipeline completes successfully. The Airflow DAG calls curl -X POST https://ss-tools/api/maintenance/m-abc123/end. The API returns {"task_id":"task-end-abc","status":"pending"}. Upon task completion, banners disappear from all dashboards. In the ss-tools UI, the administrator sees the event marked as "Completed".
Manual override (Administrator → ss-tools UI → Superset)
An administrator notices the ETL has stalled and banners remain past the scheduled end time. They navigate to ss-tools → Maintenance → see the list of active events → click "Remove All". Within 10 seconds, all banners are removed.
3. Interface Mockups
API Interaction
# ===== Start maintenance =====
$ curl -X POST https://ss-tools/api/maintenance/start \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{
"tables": ["raw.sales", "raw.inventory"],
"start_time": "2026-05-21T22:00:00+03:00",
"end_time": "2026-05-22T02:00:00+03:00",
"message": "Scheduled data mart refresh"
}'
# Success response — async (HTTP 202):
{
"task_id": "task-maint-abc123",
"maintenance_id": "m-ev-20260521-abc123",
"status": "pending"
}
# Poll task status:
$ curl https://ss-tools/api/tasks/task-maint-abc123 \
-H "Authorization: Bearer $JWT"
# Task completed response:
{
"task_id": "task-maint-abc123",
"status": "completed",
"result": {
"maintenance_id": "m-ev-20260521-abc123",
"affected_dashboards": 12,
"dashboards": [
{"id": 42, "title": "Daily Sales Overview", "chart_id": 902},
{"id": 57, "title": "Inventory Health Check", "chart_id": 903}
],
"unmatched_tables": []
}
}
# Partial success — async (HTTP 202):
{
"task_id": "task-maint-def456",
"maintenance_id": "m-ev-20260521-def456",
"status": "pending"
}
# Task result on completion (via GET /api/tasks/{task_id}):
{
"task_id": "task-maint-def456",
"status": "completed",
"result": {
"maintenance_id": "m-ev-20260521-def456",
"status": "partial",
"affected_dashboards": 10,
"failed_dashboards": [
{"id": 99, "title": "Archived Report", "error": "Dashboard not found in Superset"}
],
"unmatched_tables": ["raw.unknown_table"]
}
}
# No dashboards found — sync fast-path (HTTP 200, no task needed):
{
"maintenance_id": null,
"status": "no_match",
"affected_dashboards": 0,
"message": "No dashboards found referencing tables: raw.archived",
"unmatched_tables": ["raw.archived"]
}
# ===== End maintenance =====
$ curl -X POST https://ss-tools/api/maintenance/m-ev-20260521-abc123/end \
-H "Authorization: Bearer $JWT"
# Success response — async (HTTP 202):
{
"task_id": "task-end-abc123",
"status": "pending"
}
# Task result on completion:
{
"task_id": "task-end-abc123",
"status": "completed",
"result": {
"maintenance_id": "m-ev-20260521-abc123",
"removed_from": 12,
"failed_removals": []
}
}
# Already completed (HTTP 200 — sync, no task needed):
{
"maintenance_id": "m-ev-20260521-abc123",
"status": "already_completed",
"message": "Maintenance was already completed at 2026-05-22T02:05:00+03:00"
}
# ===== Bulk removal =====
$ curl -X POST https://ss-tools/api/maintenance/end-all \
-H "Authorization: Bearer $JWT"
# Success response — async (HTTP 202):
{
"task_id": "task-endall-xyz",
"status": "pending"
}
# Task result on completion:
{
"task_id": "task-endall-xyz",
"status": "completed",
"result": {
"closed_events": 3,
"removed_from": 25,
"failed_removals": []
}
}
# ===== Settings =====
$ curl https://ss-tools/api/maintenance/settings \
-H "Authorization: Bearer $JWT"
# Response:
{
"target_environment_id": "prod",
"dashboard_scope": "published_only",
"excluded_dashboard_ids": [99, 101],
"forced_dashboard_ids": [10, 42],
"updated_at": "2026-05-20T15:00:00+03:00"
}
$ curl -X PUT https://ss-tools/api/maintenance/settings \
-H "Authorization: Bearer $JWT" \
-H "Content-Type: application/json" \
-d '{"dashboard_scope": "all", "excluded_dashboard_ids": [99]}'
# Response: (settings object, same shape as GET)
UI Layout & Flow
Screen 1: Maintenance Banners Management (/maintenance)
- Layout: Single-page management panel. Top section — settings, bottom section — events table.
- Key Elements:
- Settings Panel (collapsible):
Dashboard Scope: Radio group: "Published only" / "Drafts only" / "All".Excluded Dashboards: Multi-select search by dashboard title. Removable badge chips.Forced Dashboards: Multi-select search by dashboard title. Removable badge chips.- "Save Settings" button (Primary, blue).
- Active Events Table:
- Columns: Event ID, Tables (truncated list + tooltip), Affected Dashboards, Start Time, End Time, Status (badge: 🟢 Active / ✅ Completed / ❌ Failed), Actions.
- Actions: "Remove Banners" button (Warning, orange) per active event.
- Above table: "Remove All Banners" button (Danger, red) with confirmation dialog.
- Completed Events Table (collapsible, hidden by default):
- Same columns + "Completed At" timestamp. No action buttons.
- Contract Mapping:
@UX_STATE:idle(table loaded),loading(skeleton),action_in_progress(remove button shows spinner, others disabled),error(error toast, retry available),empty(no active events — placeholder "No active maintenance events").@UX_FEEDBACK: Toast notifications: success (green "Banners removed from N dashboards"), error (red "Removal error: ..."), warning (yellow "Partial success: X/Y processed").@UX_RECOVERY: On removal error — "Retry" button per failed dashboard. On network error — auto-retry after 3s with countdown display.@UX_REACTIVITY:$statefor activeEvents, completedEvents, settings;$derivedfor filtered/sorted views;$effectfor auto-refresh (30s polling).
- Settings Panel (collapsible):
Screen 2: Dashboard Hub — Maintenance Indicator (modification to existing /dashboards)
- Layout: New indicator added to each dashboard row (alongside existing Git/LLM status badges).
- Key Elements:
- Maintenance Badge: Orange badge "⚠️ Maintenance" in the dashboard row.
- Tooltip: On hover — start and end times, source event ID.
- Contract Mapping:
@UX_STATE:no_maintenance(no badge),maintenance_active(orange badge),loading(grey skeleton badge).@UX_REACTIVITY:$derivedfrom maintenance state data, refreshed alongside dashboard polling.
Screen 3: Superset Dashboard — Maintenance Markdown Chart (result in third-party application)
- Layout: Markdown chart at the very top of the dashboard (position (0,0) in layout), full width.
- Appearance:
⚠️ Maintenance in progress Scheduled data mart refresh Start: May 21, 2026, 10:00 PM (MSK) End: May 22, 2026, 2:00 AM (MSK) Data may be incomplete or temporarily unavailable. - Style: Markdown with orange-tinted background (CSS via markdown HTML or Superset markdown CSS class). Bold header.
4. The "Error" Experience
Philosophy: API returns maximally detailed responses so the caller can make informed decisions. UI provides explicit recovery paths.
Scenario A: Table not found in any dashboard
- User Action (Operator): Passes a table name with a typo:
raw.sales_typo. - System Response:
{"status": "no_match", "affected_dashboards": 0, "message": "No dashboards found referencing tables: raw.sales_typo"}. - Recovery: Operator corrects the table name and retries.
Scenario B: Some dashboards no longer exist in Superset
- User Action (Operator): Initiates maintenance, but 3 of 15 dashboards have been deleted from Superset.
- System Response: HTTP 207 Multi-Status with
failed_dashboardsfield containing id and reason for each problematic dashboard. Remaining 12 dashboards successfully receive banners. - Recovery: Operator reviews failed_dashboards and adjusts the exclusion list if needed.
Scenario C: Superset API network timeout
- User Action (Operator): Initiates maintenance, but Superset responds slowly.
- System Response: Timeout after 30 seconds. Returns HTTP 207 with
status: "partial", listing processed and unprocessed dashboards. - Recovery: Operator can re-call
/maintenance/startwith the same parameters — the system only processes dashboards that don't yet have an active banner.
Scenario D: Multiple maintenance events on one dashboard
- User Action: Two ETL processes simultaneously update tables affecting dashboard D.
- System Response: Dashboard D shows a single markdown chart with aggregated text: "⚠️ Multiple data refresh processes in progress. ..."
- Recovery: The banner is only removed when ALL maintenance events for D are completed. Premature
/endfor one event updates the banner text but keeps it visible.
Scenario E: Insufficient permissions
- User Action: API call without
operatororadminrole. - System Response: HTTP 403
{"detail": "Insufficient permissions. Required role: operator or admin"}. - Recovery: Request role assignment from administrator.
5. Tone & Voice
- Style: Technical, concise, informative. No alarmism, but clear indication of possible data issues.
- Terminology:
- "Maintenance" (not "downtime" or "outage" — data is still accessible, just potentially stale)
- "Dashboard" (not "report" or "view")
- "Banner" (used in API/UI/internal docs for the markdown chart)
- "Dataset" (not "datasource")
- "Table" (not "source" or "relation")
- Default banner template:
If
⚠️ Maintenance in progress {message} Start: {start_time_formatted} End: {end_time_formatted} Data may be incomplete or temporarily unavailable.messageis not provided, the first body line is omitted. - Dashboard Hub tooltip: "Maintenance active: {start} — {end}"