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