feat(web): hide unused Stampli modules for Sea Haven ops (AP-40) (#13)

This commit is contained in:
Adam Moussa 2026-08-10 20:28:48 -04:00 • committed by GitHub
parent 9d3646876e
commit ba20f365e3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 100 additions and 29 deletions

View file

@ -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();
});
});

View file

@ -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,
},
});

View file

@ -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<string, typeof ReceiptLongOutlinedIcon> = {
"nav-past-invoices": FolderOutlinedIcon,
"nav-reports": AssessmentOutlinedIcon,
"nav-settings": SettingsOutlinedIcon,
"nav-billing": PaidOutlinedIcon,
};
type AppShellProps = {

View file

@ -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" },
];

32
src/lib/brand.test.ts Normal file
View file

@ -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");
});
});

View file

@ -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",

View file

@ -1,12 +0,0 @@
import { ModuleStubPage } from "@/components/ModuleStubPage";
export default function BillingPage() {
return (
<ModuleStubPage
testId="billing-page"
title="Billing"
headline="Subscription and billing settings."
body="Parity stub for Billing."
/>
);
}