---
phase: 14-pax8-ui-surface
plan: 04
subsystem: frontend
tags: [next.js, react, pax8, datatable, detailmodal, navigation]

# Dependency graph
requires:
  - phase: 14-pax8-ui-surface
    plan: 01
    provides: "GET /api/pax8/companies, GET /api/pax8/companies/[id]"
  - phase: 14-pax8-ui-surface
    plan: 03
    provides: "DetailModal kind='pax8_company' support + subscriptions cost-breakdown table"
provides:
  - "app/pax8/page.tsx — top-level /pax8 page: PageHeader + Companies/Needs Review Tabs shell, Companies tab fully wired (DataTable + drill-down modal)"
  - "Top-level PAX8 nav entry visible to all authenticated users"
affects: ["14-05 (fills the Needs Review TabsContent placeholder)", "14-06 (manual verification of the full page)"]

# Tech tracking
tech-stack:
  added: []
  patterns:
    - "Fetch-then-open drill-down: row click fetches /api/pax8/companies/[id] before setting selectedCompany and opening DetailModal, keeping the loading state on the DataTable rather than an empty dialog"
    - "DataTable column-key -> API sort-value map (SORT_KEY_MAP) for columns whose key doesn't match the API's whitelisted sort param 1:1 (matched -> match, activeSubscriptionCount -> subscriptions, location -> city)"

key-files:
  created:
    - app/pax8/page.tsx
  modified:
    - components/navigation/app-navigation.tsx

key-decisions:
  - "Companies tab uses page size 25 (1-based page state converted to offset = (page-1)*pageSize for the API's limit/offset contract)"
  - "The 'location' column combines city, stateOrProvince, country into one display string but maps to the API's 'city' sort key (no combined-location sort exists server-side)"
  - "Needs Review TabsContent is a single-line muted placeholder with an explicit '/* Needs Review tab implemented in Plan 14-05 */' comment, per the plan's interface-first hand-off"
  - "Nav icon: ShoppingCart (not previously used at the top level) — added to the existing lucide-react import in app-navigation.tsx"

requirements-completed: [PAX8-13]

# Metrics
duration: ~20min
completed: 2026-07-11
---

# Phase 14 Plan 04: /pax8 Page Shell + Companies Tab + Nav Entry Summary

**New top-level `/pax8` page: a PageHeader + Companies/Needs Review Tabs shell with the Companies tab fully wired to a sortable/searchable/paginated DataTable of PAX8 companies whose row-click fetches the subscriptions/cost-breakdown drill-down and opens the extended DetailModal; plus a top-level PAX8 nav entry visible to every authenticated user.**

## Performance

- **Duration:** ~20 min
- **Tasks:** 2 completed
- **Files modified:** 2 (1 new, 1 modified)

## Accomplishments

- `app/pax8/page.tsx` — a `'use client'` page rendering `PageHeader` (title "PAX8", `breadcrumbs=[{label:'PAX8'}]`, `accent`) followed by a `container mx-auto px-6 py-6 space-y-6` wrapper holding a `Tabs` (`companies` / `needs-review`).
- Companies tab: `fetchCompanies(page, search?, sort?, order?)` converts the DataTable's 1-based `page` to the API's `offset = (page-1)*pageSize` (page size 25), maps DataTable column keys to the API's whitelisted `sort` values via `SORT_KEY_MAP`, and renders a `DataTable` with 4 columns — `name` (sortable), `matched` (matched Autotask company name as a primary-colored span, or a secondary `Badge` reading "Unmatched"), `activeSubscriptionCount` (labeled "Subs", `font-mono tabular-nums`), and `location` (city/state/country joined, em-dash fallback).
- Row click (`handleRowClick`) fetches `/api/pax8/companies/${id}` first, then sets `selectedCompany` to `{ ...company, subscriptions, costTotal }` and opens `DetailModal` with `kind="pax8_company"` — fetch-then-open, keeping the loading indicator on the DataTable rather than an empty dialog.
- Empty-state copy matches UI-SPEC exactly: "No PAX8 companies synced yet" / "Run the PAX8 sync from /admin/integrations, then refresh this page."
- Needs Review `TabsContent` is a clearly marked placeholder (`{/* Needs Review tab implemented in Plan 14-05 */}` + a muted "Loading…" line) — not blank, not implemented, ready for Plan 05.
- `components/navigation/app-navigation.tsx` — added one top-level `navigationItems` entry (`title: 'PAX8'`, `href: '/pax8'`, `icon: ShoppingCart`, description) placed right after "Configuration Items". Not added to the `Engagement`/`Admin` super-admin `visibleItems` gate, so it's visible to every authenticated user on both the desktop `NavigationMenu` and mobile `Sheet` (both consume the same array) — satisfying D-07.

