docs(14): capture phase context
This commit is contained in:
parent
80771a4d2c
commit
4378fa5ebc
2 changed files with 264 additions and 0 deletions
205
.planning/phases/14-pax8-ui-surface/14-CONTEXT.md
Normal file
205
.planning/phases/14-pax8-ui-surface/14-CONTEXT.md
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
# Phase 14: /pax8 UI Surface - Context
|
||||
|
||||
**Gathered:** 2026-07-11
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
A new `/pax8` page where any authenticated manager can see PAX8 companies with
|
||||
their subscriptions and a cost breakdown, and an admin can resolve flagged or
|
||||
ambiguous company matches (populated by Phase 12's fuzzy matcher) directly
|
||||
from that same page — no `psql` required. No new sync logic (Phases 11-12
|
||||
already built and schedule it — Phase 13), no changes to the matching
|
||||
algorithm itself, no PAX8 write access (read-only view + resolve-to-Autotask
|
||||
metadata only).
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
User deferred to Claude's judgment on all four gray areas identified. Decisions below are grounded in existing Pulse conventions and Phase 12/13 schema, documented as concrete direction for research/planning — not open questions.
|
||||
|
||||
### Company List & Cost Breakdown Layout
|
||||
- **D-01:** Main company list uses `components/admin/DataTable.tsx` (the
|
||||
established `@tanstack/react-table` wrapper used throughout Pulse) rather
|
||||
than a bespoke card grid — consistent with every other tabular list in the
|
||||
app, gives sorting/search/pagination for free.
|
||||
- **D-02:** Per-company cost breakdown is a drill-down via
|
||||
`components/admin/DetailModal.tsx` (formatted/raw tab pattern), not inline
|
||||
in the table row — keeps the main list scannable. The formatted tab shows
|
||||
subscriptions (product name, quantity, billing term) and a cost summary;
|
||||
raw tab shows the underlying JSON for support/debugging, matching every
|
||||
other DetailModal usage in the codebase.
|
||||
- **D-03:** Cost breakdown is grouped **by subscription/product**, not a
|
||||
single total — matches `pax8_subscriptions`/`pax8_order_items` granularity
|
||||
and gives managers the "what am I paying for" answer, not just "how much."
|
||||
Note the schema quirk from Phase 12 research: `pax8_orders.pax8_company_id`
|
||||
is always NULL (PAX8's `/invoices` API never populates it) — actual
|
||||
per-company cost data must be joined from `pax8_order_items.pax8_company_id`
|
||||
and `start_period`/`end_period`, not from `pax8_orders` directly.
|
||||
|
||||
### Review/Resolution Section & Flow
|
||||
- **D-04:** The flagged/ambiguous review queue is a **section/tab within
|
||||
`/pax8`** (e.g., "Companies" / "Needs Review" tabs on the same page), not a
|
||||
separate top-level route — matches the roadmap's explicit "directly from
|
||||
that page" wording (SC#4), even though the closest existing precedent
|
||||
(`app/admin/device-link-conflicts/page.tsx`) is a standalone page. Reuse
|
||||
that page's card-list-with-resolve-action UI pattern, just embedded as a
|
||||
tab rather than routed separately.
|
||||
- **D-05:** Resolution UI offers **both**: pick from the top-3 stored
|
||||
candidates (`pax8_company_match_review.candidate_company_ids` +
|
||||
`match_confidences`, already computed by Phase 12 — zero extra query cost)
|
||||
as the primary path, with a manual company search/picker as a fallback —
|
||||
required regardless for Phase 12's D-03 zero-candidate case (empty
|
||||
`candidate_company_ids` array), and useful even when candidates exist but
|
||||
none are correct.
|
||||
- **D-06:** Resolving writes `resolved_at`, `resolved_by_user_id`,
|
||||
`resolved_to_company_id`, optional `resolution_note` — mirrors
|
||||
`device_link_review`'s resolve shape exactly (same columns exist on
|
||||
`pax8_company_match_review` per Phase 12's schema). This is what makes the
|
||||
resolution "persist and be respected by future syncs" (SC#4) — Phase 12's
|
||||
D-05 already scopes re-matching to `resolved_at IS NULL` rows only.
|
||||
|
||||
### Access & Permissions Split
|
||||
- **D-07:** Viewing `/pax8` (company list + cost breakdown) requires only
|
||||
`requireAuth()` — any authenticated user, matching the roadmap's "a manager
|
||||
can open /pax8" framing and how other read-heavy pages (dashboard, tickets)
|
||||
work today.
|
||||
- **D-08:** The resolve action (mutating a match) requires
|
||||
`requirePermission('admin', 'access')` — matching
|
||||
`/api/admin/device-link-conflicts/[id]/resolve`'s existing pattern exactly.
|
||||
This intentionally differs from that precedent's GET route (which is
|
||||
admin-gated end-to-end) because Phase 14's roadmap explicitly splits
|
||||
"manager views" from "admin resolves" — the read/write permission split is
|
||||
a deliberate phase-14 decision, not an oversight.
|
||||
- **Note for the planner:** Phase 13's code review (`13-REVIEW.md`, CR-02)
|
||||
flagged that the adjacent `POST /api/pax8/sync` route has no role check at
|
||||
all — a pre-existing gap, out of Phase 13's scope. Phase 14 should NOT
|
||||
repeat that gap: every new API route this phase adds must have an explicit
|
||||
`requireAuth()`/`requirePermission()` call, not an implicit "whatever
|
||||
middleware allows."
|
||||
|
||||
### No-Candidate / Edge-Case Handling
|
||||
- **D-09:** A review row with an empty `candidate_company_ids` array shows
|
||||
the same review-section UI with an explicit empty state ("No suggested
|
||||
matches — search manually") rather than a separate flow or page — the
|
||||
manual-search fallback from D-05 handles this case naturally, no special-
|
||||
casing needed beyond the empty-state copy.
|
||||
|
||||
### Claude's Discretion
|
||||
- Exact tab/section labels, table column set, and DetailModal field layout —
|
||||
left to the planner/researcher; no specific mockup or wording was given.
|
||||
- Whether "Needs Review" tab shows a count badge, and sort/filter options on
|
||||
the main company list — standard implementation details, not discussed.
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## Canonical References
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
### Project scope & requirements
|
||||
- `.planning/PROJECT.md` — Current Milestone: v2.0 PAX8 Integration section
|
||||
- `.planning/REQUIREMENTS.md` — PAX8-12, PAX8-13, PAX8-14 (this phase's
|
||||
requirement IDs)
|
||||
- `.planning/ROADMAP.md` — Phase 14 section (goal, 4 success criteria,
|
||||
depends on Phase 12)
|
||||
|
||||
### Prior phase foundation this phase builds on
|
||||
- `migrations/091_pax8_tables.sql` — `pax8_companies`, `pax8_products`,
|
||||
`pax8_subscriptions`, `pax8_orders`, `pax8_order_items`,
|
||||
`pax8_company_match_review` — full schema this page reads from
|
||||
- `migrations/093_pax8_orders_company_matching.sql` — `pax8_companies`
|
||||
auto-match columns (`autotask_company_id`, `match_confidence`,
|
||||
`match_method`, `matched_at`) and the critical note that
|
||||
`pax8_orders.pax8_company_id` is always NULL — cost-per-company data comes
|
||||
from `pax8_order_items` instead
|
||||
- `.planning/phases/12-orders-invoices-company-matching/12-CONTEXT.md` —
|
||||
D-01 through D-05 (match confidence, ambiguous handling, empty-candidate
|
||||
flagging, top-3 candidate cap, re-scoring rules) — the review queue this
|
||||
phase surfaces
|
||||
- `.planning/phases/13-scheduler-admin-toggle/13-REVIEW.md` — CR-02 finding
|
||||
(missing permission check on `/api/pax8/sync`) — this phase's new routes
|
||||
must not repeat that gap (see D-08 note above)
|
||||
|
||||
### Existing patterns to follow
|
||||
- `app/admin/device-link-conflicts/page.tsx` — direct precedent for the
|
||||
review/resolve UI (list + resolve action, card-based); this phase embeds
|
||||
the same pattern as a tab/section rather than a standalone route
|
||||
- `app/api/admin/device-link-conflicts/route.ts` and
|
||||
`app/api/admin/device-link-conflicts/[id]/resolve/route.ts` — API shape
|
||||
(list with filters, resolve with `requirePermission('admin', 'access')`)
|
||||
to mirror for `pax8_company_match_review`
|
||||
- `components/admin/DataTable.tsx` — table wrapper for the main company list
|
||||
- `components/admin/DetailModal.tsx` — formatted/raw tab pattern for the
|
||||
per-company cost breakdown drill-down
|
||||
- `lib/auth-utils.ts` — `requireAuth()` / `requirePermission()` helpers this
|
||||
phase's routes must use explicitly (D-07/D-08)
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `components/admin/DataTable.tsx` — sortable/searchable/paginated table,
|
||||
used as-is for the company list
|
||||
- `components/admin/DetailModal.tsx` — formatted/raw tabs, used as-is for
|
||||
the cost-breakdown drill-down
|
||||
- `postgresClient` singleton — parameterized queries, no ORM, joins across
|
||||
`pax8_companies` / `pax8_subscriptions` / `pax8_order_items` /
|
||||
`pax8_company_match_review`
|
||||
|
||||
### Established Patterns
|
||||
- Admin CRUD pages live at `app/admin/<name>/page.tsx` with a matching
|
||||
`app/api/admin/<name>/route.ts` — but this page is NOT admin-only
|
||||
(D-07/D-08 split), so its route likely lives at `app/pax8/page.tsx` (top
|
||||
level, alongside `/dashboard`, `/mobile`), not under `/admin`
|
||||
- `*_match_review` tables (both `device_link_review` and
|
||||
`pax8_company_match_review`) follow list-with-filter + single-item-resolve
|
||||
API shape: `GET /api/.../conflicts?source=X` + `POST /api/.../[id]/resolve`
|
||||
- Toasts via `sonner`, icons via `lucide-react` — standard for the resolve
|
||||
action's success/error feedback
|
||||
|
||||
### Integration Points
|
||||
- New `app/pax8/page.tsx` — main page with Companies / Needs Review tabs
|
||||
- New API routes for: company list + cost data, match-review list, and
|
||||
resolve action (exact paths left to planner — likely
|
||||
`app/api/pax8/companies/route.ts`,
|
||||
`app/api/pax8/company-matches/route.ts` +
|
||||
`app/api/pax8/company-matches/[id]/resolve/route.ts`)
|
||||
- `components/navigation/app-navigation.tsx` — needs a new top-level nav
|
||||
entry for `/pax8` (not nested under Admin, given D-07's broader view
|
||||
access)
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
No UI mockups or specific behavioral scripts were provided — the nine
|
||||
numbered decisions above (D-01 through D-09) are the concrete specifics:
|
||||
DataTable + DetailModal-drill-down layout, embedded review tab (not a
|
||||
separate page) with dual pick-from-candidates/search resolution, an explicit
|
||||
manager-view/admin-resolve permission split, and empty-state handling for
|
||||
the zero-candidate case.
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
None — discussion stayed within Phase 14's scope. (PAX8 write access,
|
||||
seat-adjustment actions, and any general natural-language assistant over
|
||||
this data are already explicitly out of scope per PROJECT.md, not deferred
|
||||
from this discussion.)
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 14-pax8-ui-surface*
|
||||
*Context gathered: 2026-07-11*
|
||||
59
.planning/phases/14-pax8-ui-surface/14-DISCUSSION-LOG.md
Normal file
59
.planning/phases/14-pax8-ui-surface/14-DISCUSSION-LOG.md
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
# Phase 14: /pax8 UI Surface - Discussion Log
|
||||
|
||||
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||
|
||||
**Date:** 2026-07-11
|
||||
**Phase:** 14-pax8-ui-surface
|
||||
**Areas discussed:** Company list & cost breakdown layout, Review/resolution section & flow, Access & permissions split, No-candidate / edge-case handling
|
||||
|
||||
---
|
||||
|
||||
All four gray areas were presented via a single multiSelect question. The user responded "Use your best judgement" rather than selecting specific areas or answering individually — deferring all four decisions to Claude, grounded in existing codebase precedent.
|
||||
|
||||
## Company List & Cost Breakdown Layout
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| DataTable (list) + DetailModal (cost drill-down) | Reuses `components/admin/DataTable.tsx` + `components/admin/DetailModal.tsx`, matches every other tabular list in Pulse | ✓ (Claude's judgment) |
|
||||
| Custom card grid | Would be a new visual pattern, not currently used elsewhere | |
|
||||
| Inline cost breakdown in table rows | Keeps everything in one view but risks cluttering the row | |
|
||||
|
||||
**Notes:** Cost breakdown grouped by subscription/product (D-03), not a single total — matches schema granularity. Flagged the `pax8_orders.pax8_company_id` always-NULL quirk from Phase 12 research; cost data must join through `pax8_order_items` instead.
|
||||
|
||||
## Review/Resolution Section & Flow
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Embedded tab/section within `/pax8` | Matches roadmap's "directly from that page" wording | ✓ (Claude's judgment) |
|
||||
| Separate standalone page (device-link-conflicts precedent) | Existing precedent's actual shape, but roadmap explicitly wants it on the same page | |
|
||||
|
||||
**Notes:** Resolution UI offers both pick-from-top-3-candidates (already computed by Phase 12) and manual search fallback (required anyway for the zero-candidate case).
|
||||
|
||||
## Access & Permissions Split
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| View = any authenticated user, Resolve = admin-only | Matches roadmap's explicit "manager views / admin resolves" framing | ✓ (Claude's judgment) |
|
||||
| Entire page admin-gated | Matches the closest existing precedent (device-link-conflicts) exactly, but contradicts roadmap wording | |
|
||||
|
||||
**Notes:** Explicitly flagged Phase 13's CR-02 code-review finding (missing permission check on `/api/pax8/sync`) as a gap this phase's new routes must not repeat.
|
||||
|
||||
## No-Candidate / Edge-Case Handling
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Same review UI with an empty state | No special-casing needed beyond copy — manual search fallback already covers it | ✓ (Claude's judgment) |
|
||||
| Separate flow/page for zero-candidate companies | Unnecessary complexity for a copy-level difference | |
|
||||
|
||||
---
|
||||
|
||||
## Claude's Discretion
|
||||
|
||||
- Exact tab/section labels, table column set, DetailModal field layout
|
||||
- Whether a count badge appears on the "Needs Review" tab
|
||||
- Sort/filter options on the main company list
|
||||
|
||||
## Deferred Ideas
|
||||
|
||||
None — discussion stayed within Phase 14's scope.
|
||||
Loading…
Add table
Add a link
Reference in a new issue