docs: specify operational control panel ux
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
# Control Panel Operational UX Refinement V1
|
||||
|
||||
## Scope
|
||||
|
||||
Refine the existing FastAPI/Jinja2 control panel for daily GTI operations. The
|
||||
panel must answer which client/server needs attention, why, and which safe
|
||||
operator action is available. This work does not change fiscal rules, the
|
||||
Hub/Agent protocol, Firebird access, AI write controls, or deployment.
|
||||
|
||||
## Design Choices
|
||||
|
||||
The implementation keeps the existing server-rendered architecture:
|
||||
|
||||
- `control_plane.py` remains the bounded query/view-model layer.
|
||||
- A small presentation helper formats UTC timestamps using a configurable UI
|
||||
timezone, defaulting to `America/Bahia`.
|
||||
- Existing models are reused. `FiscalContextEntry` stores validated operator
|
||||
context, `Job` schedules reprocessing, and `AuditEvent` records the complete
|
||||
action context. No new write capability is introduced.
|
||||
- Jinja2 templates receive operational view models, not raw ORM rows. Technical
|
||||
IDs remain secondary detail information.
|
||||
- CSS remains project-local and JavaScript is limited to progressive enhancement;
|
||||
blocker resolution is a server-rendered form, not a `window.confirm` action.
|
||||
|
||||
## Dashboard Semantics
|
||||
|
||||
Health cards include `ONLINE`, `DEGRADED`, `OFFLINE`, `FAILED`, and `UNKNOWN`.
|
||||
The query layer exposes a reconciled total and tests enforce:
|
||||
|
||||
`agents_total == online + degraded + offline + failed + unknown`.
|
||||
|
||||
Fiscal metrics are separate:
|
||||
|
||||
- `safe_blocked_products`: distinct active products represented by open blockers.
|
||||
- `open_blockers`: active blocker rows.
|
||||
|
||||
The dashboard labels these independently and never presents one as the other.
|
||||
Attention items are a deterministic maximum of ten, ordered by `FAILED`,
|
||||
`DEGRADED`, `OFFLINE`, `UNKNOWN`, required blockers, and important version
|
||||
drift. Each item contains client, display name, hostname, main problem, age,
|
||||
and a safe detail/filter link. Failed jobs and outdated Agents are compact
|
||||
sections with empty states.
|
||||
|
||||
## Presentation
|
||||
|
||||
UTC remains the storage/API representation. The UI renders both a human short
|
||||
form (`22/09/2026 16:11`) and relative age (`há 8 min`); the full technical
|
||||
timestamp is available as a `title` attribute. The timezone comes from
|
||||
`UI_TIMEZONE`, with `America/Bahia` as the development default.
|
||||
|
||||
Agents lead with client, display name, and hostname. Technical health and fiscal
|
||||
status are separate badges. Fiscal coverage includes total, complete, safe
|
||||
blocked, failed, and percentage with a worst-coverage sort option.
|
||||
|
||||
Jobs show client, installation/display name, hostname, job ID, kind, status,
|
||||
human timestamps, duration, and error. Blockers show client/installation,
|
||||
product, field, type, deterministic operational priority, age, and links to a
|
||||
detail page.
|
||||
|
||||
## Blocker Operations
|
||||
|
||||
The blockers page has visible server-side filters for client, installation,
|
||||
field, blocker type, status, age, and product/search. Its summary reports open
|
||||
blockers, distinct affected products, distinct affected clients, and field
|
||||
breakdown (`CFOP`, `ST`, `ICMS`, `CSOSN`, `NCM`, `OTHER`).
|
||||
|
||||
Priority is operational only and deterministic:
|
||||
|
||||
- `CRITICAL`: technical/unsafe condition that prevents processing.
|
||||
- `HIGH`: required field missing for an active product or a relevant conflict.
|
||||
- `NORMAL`: safe blocker without immediate operational urgency.
|
||||
- `LOW`: optional/reference enrichment.
|
||||
|
||||
Clicking a blocker opens a detail page with evidence, first/last seen, retry
|
||||
count, related events/jobs, and resolution history. The resolution form shows
|
||||
the client, server, product, description, field, type, reason, evidence, and
|
||||
current value. It accepts a validated context key, value, scope (`PRODUCT`,
|
||||
`GROUP`, or `COMPANY`), and reason/note. Known enum keys are validated by the
|
||||
existing `context_keys` rules; arbitrary context values cannot bypass backend
|
||||
validation.
|
||||
|
||||
On save, the transaction creates/updates the validated `FiscalContextEntry`,
|
||||
creates a bounded reprocess job for the product, and records an audit event
|
||||
containing operator, timestamp, client, installation, product, blocker,
|
||||
context key/scope/value, reason, before/after values, and reprocess job ID.
|
||||
The operation remains protected by human auth, CSRF, and authorization. It
|
||||
cannot enable AI writes, Firebird writes, SQL, or shell execution.
|
||||
|
||||
## Detail Pages and Events
|
||||
|
||||
Agent detail shows client/display name/hostname first, technical/fiscal/version
|
||||
badges, human last-seen, version, company, UF, CRT, and secondary technical IDs.
|
||||
Fiscal summary is bounded and links to filtered blockers. Event rows have human
|
||||
timestamps, severity, event type, message, and related job/product links, with
|
||||
server-side severity/type/date/job/product filters and pagination.
|
||||
|
||||
## Password Policy
|
||||
|
||||
Weak local development passwords are allowed only when explicitly enabled by
|
||||
`ALLOW_WEAK_DEV_PASSWORD=1` and only when `APP_ENV` is not `production`.
|
||||
Production always requires the strong password policy. The existing local admin
|
||||
credential is not changed.
|
||||
|
||||
## Verification
|
||||
|
||||
Tests cover health reconciliation, metrics distinction, timestamps, attention
|
||||
ordering/limit, fiscal percentage, human job identity/duration, blocker filters
|
||||
and detail, resolution validation/audit/CSRF/authorization, events, and
|
||||
environment-aware password policy. Full `tests`, `central/tests`, and
|
||||
`agent/tests` regressions plus local route/runtime and bounded performance
|
||||
smokes are required before completion.
|
||||
Reference in New Issue
Block a user