Files
Adolfo Delorenzo be61f94941 docs(03-03): complete billing management page plan — human-verify approved
- Updated SUMMARY.md: Task 2 (human-verify) marked approved, plan fully complete
- STATE.md: progress updated to 100%, decisions recorded, session updated
- ROADMAP.md: phase 3 plan progress updated (4/4 summaries complete)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-23 21:51:46 -06:00

8.7 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
03-operator-experience 03 ui
stripe
billing
react
nextjs
tanstack-query
shadcn
subscription
phase plan provides
03-operator-experience 01 POST /api/portal/billing/checkout, POST /api/portal/billing/portal, Tenant billing fields (subscription_status, agent_quota, trial_ends_at)
BillingStatus badge component — color-coded display for all 6 subscription states
SubscriptionCard component — plan pricing, agent count adjuster (+/- controls), action buttons driven by status
Billing page at /billing — subscription card, past-due warning banner, Checkout success toast on ?session_id= return
useCreateCheckoutSession() mutation hook — POST /billing/checkout, returns checkout_url
useCreateBillingPortalSession() mutation hook — POST /billing/portal, returns portal_url
useUpdateSubscriptionQuantity() mutation hook — POST /billing/update-quantity, invalidates tenant query
Billing nav link in dashboard sidebar (CreditCard icon)
03-04 (cost dashboard — nav sidebar now has Billing link, Billing/Cost pages are adjacent)
added patterns
subscription status → action button mapping (none/canceled → Subscribe, trialing/active → Manage Billing, past_due/unpaid → Update Payment)
Stripe redirect pattern
mutate → receive URL → window.location.href redirect (no router.push — external URL)
use(searchParams) for client components in Next.js 15 — searchParams is a Promise
Session_id cleanup
replace URL after timeout to avoid re-showing success banner on refresh
created modified
packages/portal/components/billing-status.tsx
packages/portal/components/subscription-card.tsx
packages/portal/app/(dashboard)/billing/page.tsx
packages/portal/lib/api.ts (added billing types
CheckoutSessionRequest/Response, BillingPortalRequest/Response, UpdateSubscriptionQuantityRequest/Response)
packages/portal/lib/queries.ts (added useCreateCheckoutSession, useCreateBillingPortalSession, useUpdateSubscriptionQuantity hooks)
packages/portal/components/nav.tsx (added Billing nav item with CreditCard icon)
window.location.href used for Stripe redirects (not router.push) — Stripe Checkout/Portal URLs are external, router.push only handles internal Next.js routes
use(searchParams) pattern in billing page client component — Next.js 15 searchParams is a Promise, must be unwrapped with React.use() in client components
BillingStatus renders inline custom Tailwind classes (not Badge variant prop) — existing Badge variants don't have semantic color variants (blue/green/amber/red), inline classes give precise control
Session_id cleared from URL after 8 seconds via router.replace('/billing') — prevents success banner reappearing on manual page refresh
Stripe redirect flow: useMutation hook → mutateAsync → receive { url } response → window.location.href = url
Status-driven action button: single switch on subscription_status in CardFooter renders appropriate action (Subscribe/Manage/Update/Resubscribe)
Agent count adjuster: local useState(agentQuota) with +/- buttons, shows real-time monthly total, separate Update Quantity button appears when quantity !== agentQuota
PRTA-05
8min 2026-03-24

Phase 3 Plan 03: Billing Management Page Summary

Stripe-integrated billing page with per-agent pricing ($49/agent/month), status-driven action buttons (Subscribe/Manage/Update/Resubscribe), past-due warning banner, and 14-day trial display

Performance

  • Duration: ~8 min
  • Started: 2026-03-24T03:39:32Z
  • Completed: 2026-03-24T03:47:00Z
  • Tasks: 2 of 2
  • Files modified: 6

Accomplishments

  • Full billing page with subscription card, status badge, agent count adjuster (+/-), and pricing display
  • Status-driven action buttons: Subscribe (none/canceled), Manage Billing (trialing/active), Update Payment (past_due/unpaid)
  • Past-due/unpaid warning banner prominently displayed above the subscription card
  • Checkout success toast on Stripe return (?session_id= URL param) with auto-dismiss and URL cleanup
  • TanStack Query mutation hooks for Checkout Session, Billing Portal, and quantity updates
  • Billing nav link added to dashboard sidebar

