Files
Stirling-PDF/frontend/shared/components/SettingsShell.stories.tsx
T
Reece Browne 2b05865a84 Portal: unified-design surfaces (Policies, Users, Components, Agent Builder, Editor deploy) + Settings rebuild (#6696)
Builds the remaining developer-portal surfaces from the unified design
and rebuilds Settings, on top of the portal scaffold merged in #6686.
All tier-aware, mock-driven (MSW), componentised with Storybook
coverage. Touches only `frontend/portal` + `frontend/shared` — the
editor is untouched.

## New surfaces
- **Policies** — org-wide governance across the five categories
(Ingestion / Security / Compliance / Routing / Retention) with a
designer + per-doc-type overrides
- **Users** — members, roles, invite, tier-scaled SSO/SCIM access
- **Components** — embeddable `@stirling/*` SDK catalogue with
per-action pricing
- **Getting Started** — three-step funnel (use case → analyse a document
→ API key + snippets)
- **Agent Builder** — agent lifecycle (scenarios, tool modes,
evals/golden-sets, versions), reached from Sources
- **Editor deployment** — deploy/pair/operate the editor (targets,
pairing, health, credential rotation, air-gapped bundle), reached from
Infrastructure

## Reworks
- **Documents** → review/approval queue (confidence, extractions, audit
drawer, zero-standing-access elevation); the doc-type catalogue is
retained as a second tab
- **Pipelines** → golden-set pass column + "Promoted from the Editor"
section
- **Infrastructure** → new **Models** tab; deeper **Security** (managed
/ BYOK / HYOK + SOC 2 / ISO 27001 / HIPAA / GDPR / PCI attestations)
- **Home** → "What runs on your PDFs" policy summary + tier-aware
processing-status strip + pipeline-fork wizard

## Settings & shared
- New shared **`SettingsShell`** (grouped left-nav + content pane),
modelled on the editor's account-settings modal so both apps can
converge on one layout
- Portal **Settings** rebuilt on it as scoped sections — Account /
Workspace / Admin (Authentication, Active sessions, Early access)

## Brand
- Adopt the editor's brand mark + favicon; sidebar reads **Stirling
Processor**; app-switcher labels the active app "Processor"

## Mock contract
- Every surface follows the 3-layer pattern (typed `api/*` → MSW handler
→ fixtures); new endpoints documented in `MOCKS.md`. The read contract
is backend-ready; writes are marked `// TODO(backend): <METHOD> <path>`.

## Verification
- tsc (portal + shared) ✓ · eslint ✓ · dpdm (no circular) ✓ ·
`build:portal` ✓ · `storybook:build` ✓ · Prettier ✓

## Deferred (noted, not in scope)
- Unified shell / auth / role→surface routing / Workspace=Plan
(architectural epic)
- Tier rename (Editor / Processor / Bespoke) and the Usage flat-pricing
+ PAYG quick-amounts + Bespoke modal
- Editor adopting the shared `SettingsShell`; converting marked
write-stubs into live `api/` seams
2026-06-18 10:28:03 +00:00

77 lines
1.9 KiB
TypeScript

import { useState } from "react";
import type { Meta, StoryObj } from "@storybook/react-vite";
import {
SettingsShell,
type SettingsNavSection,
} from "@shared/components/SettingsShell";
import { Button } from "@shared/components/Button";
const SECTIONS: SettingsNavSection[] = [
{
title: "Account",
items: [
{ key: "profile", label: "Profile" },
{ key: "appearance", label: "Appearance" },
{ key: "notifications", label: "Notifications" },
],
},
{
title: "Workspace",
items: [{ key: "general", label: "General" }],
},
{
title: "Admin",
items: [
{ key: "auth", label: "Authentication" },
{ key: "sessions", label: "Active sessions" },
{ key: "beta", label: "Early access", badge: "New" },
],
},
];
const LABELS: Record<string, string> = {
profile: "Profile",
appearance: "Appearance",
notifications: "Notifications",
general: "General",
auth: "Authentication",
sessions: "Active sessions",
beta: "Early access",
};
const meta: Meta<typeof SettingsShell> = {
title: "Shared/SettingsShell",
component: SettingsShell,
parameters: { layout: "fullscreen" },
};
export default meta;
type Story = StoryObj<typeof SettingsShell>;
export const Default: Story = {
render: () => {
const [active, setActive] = useState("profile");
return (
<div style={{ height: "36rem", border: "1px solid var(--color-border)" }}>
<SettingsShell
sections={SECTIONS}
activeKey={active}
onSelect={setActive}
title={LABELS[active]}
onClose={() => {}}
footer={
<>
<Button variant="ghost">Cancel</Button>
<Button variant="gradient">Save changes</Button>
</>
}
>
<p style={{ color: "var(--color-text-3)" }}>
Content for the {LABELS[active]} section renders here.
</p>
</SettingsShell>
</div>
);
},
};