## Task Commits

Each task was committed atomically:

1. **Task 1: /pax8 page shell + Companies tab (DataTable + DetailModal drill-down)** - `51470f8` (feat)
2. **Task 2: Add top-level PAX8 nav entry** - `543ac39` (feat)

## Files Created/Modified

- `app/pax8/page.tsx` — new top-level page: tab shell + Companies tab (DataTable + fetch-then-open DetailModal drill-down) + Needs Review placeholder
- `components/navigation/app-navigation.tsx` — added top-level PAX8 nav entry + `ShoppingCart` import

## Decisions Made

- Page size fixed at 25 for the Companies tab (no page-size selector this phase).
- `location` column is a display-only composite of `city`/`stateOrProvince`/`country`; its sort maps to the API's `city` sort key since no combined-location server-side sort exists.
- Chose `ShoppingCart` for the nav icon — not previously used at the top level of `navigationItems`.

## Deviations from Plan

None — plan executed exactly as written. Both tasks matched their `<action>` and `<acceptance_criteria>` blocks without needing any Rule 1-4 deviation.

## Verification

- `npx tsc --noEmit --pretty` — passes (run after each task).
- Grep-confirmed: `value="companies"` and `value="needs-review"` both present; `kind="pax8_company"` present; `{/* Needs Review tab implemented in Plan 14-05 */}` present verbatim; no `swr`/`react-query` imports; `/api/pax8/companies` and `/api/pax8/companies/${row.id}` both fetched; `'/pax8'` present in `app-navigation.tsx`; `visibleItems` filter block unchanged (still only special-cases `'Engagement'`/`'Admin'`).
- Manual verification (page loads, row opens modal, nav item visible) is deferred to Plan 06 per this plan's own `<verification>` block.

## Threat Flags

None — this plan's threat model (T-14-03 info disclosure via `requireAuth()`-gated fetches, T-14-06 XSS via React auto-escaping, T-14-08 nav visibility accepted by design, T-14-SC zero new packages) is fully addressed by construction. No new network endpoints, auth paths, or schema changes were introduced — this plan only consumes existing Plan 01 routes and Plan 03's DetailModal extension.

## Known Stubs

- **Needs Review tab** (`app/pax8/page.tsx`, `TabsContent value="needs-review"`): renders a static muted "Loading…" line with no data source wired. This is the plan's intended interface-first hand-off — Plan 14-05 replaces this block with the full review-card UI. Not a defect; explicitly scoped out of this plan's `<objective>`.

## User Setup Required

None — no external service configuration required.

## Next Phase Readiness

- Plan 14-05 can now implement the Needs Review `TabsContent` in `app/pax8/page.tsx` without touching the Companies tab or the tab shell.
- Plan 14-06 (manual verification) can load `/pax8`, confirm the Companies tab lists real companies, click a row to see the cost-breakdown modal, and confirm the PAX8 nav item appears for an authenticated user.

---
*Phase: 14-pax8-ui-surface*
*Completed: 2026-07-11*

## Self-Check: PASSED

- FOUND: app/pax8/page.tsx
- FOUND: components/navigation/app-navigation.tsx
- FOUND: .planning/phases/14-pax8-ui-surface/14-04-SUMMARY.md
- FOUND commit: 51470f8
- FOUND commit: 543ac39
- FOUND commit: 9b6faa9