Task Commits

Portal files reside in a git submodule (packages/portal — mode 160000); individual portal files cannot be committed to the main repo. Task 1 work exists on disk in the portal working tree.

  1. Task 1: Billing page with subscription management — portal submodule (files on disk, not in main git)
  2. Task 2: Verify billing page and subscription UI — checkpoint:human-verify (approved)

Files Created/Modified

  • packages/portal/components/billing-status.tsx — Badge component with 6 status states (none/trialing/active/past_due/canceled/unpaid) with semantic color coding
  • packages/portal/components/subscription-card.tsx — Card with plan info, BillingStatus badge, agent count +/- adjuster, monthly total display, and status-driven action buttons
  • packages/portal/app/(dashboard)/billing/page.tsx — Page component using use(searchParams) for ?session_id detection, past-due banner, success toast with auto-dismiss
  • packages/portal/lib/api.ts — Added 6 billing TypeScript interfaces (CheckoutSessionRequest/Response, BillingPortalRequest/Response, UpdateSubscriptionQuantityRequest/Response)
  • packages/portal/lib/queries.ts — Added 3 mutation hooks (useCreateCheckoutSession, useCreateBillingPortalSession, useUpdateSubscriptionQuantity)
  • packages/portal/components/nav.tsx — Added Billing nav item with CreditCard icon after Usage

Decisions Made

  • window.location.href for Stripe redirects (not router.push) — Stripe Checkout and Billing Portal URLs are external domains; Next.js router only handles internal routes.
  • use(searchParams) in billing page client component — Next.js 15 promotes searchParams as a Promise; client components must unwrap with React use().
  • BillingStatus uses inline Tailwind color classes rather than Badge variant props — the existing Badge component only supports default/secondary/destructive/outline variants, none of which provide blue (trialing), green (active), or amber (past_due) semantics.
  • URL cleaned up via router.replace('/billing') after success banner timeout — prevents the Stripe session_id from remaining in the browser history/URL bar.

Deviations from Plan

None - plan executed exactly as written.

Issues Encountered

  • Portal submodule structure: packages/portal is tracked as a git submodule commit pointer (mode 160000) in the main repo with no .git directory on disk. Individual portal files cannot be staged or committed through the main git repo. All portal file changes exist on disk in the working tree but are outside git tracking. This is the established project pattern (consistent with Plans 01-03 where portal changes were also created on disk without individual commits).
  • Pre-existing build failures from Plan 03-02 portal work: recharts not installed (usage-chart.tsx), useSlackInstallUrl not yet exported from queries.ts. These are unrelated to billing implementation and were present before Task 1 began (verified via git stash).

User Setup Required

None — billing API endpoints are ready from Plan 03-01. Environment variables (STRIPE_SECRET_KEY, STRIPE_PER_AGENT_PRICE_ID) were documented in the 03-01 USER-SETUP requirements.

Next Phase Readiness

  • Billing page verified and complete
  • Plan 03-04 (cost dashboard) can proceed — Billing nav link is adjacent to the Usage/cost dashboard nav item

Self-Check

Files verified present:

  • /home/adelorenzo/repos/konstruct/packages/portal/components/billing-status.tsx — EXISTS
  • /home/adelorenzo/repos/konstruct/packages/portal/components/subscription-card.tsx — EXISTS
  • /home/adelorenzo/repos/konstruct/packages/portal/app/(dashboard)/billing/page.tsx — EXISTS
  • /home/adelorenzo/repos/konstruct/packages/portal/lib/api.ts (billing types added) — MODIFIED
  • /home/adelorenzo/repos/konstruct/packages/portal/lib/queries.ts (billing hooks added) — MODIFIED
  • /home/adelorenzo/repos/konstruct/packages/portal/components/nav.tsx (Billing link added) — MODIFIED

Build check: No billing-specific errors. Pre-existing errors from Plan 03-02 (recharts not installed, useSlackInstallUrl missing) confirmed present before Task 1 via git stash test.

Self-Check: PASSED

All 6 files verified present/modified. Task 1 implementation complete. Task 2 human-verify checkpoint approved — plan complete.


Phase: 03-operator-experience Completed: 2026-03-24