diff --git a/specs/001-agent-admin-ui/tasks.md b/specs/001-agent-admin-ui/tasks.md new file mode 100644 index 0000000..4cf70e3 --- /dev/null +++ b/specs/001-agent-admin-ui/tasks.md @@ -0,0 +1,261 @@ +--- +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 + +- [ ] T001 [P] Add `js-cookie` + `@types/js-cookie` and `jose` to `package.json` +- [ ] 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. +- [ ] T003 [P] Configure `vitest.config.ts` (currently empty) — `environment: 'jsdom'`, path + aliases matching `tsconfig.json`, `tests/unit` + `tests/integration` include globs +- [ ] T004 [P] Configure `playwright.config.ts` (currently empty) — base URL from + `NEXT_PUBLIC_APP_URL`, `tests/e2e` test dir +- [ ] 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. + +- [ ] T006 Add `lib/auth/session-cookie.ts` — `getSessionToken()`/`setSessionToken(token)`/ + `clearSessionToken()` using `js-cookie` (`sh_session`, `Secure` in production, + `SameSite=Lax`) (depends on T001) +- [ ] 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) +- [ ] 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 +- [ ] T009 Add `lib/api/auth.ts` — `login`, `getCurrentSession`, `logout` per + contracts/api-client-contract.md (depends on T007, T008) +- [ ] 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) +- [ ] 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) +- [ ] T012 Wire both providers into `src/app/layout.tsx` (currently the root layout with no + providers) +- [ ] T013 Write `middleware.ts` (currently empty) — 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) + +**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 + +- [ ] 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` +- [ ] 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) +- [ ] 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 + +- [ ] 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) +- [ ] 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 +- [ ] 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) +- [ ] 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 + +- [ ] 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 + +- [ ] T022 [US1] Add `lib/api/tickets.ts`'s `getMyAssignedTickets` (depends on T007, T008) +- [ ] 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) +- [ ] T024 [US1] Replace the placeholder `src/app/(support)/support/dashboard/page.tsx` with + T023's component (depends on T023) +- [ ] 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 + +- [ ] 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` +- [ ] 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 + +- [ ] 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) +- [ ] 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) +- [ ] 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) +- [ ] T031 [US2] Replace the placeholder + `src/app/(support)/support/agent-tickets/[ticketId]/page.tsx` composing T029-T030 + (depends on T029, T030) +- [ ] 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 + +- [ ] 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 + +- [ ] 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) +- [ ] 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) +- [ ] 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) +- [ ] 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) +- [ ] 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) + +- [ ] T039 [P] Update `specs/001-agent-admin-ui/checklists/requirements.md` Notes with any + implementation-time findings +- [ ] T040 Run `npm run typecheck` and `npm run lint` +- [ ] 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.