docs(03-operator-experience): create phase plan
This commit is contained in:
192
.planning/phases/03-operator-experience/03-03-PLAN.md
Normal file
192
.planning/phases/03-operator-experience/03-03-PLAN.md
Normal file
@@ -0,0 +1,192 @@
|
||||
---
|
||||
phase: 03-operator-experience
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on: ["03-01"]
|
||||
files_modified:
|
||||
- packages/portal/app/(dashboard)/billing/page.tsx
|
||||
- packages/portal/components/subscription-card.tsx
|
||||
- packages/portal/components/billing-status.tsx
|
||||
- packages/portal/lib/queries.ts
|
||||
autonomous: false
|
||||
requirements:
|
||||
- PRTA-05
|
||||
|
||||
must_haves:
|
||||
truths:
|
||||
- "Operator can subscribe to a per-agent monthly plan via Stripe Checkout"
|
||||
- "Operator can upgrade (add agents) and downgrade (remove agents) their subscription"
|
||||
- "Operator can cancel their subscription"
|
||||
- "Operator can manage payment methods and view invoices via Stripe Billing Portal"
|
||||
- "Feature limits are enforced based on subscription state (agents deactivated on cancellation)"
|
||||
- "14-day free trial with full access is available"
|
||||
artifacts:
|
||||
- path: "packages/portal/app/(dashboard)/billing/page.tsx"
|
||||
provides: "Billing management page with subscription status and actions"
|
||||
- path: "packages/portal/components/subscription-card.tsx"
|
||||
provides: "Card showing current plan, agent count, status, trial info"
|
||||
- path: "packages/portal/components/billing-status.tsx"
|
||||
provides: "Status badge for subscription state (trialing, active, past_due, canceled)"
|
||||
key_links:
|
||||
- from: "packages/portal/app/(dashboard)/billing/page.tsx"
|
||||
to: "/api/portal/billing/checkout"
|
||||
via: "POST to create Checkout Session, then redirect to Stripe"
|
||||
pattern: "billing/checkout"
|
||||
- from: "packages/portal/app/(dashboard)/billing/page.tsx"
|
||||
to: "/api/portal/billing/portal"
|
||||
via: "POST to create Billing Portal session, then redirect"
|
||||
pattern: "billing/portal"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Billing management page with Stripe subscription integration -- subscribe, upgrade, downgrade, cancel, and manage payment via Stripe Billing Portal.
|
||||
|
||||
Purpose: Operators can self-serve their subscription lifecycle through the portal, with per-agent monthly pricing that matches the "hire an employee" metaphor.
|
||||
Output: Billing page with subscription card, Checkout redirect, Billing Portal redirect, status display.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/home/adelorenzo/.claude/get-shit-done/workflows/execute-plan.md
|
||||
@/home/adelorenzo/.claude/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/03-operator-experience/03-CONTEXT.md
|
||||
@.planning/phases/03-operator-experience/03-RESEARCH.md
|
||||
@.planning/phases/03-operator-experience/03-01-SUMMARY.md
|
||||
|
||||
<interfaces>
|
||||
<!-- From Plan 01 backend APIs -->
|
||||
|
||||
From packages/shared/shared/api/billing.py (created in Plan 01):
|
||||
```python
|
||||
# POST /api/portal/billing/checkout -> { "checkout_url": "https://checkout.stripe.com/..." }
|
||||
# Body: { "tenant_id": str, "agent_count": int }
|
||||
# POST /api/portal/billing/portal -> { "portal_url": "https://billing.stripe.com/..." }
|
||||
# Body: { "tenant_id": str }
|
||||
# POST /api/webhooks/stripe -> webhook handler (no portal UI interaction)
|
||||
```
|
||||
|
||||
From packages/shared/shared/models/tenant.py (updated in Plan 01):
|
||||
```python
|
||||
class Tenant(Base):
|
||||
# New billing fields:
|
||||
stripe_customer_id: Mapped[str | None]
|
||||
stripe_subscription_id: Mapped[str | None]
|
||||
stripe_subscription_item_id: Mapped[str | None]
|
||||
subscription_status: Mapped[str] # "none" | "trialing" | "active" | "past_due" | "canceled" | "unpaid"
|
||||
trial_ends_at: Mapped[datetime | None]
|
||||
agent_quota: Mapped[int]
|
||||
```
|
||||
|
||||
Established portal patterns:
|
||||
- shadcn/ui components (Card, Badge, Button, Dialog)
|
||||
- TanStack Query for data fetching
|
||||
- API client in lib/api.ts
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Billing page with subscription management</name>
|
||||
<files>
|
||||
packages/portal/app/(dashboard)/billing/page.tsx,
|
||||
packages/portal/components/subscription-card.tsx,
|
||||
packages/portal/components/billing-status.tsx,
|
||||
packages/portal/lib/queries.ts
|
||||
</files>
|
||||
<action>
|
||||
1. Create `packages/portal/components/billing-status.tsx`:
|
||||
- Badge component showing subscription status with color coding:
|
||||
- "trialing" -> blue badge with trial end date
|
||||
- "active" -> green badge
|
||||
- "past_due" -> amber badge with "Payment required" text
|
||||
- "canceled" -> red badge
|
||||
- "none" -> gray badge "No subscription"
|
||||
- Uses shadcn/ui Badge component
|
||||
|
||||
2. Create `packages/portal/components/subscription-card.tsx`:
|
||||
- shadcn/ui Card displaying:
|
||||
- BillingStatus badge (top right)
|
||||
- Plan name: "AI Employee Plan" (per-agent monthly)
|
||||
- Price: "$49/agent/month" (from user decision)
|
||||
- Current agent count vs quota
|
||||
- Trial info: if trialing, show "Trial ends {date}" with days remaining
|
||||
- Agent count adjuster: +/- buttons to change quantity (triggers Stripe subscription item update)
|
||||
- Action buttons:
|
||||
- If status="none": "Subscribe" button -> creates Checkout Session -> redirects to Stripe
|
||||
- If status="trialing" or "active": "Manage Billing" button -> creates Billing Portal session -> redirects to Stripe hosted portal
|
||||
- If status="past_due": "Update Payment" button -> Billing Portal redirect
|
||||
- If status="canceled": "Resubscribe" button -> new Checkout Session
|
||||
|
||||
3. Create `packages/portal/app/(dashboard)/billing/page.tsx`:
|
||||
- Reads tenant_id from query or session
|
||||
- Fetches tenant data (includes billing fields) via existing useTenant() hook
|
||||
- Renders SubscriptionCard
|
||||
- Handles ?session_id= searchParam (Stripe Checkout success redirect): show success toast, refetch tenant
|
||||
- If subscription_status is "past_due": show a top banner warning about payment failure
|
||||
- Per user decision: per-agent monthly pricing ($49/agent/month), 14-day free trial with full access, credit card required upfront
|
||||
|
||||
4. Add TanStack Query hooks to `packages/portal/lib/queries.ts`:
|
||||
- useCreateCheckoutSession() — mutation POST /billing/checkout, returns { checkout_url }
|
||||
- useCreateBillingPortalSession() — mutation POST /billing/portal, returns { portal_url }
|
||||
- useUpdateSubscriptionQuantity() — mutation (if needed for +/- agent count)
|
||||
|
||||
5. Add "Billing" link to dashboard navigation/sidebar.
|
||||
</action>
|
||||
<verify>
|
||||
<automated>cd /home/adelorenzo/repos/konstruct/packages/portal && npx next build 2>&1 | tail -20</automated>
|
||||
</verify>
|
||||
<done>
|
||||
- Billing page renders subscription card with current status
|
||||
- Subscribe button creates Checkout Session and redirects to Stripe
|
||||
- Manage Billing button redirects to Stripe Billing Portal
|
||||
- Status badges show correct colors for all subscription states
|
||||
- Trial info displayed when status is "trialing"
|
||||
- Past due banner shown when payment has failed
|
||||
- Portal builds without errors
|
||||
</done>
|
||||
</task>
|
||||
|
||||
<task type="checkpoint:human-verify" gate="blocking">
|
||||
<name>Task 2: Verify billing page and subscription UI</name>
|
||||
<files>n/a</files>
|
||||
<action>
|
||||
Human verifies the billing management page:
|
||||
1. Start the portal: `cd packages/portal && npm run dev`
|
||||
2. Navigate to /billing — verify subscription card renders
|
||||
3. Verify status badge shows "No subscription" for new tenant
|
||||
4. Verify "Subscribe" button is present
|
||||
5. Verify pricing shows "$49/agent/month" and "14-day free trial"
|
||||
6. Verify "Billing" link appears in dashboard navigation
|
||||
7. Check that agent count +/- controls are present
|
||||
</action>
|
||||
<verify>Human visual inspection of billing page</verify>
|
||||
<done>Operator confirms billing page renders correctly with subscription card, pricing, and action buttons</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
- Portal builds: `cd packages/portal && npx next build`
|
||||
- Billing page renders without errors
|
||||
- All subscription states display correctly
|
||||
- Navigation to billing page works
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Operator can see subscription status and pricing on billing page
|
||||
- Subscribe flow initiates Stripe Checkout redirect
|
||||
- Billing Portal accessible for managing payment/invoices
|
||||
- Status badges accurately reflect subscription_status field
|
||||
- Portal builds successfully
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/03-operator-experience/03-03-SUMMARY.md`
|
||||
</output>
|
||||
Reference in New Issue
Block a user