diff --git a/frontend/editor/public/locales/en-GB/translation.toml b/frontend/editor/public/locales/en-GB/translation.toml index 10b90e2f8a..431fa7af66 100644 --- a/frontend/editor/public/locales/en-GB/translation.toml +++ b/frontend/editor/public/locales/en-GB/translation.toml @@ -7202,6 +7202,10 @@ sources = "Sources" usage = "Usage & Billing" users = "Users" +[portal.nav.section] +platform = "PDF Platform" +processor = "PDF Processor" + [portal.notifications] markAllRead = "Mark all read" title = "Notifications" diff --git a/frontend/editor/public/locales/en-US/translation.toml b/frontend/editor/public/locales/en-US/translation.toml index 9681a69f2f..1589cad8a7 100644 --- a/frontend/editor/public/locales/en-US/translation.toml +++ b/frontend/editor/public/locales/en-US/translation.toml @@ -7144,6 +7144,10 @@ sources = "Sources" usage = "Usage & Billing" users = "Users" +[portal.nav.section] +platform = "PDF Platform" +processor = "PDF Processor" + [portal.pipelines] subtitle = "Every automated document pipeline on the backend: an ordered chain of operations over a set of sources, run on a trigger. Click a row for its steps and sources." title = "Pipelines" diff --git a/frontend/editor/src/portal-saas/components/sidebarGroups.test.ts b/frontend/editor/src/portal-saas/components/sidebarGroups.test.ts index 892d1b0514..aaec0024f1 100644 --- a/frontend/editor/src/portal-saas/components/sidebarGroups.test.ts +++ b/frontend/editor/src/portal-saas/components/sidebarGroups.test.ts @@ -1,19 +1,14 @@ import { describe, expect, it } from "vitest"; // Resolves to the SaaS override (src/portal-saas) via the @portal cascade. import { - GROUP_PRIMARY, - GROUP_OPERATIONAL, + GROUP_PROCESSOR, GROUP_PLATFORM, } from "@portal/components/sidebarGroups"; describe("sidebarGroups (SaaS)", () => { - it("drops Components from the operational nav", () => { - expect(GROUP_OPERATIONAL.map((e) => e.id)).not.toContain("components"); - }); - - it("inherits the other operational items from base", () => { - expect(GROUP_OPERATIONAL.map((e) => e.id)).toEqual([ - "users", + it("inherits the processor group unchanged from base", () => { + expect(GROUP_PROCESSOR.map((e) => e.id)).toEqual([ + "home", "sources", "policies", "pipelines", @@ -21,9 +16,9 @@ describe("sidebarGroups (SaaS)", () => { ]); }); - it("inherits the primary + platform groups unchanged", () => { - expect(GROUP_PRIMARY.map((e) => e.id)).toEqual(["home"]); + it("inherits the platform group unchanged from base", () => { expect(GROUP_PLATFORM.map((e) => e.id)).toEqual([ + "users", "infrastructure", "usage", "docs", diff --git a/frontend/editor/src/portal-saas/components/sidebarGroups.tsx b/frontend/editor/src/portal-saas/components/sidebarGroups.tsx index 3301f567d5..9e51b1b307 100644 --- a/frontend/editor/src/portal-saas/components/sidebarGroups.tsx +++ b/frontend/editor/src/portal-saas/components/sidebarGroups.tsx @@ -1,11 +1,11 @@ import { - GROUP_PRIMARY, - GROUP_OPERATIONAL, + GROUP_PROCESSOR, GROUP_PLATFORM, type NavEntry, + type NavGroup, } from "@portal-proprietary/components/sidebarGroups"; // SaaS shadows the base nav groups so sections not yet shipped there can be // dropped. Nothing is currently removed, so the base groups pass through as-is. -export { GROUP_PRIMARY, GROUP_OPERATIONAL, GROUP_PLATFORM }; -export type { NavEntry }; +export { GROUP_PROCESSOR, GROUP_PLATFORM }; +export type { NavEntry, NavGroup }; diff --git a/frontend/editor/src/portal/components/Sidebar.css b/frontend/editor/src/portal/components/Sidebar.css index 98a9b63ec3..935a55e722 100644 --- a/frontend/editor/src/portal/components/Sidebar.css +++ b/frontend/editor/src/portal/components/Sidebar.css @@ -46,18 +46,32 @@ gap: 0.5rem; } +/* Each section is a labelled card: a small header above its nav items. */ +.portal-sidebar__section { + background: var(--color-surface); + border: 1px solid var(--color-sidebar-divider); + border-radius: 0.625rem; + padding: 0.5rem 0.375rem 0.375rem; + display: flex; + flex-direction: column; + gap: 0.375rem; +} + +.portal-sidebar__section-label { + margin: 0; + padding: 0 0.5rem; + font-size: 0.6875rem; + font-weight: 600; + letter-spacing: 0.02em; + color: var(--color-text-3); +} + .portal-sidebar__group { display: flex; flex-direction: column; gap: 0.125rem; } -.portal-sidebar__divider { - height: 1px; - background: var(--color-sidebar-divider); - margin: 0.25rem 0; -} - /* Footer */ .portal-sidebar__footer { border-top: 1px solid var(--color-sidebar-divider); diff --git a/frontend/editor/src/portal/components/Sidebar.tsx b/frontend/editor/src/portal/components/Sidebar.tsx index 6e21d8b8d2..5b512a7184 100644 --- a/frontend/editor/src/portal/components/Sidebar.tsx +++ b/frontend/editor/src/portal/components/Sidebar.tsx @@ -11,13 +11,18 @@ import wordmarkLight from "@app/assets/brand/modern-logo/StirlingProcessorLogoBl import wordmarkDark from "@app/assets/brand/modern-logo/StirlingProcessorLogoWhiteText.svg"; import { SettingsIcon } from "@portal/components/icons"; import { - GROUP_PRIMARY, - GROUP_OPERATIONAL, + GROUP_PROCESSOR, GROUP_PLATFORM, type NavEntry, + type NavGroup, } from "@portal/components/sidebarGroups"; import "@portal/components/Sidebar.css"; +const NAV_SECTIONS: NavGroup[] = [ + { labelKey: "portal.nav.section.processor", entries: GROUP_PROCESSOR }, + { labelKey: "portal.nav.section.platform", entries: GROUP_PLATFORM }, +]; + export function Sidebar() { const { activeView, setActiveView } = useView(); const { theme } = useTheme(); @@ -76,17 +81,16 @@ export function Sidebar() {