From 3ddfa25acbd834b0f48d3d66524b4dad59d3534f Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Tue, 10 Mar 2026 10:34:31 -0700 Subject: [PATCH] fix(web): harden shell routes and add regression coverage --- web/src/app/(app)/dashboard/page.tsx | 44 +++++++++++- web/src/app/(app)/reviews/page.tsx | 57 +++++++++++---- web/src/app/(app)/search/page.test.tsx | 26 +++++++ web/src/app/(app)/search/page.tsx | 82 ++++++++++++++++------ web/src/app/(app)/workspaces/page.test.tsx | 26 +++++++ web/src/app/(app)/workspaces/page.tsx | 44 +++++++++--- web/src/components/Sidebar.tsx | 2 +- web/src/lib/mock-data.ts | 62 +++++++++++++++- 8 files changed, 292 insertions(+), 51 deletions(-) create mode 100644 web/src/app/(app)/search/page.test.tsx create mode 100644 web/src/app/(app)/workspaces/page.test.tsx diff --git a/web/src/app/(app)/dashboard/page.tsx b/web/src/app/(app)/dashboard/page.tsx index 0c82c3b..0381ee2 100644 --- a/web/src/app/(app)/dashboard/page.tsx +++ b/web/src/app/(app)/dashboard/page.tsx @@ -1,5 +1,5 @@ import { AppShell } from "@/components/AppShell"; -import { mockNotes, mockWorkspaces } from "@/lib/mock-data"; +import { mockNotes, mockOperatorWorkflows, mockSavedViews, mockWorkspaces } from "@/lib/mock-data"; export default function DashboardPage() { const recentNotes = mockNotes.slice(0, 3); @@ -8,7 +8,7 @@ export default function DashboardPage() { Scaffold milestone} + actions={
Operational shell
} >
@@ -25,6 +25,46 @@ export default function DashboardPage() {
+
+
+
Saved views
+
+ {mockSavedViews.map((view) => ( +
+
+ {view.name} + {view.scope} +
+
{view.description}
+
+ {view.query} + {view.resultCount} results +
+
+ ))} +
+
+ +
+
Operator workflows
+
+ {mockOperatorWorkflows.map((workflow) => ( +
+
+ {workflow.name} + {workflow.status} +
+
Owner: {workflow.owner}
+
+ Queue: {workflow.queueCount} + SLA: {workflow.sla} +
+
+ ))} +
+
+
+
Recent note activity
diff --git a/web/src/app/(app)/reviews/page.tsx b/web/src/app/(app)/reviews/page.tsx index 7228fbe..f999ad7 100644 --- a/web/src/app/(app)/reviews/page.tsx +++ b/web/src/app/(app)/reviews/page.tsx @@ -1,6 +1,7 @@ import { AppShell } from "@/components/AppShell"; import { AgentTimeline } from "@/components/AgentTimeline"; import { ProposalReviewCard } from "@/components/ProposalReviewCard"; +import { mockOperatorWorkflows } from "@/lib/mock-data"; import { mockAgentTimeline, mockApprovalQueue } from "@/lib/review-data"; export default function ReviewsPage() { @@ -8,24 +9,50 @@ export default function ReviewsPage() { W3 in progress
} + actions={
Operator workflow shell
} > -
-
Approval queue
-
- {mockApprovalQueue.map((item) => ( -
-
- {item.title} - {item.owner} -
-
- {item.severity} - {item.status} +
+ + +
+
+
Approval queue
+
+ severity:medium+ + status:pending + owner:any
- ))} -
+
+
+ {mockApprovalQueue.map((item) => ( +
+
+ {item.title} + {item.owner} +
+
+ {item.severity} + {item.status} +
+
+ ))} +
+
({ + default: ({ href, children, ...props }: React.ComponentProps<"a"> & { href: string }) => ( + + {children} + + ), +})); + +describe("SearchPage", () => { + it("renders an accessible search field, saved searches, and note links", () => { + render(); + + expect(screen.getByRole("heading", { level: 1, name: "Search" })).toBeInTheDocument(); + expect(screen.getByRole("textbox", { name: "Search notes" })).toBeInTheDocument(); + expect(screen.getByText("Saved searches")).toBeInTheDocument(); + expect(screen.getByText("Launch readiness")).toBeInTheDocument(); + expect(screen.getByRole("link", { name: /MVP cut line for agentic notes launch/i })).toHaveAttribute( + "href", + "/notes/note-prd-cutline" + ); + }); +}); diff --git a/web/src/app/(app)/search/page.tsx b/web/src/app/(app)/search/page.tsx index 0f3da96..dc85c87 100644 --- a/web/src/app/(app)/search/page.tsx +++ b/web/src/app/(app)/search/page.tsx @@ -1,33 +1,71 @@ +import Link from "next/link"; import { AppShell } from "@/components/AppShell"; -import { mockNotes } from "@/lib/mock-data"; +import { mockNotes, mockSavedViews } from "@/lib/mock-data"; export default function SearchPage() { return ( Advanced retrieval next} + actions={
Dense retrieval shell
} > -
- -
- workspace:all - status:active - source:manual+agent -
-
- {mockNotes.map((note) => ( -
- {note.title} - {note.excerpt} -
- {note.tags.map((tag) => ( - #{tag} - ))} -
-
- ))} -
+
+ + +
+ +
+ workspace:all + status:active + source:manual+agent + matched:title+tags +
+
+ {mockNotes.map((note) => ( + +
+
+ {note.title} + {note.excerpt} +
+ {note.status} + {note.updatedBy} + {note.workspaceId.replace("workspace-", "")} +
+
+ {note.tags.map((tag) => ( + #{tag} + ))} +
+ + ))} +
+
); diff --git a/web/src/app/(app)/workspaces/page.test.tsx b/web/src/app/(app)/workspaces/page.test.tsx new file mode 100644 index 0000000..1d677f3 --- /dev/null +++ b/web/src/app/(app)/workspaces/page.test.tsx @@ -0,0 +1,26 @@ +import { render, screen } from "@testing-library/react"; +import { describe, expect, it, vi } from "vitest"; +import WorkspacesPage from "./page"; + +vi.mock("next/link", () => ({ + default: ({ href, children, ...props }: React.ComponentProps<"a"> & { href: string }) => ( + + {children} + + ), +})); + +describe("WorkspacesPage", () => { + it("renders an accessible workspace filter and saved workspace views", () => { + render(); + + expect(screen.getByRole("heading", { level: 1, name: "Workspaces" })).toBeInTheDocument(); + expect(screen.getByRole("textbox", { name: "Filter workspaces" })).toBeInTheDocument(); + expect(screen.getByText("Saved views")).toBeInTheDocument(); + expect(screen.getByText("All workspaces")).toBeInTheDocument(); + expect(screen.getByRole("link", { name: /MVP cut line for agentic notes launch/i })).toHaveAttribute( + "href", + "/notes/note-prd-cutline" + ); + }); +}); diff --git a/web/src/app/(app)/workspaces/page.tsx b/web/src/app/(app)/workspaces/page.tsx index eea7b04..19228b9 100644 --- a/web/src/app/(app)/workspaces/page.tsx +++ b/web/src/app/(app)/workspaces/page.tsx @@ -1,6 +1,6 @@ import Link from "next/link"; import { AppShell } from "@/components/AppShell"; -import { getNotesForWorkspace, mockWorkspaces } from "@/lib/mock-data"; +import { getNotesForWorkspace, mockSavedViews, mockWorkspaces } from "@/lib/mock-data"; export default function WorkspacesPage() { return ( @@ -9,15 +9,36 @@ export default function WorkspacesPage() { description="Workspace-level organization, filters, and saved-view entry points for note collections." actions={
Saved views scaffolded
} > -
-
- -
- Saved view: All workspaces - Saved view: Product strategy - Saved view: Agent review +
+
+ + +
+
+ +
+ owner:any + visibility:any + sort:updated +
+
+
@@ -30,7 +51,10 @@ export default function WorkspacesPage() {
{workspace.name}
{workspace.description}
-
{workspace.visibility}
+
+
{workspace.visibility}
+ Owner: {workspace.owner} +
{workspace.tags.map((tag) => ( diff --git a/web/src/components/Sidebar.tsx b/web/src/components/Sidebar.tsx index ea93037..113fba3 100644 --- a/web/src/components/Sidebar.tsx +++ b/web/src/components/Sidebar.tsx @@ -15,7 +15,7 @@ const navItems = [ ]; export function Sidebar() { - const pathname = usePathname(); + const pathname = usePathname() ?? ""; return (