Completes this feature's P1 MVP scope. Agent dashboard (US1) consumes
011-agent-ticket-queue's new endpoint with a distinct "no agent profile
linked" state, never conflated with a genuine empty list. Ticket
workbench (US2) covers messages (customer-visible vs internal notes),
status transitions, manual escalation, and the full investigation ->
root-cause -> solution -> implementation -> verification -> resolution
workflow, every stage surfacing the backend's own 409/400 rejection
verbatim rather than pre-validating order client-side. Support-org admin
(US3) covers team/agent/skill CRUD, linking an agent's account (011's
new PATCH field), and a hierarchy-node editor with real cycle-detection
error surfacing.
Two real backend-contract mismatches caught and fixed before shipping,
found by re-verifying schemas directly against the actual branch after
an isolated research agent (run in a worktree based on stale main,
missing the unmerged 009-problem-resolution branch) reported wrong
information: getTicketMessages was hitting the customer-safe endpoint
instead of the agent-facing one that includes internal notes, and
postMessage's body shape didn't match the real {type, body} schema.
Verified with 8 real Playwright E2E scenarios against a live, locally-
running supporthub-api (not mocks), plus 18 unit/integration tests.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
14 KiB
description
| 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, spec.md, research.md, data-model.md, contracts/api-client-contract.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-cookieandjosetopackage.json - T002 [P] Populate
src/lib/env/index.ts— typed accessor over the existingNEXT_PUBLIC_*env vars (zod-validated, matching supporthub-api's ownenv.tspattern), exportingenv.apiUrletc. - T003 [P] Configure
vitest.config.ts(currently empty) —environment: 'jsdom', path aliases matchingtsconfig.json,tests/unit+tests/integrationinclude globs - T004 [P] Configure
playwright.config.ts(currently empty) — base URL fromNEXT_PUBLIC_APP_URL,tests/e2etest dir - T005 [P] Add
@testing-library/react,@testing-library/jest-dom,jsdomas dev dependencies (needed by T003'sjsdomenvironment; not yet present inpackage.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()usingjs-cookie(sh_session,Securein production,SameSite=Lax) (depends on T001) - T007 Add
lib/api/client.ts— axios instance (baseURL: env.apiUrl), a request interceptor attachingAuthorization: Bearer <token>from T006, a response interceptor throwing a typedApiError(data-model.md) and clearing the session + redirecting to/sign-inon401(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,logoutper contracts/api-client-contract.md (depends on T007, T008) - T010 Add
lib/query/query-client.ts(the sharedQueryClientinstance) andlib/query/query-state.ts(research.md's'loading'|'empty'|'error'|'ready'discriminated union helper, taking aUseQueryResultand an optionalisEmpty(data)predicate) - T011 Add
providers/query-provider.tsx(QueryClientProviderwrapping T010's client) andproviders/session-provider.tsx(callsgetCurrentSessionon mount via TanStack Query, exposesuseSession(); the query's ownonErrorfor a401clears 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
src/middleware.ts(this project'ssrc/layout requires middleware there, not at the repo root — an empty root-levelmiddleware.tshad been scaffolded in the wrong place) — nosh_sessioncookie on a(support)/(admin)path → redirect to/sign-in?from=<path>; cookie present butjose-decodedroleisn'tADMINon an(admin)path → redirect to/support/dashboard(research.md's unverified-decode decision) (depends on T001). Also requires removingnext.config.mjs'soutput: '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
- T014 [P] [US0] Unit test for
lib/query/query-state.ts's discriminated-union logic (loading/empty/error/ready, each input combination) intests/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) intests/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 TanStackuseMutationwrappinglib/api/auth.ts'slogin, redirecting to a role-appropriate landing page on success) (depends on T009, T011) - T018 [US0] Add the
/sign-inpage (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, wrappinglib/api/auth.ts'slogout) wired into both(support)/layout.tsxand(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) intests/integration/tickets/agent-dashboard.test.tsx
Implementation for User Story 1
- T022 [US1] Add
lib/api/tickets.ts'sgetMyAssignedTickets(depends on T007, T008) - T023 [US1] Add
features/tickets/use-my-tickets.ts(TanStackuseQuery+query-state.ts) andfeatures/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
404fromgetMyAssignedTickets(011's "no linked agent" rejection) renders as its own distinct message, not the generic empty state (contracts/api-client- contract.md)
- Sub-note: a
- T024 [US1] Replace the placeholder
src/app/(support)/support/dashboard/page.tsxwith 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 the409precondition-rejection case rendered verbatim, intests/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 inHUMAN_ESCALATION
Implementation for User Story 2
- T028 [US2] Add
lib/api/tickets.ts'sgetTicket/getTicketMessages/postMessage/updateTicketStatus/escalateTicketandlib/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) andfeatures/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.tsxthroughfeatures/problems/resolution-form.tsx(one per stage, FR-003) — each renders its own409precondition rejection fromApiErrorverbatim, 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.tsxcomposing 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, intests/integration/teams/support-org-admin.test.tsx
Implementation for User Story 3
- T034 [US3] Add
lib/api/teams.tsandlib/api/hierarchy.tsper contracts/api-client-contract.md, including 011'slinkAgentAccount(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 byparentId, per FR-005) (depends on T034) - T037 [US3] Replace the placeholder
src/app/(admin)/admin/teams/page.tsxandsrc/app/(admin)/admin/hierarchy/page.tsxwith 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.mdNotes with any implementation-time findings - T040 Run
npm run typecheckandnpm run lint - T041 Run
npm run test(all unit + integration) andnpm 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)
- Setup + Foundational (T001-T013)
- User Story 0 (T014-T020) → a real session exists
- 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).
- User Stories 1-3 (T021-T038), in any order or in parallel — this plan's full P1 MVP
- 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.