Automated PR created via Copilot per user request: merge current branch into platform-dev. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #331
1460 lines
32 KiB
Markdown
1460 lines
32 KiB
Markdown
# TenantPilot Enterprise UI Standards**Status:** Active **Owner:** Product / Engineering **Applies to:** TenantPilot custom Filament UI surfaces **Last reviewed:** 2026-05-03 ---## 1. Purpose and ScopeThis document defines the mandatory UI standards for TenantPilot custom UI surfaces that are not fully covered by native Filament components.TenantPilot is an enterprise SaaS platform for Microsoft Tenant Governance, Evidence, Tenant Reviews, Drift Detection, Baselines, Provider Health, Supportability, and MSP-/Customer-Governance workflows.The UI goal is:- Governance-of-Record- decision-first UX- Filament-native enterprise SaaS look- calm operational surfaces- clear action hierarchy- no false affordances- no fake routes- no fake data- no fake customer-safe maturityThis document applies to:- dashboards- custom cards- page headers- context chips- KPI rows- status cards- aside cards- action rows- governance summaries- evidence and review surfaces- provider health surfaces- supportability surfaces- customer-safe read-only surfaces- empty states- progress/readiness blocks- custom table/list enhancements- custom form/detail layoutsNative Filament components MUST be used first. Custom UI is allowed only when native Filament patterns are insufficient for the product need.---## 2. Non-Negotiable PrinciplesTenantPilot custom UI MUST follow these principles:1. **Decision-first, diagnostics-second, evidence-third.**2. **Filament-native first.**3. **Exactly one visually dominant Primary Action per page.**4. **Status colors belong to badges, labels and chips — not to buttons.**5. **Hover implies interactivity.**6. **Interactive affordance requires a repo-real route/action and permitted capability.**7. **No fake data, fake progress, fake routes or fake customer maturity.**8. **Blade renders. Builders/ViewModels/Presenters decide.**9. **Customer-safe surfaces MUST NOT expose raw technical details by default.**10. **Custom UI MUST use documented patterns, not ad-hoc styles.**Custom UI MUST NOT introduce one-off styling for cards, hovers, buttons, badges, icons, progressbars, empty states or interactive rows.---## 3. Surface TaxonomyTenantPilot uses the following UI surface types.| Surface | Purpose | Example ||---|---|---|| Page Header | Context and primary action | Tenant Dashboard title + Findings prüfen || Context Bar / Context Chips | Lightweight environment context | Workspace, Provider, Latest activity || KPI Row | High-level measurable status | Findings, Operations, Permissions || Decision Card | Recommended next action | Findings prüfen || Status Card | Current posture/readiness | Governance Status || Aside Card | Compact secondary context | Provider Health || Interactive Row | Clickable status or operation row | Governance row with actionUrl || Static Row | Read-only status line | Governance row without actionUrl || Empty State | Honest unavailable state | Kein aktives Review || Progress Block | Real measured progress | 2/3 Findings with outcome || Technical Details | Progressive disclosure only | Logs, JSON, raw payload || Inline Alert | Short contextual warning/info | Missing permissions warning || Table/List Row | Structured list item | Operation runs, findings, reviews || Form Section | Grouped form inputs | Settings section || Detail Section | Read-only structured information | Review metadata |A surface MUST use the matching pattern from this document. Do not treat all surfaces as generic cards.---## 4. Filament-native FirstNative Filament components SHOULD be used whenever possible.Preferred native patterns include:- Filament Pages- Filament Widgets- Filament Stats Overview- Filament Tables- Filament Actions- Filament Badges- Filament Sections- Filament Forms- Filament Infolists- Filament Modals / Slide-overs where appropriateCustom Blade markup MAY be used when:- the desired product surface is not well represented by a native Filament component- a composed dashboard/card pattern is needed- a read-only productized summary is needed- the UI must combine several domain signals into one decision-first surfaceCustom Blade markup MUST still follow this document.---## 5. Page Header PatternThe page header establishes context and action hierarchy.A Page Header SHOULD contain:- title- optional subtitle- optional status pill- exactly one primary action, if a primary action exists- secondary actions grouped or visually de-emphasizedRules:- The Page Header MUST NOT contain a button flood.- The Page Header MUST NOT expose diagnostics as primary actions.- The Page Header MUST NOT show multiple equal-weight primary buttons.- The primary action MUST represent the most important next operator action.- Secondary actions SHOULD be gray/neutral or grouped behind a menu.Example:| Page | Primary Action | Secondary Actions ||---|---|---|| Tenant Dashboard | Findings prüfen | Support anfragen, Support-Diagnostik öffnen || Review Detail | Review fortsetzen / Publish | Export, Evidence anzeigen || Provider Health | Berechtigungen öffnen | Refresh, Details |---## 6. Card and Surface PatternCustom cards MUST use a quiet Filament-like surface.Recommended base:```html<div class="rounded-xl border border-gray-200 bg-white"> ...</div>
|
||
Allowed:
|
||
|
||
|
||
bg-white
|
||
|
||
|
||
border border-gray-200
|
||
|
||
|
||
rounded-xl or rounded-2xl
|
||
|
||
|
||
compact, consistent padding
|
||
|
||
|
||
subtle shadow-sm only when the card represents a distinct elevated surface
|
||
|
||
|
||
Not allowed:
|
||
|
||
|
||
hard black borders
|
||
|
||
|
||
dark rings as default card borders
|
||
|
||
|
||
large colored status backgrounds
|
||
|
||
|
||
heavy shadows
|
||
|
||
|
||
unique one-off card treatments per feature
|
||
|
||
|
||
mixing different hover models in equivalent surfaces
|
||
|
||
|
||
Equivalent surface families MUST share the same hover and border model.
|
||
For example:
|
||
Interactive card row = border + hover:shadow-smStatic card row = border + no hover
|
||
Do not mix:
|
||
Governance rows = hover:bg-gray-50Recent operations = hover:shadow-sm
|
||
unless there is a documented product reason.
|
||
|
||
7. Interactive Row Pattern
|
||
Hover is an affordance. A row MAY only have hover, pointer, focus or shadow interaction when it is actually interactive.
|
||
7.1 Interactive Row
|
||
Allowed only when all of the following are true:
|
||
|
||
|
||
a repo-real route or action exists
|
||
|
||
|
||
the current user has the required capability/policy
|
||
|
||
|
||
the target is tenant/workspace scoped correctly
|
||
|
||
|
||
the interaction is useful in the product flow
|
||
|
||
|
||
Pattern:
|
||
|
||
|
||
render as <a> if it navigates
|
||
|
||
|
||
render as <button> if it performs an action
|
||
|
||
|
||
use cursor-pointer
|
||
|
||
|
||
use hover:shadow-sm
|
||
|
||
|
||
use focus-visible:ring
|
||
|
||
|
||
include stable data-testid
|
||
|
||
|
||
Interactive rows SHOULD use:
|
||
border border-gray-200 bg-white hover:shadow-sm focus-visible:ring
|
||
7.2 Static Row
|
||
When no route/capability exists:
|
||
|
||
|
||
render as static container
|
||
|
||
|
||
no hover state
|
||
|
||
|
||
no cursor pointer
|
||
|
||
|
||
no focus state
|
||
|
||
|
||
no fake action
|
||
|
||
|
||
no fake route
|
||
|
||
|
||
Static rows MUST NOT look clickable.
|
||
7.3 Anti-patterns
|
||
Do not introduce:
|
||
|
||
|
||
div with hover but no click target
|
||
|
||
|
||
cursor-pointer without route/action
|
||
|
||
|
||
row looks clickable but only text is shown
|
||
|
||
|
||
fake target route added just to satisfy UI
|
||
|
||
|
||
hover background on static status rows
|
||
|
||
|
||
|
||
8. Button and Action Hierarchy
|
||
TenantPilot uses a strict action hierarchy.
|
||
8.1 Page Primary Action
|
||
Each page MAY have at most one visually dominant primary action.
|
||
Allowed:
|
||
|
||
|
||
primary color
|
||
|
||
|
||
filled button
|
||
|
||
|
||
page header placement
|
||
|
||
|
||
clear action label
|
||
|
||
|
||
Example:
|
||
Tenant Dashboard: Findings prüfen
|
||
8.2 Secondary Actions
|
||
All card, aside, row and support actions are secondary unless explicitly defined otherwise.
|
||
Examples:
|
||
|
||
|
||
Review fortsetzen
|
||
|
||
|
||
Risiken prüfen
|
||
|
||
|
||
Erforderliche Berechtigungen öffnen
|
||
|
||
|
||
Export-Artefakte anzeigen
|
||
|
||
|
||
Backup-Status öffnen
|
||
|
||
|
||
Überfällige Findings öffnen
|
||
|
||
|
||
Required style:
|
||
|
||
|
||
gray/secondary/neutral
|
||
|
||
|
||
no status color
|
||
|
||
|
||
consistent height
|
||
|
||
|
||
consistent radius
|
||
|
||
|
||
consistent typography
|
||
|
||
|
||
consistent padding
|
||
|
||
|
||
8.3 Status Colors and Buttons
|
||
Status colors MUST NOT be used as button colors.
|
||
Status is represented through:
|
||
|
||
|
||
badge
|
||
|
||
|
||
chip
|
||
|
||
|
||
label
|
||
|
||
|
||
description text
|
||
|
||
|
||
Buttons communicate action hierarchy, not status.
|
||
Bad:
|
||
Failed = red buttonHealthy = green buttonWarning = yellow button
|
||
Good:
|
||
Failed = danger badge + gray action buttonHealthy = success badge + gray action buttonWarning = warning badge + gray action button
|
||
|
||
9. Badge and Status Pattern
|
||
Badges communicate state.
|
||
Recommended mapping:
|
||
MeaningBadge ToneReady / Healthy / Published / CompletesuccessWarning / Stale / Attention / Overdue soonwarningFailed / Error / BlockeddangerUnknown / Unavailable / Not configuredgray
|
||
Rules:
|
||
|
||
|
||
badges MUST be small and quiet
|
||
|
||
|
||
status text MUST remain visible
|
||
|
||
|
||
color alone MUST NOT be the only status signal
|
||
|
||
|
||
badges MUST NOT become large colored panels
|
||
|
||
|
||
badges SHOULD NOT be duplicated by additional status icons unless needed for accessibility
|
||
|
||
|
||
buttons MUST NOT inherit badge/status colors
|
||
|
||
|
||
Example:
|
||
Provider Health [Healthy] 0 missing permissions
|
||
|
||
10. Icon Pattern
|
||
Icons improve scanability. They do not replace labels or badges.
|
||
Rules:
|
||
|
||
|
||
use Heroicons / Filament-native icons
|
||
|
||
|
||
size SHOULD be 16px / h-4 w-4
|
||
|
||
|
||
color SHOULD be neutral gray
|
||
|
||
|
||
exactly one semantic icon per row/action when used
|
||
|
||
|
||
icon describes type, not status
|
||
|
||
|
||
outcome/status remains in badges
|
||
|
||
|
||
no colorful icon bubbles
|
||
|
||
|
||
no decorative illustrations in operational surfaces
|
||
|
||
|
||
no large circle containers around standard operational icons
|
||
|
||
|
||
Recommended icon mapping:
|
||
ConceptIconBaseline Compareheroicon-m-scaleEvidence Coverageheroicon-m-document-checkReviewheroicon-m-clipboard-document-checkProvider Permissionsheroicon-m-keyBackup / Recoveryheroicon-m-arrow-path-rounded-squareInventory Syncheroicon-m-arrow-pathReview Packheroicon-m-document-arrow-downReview Compositionheroicon-m-document-textEvidence Snapshotheroicon-m-document-duplicateProvider Healthheroicon-m-shield-checkPermission Postureheroicon-m-keyFindings / Attentionheroicon-m-shield-exclamationOverdue / Timeheroicon-m-clock
|
||
Principle:
|
||
Icon = TypeBadge = StatusButton = Action
|
||
|
||
11. Progress Bar Pattern
|
||
Progress bars are only allowed when real numerator/denominator or percent values exist.
|
||
Allowed examples:
|
||
|
||
|
||
findings reviewed / total findings
|
||
|
||
|
||
findings with outcome / total findings
|
||
|
||
|
||
completed sections / total sections
|
||
|
||
|
||
evidence attached % only if real numerator/denominator exists
|
||
|
||
|
||
review completion % only if real section/status data exists
|
||
|
||
|
||
Not allowed:
|
||
|
||
|
||
fake percentages
|
||
|
||
|
||
static demo arrays
|
||
|
||
|
||
inferred readiness without real denominator
|
||
|
||
|
||
progress bars for Provider Health unless real progress data exists
|
||
|
||
|
||
progress bars for Risk Exceptions unless real progress data exists
|
||
|
||
|
||
Required:
|
||
|
||
|
||
visible text value remains present
|
||
|
||
|
||
role="progressbar"
|
||
|
||
|
||
aria-valuenow
|
||
|
||
|
||
aria-valuemin="0"
|
||
|
||
|
||
aria-valuemax="100"
|
||
|
||
|
||
percentage must be derived from repo-real values
|
||
|
||
|
||
Visual pattern:
|
||
|
||
|
||
full width
|
||
|
||
|
||
6–8px height
|
||
|
||
|
||
rounded-full
|
||
|
||
|
||
gray track
|
||
|
||
|
||
quiet primary/success/warning fill
|
||
|
||
|
||
no animation by default
|
||
|
||
|
||
Recommended markup shape:
|
||
<div class="space-y-1.5"> <div class="flex items-center justify-between gap-3 text-sm"> <span class="text-gray-600">Findings with outcome</span> <span class="font-medium text-gray-900">2/3 (67%)</span> </div> <div class="w-full overflow-hidden rounded-full bg-gray-100" style="height: 8px;" > <div class="h-full rounded-full" style="width: 67%; background-color: var(--primary-500);" role="progressbar" aria-valuenow="67" aria-valuemin="0" aria-valuemax="100" aria-valuetext="2/3 (67%)" ></div> </div></div>
|
||
For custom progress bars, fixed inline height MAY be used when Tailwind height utilities are not reliably present in the loaded Filament theme.
|
||
|
||
12. Context Chip Pattern
|
||
Context chips provide lightweight environment context.
|
||
Examples:
|
||
|
||
|
||
Workspace
|
||
|
||
|
||
Provider / Tenant type
|
||
|
||
|
||
Latest activity
|
||
|
||
|
||
Billing state
|
||
|
||
|
||
Scope
|
||
|
||
|
||
Rules:
|
||
|
||
|
||
chips SHOULD appear below the page header and above the KPI row
|
||
|
||
|
||
chips are not primary actions
|
||
|
||
|
||
chips MUST NOT collapse to 0px
|
||
|
||
|
||
long values SHOULD truncate
|
||
|
||
|
||
mobile MAY wrap
|
||
|
||
|
||
desktop SHOULD remain horizontal where possible
|
||
|
||
|
||
no fake provider labels
|
||
|
||
|
||
provider labels MUST be repo-real or neutral fallback
|
||
|
||
|
||
Allowed provider fallback:
|
||
Provider-VerbindungProvider connection
|
||
Do not invent:
|
||
Microsoft Graph
|
||
unless the repo-real provider identity supports it.
|
||
|
||
13. Dashboard Main / Aside Pattern
|
||
TenantPilot dashboards use a decision-first main/aside layout.
|
||
Desktop:
|
||
|
||
|
||
Main content: col-span-8
|
||
|
||
|
||
Aside: col-span-4
|
||
|
||
|
||
Mobile:
|
||
|
||
|
||
stacked layout
|
||
|
||
|
||
Main content SHOULD contain:
|
||
|
||
|
||
recommended actions
|
||
|
||
|
||
governance status
|
||
|
||
|
||
primary decision surfaces
|
||
|
||
|
||
recent operational context where appropriate
|
||
|
||
|
||
Aside SHOULD contain:
|
||
|
||
|
||
current review
|
||
|
||
|
||
risk exceptions
|
||
|
||
|
||
provider health
|
||
|
||
|
||
customer-safe output
|
||
|
||
|
||
readiness context
|
||
|
||
|
||
Rules:
|
||
|
||
|
||
aside cards are compact
|
||
|
||
|
||
no long tables in aside
|
||
|
||
|
||
no raw logs/json in aside
|
||
|
||
|
||
each aside card has at most one action
|
||
|
||
|
||
actions inside aside cards are secondary/gray
|
||
|
||
|
||
aside cards must not imply unavailable product maturity
|
||
|
||
|
||
|
||
14. Empty State Pattern
|
||
Empty states must be honest and action-aware.
|
||
Every empty state SHOULD include:
|
||
|
||
|
||
clear title
|
||
|
||
|
||
short explanation
|
||
|
||
|
||
optional next action only if repo-real route and capability exist
|
||
|
||
|
||
Good:
|
||
Kein aktives ReviewFür diesen Tenant ist aktuell kein Review in Bearbeitung.
|
||
Good:
|
||
Keine kundensichere Ausgabe verfügbarErzeuge ein Review-Paket, sobald Evidence und Review bereit sind.
|
||
Bad:
|
||
Kunden-Workspace öffnen
|
||
when no customer-safe workspace is repo-verified.
|
||
Rules:
|
||
|
||
|
||
no fake CTA
|
||
|
||
|
||
no implied product maturity
|
||
|
||
|
||
no placeholder/demo content
|
||
|
||
|
||
no customer-safe claim without repo verification
|
||
|
||
|
||
no action without route and capability
|
||
|
||
|
||
|
||
15. Loading, Stale and Error States
|
||
15.1 Loading
|
||
Loading states SHOULD use:
|
||
|
||
|
||
skeleton
|
||
|
||
|
||
quiet placeholder
|
||
|
||
|
||
short loading text when needed
|
||
|
||
|
||
Avoid:
|
||
|
||
|
||
spinner spam
|
||
|
||
|
||
layout shifting
|
||
|
||
|
||
blocking entire dashboard unless necessary
|
||
|
||
|
||
15.2 Stale
|
||
Stale states SHOULD show:
|
||
|
||
|
||
badge or label
|
||
|
||
|
||
reason if available
|
||
|
||
|
||
last refreshed/checked timestamp when repo-real
|
||
|
||
|
||
Stale states MUST NOT use alarming surfaces unless operator action is required.
|
||
15.3 Error
|
||
Error states SHOULD show:
|
||
|
||
|
||
reason
|
||
|
||
|
||
impact
|
||
|
||
|
||
next action if available
|
||
|
||
|
||
Technical details MUST remain behind progressive disclosure.
|
||
Never default to:
|
||
|
||
|
||
raw exception
|
||
|
||
|
||
JSON payload
|
||
|
||
|
||
stack trace
|
||
|
||
|
||
logs
|
||
|
||
|
||
raw API response
|
||
|
||
|
||
|
||
16. Table and List Pattern
|
||
Tables should remain Filament-native unless there is a strong product reason.
|
||
Rules:
|
||
|
||
|
||
row hover only if row click is enabled
|
||
|
||
|
||
otherwise use explicit row action
|
||
|
||
|
||
status columns use badges
|
||
|
||
|
||
raw IDs hidden by default
|
||
|
||
|
||
technical columns behind toggle or detail view
|
||
|
||
|
||
destructive actions are never primary
|
||
|
||
|
||
no button flood in each row
|
||
|
||
|
||
no raw technical fields as default operator experience
|
||
|
||
|
||
TenantPilot tables SHOULD support decision-first scanning:
|
||
|
||
|
||
status
|
||
|
||
|
||
reason
|
||
|
||
|
||
impact
|
||
|
||
|
||
owner/scope
|
||
|
||
|
||
next action
|
||
|
||
|
||
details only on demand
|
||
|
||
|
||
Anti-pattern:
|
||
Operator must inspect raw IDs, payloads and logs to understand what matters.
|
||
|
||
17. Form and Detail Page Pattern
|
||
17.1 Forms
|
||
Forms SHOULD:
|
||
|
||
|
||
group fields into sections
|
||
|
||
|
||
use clear section titles
|
||
|
||
|
||
keep primary save/submit action consistent
|
||
|
||
|
||
separate destructive actions
|
||
|
||
|
||
hide advanced/technical fields behind collapsible sections where appropriate
|
||
|
||
|
||
Forms MUST NOT:
|
||
|
||
|
||
expose raw internal fields as primary inputs unless needed
|
||
|
||
|
||
mix destructive and primary actions at equal visual weight
|
||
|
||
|
||
rely on placeholder text as the only explanation
|
||
|
||
|
||
17.2 Detail Pages
|
||
Detail pages SHOULD:
|
||
|
||
|
||
be read-first
|
||
|
||
|
||
show status/reason/impact before technical details
|
||
|
||
|
||
use progressive disclosure for diagnostics
|
||
|
||
|
||
avoid looking like edit forms when read-only
|
||
|
||
|
||
Technical details SHOULD be grouped under:
|
||
Technical DetailsDiagnosticsRaw Details
|
||
and collapsed or secondary by default.
|
||
|
||
18. Customer-safe Surface Rules
|
||
Customer-safe surfaces must be read-only, calm and evidence-first.
|
||
Rules:
|
||
|
||
|
||
no raw technical logs by default
|
||
|
||
|
||
no internal operation noise by default
|
||
|
||
|
||
no admin-only actions
|
||
|
||
|
||
no unsupported maturity claims
|
||
|
||
|
||
no Open customer workspace unless Customer Review Workspace is repo-verified
|
||
|
||
|
||
use Export-Artefakte anzeigen or Review-Paket öffnen when only artifacts/packs exist
|
||
|
||
|
||
customer-safe copy must be especially precise
|
||
|
||
|
||
customer-safe output must distinguish between:
|
||
|
||
|
||
Review Pack
|
||
|
||
|
||
Evidence Snapshot
|
||
|
||
|
||
Export Artifact
|
||
|
||
|
||
Customer Workspace
|
||
|
||
|
||
|
||
|
||
Do not label the whole customer-safe output as failed if only the latest export/review-pack generation failed.
|
||
Prefer precise copy:
|
||
Review-Paket fehlgeschlagenExport fehlgeschlagenKeine kundensichere Ausgabe verfügbar
|
||
|
||
19. Component Ownership Rules
|
||
Business logic belongs in:
|
||
|
||
|
||
SummaryBuilder
|
||
|
||
|
||
ViewModel
|
||
|
||
|
||
Presenter
|
||
|
||
|
||
Link service
|
||
|
||
|
||
Policy/capability layer
|
||
|
||
|
||
domain service
|
||
|
||
|
||
Blade may:
|
||
|
||
|
||
render fields
|
||
|
||
|
||
branch on explicit view model flags
|
||
|
||
|
||
render provided URLs/icons/badges/progress values
|
||
|
||
|
||
Blade MUST NOT:
|
||
|
||
|
||
invent routes
|
||
|
||
|
||
infer capabilities
|
||
|
||
|
||
calculate business status
|
||
|
||
|
||
hardcode provider maturity
|
||
|
||
|
||
calculate fake progress
|
||
|
||
|
||
infer customer-safe maturity
|
||
|
||
|
||
parse raw payloads for product decisions
|
||
|
||
|
||
duplicate business logic from services/builders
|
||
|
||
|
||
Preferred flow:
|
||
Domain model/service→ SummaryBuilder/ViewModel/Presenter→ Blade render
|
||
Not allowed:
|
||
Blade template→ queries database→ invents status→ decides capability→ renders action
|
||
|
||
20. Localization Rules
|
||
Visible UI copy MUST use localization keys.
|
||
Rules:
|
||
|
||
|
||
no new hardcoded user-facing copy in Blade
|
||
|
||
|
||
DE/EN mixture is accepted only as explicit Localization v1 debt
|
||
|
||
|
||
customer-facing copy must be especially careful
|
||
|
||
|
||
labels must not overclaim product maturity
|
||
|
||
|
||
status labels should use shared badge/status language where possible
|
||
|
||
|
||
Until Localization v1 is complete:
|
||
|
||
|
||
mixed language may exist in operator-only surfaces
|
||
|
||
|
||
new copy should still be added through localization files
|
||
|
||
|
||
PR/close-out notes SHOULD mention known localization debt when relevant
|
||
|
||
|
||
|
||
21. Accessibility and Keyboard Rules
|
||
Custom interactive UI MUST be accessible.
|
||
Rules:
|
||
|
||
|
||
clickable navigation uses <a>
|
||
|
||
|
||
actions use <button> or Filament Action components
|
||
|
||
|
||
do not use clickable div without semantics
|
||
|
||
|
||
focus-visible style required for custom interactive elements
|
||
|
||
|
||
icon-only buttons require accessible label
|
||
|
||
|
||
color alone must not communicate status
|
||
|
||
|
||
progress bars require ARIA attributes
|
||
|
||
|
||
keyboard navigation must not be broken
|
||
|
||
|
||
Required for progress bars:
|
||
role="progressbar"aria-valuenow="..."aria-valuemin="0"aria-valuemax="100"
|
||
Required for icon-only actions:
|
||
aria-label="..."
|
||
or visible text.
|
||
|
||
22. Responsive, Truncation and Overflow Rules
|
||
Custom UI MUST remain stable on desktop and mobile.
|
||
Rules:
|
||
|
||
|
||
desktop dashboards may use main/aside grid
|
||
|
||
|
||
mobile stacks main and aside
|
||
|
||
|
||
chips may wrap on mobile
|
||
|
||
|
||
chips must not overlap
|
||
|
||
|
||
long names must truncate
|
||
|
||
|
||
no 0px grid columns
|
||
|
||
|
||
no horizontal scroll leaks
|
||
|
||
|
||
cards must not collapse due to long tenant/workspace/provider names
|
||
|
||
|
||
actions must remain visible and usable on mobile
|
||
|
||
|
||
Recommended:
|
||
truncate long labelsuse minmax() carefullytest actual browser widthavoid fixed widths unless intentional
|
||
Every dashboard-like custom surface SHOULD be checked in:
|
||
|
||
|
||
desktop viewport
|
||
|
||
|
||
narrow/mobile viewport
|
||
|
||
|
||
long tenant/workspace name scenario where practical
|
||
|
||
|
||
|
||
23. Data Freshness Rules
|
||
Governance, Evidence, Review and Provider statements SHOULD show freshness when repo-real freshness exists.
|
||
Examples:
|
||
|
||
|
||
Last checked 20 minutes ago
|
||
|
||
|
||
Last snapshot 2 days ago
|
||
|
||
|
||
Latest activity 5 minutes ago
|
||
|
||
|
||
Likely stale
|
||
|
||
|
||
No recent signal
|
||
|
||
|
||
Rules:
|
||
|
||
|
||
stale data must not be presented as fresh
|
||
|
||
|
||
stale state should be quiet unless action is required
|
||
|
||
|
||
freshness belongs in badge/description/context, not aggressive layout
|
||
|
||
|
||
no fake timestamps
|
||
|
||
|
||
no generic “synced recently” without a real timestamp
|
||
|
||
|
||
|
||
24. Raw Technical Details and Progressive Disclosure
|
||
TenantPilot is not a raw admin mirror.
|
||
Default surfaces MUST NOT show:
|
||
|
||
|
||
raw JSON
|
||
|
||
|
||
raw logs
|
||
|
||
|
||
stack traces
|
||
|
||
|
||
internal IDs
|
||
|
||
|
||
raw API payloads
|
||
|
||
|
||
technical exception details
|
||
|
||
|
||
provider-specific debug output
|
||
|
||
|
||
These MAY be available under:
|
||
|
||
|
||
Technical Details
|
||
|
||
|
||
Diagnostics
|
||
|
||
|
||
Raw Payload
|
||
|
||
|
||
Operation Log
|
||
|
||
|
||
Developer / Support view
|
||
|
||
|
||
Decision-first default:
|
||
StatusReasonImpactNext Action
|
||
Then:
|
||
EvidenceDiagnosticsRaw Details
|
||
|
||
25. Data-testid and Browser-Smoke Rules
|
||
Custom UI surfaces SHOULD use stable data-testid attributes.
|
||
Rules:
|
||
|
||
|
||
test product concepts, not styling implementation
|
||
|
||
|
||
avoid fragile assertions on Tailwind class order
|
||
|
||
|
||
avoid assertions on SVG attribute order
|
||
|
||
|
||
use browser smoke for layout-critical surfaces
|
||
|
||
|
||
use feature tests for capability/action visibility
|
||
|
||
|
||
use markup tests for stable structural contracts
|
||
|
||
|
||
do not assert exact class ordering unless explicitly testing a UI contract
|
||
|
||
|
||
Examples:
|
||
tenant-dashboard-governance-statustenant-dashboard-secondary-actiontenant-dashboard-current-review-progresstenant-dashboard-context-chipstenant-dashboard-recommended-actiontenant-dashboard-recent-operation
|
||
Browser-smoke SHOULD verify:
|
||
|
||
|
||
page loads
|
||
|
||
|
||
key sections render
|
||
|
||
|
||
critical layout does not collapse
|
||
|
||
|
||
important actions are visible/hidden correctly
|
||
|
||
|
||
no false interactive affordance where known
|
||
|
||
|
||
Browser-smoke SHOULD NOT rely on:
|
||
|
||
|
||
theme CSS being loaded in unit-test-only contexts
|
||
|
||
|
||
exact Tailwind class order
|
||
|
||
|
||
fragile screenshot pixel matching unless explicitly required
|
||
|
||
|
||
|
||
26. UI Review Checklist
|
||
Before merging custom UI changes:
|
||
|
||
|
||
Filament-native component was used where possible.
|
||
|
||
|
||
Custom styling follows this standard.
|
||
|
||
|
||
Exactly one visually dominant Primary Action exists on the page.
|
||
|
||
|
||
Card actions are secondary/gray.
|
||
|
||
|
||
Status colors are only used in badges/chips/labels.
|
||
|
||
|
||
Hover exists only on interactive elements.
|
||
|
||
|
||
Every interactive element has a repo-real route/action.
|
||
|
||
|
||
Capabilities/policies are respected.
|
||
|
||
|
||
Tenant/workspace scope is respected.
|
||
|
||
|
||
No fake routes were introduced.
|
||
|
||
|
||
No fake actions were introduced.
|
||
|
||
|
||
No fake progress or demo values were introduced.
|
||
|
||
|
||
Empty states are honest.
|
||
|
||
|
||
Customer-safe maturity is not overclaimed.
|
||
|
||
|
||
Raw JSON/logs are not shown by default.
|
||
|
||
|
||
Icons are small, neutral and semantic.
|
||
|
||
|
||
Progressbars include accessible attributes.
|
||
|
||
|
||
Mobile layout does not overlap or collapse.
|
||
|
||
|
||
Long labels truncate or wrap safely.
|
||
|
||
|
||
Browser smoke was run or explicitly documented as not run.
|
||
|
||
|
||
New visible copy uses localization keys.
|
||
|
||
|
||
Blade does not invent domain logic, routes, capabilities or maturity.
|
||
|
||
|
||
|
||
27. Anti-Patterns
|
||
Do not introduce:
|
||
|
||
|
||
hover without clickability
|
||
|
||
|
||
cursor-pointer without route/action
|
||
|
||
|
||
primary buttons inside cards
|
||
|
||
|
||
status-colored buttons
|
||
|
||
|
||
fake customer workspace actions
|
||
|
||
|
||
fake provider names
|
||
|
||
|
||
fake progress bars
|
||
|
||
|
||
static demo chart arrays
|
||
|
||
|
||
raw JSON/logs as default
|
||
|
||
|
||
technical table sprawl
|
||
|
||
|
||
new top-level page for every technical state
|
||
|
||
|
||
custom one-off card styles
|
||
|
||
|
||
mixed hover models in the same surface family
|
||
|
||
|
||
icon-only status without text
|
||
|
||
|
||
large colored status panels
|
||
|
||
|
||
decorative icon bubbles in operational UI
|
||
|
||
|
||
route guessing in Blade
|
||
|
||
|
||
capability guessing in Blade
|
||
|
||
|
||
customer-safe maturity overclaims
|
||
|
||
|
||
provider-specific hardcoding in platform core unless repo-real and scoped
|
||
|
||
|
||
destructive actions with primary visual weight
|
||
|
||
|
||
button floods in page headers or table rows
|
||
|
||
|
||
|
||
28. Pattern Decision Matrix
|
||
Use this matrix when choosing a pattern.
|
||
NeedUseDo Not UseShow statusBadge / chip / labelButton colorShow actionButton / link / Filament ActionBadgeShow typeSmall neutral iconColored status iconShow progressProgress bar with real denominatorFake percentShow unavailable stateEmpty stateDisabled fake CTANavigate row<a> row with hover/focusdiv hoverStatic informationStatic row/cardHover rowShow detailsProgressive disclosureRaw details by defaultCustomer outputReview Pack / Export Artifact wordingCustomer Workspace wording unless repo-real
|
||
|
||
29. TenantPilot-specific Product Guardrails
|
||
TenantPilot MUST avoid drifting into:
|
||
|
||
|
||
generic M365 admin mirror
|
||
|
||
|
||
helpdesk tool
|
||
|
||
|
||
raw device-action tool
|
||
|
||
|
||
debug surface collection
|
||
|
||
|
||
log viewer as primary UX
|
||
|
||
|
||
feature-per-page sprawl
|
||
|
||
|
||
TenantPilot SHOULD reinforce:
|
||
|
||
|
||
Governance-of-Record
|
||
|
||
|
||
OperationRun truth
|
||
|
||
|
||
Evidence-first reporting
|
||
|
||
|
||
capability-first RBAC
|
||
|
||
|
||
workspace-first multi-tenancy
|
||
|
||
|
||
customer-safe review consumption
|
||
|
||
|
||
decision-first surfaces
|
||
|
||
|
||
provider-extensible architecture
|
||
|
||
|
||
UI copy and actions MUST NOT claim a product capability is mature unless repo-verified.
|
||
Examples:
|
||
CapabilityAllowed CopyAvoidReview Pack existsReview-Paket öffnenKunden-Workspace öffnenExport artifact existsExport-Artefakte anzeigenCustomer Portal öffnenEvidence existsNachweise anzeigenAudit abgeschlossenProvider permissions missingBerechtigungen öffnenFix automaticallyCustomer Review Workspace not implementedKundensichere Ausgabe nicht verfügbarOpen customer workspace
|
||
|
||
29.1 OperationRun Activity Feedback Pattern
|
||
The tenant-shell OperationRun activity hint is a bounded start-surface hint, not a second monitoring page.
|
||
|
||
It MUST:
|
||
|
||
derive rows from existing OperationRun truth only
|
||
|
||
show queued or running rows plus only the bounded terminal states that complete the same-shell feedback loop:
|
||
|
||
- just-completed successful runs that remain visible briefly
|
||
- unresolved terminal follow-up runs that were blocked, partial, failed, or automatically reconciled
|
||
|
||
render inside the TenantPilot application shell, directly below the topbar/context bar or inside the main content shell as a compact in-flow banner
|
||
|
||
stay in-flow and non-obstructive instead of floating over row actions or page CTAs
|
||
|
||
show at most three items in the shell
|
||
|
||
keep the visible shell order deterministic by using the existing shell ordering contract:
|
||
|
||
- active queued or running rows first
|
||
- unresolved terminal follow-up rows next
|
||
- recent terminal success rows only as the bounded close-the-loop state
|
||
|
||
use one grouped banner action area with a dominant action that reflects whether the banner is about one operation or multiple operation updates:
|
||
|
||
- when exactly one visible operation update is shown, use View operation to open that run detail
|
||
- when multiple visible operation updates are shown, use Review operations to open the canonical Operations collection instead of any single run detail
|
||
|
||
use Active operations as the header label only when all visible rows are active queued or running work. If any visible row is terminal, switch the header label to Operation updates.
|
||
|
||
keep a secondary action labeled Show all operations and one lifecycle-sensitive tertiary affordance for the current browser session:
|
||
|
||
- Hide activity for queued or running rows
|
||
- Dismiss or Close for terminal-success rows
|
||
- Acknowledge for unresolved terminal follow-up rows, including grouped states
|
||
|
||
use Show all operations to open the canonical Operations collection with the current tenant encoded as the contextual tenant_id prefilter
|
||
|
||
keep overflow copy collective and calm, for example 19 more operation updates available in Operations.
|
||
|
||
show at most one concise next-step cue per item and leave diagnostics, logs, raw payloads, and failure detail to the canonical Operations pages
|
||
|
||
show determinate progress only when summary_counts.total and summary_counts.processed are real numeric values, clamp the progressbar to 0-100, and only show processed or percentage text when it is derived from those repo-real values
|
||
|
||
derive progress treatment from one shared OperationRun progress contract instead of local Blade or widget math
|
||
|
||
allow counted progress only for current run families that persist truthful summary_counts.total plus summary_counts.processed during execution:
|
||
|
||
- inventory sync
|
||
- review-pack generation
|
||
- evidence-snapshot generation
|
||
- backup-set policy additions
|
||
- backup-set bulk restore
|
||
|
||
keep all other run families on activity-only, phased, or composite treatment until they persist equally trustworthy progress truth through the shared contract
|
||
|
||
limit the current non-counted phase or composite contract to these run families only:
|
||
|
||
- baseline_capture
|
||
- baseline_compare
|
||
- tenant.review.compose
|
||
|
||
keep queued rows activity-only even when a planned total exists
|
||
|
||
keep running rows without trustworthy processed and total counts activity-only or indeterminate
|
||
|
||
keep summary_counts.succeeded, summary_counts.failed, and summary_counts.skipped outcome-only; they must not silently replace processed as progress truth
|
||
|
||
for baseline_capture and baseline_compare, use canonical progress.phase metadata with short operator-safe, non-technical labels such as Preparing baseline capture., Capturing evidence., Refreshing comparison evidence., Evaluating baseline drift., Saving baseline snapshot., and Finalizing baseline comparison.
|
||
|
||
for tenant.review.compose, use canonical progress.composite metadata only for bounded aggregate status copy such as Review composition is aggregating 3 operations.; this remains composite indeterminate copy, not counted progress, even when operation_count is visible
|
||
|
||
when canonical phase or composite metadata is absent or malformed, degrade safely to the existing generic phased or composite fallback instead of inventing percentages, strategy detail, provider detail, or raw technical diagnostics in the shell
|
||
|
||
switch terminal-success rows to success-state copy instead of showing active progress after completion
|
||
|
||
separate successful completion from unresolved terminal follow-up in the shell helper copy:
|
||
|
||
- successful terminal rows use no-action-needed completion copy and remain locally dismissible
|
||
- unresolved terminal follow-up rows use review-needed copy and MUST NOT inherit generic Dismiss semantics
|
||
|
||
keep hide, dismiss, or acknowledge behavior browser-session-only and re-open the hint when a new run-enqueued event is accepted for the current tenant
|
||
|
||
record these as follow-up work instead of widening the current contract:
|
||
|
||
- provider health or support-diagnostics progress rollout
|
||
- review-pack or evidence-snapshot overlap with other progress specs
|
||
- child-run graph persistence or composite child-link expansion
|
||
- dashboard cards or workflow-engine generated progress explanations
|
||
|
||
It MUST NOT:
|
||
|
||
render as a detached document-level BODY_START banner above the TenantPilot application chrome
|
||
|
||
turn the shell hint into a permanent terminal inbox, tray, or second monitoring page
|
||
|
||
use raw route strings or non-canonical OperationRun links
|
||
|
||
show fake percentages, guessed completion, or fake progress
|
||
|
||
let outcome counters, aggregate operation counts, or phase hints masquerade as counted progress
|
||
|
||
show active progress UI after a run is already terminal
|
||
|
||
persist hide or dismiss state in the database or on the OperationRun record
|
||
|
||
persist acknowledge state in the database or on the OperationRun record
|
||
|
||
render as a fixed overlay that can cover findings row actions, table actions, or page-primary actions
|
||
|
||
30. Maintenance Rules
|
||
This document is a living standard.
|
||
Changes to this document SHOULD happen when:
|
||
|
||
|
||
a repeated UI decision appears in multiple specs
|
||
|
||
|
||
a new custom pattern is introduced
|
||
|
||
|
||
a drift issue is found during browser smoke
|
||
|
||
|
||
a Filament-native alternative becomes available
|
||
|
||
|
||
Localization v1 changes copy rules
|
||
|
||
|
||
Customer Review Workspace v1 changes customer-safe surface rules
|
||
|
||
|
||
Feature specs SHOULD NOT override this document casually.
|
||
Any intentional exception SHOULD document:
|
||
|
||
|
||
affected surface
|
||
|
||
|
||
reason for exception
|
||
|
||
|
||
expected duration
|
||
|
||
|
||
follow-up cleanup if needed
|
||
|
||
|
||
|
||
31. Minimal Constitution Reference
|
||
The project constitution SHOULD reference this document with a short rule, not duplicate the full standard.
|
||
Recommended constitution text:
|
||
### Custom Filament UITenantPilot custom Filament UI MUST follow `docs/ui/tenantpilot-enterprise-ui-standards.md`.Feature specs MUST NOT introduce ad-hoc card, hover, button, badge, icon, progressbar, empty-state or interactive-row styling.Interactive affordance MUST only be used when a repo-real route/action and permitted capability exist.Status colors MUST be represented through badges, chips or labels, not through action buttons.Customer-safe maturity MUST NOT be implied by copy, routes or actions unless repo-verified.
|
||
|
||
32. Spec / PR Close-out Reference
|
||
Visible custom UI changes SHOULD include a short close-out note.
|
||
Recommended format:
|
||
## UI Standards CheckChanged custom UI surfaces:- ...Patterns used:- Card / Surface- Interactive Row- Secondary Action- Progress Bar- Empty StateConfirmed:- [ ] Filament-native used where possible- [ ] No ad-hoc styling- [ ] No fake data/routes/progress- [ ] Capabilities respected- [ ] Browser smoke run- [ ] Localization debt documented if applicable
|
||
|
||
33. Quick Reference
|
||
Buttons
|
||
Page primary action = primary filledCard action = gray/secondaryAside action = gray/secondaryStatus = badge, not button color
|
||
Rows
|
||
Interactive row = route + capability + hover/focusStatic row = no hover, no pointer
|
||
Icons
|
||
SmallNeutralOne per row/actionType, not status
|
||
Progress
|
||
Only real numerator/denominator/percentText value remains visibleARIA requiredNo fake progress
|
||
Customer-safe
|
||
Do not imply Customer Review Workspace maturity unless repo-verifiedUse Review Pack / Export Artifact wording when that is what exists
|
||
Blade
|
||
Render onlyDo not invent domain logicDo not invent routesDo not infer capabilities
|