diff --git a/frontend/cypress/e2e/mobile_sidebar.cy.ts b/frontend/cypress/e2e/mobile_sidebar.cy.ts new file mode 100644 index 00000000..a4a74a6c --- /dev/null +++ b/frontend/cypress/e2e/mobile_sidebar.cy.ts @@ -0,0 +1,155 @@ +/// + +import { setupCommonPageTestHooks } from "../support/testHooks"; + +describe("/dashboard - mobile sidebar", () => { + const apiBase = "**/api/v1"; + + setupCommonPageTestHooks(apiBase); + + const emptySeries = { + primary: { range: "7d", bucket: "day", points: [] }, + comparison: { range: "7d", bucket: "day", points: [] }, + }; + + function stubDashboardApis() { + cy.intercept("GET", `${apiBase}/metrics/dashboard*`, { + statusCode: 200, + body: { + generated_at: new Date().toISOString(), + range: "7d", + kpis: { + inbox_tasks: 0, + in_progress_tasks: 0, + review_tasks: 0, + done_tasks: 0, + tasks_in_progress: 0, + active_agents: 0, + error_rate_pct: 0, + median_cycle_time_hours_7d: null, + }, + throughput: emptySeries, + cycle_time: emptySeries, + error_rate: emptySeries, + wip: emptySeries, + pending_approvals: { items: [], total: 0 }, + }, + }).as("dashboardMetrics"); + + cy.intercept("GET", `${apiBase}/boards*`, { + statusCode: 200, + body: { items: [], total: 0 }, + }).as("boardsList"); + + cy.intercept("GET", `${apiBase}/agents*`, { + statusCode: 200, + body: { items: [], total: 0 }, + }).as("agentsList"); + + cy.intercept("GET", `${apiBase}/activity*`, { + statusCode: 200, + body: { items: [], total: 0 }, + }).as("activityList"); + + cy.intercept("GET", `${apiBase}/gateways/status*`, { + statusCode: 200, + body: { gateways: [] }, + }).as("gatewaysStatus"); + + cy.intercept("GET", `${apiBase}/board-groups*`, { + statusCode: 200, + body: { items: [], total: 0 }, + }).as("boardGroupsList"); + } + + function visitDashboardAuthenticated() { + stubDashboardApis(); + cy.loginWithLocalAuth(); + cy.visit("/dashboard"); + cy.waitForAppLoaded(); + } + + it("auth negative: signed-out user does not see hamburger button", () => { + cy.visit("/dashboard"); + cy.contains("h1", /local authentication/i, { timeout: 30_000 }).should( + "be.visible", + ); + cy.get('[aria-label="Toggle navigation"]').should("not.exist"); + }); + + it("mobile: hamburger button visible and sidebar hidden by default", () => { + cy.viewport(375, 812); + visitDashboardAuthenticated(); + + cy.get('[aria-label="Toggle navigation"]').should("be.visible"); + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "closed"); + cy.get("aside").should("not.be.visible"); + }); + + it("desktop: hamburger button hidden and sidebar always visible", () => { + cy.viewport(1280, 800); + visitDashboardAuthenticated(); + + cy.get('[aria-label="Toggle navigation"]').should("not.be.visible"); + cy.get("aside").should("be.visible"); + }); + + it("mobile: click hamburger opens sidebar and shows backdrop", () => { + cy.viewport(375, 812); + visitDashboardAuthenticated(); + + cy.get('[aria-label="Toggle navigation"]').click(); + + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "open"); + cy.get("aside").should("be.visible"); + cy.get('[data-cy="sidebar-backdrop"]').should("exist"); + }); + + it("mobile: click backdrop closes sidebar", () => { + cy.viewport(375, 812); + visitDashboardAuthenticated(); + + // Open sidebar first + cy.get('[aria-label="Toggle navigation"]').click(); + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "open"); + + // Click the backdrop overlay + cy.get('[data-cy="sidebar-backdrop"]').click({ force: true }); + + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "closed"); + cy.get("aside").should("not.be.visible"); + }); + + it("mobile: clicking a nav link closes sidebar", () => { + cy.viewport(375, 812); + visitDashboardAuthenticated(); + + // Open sidebar + cy.get('[aria-label="Toggle navigation"]').click(); + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "open"); + cy.get("aside").should("be.visible"); + + // Click a navigation link inside the sidebar + cy.get("aside").within(() => { + cy.contains("a", "Boards").click(); + }); + + // Sidebar should close after navigation + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "closed"); + }); + + it("mobile: pressing Escape closes sidebar", () => { + cy.viewport(375, 812); + visitDashboardAuthenticated(); + + // Open sidebar + cy.get('[aria-label="Toggle navigation"]').click(); + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "open"); + + // Press Escape + cy.get("body").type("{esc}"); + + cy.get("[data-sidebar]").should("have.attr", "data-sidebar", "closed"); + cy.get("aside").should("not.be.visible"); + }); +}); diff --git a/frontend/src/app/activity/page.tsx b/frontend/src/app/activity/page.tsx index d9d884ac..c83c2a59 100644 --- a/frontend/src/app/activity/page.tsx +++ b/frontend/src/app/activity/page.tsx @@ -1508,7 +1508,7 @@ export default function ActivityPage() {
-
+
@@ -1526,7 +1526,7 @@ export default function ActivityPage() {
-
+
{hasUnresolvedDeepLink ? (
Requested activity item is not in the current feed window yet. diff --git a/frontend/src/app/agents/[agentId]/page.tsx b/frontend/src/app/agents/[agentId]/page.tsx index 8e739a46..cf25d18c 100644 --- a/frontend/src/app/agents/[agentId]/page.tsx +++ b/frontend/src/app/agents/[agentId]/page.tsx @@ -162,13 +162,13 @@ export default function AgentDetailPage() { {!isAdmin ? ( -
+
Only organization owners and admins can access agents.
) : ( -
+

diff --git a/frontend/src/app/approvals/page.tsx b/frontend/src/app/approvals/page.tsx index 060e8cad..fe034869 100644 --- a/frontend/src/app/approvals/page.tsx +++ b/frontend/src/app/approvals/page.tsx @@ -167,8 +167,8 @@ function GlobalApprovalsInner() { return (

-
-
+
+
-
+

@@ -816,7 +816,7 @@ export default function BoardGroupDetailPage() {

-
+
-
+
{heartbeatApplyError ? (
diff --git a/frontend/src/app/boards/[boardId]/approvals/page.tsx b/frontend/src/app/boards/[boardId]/approvals/page.tsx index 531ca138..0a2fb8c4 100644 --- a/frontend/src/app/boards/[boardId]/approvals/page.tsx +++ b/frontend/src/app/boards/[boardId]/approvals/page.tsx @@ -33,9 +33,9 @@ export default function BoardApprovalsPage() {
-
+
{boardId ? ( -
+
) : null} diff --git a/frontend/src/app/boards/[boardId]/page.tsx b/frontend/src/app/boards/[boardId]/page.tsx index ccb7358a..a30c4078 100644 --- a/frontend/src/app/boards/[boardId]/page.tsx +++ b/frontend/src/app/boards/[boardId]/page.tsx @@ -3101,7 +3101,7 @@ export default function BoardDetailPage() { )} >
-
+

@@ -3237,9 +3237,9 @@ export default function BoardDetailPage() {

-
+
{isOrgAdmin ? ( -