From 55087313b7370ec583a7c45bf0d104f1c2707d39 Mon Sep 17 00:00:00 2001 From: EthanHealy01 <80844253+EthanHealy01@users.noreply.github.com> Date: Fri, 14 Aug 2026 10:55:15 +0000 Subject: [PATCH] Processor UI snags: fat CTAs, real Infrastructure tabs, one surface style (#7497) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Five unrelated snags in the processor (portal) UI, plus fixes they turned up. No backend changes. `84 files changed, +892 / −3364` ## Fat CTA buttons - New `fat` prop on the SUI `Button`: 2.75rem tall, 1.25rem side padding, 0.75rem corners, semibold. Composes with all four variants/accents. - Applied to the page-header CTA on Sources, Documents, Pipelines, Users (both), Usage, Integrations, Infrastructure — 8 buttons, all in line with a page title. Nothing else. - `LandingActions` migrated onto the prop; `.landing-btn-primary` / `.landing-btn-secondary` and their four `!important`s deleted. The editor landing CTAs come down 4px with everything else. - Infrastructure's header CTA is now primary; its "Create key" dropped to secondary so they stop competing. ## Documents empty state - "Connect a source" opened the Sources *page*; it now opens the `SourceModal` connect flow in place, no route change. - No extra cache wiring: `SourceModal` already invalidates the sources query. ## Infrastructure tabs - Only API Keys and Audit Logs hit real endpoints. Deployments, Security, Models and Storage read mock-only `/v1/infrastructure/*` that no backend serves. - Those four are now disabled: native `disabled`, out of the keyboard tab order, `aria-disabled`, with the view refusing non-enabled keys as a second guard. - Real tabs moved leftmost; API Keys is the default; `?tab=` deep links validated against the enabled set (the home flow's audit link still works). - Deleted: 4 tab components, their fetch fns and ~25 dead types, MSW handlers, fixtures (908 → 253 lines), dead CSS, unused formatters, 240 lines of `en-US` strings. Most of the −3364. - Page subtitle no longer advertises the disabled tabs. ## Surface consolidation - New `Surface` primitive (`sui-surface`): fill, hairline, radius, no shadow. Kept separate from `sui-nav-surface` so nav chrome can diverge later. - `Card` composes it and no longer draws its own shadow — this changes editor Card usages too, by design. - SUI primitives that are surfaces adopt it: `MetricCard`, `MetricStrip`, `NodeCard`, `Table`, `Collapsible`, `CodeBlock`. - The portal gets its own `.portal-surface` with the same three declarations, applied to 19 elements. A `sui-` class belongs to the component that emits it, so feature markup doesn't wear one. - `raised` variant = one subtle shadow for a surface in front of another surface (the flow diagram's tiles). Same fill as its parent, so nesting never shifts a region's colour. Dark has its own value. - Floating chrome (modals, drawers, dropdowns, assistant, sidebar) keeps its elevation; sunken wells stay sunken. ## Sources list - Centred "No sources connected yet" empty state removed — it duplicated the header CTA and pushed the table down the page. The header's "Connect source" is the single way in. ## Drive-by fixes - The connect flow rendered unstyled outside the Sources view: `.portal-conn-picker__*` / `.portal-sources__connection-*` lived in `views/Sources.css`, which none of the five components rendering them imported. Moved to `components/sources/connections.css`. - Three inert custom properties (`--surface-input`, `--color-border-2`, `--text-default`) are defined nowhere in the codebase — `.portal-conn-picker__card` had no fill at all as a result. - Dead CSS removed from `Sources.css` (grep-verified unused): old expanded-row panel + its keyframes, type-card block. ## Testing - `task frontend:check` — typecheck, lint (oxlint + 4 theme-lint passes + stylelint), format, 238 files / 2063 tests. - `frontend:typecheck:all` across all 9 tsconfigs. - `frontend:storybook:a11y:changed` — 119 stories, light and dark, zero violations, no regressions vs baseline. - New tests: `Infrastructure.test.tsx` (tab order, default, disabled behaviour, deep-link filtering) and a Documents test that the empty-state CTA opens the modal without navigating. - Merged `origin/main` (#7438 replaced `PipelineHeader` with the new Create/Edit headers); full suite green at 240 files / 2072 tests after the merge. --- .../public/locales/en-US/translation.toml | 246 +------ .../core/components/shared/LandingActions.tsx | 8 +- .../core/components/shared/LandingPage.css | 10 - frontend/editor/src/core/ui/Button.css | 5 + .../editor/src/core/ui/Button.stories.tsx | 30 + frontend/editor/src/core/ui/Button.tsx | 16 +- frontend/editor/src/core/ui/Card.css | 8 +- frontend/editor/src/core/ui/Card.tsx | 4 +- frontend/editor/src/core/ui/CodeBlock.css | 3 +- frontend/editor/src/core/ui/Collapsible.css | 2 - frontend/editor/src/core/ui/Collapsible.tsx | 5 +- frontend/editor/src/core/ui/MetricCard.css | 6 - frontend/editor/src/core/ui/MetricCard.tsx | 2 + frontend/editor/src/core/ui/MetricStrip.css | 4 - frontend/editor/src/core/ui/MetricStrip.tsx | 2 + frontend/editor/src/core/ui/NodeCard.css | 4 - frontend/editor/src/core/ui/NodeCard.tsx | 2 + frontend/editor/src/core/ui/Surface.css | 20 + .../editor/src/core/ui/Surface.stories.tsx | 46 ++ frontend/editor/src/core/ui/Surface.tsx | 33 + frontend/editor/src/core/ui/Table.css | 3 - frontend/editor/src/core/ui/Table.tsx | 5 +- frontend/editor/src/core/ui/Tabs.tsx | 8 +- frontend/editor/src/core/ui/index.ts | 1 + frontend/editor/src/portal/MOCKS.md | 9 +- .../editor/src/portal/api/infrastructure.ts | 229 ------- .../portal/components/EditorStatusCard.css | 4 - .../portal/components/EditorStatusCard.tsx | 3 +- .../editor/src/portal/components/HomeHero.tsx | 2 +- .../src/portal/components/ProcessorFlow.css | 11 +- .../src/portal/components/ProcessorFlow.tsx | 1 + .../billing/BundleCheckoutModal.tsx | 3 +- .../components/billing/CardPlaceholder.tsx | 3 +- .../src/portal/components/billing/billing.css | 5 - .../docs/EndpointReferenceSection.tsx | 6 +- .../components/documents/ReviewQueue.test.tsx | 46 +- .../components/documents/ReviewQueue.tsx | 12 +- .../components/failures/FileRunEventList.tsx | 3 +- .../portal/components/failures/failures.css | 3 - .../components/infrastructure/ApiKeysTab.tsx | 1 + .../infrastructure/DeploymentsTab.stories.tsx | 46 -- .../infrastructure/DeploymentsTab.tsx | 233 ------- .../infrastructure/ModelsTab.stories.tsx | 30 - .../components/infrastructure/ModelsTab.tsx | 272 -------- .../infrastructure/SecurityTab.stories.tsx | 58 -- .../components/infrastructure/SecurityTab.tsx | 342 ---------- .../infrastructure/StorageTab.stories.tsx | 80 --- .../components/infrastructure/StorageTab.tsx | 244 ------- .../components/infrastructure/infraFormat.ts | 135 +--- .../pipelines/PipelineInspector.css | 3 - .../pipelines/PipelineInspector.tsx | 7 +- .../pipelines/graph/PipelineGraph.css | 2 +- .../policies/PolicyExternalApiConfig.tsx | 4 +- .../components/procurement/DealStatusHero.tsx | 3 +- .../procurement/ProcurementAgreement.tsx | 3 +- .../components/procurement/QuoteBuilder.tsx | 3 +- .../components/sources/ConnectionForm.tsx | 1 + .../components/sources/ConnectionModal.tsx | 1 + .../components/sources/ConnectionPicker.tsx | 1 + .../sources/ConnectionTypePicker.tsx | 4 +- .../sources/SourcesTable.stories.tsx | 19 + .../portal/components/sources/connections.css | 241 +++++++ .../components/users/PendingInvitations.tsx | 3 +- .../components/users/UsersDirectory.tsx | 7 +- .../portal/mocks/handlers/infrastructure.ts | 34 +- .../editor/src/portal/mocks/infrastructure.ts | 617 +----------------- frontend/editor/src/portal/theme/surface.css | 7 + .../editor/src/portal/views/DeveloperDocs.css | 2 - .../editor/src/portal/views/Documents.tsx | 2 +- .../src/portal/views/Infrastructure.css | 255 -------- .../src/portal/views/Infrastructure.test.tsx | 94 +++ .../src/portal/views/Infrastructure.tsx | 70 +- .../editor/src/portal/views/Integrations.css | 3 - .../editor/src/portal/views/Integrations.tsx | 4 +- .../editor/src/portal/views/Pipelines.tsx | 1 + .../editor/src/portal/views/Procurement.css | 15 +- frontend/editor/src/portal/views/Sources.css | 356 ---------- .../editor/src/portal/views/Sources.test.tsx | 4 +- frontend/editor/src/portal/views/Sources.tsx | 30 +- frontend/editor/src/portal/views/Usage.tsx | 2 +- frontend/editor/src/portal/views/Users.css | 3 - frontend/editor/src/portal/views/Users.tsx | 8 +- 82 files changed, 702 insertions(+), 3361 deletions(-) create mode 100644 frontend/editor/src/core/ui/Surface.css create mode 100644 frontend/editor/src/core/ui/Surface.stories.tsx create mode 100644 frontend/editor/src/core/ui/Surface.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/DeploymentsTab.stories.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/DeploymentsTab.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/ModelsTab.stories.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/ModelsTab.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/SecurityTab.stories.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/SecurityTab.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/StorageTab.stories.tsx delete mode 100644 frontend/editor/src/portal/components/infrastructure/StorageTab.tsx create mode 100644 frontend/editor/src/portal/components/sources/connections.css create mode 100644 frontend/editor/src/portal/theme/surface.css create mode 100644 frontend/editor/src/portal/views/Infrastructure.test.tsx diff --git a/frontend/editor/public/locales/en-US/translation.toml b/frontend/editor/public/locales/en-US/translation.toml index 1948b71bf4..4ddc2497ee 100644 --- a/frontend/editor/public/locales/en-US/translation.toml +++ b/frontend/editor/public/locales/en-US/translation.toml @@ -7444,7 +7444,7 @@ morning = "Good morning" [portal.infrastructure] manageEditorDeployment = "Manage Editor deployment" sectionsAriaLabel = "Infrastructure sections" -subtitle = "Deployments, credentials, security posture, storage, and the audit trail for your Stirling workspace." +subtitle = "API credentials and the audit trail for your Stirling workspace." title = "Infrastructure" [portal.infrastructure.apiKeys] @@ -7472,11 +7472,6 @@ cancel = "Cancel" confirm = "Revoke key" title = "Revoke API key" -[portal.infrastructure.attestationLabel] -attested = "Attested" -inScope = "In scope" -notApplicable = "N/A" - [portal.infrastructure.audit] filterAriaLabel = "Filter audit events by category" heading = "Audit logs" @@ -7548,11 +7543,6 @@ info = "Info" success = "Success" warning = "Warning" -[portal.infrastructure.certLabel] -certified = "Certified" -inProgress = "In progress" -notStarted = "Not started" - [portal.infrastructure.createKey] cancel = "Cancel" createKey = "Create key" @@ -7566,240 +7556,10 @@ subtitleCreated = "Copy this secret now — it won't be shown again." title = "Create API key" titleCreated = "Key created" -[portal.infrastructure.deployLabel] -live = "Live" -queued = "Queued" -rolledBack = "Rolled back" -rolling = "Rolling out" - -[portal.infrastructure.deployments] -loadAria = "Load for {{name}}" -msValue = "{{value}} ms" -throughputValue = "{{value}}/min" - -[portal.infrastructure.deployments.deployColumns] -deployedBy = "Deployed by" -environment = "Environment" -product = "Product" -status = "Status" -version = "Version" -when = "When" - -[portal.infrastructure.deployments.recent] -heading = "Recent deployments" -subheading = "The latest rollouts across products and environments." - -[portal.infrastructure.deployments.regionColumns] -instances = "Instances" -latency = "Latency" -load = "Load" -p99 = "P99" -region = "Region" -status = "Status" -throughput = "Throughput" -uptime = "Uptime" -version = "Version" - -[portal.infrastructure.deployments.regions] -heading = "Regions" -subheading = "Live health for every deployed Stirling region — latency, load, and rollout version." - -[portal.infrastructure.deployments.regions.empty] -description = "Deployed regions appear here once your workspace is provisioned." -title = "No regions deployed" - [portal.infrastructure.keyLabel] active = "Active" revoked = "Revoked" -[portal.infrastructure.modelLabel] -active = "Active" -degraded = "Degraded" -disabled = "Disabled" - -[portal.infrastructure.models] -heading = "Models" -loadAria = "Load for {{name}}" -msValue = "{{value}} ms" -subheading = "The model catalogue and routing that powers document processing across your workspace." - -[portal.infrastructure.models.byom] -description = "Register an on-prem or self-hosted model and pin it to a region for data-residency-bound processing." -title = "Bring your own model" - -[portal.infrastructure.models.catalogue] -heading = "Catalogue" -sub = "Managed models available to your workspace, with live latency and cost." -subEnterprise = "Managed, bring-your-own, and on-prem models — with per-region pinning available." - -[portal.infrastructure.models.catalogue.empty] -description = "Models in your workspace's catalogue appear here." -title = "No models available" - -[portal.infrastructure.models.columns] -cost = "Cost" -latency = "Latency" -load = "Load" -model = "Model" -status = "Status" -type = "Type" -version = "Version" - -[portal.infrastructure.models.cost] -perCall = "{{price}}/call" -perThousand = "{{price}}/1k" - -[portal.infrastructure.models.metrics] -activeModels = "Active models" -avgLatency = "Avg latency" -included = "Included" -monthlySpend = "Monthly model spend" - -[portal.infrastructure.models.routing] -empty = "No routing rules configured." -heading = "Routing rules" -sub = "Which model handles each operation. The default applies when no narrower rule matches." -subLocked = "Route operations to specific models — available on paid plans." - -[portal.infrastructure.models.routing.lockedBanner] -description = "Upgrade to Pro to control which model handles each operation and document type." -title = "Model routing is a paid feature" - -[portal.infrastructure.models.routingColumns] -default = "Default" -docType = "Document type" -modelForAria = "Model for {{operation}}" -operation = "Operation" -routedTo = "Routed to" - -[portal.infrastructure.modelTypeLabel] -classification = "Classification" -extraction = "Extraction" -llm = "LLM" -ocr = "OCR" - -[portal.infrastructure.regionLabel] -degraded = "Degraded" -down = "Down" -healthy = "Healthy" - -[portal.infrastructure.security.access.byok] -description = "Supply a key from your own KMS. Stirling encrypts with it but can still read." -label = "Bring your own key (BYOK)" - -[portal.infrastructure.security.access.hyok] -description = "Keys never leave your KMS. Stirling holds only ciphertext." -label = "Hold your own key (HYOK)" - -[portal.infrastructure.security.access.stirling] -description = "Stirling manages encryption keys. Simplest — zero key ops on your side." -label = "Stirling-held keys" - -[portal.infrastructure.security.accessPolicy] -heading = "Document access policy" -subheading = "Controls who can decrypt processed documents at rest." - -[portal.infrastructure.security.attestations] -heading = "Compliance attestations" -noReport = "No report available" -subheading = "Framework-by-framework audit posture, with reports available on attested controls." -viewReport = "View report →" - -[portal.infrastructure.security.compliance] -heading = "Compliance" -subheading = "Attestations and certifications covering the Stirling platform." - -[portal.infrastructure.security.empty] -description = "Your workspace's security configuration will appear here." -title = "Security posture unavailable" - -[portal.infrastructure.security.hyokBanner] -description = "With HYOK, encryption keys never leave your KMS. Stirling stores and processes only ciphertext you can revoke at any time." -title = "Stirling cannot decrypt your documents" - -[portal.infrastructure.security.ipAllowlist] -empty = "No IP ranges configured — all IPs allowed." -heading = "IP allowlist" -sub = "API access is restricted to these CIDR ranges." -subLocked = "Restrict API access to known IP ranges — available on paid plans." - -[portal.infrastructure.security.ipAllowlist.lockedBanner] -description = "Upgrade to Pro to restrict API access to specific networks." -title = "IP allowlisting is a paid feature" - -[portal.infrastructure.security.ipColumns] -added = "Added" -addedBy = "Added by" -cidr = "CIDR" -label = "Label" - -[portal.infrastructure.security.keyManagement] -algorithm = "Algorithm" -heading = "Encryption key management" -keyId = "Key identifier" -lastRotated = "Last rotated" -rotateKey = "Rotate key" -rotationPolicy = "Rotation policy" -subheading = "Custody of the keys that encrypt documents at rest — who can decrypt, and how keys rotate." - -[portal.infrastructure.security.managedBanner] -description = "Bring-your-own-key (BYOK) and hold-your-own-key (HYOK) custody are available on Enterprise. Upgrade to supply keys from your own KMS." -title = "Keys are managed by Stirling on your plan" - -[portal.infrastructure.security.residency.apac] -description = "ap-southeast-1" -label = "Asia Pacific" - -[portal.infrastructure.security.residency.eu] -description = "eu-west-1 · GDPR data boundary" -label = "European Union" - -[portal.infrastructure.security.residency.us] -description = "us-east-1 · us-west-2" -label = "United States" - -[portal.infrastructure.security.residencyHeader] -heading = "Data residency" -subheading = "Where documents are stored and processed." - -[portal.infrastructure.storage] -gbValue = "{{value}} GB" -percentUsed = "{{value}} used" - -[portal.infrastructure.storage.empty] -description = "Connected storage and usage appear here." -title = "No storage configured" - -[portal.infrastructure.storage.lifecycle] -active = "Active" -activeRange = "0–{{value}}d" -archived = "Archived" -coldStorage = "cold storage" -deleted = "Deleted" -never = "never" -purged = "purged" - -[portal.infrastructure.storage.providers] -connect = "Connect" -connected = "Connected" -heading = "Connected providers" -subheading = "Where processed artifacts are written." - -[portal.infrastructure.storage.retention] -heading = "Retention" -subheading = "How long artifacts are kept before lifecycle deletion." -windowLabel = "Default retention window" - -[portal.infrastructure.storage.retentionOption] -days_one = "{{count}} day" -days_other = "{{count}} days" -never = "Never delete" - -[portal.infrastructure.storage.totalUsage] -heading = "Total usage" -progressLabel = "Storage used" -subheading = "Storage consumed across all connected providers." - [portal.infrastructure.tabs] apiKeys = "API Keys" audit = "Audit Logs" @@ -8875,10 +8635,6 @@ cancel = "Cancel" confirm = "Delete" title = "Delete source?" -[portal.sources.empty] -description = "Connect a storage location so your policies have somewhere to pull data from." -title = "No sources connected yet" - [portal.sources.kpi] inUse = "In use" total = "Connections" diff --git a/frontend/editor/src/core/components/shared/LandingActions.tsx b/frontend/editor/src/core/components/shared/LandingActions.tsx index d69f90a1ef..750848f3c7 100644 --- a/frontend/editor/src/core/components/shared/LandingActions.tsx +++ b/frontend/editor/src/core/components/shared/LandingActions.tsx @@ -32,8 +32,7 @@ export function LandingActions({ <>