diff --git a/apps/e2e/tests/book.spec.ts b/apps/e2e/tests/book.spec.ts index 4a76f43..ecec404 100644 --- a/apps/e2e/tests/book.spec.ts +++ b/apps/e2e/tests/book.spec.ts @@ -46,7 +46,7 @@ test("complete booking flow", async ({ page }) => { // ── Step 1: Select a service ────────────────────────────────────────────── - await page.goto("/book"); + await page.goto("/admin/book"); await expect(page.getByText("Book an Appointment")).toBeVisible(); await expect(page.getByText("Choose a service")).toBeVisible(); @@ -99,7 +99,7 @@ test("booking form validation — required fields", async ({ page }) => { route.fulfill({ json: [MOCK_SLOT] }) ); - await page.goto("/book"); + await page.goto("/admin/book"); await page.getByText("Full Groom").click(); await page.getByRole("button", { name: /\d{1,2}:\d{2}/ }).first().click(); await page.getByRole("button", { name: "Continue" }).click(); @@ -115,6 +115,6 @@ test("no services available — shows message", async ({ page }) => { route.fulfill({ json: [] }) ); - await page.goto("/book"); + await page.goto("/admin/book"); await expect(page.getByText("No services available")).toBeVisible(); }); diff --git a/apps/e2e/tests/clients.spec.ts b/apps/e2e/tests/clients.spec.ts index 8e94e1a..4e681da 100644 --- a/apps/e2e/tests/clients.spec.ts +++ b/apps/e2e/tests/clients.spec.ts @@ -40,18 +40,18 @@ test.beforeEach(async ({ page }) => { }); test("clients page shows client list", async ({ page }) => { - await page.goto("/clients"); + await page.goto("/admin/clients"); await expect(page.getByText("Alice Johnson")).toBeVisible(); await expect(page.getByText("Bob Williams")).toBeVisible(); }); test("clients page shows search input", async ({ page }) => { - await page.goto("/clients"); + await page.goto("/admin/clients"); await expect(page.getByPlaceholder(/search/i)).toBeVisible(); }); test("clicking a client shows their details", async ({ page }) => { - await page.goto("/clients"); + await page.goto("/admin/clients"); await expect(page.getByText("Alice Johnson")).toBeVisible(); await page.getByText("Alice Johnson").click(); // Email appears in both the list row and the detail panel once selected diff --git a/apps/e2e/tests/navigation.spec.ts b/apps/e2e/tests/navigation.spec.ts index 1e7388a..0b8e78f 100644 --- a/apps/e2e/tests/navigation.spec.ts +++ b/apps/e2e/tests/navigation.spec.ts @@ -40,45 +40,51 @@ test.beforeEach(async ({ page }) => { }); }); -test("appointments page loads", async ({ page }) => { +test("customer portal loads at root", async ({ page }) => { await page.goto("/"); + await expect(page.getByRole("navigation").getByText("Paws & Reflect")).toBeVisible(); + await expect(page.locator("nav")).toBeVisible(); +}); + +test("admin appointments page loads", async ({ page }) => { + await page.goto("/admin"); await expect(page.getByText("Groom Book")).toBeVisible(); // Calendar/appointments view renders await expect(page.locator("nav")).toBeVisible(); }); -test("clients page loads", async ({ page }) => { - await page.goto("/clients"); +test("admin clients page loads", async ({ page }) => { + await page.goto("/admin/clients"); await expect(page.getByText("Groom Book")).toBeVisible(); await expect(page.getByRole("link", { name: "Clients" })).toBeVisible(); }); -test("services page loads", async ({ page }) => { - await page.goto("/services"); +test("admin services page loads", async ({ page }) => { + await page.goto("/admin/services"); await expect(page.getByText("Groom Book")).toBeVisible(); await expect(page.getByRole("link", { name: "Services" })).toBeVisible(); }); -test("staff page loads", async ({ page }) => { - await page.goto("/staff"); +test("admin staff page loads", async ({ page }) => { + await page.goto("/admin/staff"); await expect(page.getByText("Groom Book")).toBeVisible(); await expect(page.getByRole("link", { name: "Staff" })).toBeVisible(); }); -test("invoices page loads", async ({ page }) => { - await page.goto("/invoices"); +test("admin invoices page loads", async ({ page }) => { + await page.goto("/admin/invoices"); await expect(page.getByText("Groom Book")).toBeVisible(); await expect(page.getByRole("link", { name: "Invoices" })).toBeVisible(); }); -test("reports page loads", async ({ page }) => { - await page.goto("/reports"); +test("admin reports page loads", async ({ page }) => { + await page.goto("/admin/reports"); await expect(page.getByText("Groom Book")).toBeVisible(); await expect(page.getByRole("link", { name: "Reports" })).toBeVisible(); }); -test("booking portal loads", async ({ page }) => { - await page.goto("/book"); +test("admin booking portal loads", async ({ page }) => { + await page.goto("/admin/book"); await expect(page.getByText("Book an Appointment")).toBeVisible(); await expect(page.getByText("Choose a service")).toBeVisible(); }); diff --git a/apps/web/src/App.tsx b/apps/web/src/App.tsx index f61a751..b3ef7fc 100644 --- a/apps/web/src/App.tsx +++ b/apps/web/src/App.tsx @@ -10,14 +10,14 @@ import { GroupBookingPage } from "./pages/GroupBooking.js"; import { CustomerPortal } from "./portal/CustomerPortal.js"; const NAV_LINKS = [ - { to: "/", label: "Appointments" }, - { to: "/clients", label: "Clients" }, - { to: "/services", label: "Services" }, - { to: "/staff", label: "Staff" }, - { to: "/invoices", label: "Invoices" }, - { to: "/group-bookings", label: "Group Bookings" }, - { to: "/reports", label: "Reports" }, - { to: "/portal", label: "Customer Portal" }, + { to: "/admin", label: "Appointments" }, + { to: "/admin/clients", label: "Clients" }, + { to: "/admin/services", label: "Services" }, + { to: "/admin/staff", label: "Staff" }, + { to: "/admin/invoices", label: "Invoices" }, + { to: "/admin/group-bookings", label: "Group Bookings" }, + { to: "/admin/reports", label: "Reports" }, + { to: "/", label: "Customer Portal" }, ]; function AdminLayout() { @@ -36,7 +36,7 @@ function AdminLayout() { > Groom Book {NAV_LINKS.map(({ to, label }) => { const active = - to === "/" ? location.pathname === "/" : location.pathname.startsWith(to); + to === "/admin" + ? location.pathname === "/admin" + : location.pathname.startsWith(to); return ( ; + if (location.pathname.startsWith("/admin")) { + return ( + + } /> + + ); } - return ; + return ; } diff --git a/apps/web/src/__tests__/App.test.tsx b/apps/web/src/__tests__/App.test.tsx index 4854db3..cfd422d 100644 --- a/apps/web/src/__tests__/App.test.tsx +++ b/apps/web/src/__tests__/App.test.tsx @@ -1,5 +1,5 @@ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { render, screen, within, act } from "@testing-library/react"; +import { render, screen, within } from "@testing-library/react"; import { MemoryRouter } from "react-router-dom"; import { App } from "../App.js"; @@ -11,30 +11,28 @@ beforeEach(() => { } as unknown as Response); }); -async function renderApp(route = "/") { - await act(async () => { - render( - - - - ); - }); +function renderApp(route = "/admin") { + render( + + + + ); return screen.getByRole("navigation"); } describe("App navigation", () => { - it("renders the Groom Book brand", async () => { - const nav = await renderApp(); + it("renders the Groom Book brand", () => { + const nav = renderApp(); expect(within(nav).getByText("Groom Book")).toBeInTheDocument(); }); - it("renders the Book CTA button", async () => { - const nav = await renderApp(); + it("renders the Book CTA button", () => { + const nav = renderApp(); expect(within(nav).getByText("Book")).toBeInTheDocument(); }); - it("renders all primary nav links", async () => { - const nav = await renderApp(); + it("renders all primary nav links", () => { + const nav = renderApp(); const expectedLinks = [ "Appointments", "Clients", @@ -49,10 +47,20 @@ describe("App navigation", () => { }); }); - it("highlights the active route link", async () => { - const nav = await renderApp("/clients"); + it("highlights the active route link", () => { + const nav = renderApp("/admin/clients"); const clientsLink = within(nav).getByText("Clients"); // Active links use fontWeight 600 expect(clientsLink).toHaveStyle({ fontWeight: "600" }); }); + + it("renders customer portal at root", () => { + render( + + + + ); + // Customer portal should render at root - no admin nav present + expect(screen.queryByText("Groom Book")).not.toBeInTheDocument(); + }); });