--- description: "Task list for 001-agent-admin-ui (Setup + User Stories 0-3, this plan's immediate scope)" --- # Tasks: Agent and Admin UI (Setup + User Stories 0-3) **Input**: Design documents from `specs/001-agent-admin-ui/` **Prerequisites**: [plan.md](./plan.md), [spec.md](./spec.md), [research.md](./research.md), [data-model.md](./data-model.md), [contracts/api-client-contract.md](./contracts/api-client-contract.md), [quickstart.md](./quickstart.md) **Scope note**: Per plan.md's own Technical Context, this task list covers Setup + User Stories 0-3 (the P1 MVP) only. User Stories 4-7 (P2/P3) get their own tasks.md continuation once this scope is verified — the identical pattern established here (lib/api function → TanStack Query hook → feature component → page) applies directly. **Organization**: Tasks are grouped by user story. US0 blocks every other story (no session, no data). US1-US3 are independent of each other once US0 exists. ## Format: `[ID] [P?] [Story] Description` All file paths are relative to `supporthub-web/` (repo root). --- ## Phase 1: Setup - [x] T001 [P] Add `js-cookie` + `@types/js-cookie` and `jose` to `package.json` - [x] T002 [P] Populate `src/lib/env/index.ts` — typed accessor over the existing `NEXT_PUBLIC_*` env vars (zod-validated, matching supporthub-api's own `env.ts` pattern), exporting `env.apiUrl` etc. - [x] T003 [P] Configure `vitest.config.ts` (currently empty) — `environment: 'jsdom'`, path aliases matching `tsconfig.json`, `tests/unit` + `tests/integration` include globs - [x] T004 [P] Configure `playwright.config.ts` (currently empty) — base URL from `NEXT_PUBLIC_APP_URL`, `tests/e2e` test dir - [x] T005 [P] Add `@testing-library/react`, `@testing-library/jest-dom`, `jsdom` as dev dependencies (needed by T003's `jsdom` environment; not yet present in `package.json`) **Checkpoint**: Both test runners actually run (even with zero tests) and `env` is typed. --- ## Phase 2: Foundational (Blocking Prerequisites) **Purpose**: The typed client layer, session plumbing, and query infrastructure every user story is built on. **⚠️ CRITICAL**: No user-story work can begin until this phase is complete. - [x] T006 Add `lib/auth/session-cookie.ts` — `getSessionToken()`/`setSessionToken(token)`/ `clearSessionToken()` using `js-cookie` (`sh_session`, `Secure` in production, `SameSite=Lax`) (depends on T001) - [x] T007 Add `lib/api/client.ts` — axios instance (`baseURL: env.apiUrl`), a request interceptor attaching `Authorization: Bearer ` from T006, a response interceptor throwing a typed `ApiError` (data-model.md) and clearing the session + redirecting to `/sign-in` on `401` (depends on T002, T006) - [x] T008 [P] Add `lib/api/types/` — `Session`, `ApiError`, `AssignedTicketSummary` (011's contract), and the ticket/message/problem-resolution/team/agent/hierarchy types data-model.md names, each referencing its source backend contract in a comment - [x] T009 Add `lib/api/auth.ts` — `login`, `getCurrentSession`, `logout` per contracts/api-client-contract.md (depends on T007, T008) - [x] T010 Add `lib/query/query-client.ts` (the shared `QueryClient` instance) and `lib/query/query-state.ts` (research.md's `'loading'|'empty'|'error'|'ready'` discriminated union helper, taking a `UseQueryResult` and an optional `isEmpty(data)` predicate) - [x] T011 Add `providers/query-provider.tsx` (`QueryClientProvider` wrapping T010's client) and `providers/session-provider.tsx` (calls `getCurrentSession` on mount via TanStack Query, exposes `useSession()`; the query's own `onError` for a `401` clears the session and redirects, per contracts/api-client-contract.md's "Session guard contract") (depends on T009, T010) - [x] T012 Wire both providers into `src/app/layout.tsx` (currently the root layout with no providers) - [x] T013 Write `src/middleware.ts` (this project's `src/` layout requires middleware there, not at the repo root — an empty root-level `middleware.ts` had been scaffolded in the wrong place) — no `sh_session` cookie on a `(support)`/`(admin)` path → redirect to `/sign-in?from=`; cookie present but `jose`-decoded `role` isn't `ADMIN` on an `(admin)` path → redirect to `/support/dashboard` (research.md's unverified-decode decision) (depends on T001). Also requires removing `next.config.mjs`'s `output: 'export'` — incompatible with Middleware (discovered by actually running the dev server, not by inspection). **Checkpoint**: A session can be established, read, and cleared; every subsequent API call carries it; an unauthenticated or wrongly-roled request never reaches portal content. --- ## Phase 3: User Story 0 - An agent or admin signs in (Priority: P1) **Goal**: A real sign-in screen and the redirect/guard behavior FR-000 requires. **Independent Test**: Quickstart Scenario 0. ### Tests for User Story 0 - [x] T014 [P] [US0] Unit test for `lib/query/query-state.ts`'s discriminated-union logic (loading/empty/error/ready, each input combination) in `tests/unit/lib/query-state.test.ts` - [x] T015 [US0] Integration test (mocked `lib/api/auth.ts`) covering Quickstart Scenario 0 steps 2-3 (successful sign-in redirects and stores a session; wrong credentials show one generic error) in `tests/integration/auth/sign-in.test.tsx` (depends on T009) - [x] T016 [US0] Playwright E2E covering Quickstart Scenario 0 end-to-end against a real supporthub-api (redirect-when-unauthenticated, sign-in, role-gated redirect, sign-out) in `tests/e2e/agent-sign-in-and-resolve.spec.ts` — this is also Constitution Principle VII's journey (B), continued by User Story 2's own steps once that story is built ### Implementation for User Story 0 - [x] T017 [US0] Add `features/auth/sign-in-form.tsx` + `features/auth/use-login.ts` (a TanStack `useMutation` wrapping `lib/api/auth.ts`'s `login`, redirecting to a role-appropriate landing page on success) (depends on T009, T011) - [x] T018 [US0] Add the `/sign-in` page (`src/app/sign-in/page.tsx`, new — outside every existing route group, since it's neither an admin nor support surface) rendering T017's form - [x] T019 [US0] Add a sign-out action (`features/auth/use-logout.ts`, wrapping `lib/api/auth.ts`'s `logout`) wired into both `(support)/layout.tsx` and `(admin)/layout.tsx`'s existing nav shells (depends on T009, T011) - [x] T020 [US0] Run Quickstart Scenario 0 locally and confirm all 5 steps pass **Checkpoint**: A real session exists end-to-end. Every other user story can now consume one. --- ## Phase 4: User Story 1 - An agent sees and opens their assigned work (Priority: P1) **Goal**: The agent dashboard, backed by 011's new endpoint. **Independent Test**: Quickstart Scenario 1. ### Tests for User Story 1 - [x] T021 [P] [US1] Integration test (mocked `lib/api/tickets.ts`) covering Quickstart Scenario 1's three states (populated list, empty state, list reflects reassignment after refetch) in `tests/integration/tickets/agent-dashboard.test.tsx` ### Implementation for User Story 1 - [x] T022 [US1] Add `lib/api/tickets.ts`'s `getMyAssignedTickets` (depends on T007, T008) - [x] T023 [US1] Add `features/tickets/use-my-tickets.ts` (TanStack `useQuery` + `query-state.ts`) and `features/tickets/agent-dashboard.tsx` (the list itself — customer/ product/priority/status/SLA time-remaining per row, per FR-001) (depends on T010, T022) - Sub-note: a `404` from `getMyAssignedTickets` (011's "no linked agent" rejection) renders as its own distinct message, not the generic empty state (contracts/api-client- contract.md) - [x] T024 [US1] Replace the placeholder `src/app/(support)/support/dashboard/page.tsx` with T023's component (depends on T023) - [x] T025 [US1] Run Quickstart Scenario 1 locally and confirm all 3 steps pass **Checkpoint**: An agent has a real, live dashboard. --- ## Phase 5: User Story 2 - An agent works a ticket end-to-end (Priority: P1) **Goal**: The ticket workbench — messages, status transitions, the full problem-resolution workflow — surfacing every backend rejection verbatim. **Independent Test**: Quickstart Scenario 2. ### Tests for User Story 2 - [x] T026 [P] [US2] Integration test (mocked `lib/api/tickets.ts` + `lib/api/problems.ts`) covering Quickstart Scenario 2's three steps, including the `409` precondition-rejection case rendered verbatim, in `tests/integration/tickets/workbench.test.tsx` - [x] T027 [US2] Extend `tests/e2e/agent-sign-in-and-resolve.spec.ts` (T016) with the full investigation→root-cause→solution→implementation→verification→resolution flow against a real supporthub-api ticket already in `HUMAN_ESCALATION` ### Implementation for User Story 2 - [x] T028 [US2] Add `lib/api/tickets.ts`'s `getTicket`/`getTicketMessages`/`postMessage`/ `updateTicketStatus`/`escalateTicket` and `lib/api/problems.ts`'s full set (per contracts/api-client-contract.md) (depends on T007, T008) - [x] T029 [US2] Add `features/tickets/ticket-header.tsx` (status, priority, SLA, escalate action) and `features/tickets/message-thread.tsx` (customer/AI/agent messages + internal notes, visually distinct, per FR-002) (depends on T028) - [x] T030 [US2] Add `features/problems/investigation-form.tsx` through `features/problems/resolution-form.tsx` (one per stage, FR-003) — each renders its own `409` precondition rejection from `ApiError` verbatim, never a client-side pre-check of "is there an investigation on file yet" (depends on T028) - [x] T031 [US2] Replace the placeholder `src/app/(support)/support/agent-tickets/[ticketId]/page.tsx` composing T029-T030 (depends on T029, T030) - [x] T032 [US2] Run Quickstart Scenario 2 locally and confirm all 3 steps pass **Checkpoint**: An agent can resolve a real customer problem end-to-end from the UI alone. --- ## Phase 6: User Story 3 - An admin sets up the support organization (Priority: P1) **Goal**: Teams/agents/skills/hierarchy admin screens, replacing every placeholder under `(admin)/admin/{teams,agents,hierarchy}`. **Independent Test**: Quickstart Scenario 3. ### Tests for User Story 3 - [x] T033 [P] [US3] Integration test (mocked `lib/api/teams.ts` + `lib/api/hierarchy.ts`) covering Quickstart Scenario 3's three steps, including the cycle-detection rejection rendered verbatim, in `tests/integration/teams/support-org-admin.test.tsx` ### Implementation for User Story 3 - [x] T034 [US3] Add `lib/api/teams.ts` and `lib/api/hierarchy.ts` per contracts/api-client-contract.md, including 011's `linkAgentAccount` (depends on T007, T008) - [x] T035 [US3] Add `features/teams/team-roster.tsx` (create team, add agent, upsert skill, link an agent's account per FR-005) (depends on T034) - [x] T036 [US3] Add `features/orchestration/hierarchy-editor.tsx` (create/view a node with scope/skills/strategy, rendered as a tree by `parentId`, per FR-005) (depends on T034) - [x] T037 [US3] Replace the placeholder `src/app/(admin)/admin/teams/page.tsx` and `src/app/(admin)/admin/hierarchy/page.tsx` with T035/T036 (depends on T035, T036) - [x] T038 [US3] Run Quickstart Scenario 3 locally and confirm all 3 steps pass **Checkpoint**: All four Phase 3-6 stories (US0-US3, this plan's full immediate scope) work independently and together — this is the feature's P1 MVP. --- ## Phase 7: Polish & Cross-Cutting Concerns (this scope's own) - [x] T039 [P] Update `specs/001-agent-admin-ui/checklists/requirements.md` Notes with any implementation-time findings - [x] T040 Run `npm run typecheck` and `npm run lint` - [x] T041 Run `npm run test` (all unit + integration) and `npm run test:e2e` (both Playwright journeys) against a real, locally-running supporthub-api --- ## Dependencies & Execution Order - **Setup (Phase 1)**: No dependencies - **Foundational (Phase 2)**: Depends on Setup — BLOCKS every user story - **User Story 0 (Phase 3)**: Depends on Foundational — BLOCKS User Stories 1-3 (no session, no data) - **User Stories 1-3 (Phases 4-6)**: Each depends on US0 only — independent of each other, parallelizable once US0 is done - **Polish (Phase 7)**: Depends on all of Phases 3-6 ### Parallel Opportunities - T001-T005 (Setup, all independent) - T008 (types) alongside T006-T007 (client plumbing) once T002 exists - Once US0 (Phase 3) is complete: Phases 4, 5, and 6 can proceed in parallel — they touch disjoint `features/*` subdirectories and disjoint page files ## Implementation Strategy ### MVP First (User Story 0 alone is not shippable — it has no destination) 1. Setup + Foundational (T001-T013) 2. User Story 0 (T014-T020) → a real session exists 3. **STOP and VALIDATE**: Quickstart Scenario 0 passes end-to-end against a real supporthub-api, including the redirect-on-401 path (contracts/api-client-contract.md's "Session guard contract" — this is the one behavior that can't be verified by a mocked-API integration test alone). 4. User Stories 1-3 (T021-T038), in any order or in parallel — this plan's full P1 MVP 5. Polish (T039-T041) ### Continuing past this plan's scope User Stories 4-7 (SLA/calendar config, escalation config, monitoring views, catalog/knowledge governance) follow the identical `lib/api` function → TanStack Query hook → `features/*` component → page pattern established above, against their own backend contracts (`specs/008-sla-escalation`, `specs/004-product-knowledge` in supporthub-api) — deferred to a tasks.md continuation once this scope is verified, per plan.md's own Technical Context.