# Control Panel Operational UX V1 This document defines the operator-facing contracts for the existing server-rendered GTI Fiscal control panel. It complements [`CONTROL_PANEL_V1.md`](CONTROL_PANEL_V1.md), which documents architecture, identity, authentication, APIs, retention, and the explicit fixture command. The UX work does not change fiscal rules, the Hub/Agent protocol, Firebird access, AI write controls, or deployment. ## Dashboard health and fiscal metrics Technical health is summarized by exactly five states: `ONLINE`, `DEGRADED`, `OFFLINE`, `FAILED`, and `UNKNOWN`. The dashboard total is reconciled from those state counts and must always satisfy: ```text agents_total = online + degraded + offline + failed + unknown ``` Technical health describes Agent/installation availability and processing failures. Fiscal status and fiscal completion are separate: safe-blocked fiscal work does not make an otherwise healthy Agent technically degraded or failed. Agent views show product total, fiscally complete count, safe-blocked count, failed count, and fiscal completion percentage. The percentage is `fiscally_complete / total_products * 100`, rounded to one decimal place and bounded to 0–100; when the total is zero, the stored coverage percentage is used. The fiscal status badge is `BLOCKED` when safe-blocked work exists, `COMPLETE` when all products are complete, and otherwise `INCOMPLETE`. Two blocker counts must remain distinct: - **Safe-blocked products**: distinct open `(tenant_id, product_code)` pairs. - **Open blockers**: open blocker rows; one product can have multiple rows for different fields or evidence. Dashboard attention is limited to ten items. Items are ordered by technical health (`FAILED`, `DEGRADED`, `OFFLINE`, `UNKNOWN`), required-field blockers, then outdated Agent versions; ties are stable. Safe-blocked products and outdated versions do not imply technical failure. Failed jobs and outdated Agents are also shown in compact sections, each with an explicit empty state when there is no data. Dashboard health and fiscal summary cards remain visible when lists are empty. ## Identity and time presentation Agents, jobs, and blockers lead with human identity: client, installation display name, and hostname where available. Technical numeric IDs and UUIDs remain secondary details. Agent tables distinguish technical health from fiscal status and provide a worst-fiscal-coverage sort. Job rows identify the client and installation and show job ID, kind, status, timestamps, duration, and error. Blocker rows identify the client/installation, product, field, blocker type, operational priority, age, and detail link. Database and API timestamps remain UTC. UI timestamps render in `UI_TIMEZONE`, defaulting to `America/Bahia`, as `DD/MM/YYYY HH:MM` with a relative age alongside them; a full technical timestamp is available in the element title where applicable. Missing timestamps render as `-`. Job duration uses start/finish timestamps (`em andamento` if not finished, `-` if not started); it does not replace UTC persistence or API values. ## Blocker list, summary, and operational priority The blocker page applies filters on the server: client, installation, field, blocker type, status, age, and product/search. The filtered summary reports open blocker rows, distinct affected `(tenant_id, product_code)` products, distinct affected clients, and row breakdown by `CFOP`, `ST`, `ICMS`, `CSOSN`, `NCM`, and `OTHER`. Filtered rows and their summary use the same filter set. Priority is deterministic and operational only; it does not represent tax severity, fiscal risk, legal consequence, or a tax recommendation. The current tiers are: - `CRITICAL`: known technical/connection/job-failure blockers or technical and timeout reasons that prevent processing. - `HIGH`: missing-evidence blocker types that require operator attention. - `NORMAL`: recognized fiscal fields (`CFOP`, `ST`, `ICMS`, `CSOSN`, `NCM`) or insufficient/no-evidence reasons without a higher operational tier. - `LOW`: other optional or reference-enrichment cases. The tier changes ordering/triage only. It must not be described as invented tax severity. ## Details, events, and safe operations Agent details present client, display name, and hostname before technical IDs, separate technical/fiscal/version badges, and show human-formatted last-seen time plus company context. Fiscal summary links to filtered blockers. Event history supports server-side severity, type, date, job, and product filters with pagination; event rows expose related job/product links when present. Blocker details show evidence, first/last seen, retry count, related events/jobs, and resolution audit history when available. Resolving a blocker means saving validated operator context and queueing reprocessing for that product; it does not directly write fiscal data or immediately mark historical blocker evidence resolved. The workflow requires a human-authenticated session and valid CSRF token. Accepted scopes are `PRODUCT`, `GROUP`, and `COMPANY`; the scope/key/value are validated using the existing context-key rules, including enum restrictions, and the required reason/note is length-limited. `PRODUCT` scope must use the blocker product code as its scope key. Arbitrary fields or keys are rejected. The transaction creates or updates the validated context entry, creates a `REPROCESS_PRODUCTS` job whose product list contains only the blocker product, and records an audit event. The audit context includes operator/admin ID, tenant/client, company key, installation identifiers, product code, blocker ID, context key/scope/scope key/value, reason, before/after values, reprocess job ID, and request IP when available. Validation or persistence failure rolls back the operation. The action cannot write to Firebird, execute SQL/shell, or enable AI writes. Installation-wide reprocessing has its own confirmation page at `/agents/{agent_id}/reprocess-confirmation`; only an explicit confirmed POST with CSRF creates or reuses the pending full-reprocess job and records an audit event. The confirmation page is part of the operator workflow, not a browser `window.confirm` dialog. ## Authentication policy Production administrator creation always requires the strong password policy. Short/weak local development passwords are accepted only when `ALLOW_WEAK_DEV_PASSWORD=1` and `APP_ENV` is not `production`. The flag never weakens production policy. Existing local administrator credentials are not changed by the control panel. ## Limits, responsive behavior, and boundaries Dashboard metrics and lists use snapshots and bounded queries; the dashboard does not scan product tables. List pagination is capped at 100 rows per page; the UI requests 50 rows for Agents, Jobs, and Blockers, and 25 event rows per Agent detail page. Dashboard attention is capped at ten; recent failed-job rows are capped at ten. Blocker detail loads at most 100 related events/jobs/audits, and displays a compact subset of event/job rows. Filters and pagination are server-side. The interface remains server-rendered with project-local CSS and minimal progressive enhancement. Layouts adapt to desktop, tablet, and narrow screens; wide data tables remain horizontally scrollable, focus states remain visible, and health/status meaning is not communicated by color alone. Empty states explain when there are no failed jobs, outdated Agents, blockers, or related events/jobs. The explicit non-production fixture is generated with: ```text python -m central.scripts.generate_control_panel_fixture --agents 100 --confirm-non-production ``` The command preserves its non-production confirmation gate and is never run at startup or migration time. This UX introduces no AI writes, Firebird writes, production database changes, or deployment actions.