From ba20f365e37e1049b01a13fe0ab1baee5594710c Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Mon, 10 Aug 2026 20:28:48 -0400 Subject: [PATCH] feat(web): hide unused Stampli modules for Sea Haven ops (AP-40) (#13) --- e2e/sea-haven-brand.spec.ts | 32 ++++++++++++++++++++++++++++++++ playwright.config.ts | 35 +++++++++++++++++++++++++++-------- src/layout/AppShell.tsx | 2 -- src/layout/nav.ts | 1 - src/lib/brand.test.ts | 32 ++++++++++++++++++++++++++++++++ src/lib/brand.ts | 15 +++++++++------ src/pages/billing/index.tsx | 12 ------------ 7 files changed, 100 insertions(+), 29 deletions(-) create mode 100644 e2e/sea-haven-brand.spec.ts create mode 100644 src/lib/brand.test.ts delete mode 100644 src/pages/billing/index.tsx diff --git a/e2e/sea-haven-brand.spec.ts b/e2e/sea-haven-brand.spec.ts new file mode 100644 index 0000000..cd84bf3 --- /dev/null +++ b/e2e/sea-haven-brand.spec.ts @@ -0,0 +1,32 @@ +import { expect, test } from "@playwright/test"; + +test.describe("Sea Haven brand module hide-list (AP-40)", () => { + test("hides unused modules, keeps reports, stubs remain, billing route gone", async ({ + page, + }) => { + await page.goto("/"); + await expect(page.getByTestId("app-wordmark")).toContainText("SEA HAVEN"); + await expect(page.getByTestId("module-rail")).toBeVisible(); + + await expect(page.getByTestId("nav-procurement")).toHaveCount(0); + await expect(page.getByTestId("nav-past-invoices")).toHaveCount(0); + await expect(page.getByTestId("nav-recurring")).toHaveCount(0); + await expect(page.getByTestId("nav-billing")).toHaveCount(0); + + await expect(page.getByTestId("nav-reports")).toBeVisible(); + await expect(page.getByTestId("nav-invoice-processing")).toBeVisible(); + + await page.goto("/procurement"); + await expect(page.getByTestId("procurement-page")).toBeVisible(); + + await page.goto("/past-invoices"); + await expect(page.getByTestId("past-invoices-page")).toBeVisible(); + + await page.goto("/recurring"); + await expect(page.getByTestId("recurring-page")).toBeVisible(); + + await page.goto("/billing"); + await expect(page.getByTestId("billing-page")).toHaveCount(0); + await expect(page.getByTestId("module-rail")).toBeVisible(); + }); +}); diff --git a/playwright.config.ts b/playwright.config.ts index 7990e87..93d2bfa 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -8,19 +8,38 @@ export default defineConfig({ workers: process.env.CI ? 1 : undefined, reporter: process.env.CI ? "github" : "list", use: { - baseURL: "http://localhost:4173", trace: "on-first-retry", }, projects: [ { name: "chromium", - use: { ...devices["Desktop Chrome"] }, + testIgnore: /sea-haven-brand/, + use: { + ...devices["Desktop Chrome"], + baseURL: "http://localhost:4173", + }, + }, + { + name: "sea-haven-brand", + testMatch: /sea-haven-brand/, + use: { + ...devices["Desktop Chrome"], + baseURL: "http://localhost:4174", + }, + }, + ], + webServer: [ + { + command: "npm run dev -- --port 4173 --strictPort", + url: "http://localhost:4173", + reuseExistingServer: !process.env.CI, + timeout: 120_000, + }, + { + command: "VITE_SEA_HAVEN_BRAND=true npm run dev -- --port 4174 --strictPort", + url: "http://localhost:4174", + reuseExistingServer: !process.env.CI, + timeout: 120_000, }, ], - webServer: { - command: "npm run dev -- --port 4173 --strictPort", - url: "http://localhost:4173", - reuseExistingServer: !process.env.CI, - timeout: 120_000, - }, }); diff --git a/src/layout/AppShell.tsx b/src/layout/AppShell.tsx index df48886..bcc7448 100644 --- a/src/layout/AppShell.tsx +++ b/src/layout/AppShell.tsx @@ -5,7 +5,6 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight"; import FolderOutlinedIcon from "@mui/icons-material/FolderOutlined"; import Inventory2OutlinedIcon from "@mui/icons-material/Inventory2Outlined"; import NotificationsNoneOutlinedIcon from "@mui/icons-material/NotificationsNoneOutlined"; -import PaidOutlinedIcon from "@mui/icons-material/PaidOutlined"; import ReceiptLongOutlinedIcon from "@mui/icons-material/ReceiptLongOutlined"; import SearchIcon from "@mui/icons-material/Search"; import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined"; @@ -46,7 +45,6 @@ const iconByTestId: Record = { "nav-past-invoices": FolderOutlinedIcon, "nav-reports": AssessmentOutlinedIcon, "nav-settings": SettingsOutlinedIcon, - "nav-billing": PaidOutlinedIcon, }; type AppShellProps = { diff --git a/src/layout/nav.ts b/src/layout/nav.ts index 1e64be8..cd38a6d 100644 --- a/src/layout/nav.ts +++ b/src/layout/nav.ts @@ -29,5 +29,4 @@ export const navModules: NavModule[] = [ { label: "My Past Invoices", to: "/past-invoices", testId: "nav-past-invoices" }, { label: "Dashboards & Reports", to: "/reports", testId: "nav-reports" }, { label: "System Settings", to: "/settings", testId: "nav-settings" }, - { label: "Billing", to: "/billing", testId: "nav-billing" }, ]; diff --git a/src/lib/brand.test.ts b/src/lib/brand.test.ts new file mode 100644 index 0000000..0f41b07 --- /dev/null +++ b/src/lib/brand.test.ts @@ -0,0 +1,32 @@ +import { describe, expect, it } from "vitest"; +import { SEA_HAVEN_HIDDEN_MODULE_TEST_IDS } from "@/lib/brand"; +import { navModules } from "@/layout/nav"; + +describe("AP-40 Sea Haven module hide-list", () => { + it("locks the ops-confirmed hide-list (Billing removed from nav entirely)", () => { + expect([...SEA_HAVEN_HIDDEN_MODULE_TEST_IDS]).toEqual([ + "nav-procurement", + "nav-past-invoices", + "nav-recurring", + ]); + }); + + it("keeps Dashboards & Reports in nav and omits Billing", () => { + const testIds = navModules.map((item) => item.testId); + expect(testIds).toContain("nav-reports"); + expect(testIds).not.toContain("nav-billing"); + }); + + it("hides only the locked modules from the rail when applied", () => { + const visible = navModules.filter( + (item) => !SEA_HAVEN_HIDDEN_MODULE_TEST_IDS.includes(item.testId as never), + ); + const visibleIds = visible.map((item) => item.testId); + + expect(visibleIds).not.toContain("nav-procurement"); + expect(visibleIds).not.toContain("nav-past-invoices"); + expect(visibleIds).not.toContain("nav-recurring"); + expect(visibleIds).toContain("nav-reports"); + expect(visibleIds).toContain("nav-invoice-processing"); + }); +}); diff --git a/src/lib/brand.ts b/src/lib/brand.ts index da1664a..75ac181 100644 --- a/src/lib/brand.ts +++ b/src/lib/brand.ts @@ -3,6 +3,14 @@ * Default: Stampli-parity wordmark for AP-22 mocks. * Set `VITE_SEA_HAVEN_BRAND=true` for Sea Haven cutover (AP-38). */ + +/** AP-40 ops-confirmed modules hidden in Sea Haven brand mode. Billing removed from nav/routes. */ +export const SEA_HAVEN_HIDDEN_MODULE_TEST_IDS = [ + "nav-procurement", + "nav-past-invoices", + "nav-recurring", +] as const; + export const useSeaHavenBrand = import.meta.env.VITE_SEA_HAVEN_BRAND === "true"; export const brand = useSeaHavenBrand @@ -12,12 +20,7 @@ export const brand = useSeaHavenBrand orgLabel: "Sea Haven", headerGradient: "linear-gradient(90deg, #0b3d5c 0%, #1f6f8b 100%)", showBillyBanner: false, - hideModuleTestIds: [ - "nav-procurement", - "nav-billing", - "nav-recurring", - "nav-reports", - ] as const, + hideModuleTestIds: SEA_HAVEN_HIDDEN_MODULE_TEST_IDS, } : { productName: "STAMPLI", diff --git a/src/pages/billing/index.tsx b/src/pages/billing/index.tsx deleted file mode 100644 index d4d16c8..0000000 --- a/src/pages/billing/index.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import { ModuleStubPage } from "@/components/ModuleStubPage"; - -export default function BillingPage() { - return ( - - ); -}