diff --git a/App/pelagia-portal/app/(portal)/dashboard/page.tsx b/App/pelagia-portal/app/(portal)/dashboard/page.tsx index 878734a..dc5ebc3 100644 --- a/App/pelagia-portal/app/(portal)/dashboard/page.tsx +++ b/App/pelagia-portal/app/(portal)/dashboard/page.tsx @@ -2,7 +2,8 @@ import { auth } from "@/auth"; import { db } from "@/lib/db"; import { StatCard } from "@/components/dashboard/stat-card"; import { SpendCharts } from "@/components/dashboard/spend-charts"; -import { formatCurrency, formatDate, PO_STATUS_LABELS } from "@/lib/utils"; +import { PoStatusBadge } from "@/components/po/po-status-badge"; +import { formatCurrency, formatDate } from "@/lib/utils"; import { FileText, Clock, CheckCircle, DollarSign } from "lucide-react"; import Link from "next/link"; import type { Metadata } from "next"; @@ -89,9 +90,7 @@ async function SubmitterDashboard({ userId }: { userId: string }) { {po.title} - - {PO_STATUS_LABELS[po.status]} - + {formatCurrency(Number(po.totalAmount))} {formatDate(po.updatedAt)} @@ -207,9 +206,7 @@ async function ManagerDashboard() { {po.title} {po.vessel.name} - - {PO_STATUS_LABELS[po.status]} - + {formatCurrency(Number(po.totalAmount))} {po.approvedAt ? formatDate(po.approvedAt) : "—"} diff --git a/App/pelagia-portal/tests/e2e/dashboard-status-badges.spec.ts b/App/pelagia-portal/tests/e2e/dashboard-status-badges.spec.ts new file mode 100644 index 0000000..6742578 --- /dev/null +++ b/App/pelagia-portal/tests/e2e/dashboard-status-badges.spec.ts @@ -0,0 +1,99 @@ +import { test, expect } from "@playwright/test"; + +test.describe("Dashboard – PO status badges", () => { + test("Technical user dashboard shows color-coded status badges", async ({ page }) => { + // Log in as a technical user + await page.goto("/login"); + await page.getByLabel("Email address").fill("tech@pelagia.local"); + await page.getByLabel("Password").fill("tech1234"); + await page.getByRole("button", { name: /sign in/i }).click(); + await expect(page).toHaveURL(/\/dashboard/); + + // The Recent Orders table must be present + const table = page.locator("table"); + const rowCount = await table.locator("tbody tr").count(); + + if (rowCount === 0) { + // No orders seeded for this user – skip badge assertion but confirm page renders + await expect(page.getByText("Dashboard")).toBeVisible(); + return; + } + + // Every status cell must contain a badge rendered by PoStatusBadge. + // PoStatusBadge renders a with rounded-full and one of the + // CVA variant classes. The old hardcoded span used bg-neutral-100; + // the new component will never emit that class for non-secondary/default states. + // We verify that at least one badge is NOT neutral (i.e. coloured). + const statusCells = table.locator("tbody tr td:nth-child(3) span"); + const count = await statusCells.count(); + expect(count).toBeGreaterThan(0); + + // Take a screenshot for visual confirmation + await page.screenshot({ path: "test-results/dashboard-tech-badges.png", fullPage: false }); + + // Verify each badge has the rounded-full pill shape (all PoStatusBadge variants share this) + for (let i = 0; i < count; i++) { + await expect(statusCells.nth(i)).toHaveClass(/rounded-full/); + } + + // At least one badge should carry a colour class that is NOT neutral-100 + // (proving the new component is active, not the old hardcoded span) + const allClasses: string[] = []; + for (let i = 0; i < count; i++) { + const cls = await statusCells.nth(i).getAttribute("class") ?? ""; + allClasses.push(cls); + } + const hasColoredBadge = allClasses.some( + (cls) => + cls.includes("success") || + cls.includes("warning") || + cls.includes("danger") || + cls.includes("primary") || + cls.includes("border") // outline variant + ); + // If all POs happen to be in a secondary/closed state this may be false; + // in that case we at least confirm the old bg-neutral-100 inline span is gone. + const hasOldHardcodedBadge = allClasses.some( + (cls) => cls === "rounded-full bg-neutral-100 px-2.5 py-0.5 text-xs font-medium text-neutral-700" + ); + expect(hasOldHardcodedBadge).toBe(false); + }); + + test("Manager dashboard shows color-coded status badges on approved orders", async ({ page }) => { + await page.goto("/login"); + await page.getByLabel("Email address").fill("manager@pelagia.local"); + await page.getByLabel("Password").fill("manager1234"); + await page.getByRole("button", { name: /sign in/i }).click(); + await expect(page).toHaveURL(/\/dashboard/); + + await page.screenshot({ path: "test-results/dashboard-manager-badges.png", fullPage: false }); + + const table = page.locator("table"); + const rowCount = await table.locator("tbody tr").count(); + + if (rowCount === 0) { + await expect(page.getByText("Dashboard")).toBeVisible(); + return; + } + + // Status column is 4th column in manager table (PO | Title | Cost Centre | Status | Amount | Approved) + const statusCells = table.locator("tbody tr td:nth-child(4) span"); + const count = await statusCells.count(); + expect(count).toBeGreaterThan(0); + + for (let i = 0; i < count; i++) { + await expect(statusCells.nth(i)).toHaveClass(/rounded-full/); + } + + // The old hardcoded manager badge was always success — verify it's gone + const allClasses: string[] = []; + for (let i = 0; i < count; i++) { + const cls = await statusCells.nth(i).getAttribute("class") ?? ""; + allClasses.push(cls); + } + const hasOldHardcoded = allClasses.some( + (cls) => cls === "rounded-full bg-success-100 px-2.5 py-0.5 text-xs font-medium text-success-700" + ); + expect(hasOldHardcoded).toBe(false); + }); +});