Conversation
- Add prominent trial banner at top with end date and auto-charge notice - Separate cancel-at-period-end warning into its own banner - Simplify plan header: name + badge + billing interval left, Change plan right - Reduce stats grid from 5 cramped columns to 4 clean columns - Remove redundant "Pageview Limit" stat (duplicated "Pageviews") - Replace bulky cancel section with inline text links below the card - Make "Payment method & invoices" a clear link with icon - Compact invoice rows: remove avatar icons, inline date with amount - Tighter spacing throughout for cohesive feel Co-authored-by: Cursor <cursoragent@cursor.com>
Ciphera values clarity — if a user wants to cancel, the option should be honest and easy to find, not hidden. Neutral bordered pill that highlights red on hover communicates this respectfully. Co-authored-by: Cursor <cursoragent@cursor.com>
Greptile OverviewGreptile SummaryThis PR redesigns the Organization Settings → Billing tab UI and adds in-app subscription actions.
Key integration point is Confidence Score: 3/5
|
| Filename | Overview |
|---|---|
| components/settings/OrganizationSettings.tsx | Redesigned Billing tab with trial/cancel/change-plan UI and new modals; found logic issues where trialing w/o payment method is treated as no subscription and invoices UI is hidden behind invoices.length > 0 so loading/empty states never show. |
| lib/api/billing.ts | Added cancel_at_period_end to SubscriptionDetails and new cancelSubscription/changePlan API helpers; appears consistent with existing billingFetch usage. |
| lib/plans.ts | Introduced shared Solo plan ID and traffic tier helpers; simple mapping with sane defaults. |
Sequence Diagram
sequenceDiagram
autonumber
actor User
participant UI as OrganizationSettings(Billing tab)
participant BillingAPI as lib/api/billing.ts
participant Backend as /api/billing/*
participant Stripe as Stripe (portal/checkout)
User->>UI: Open Org Settings → Billing tab
UI->>BillingAPI: getSubscription()
BillingAPI->>Backend: GET /api/billing/subscription
Backend-->>BillingAPI: SubscriptionDetails (incl cancel_at_period_end)
BillingAPI-->>UI: subscription
UI->>BillingAPI: getInvoices()
BillingAPI->>Backend: GET /api/billing/invoices
Backend-->>BillingAPI: Invoice[]
BillingAPI-->>UI: invoices
alt User clicks “Payment method & invoices”
UI->>BillingAPI: createPortalSession()
BillingAPI->>Backend: POST /api/billing/portal
Backend-->>BillingAPI: {url}
BillingAPI-->>UI: {url}
UI->>Stripe: Redirect to Stripe portal URL
end
alt User opens Change plan modal
User->>UI: Click “Change plan”
UI->>UI: Select tier + interval
alt Existing subscription (hasActiveSubscription)
UI->>BillingAPI: changePlan({plan_id, interval, limit})
BillingAPI->>Backend: POST /api/billing/change-plan
Backend-->>BillingAPI: {ok}
BillingAPI-->>UI: {ok}
UI->>BillingAPI: getSubscription()
else New subscription
UI->>BillingAPI: createCheckoutSession({plan_id, interval, limit})
BillingAPI->>Backend: POST /api/billing/checkout
Backend-->>BillingAPI: {url}
BillingAPI-->>UI: {url}
UI->>Stripe: Redirect to Stripe checkout URL
end
end
alt User cancels subscription
User->>UI: Click “Cancel subscription”
alt Cancel at period end
UI->>BillingAPI: cancelSubscription({at_period_end:true})
BillingAPI->>Backend: POST /api/billing/cancel
Backend-->>BillingAPI: {ok, at_period_end:true}
BillingAPI-->>UI: response
UI->>BillingAPI: getSubscription()
else Cancel immediately
UI->>BillingAPI: cancelSubscription({at_period_end:false})
BillingAPI->>Backend: POST /api/billing/cancel
Backend-->>BillingAPI: {ok, at_period_end:false}
BillingAPI-->>UI: response
UI->>BillingAPI: getSubscription()
end
end
Greptile OverviewGreptile SummaryThis PR redesigns the Organization Settings → Billing tab UI and adds in-app flows to cancel a subscription (at period end vs immediately) and change plan/tier (monthly vs yearly), backed by new billing client endpoints ( The main integration point is Issues to fix before merge are localized to the billing UI: the invoices list currently contains invalid JSX around Confidence Score: 3/5
|
| Filename | Overview |
|---|---|
| components/settings/OrganizationSettings.tsx | Adds cancel/change-plan UI and modals; introduces a JSX render bug in the invoices list and a loading-state bug that can leave the members section stuck loading when org_id hydrates late. |
| lib/api/billing.ts | Extends billing client with cancelSubscription and changePlan endpoints and adds cancel_at_period_end to SubscriptionDetails; changes are straightforward. |
| lib/plans.ts | Introduces shared Solo plan traffic tiers and helpers for tier<->limit mapping; simple constants/utilities. |
Sequence Diagram
sequenceDiagram
participant U as User
participant OS as OrganizationSettings (Billing tab)
participant BA as lib/api/billing.ts
participant API as Backend /api/billing
participant Stripe as Stripe
U->>OS: Open Billing tab
OS->>BA: getSubscription()
BA->>API: GET /subscription
API-->>BA: SubscriptionDetails
BA-->>OS: SubscriptionDetails
OS->>BA: getInvoices()
BA->>API: GET /invoices
API-->>BA: Invoice[]
BA-->>OS: Invoice[]
U->>OS: Click "Payment method & invoices"
OS->>BA: createPortalSession()
BA->>API: POST /portal
API-->>BA: {url}
BA-->>OS: {url}
OS->>Stripe: Redirect to Stripe portal
U->>OS: Click "Cancel subscription"
OS->>BA: cancelSubscription({at_period_end})
BA->>API: POST /cancel
API-->>BA: {ok, at_period_end}
BA-->>OS: {ok, at_period_end}
OS->>BA: getSubscription() (refresh)
U->>OS: Click "Change plan" and submit
alt Existing subscription (active/trialing)
OS->>BA: changePlan({plan_id, interval, limit})
BA->>API: POST /change-plan
API-->>BA: {ok}
BA-->>OS: {ok}
OS->>BA: getSubscription() (refresh)
else No active subscription
OS->>BA: createCheckoutSession({plan_id, interval, limit})
BA->>API: POST /checkout
API-->>BA: {url}
BA-->>OS: {url}
OS->>Stripe: Redirect to Stripe Checkout
end
Additional Comments (1)
Prompt To Fix With AIThis is a comment left during a code review.
Path: components/settings/OrganizationSettings.tsx
Line: 62:67
Comment:
**Loading state never clears**
`loadMembers` returns early when `currentOrgId` is falsy, but `isLoadingMembers` only gets set to `false` in the `finally` of the try/catch. If `currentOrgId` is temporarily null (e.g., auth context hydrates after first render), `isLoadingMembers` will stay stuck `true` because the effect’s `else` branch only runs when `currentOrgId` changes to falsy; it won’t run again when it becomes truthy. Consider ensuring the early-return path also clears the loading flag.
How can I resolve this? If you propose a fix, please make it concise. |
Greptile OverviewGreptile SummaryBilling UI redesign adds trial banner, cancel-at-period-end notice, modernized plan/usage card, and in-app Change plan and Cancel subscription flows. Copy clearly communicates trial auto-charge, access end dates, and 30-day data retention. Key changes:
Minor issues found:
Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| lib/plans.ts | New file defining traffic tiers and plan constants. Clean, well-documented utility functions with proper fallbacks. |
| lib/api/billing.ts | Added cancel and change-plan API functions. Type-safe with proper JSDoc, follows existing patterns. |
| components/settings/OrganizationSettings.tsx | Redesigned billing UI with cancel/change-plan modals. Previous issues addressed, but modal state could be simplified. |
Sequence Diagram
sequenceDiagram
participant User
participant UI as OrganizationSettings
participant API as Billing API
participant Stripe
Note over User,Stripe: Change Plan Flow
User->>UI: Click "Change plan"
UI->>UI: openChangePlanModal()<br/>(load current tier)
UI->>User: Show modal with tier dropdown
User->>UI: Select tier & interval
User->>UI: Click "Update plan"/"Start subscription"
alt Has Active Subscription (trial/active)
UI->>API: changePlan({plan_id, interval, limit})
API->>Stripe: Update subscription
Stripe-->>API: Updated subscription
API-->>UI: {ok: true}
UI->>UI: loadSubscription()
UI->>User: Toast: "Plan updated"
else No Active Subscription
UI->>API: createCheckoutSession({plan_id, interval, limit})
API->>Stripe: Create checkout session
Stripe-->>API: Session URL
API-->>UI: {url}
UI->>User: Redirect to Stripe checkout
end
Note over User,Stripe: Cancel Subscription Flow
User->>UI: Click "Cancel subscription"
UI->>User: Show cancel modal
User->>UI: Choose "at period end" or "immediately"
UI->>API: cancelSubscription({at_period_end})
API->>Stripe: Cancel subscription
Stripe-->>API: Canceled subscription
API-->>UI: {ok: true, at_period_end}
UI->>UI: loadSubscription()
UI->>User: Show cancel notice banner
…zationSettings component
Greptile OverviewGreptile SummaryThis PR redesigns the billing tab with trial banners, cancel-at-period-end notices, and in-app change-plan and cancel-subscription flows. It adds API client functions for Key Changes:
Critical Issue:
Notes:
Confidence Score: 2/5
|
| Filename | Overview |
|---|---|
| components/settings/OrganizationSettings.tsx | Billing tab redesign with cancel/change-plan modals; JSX syntax error in invoices rendering will prevent compilation |
| lib/api/billing.ts | Added cancelSubscription, changePlan, and createCheckoutSession API client functions with proper TypeScript types |
| lib/plans.ts | New shared plan definitions file with traffic tiers and helper functions for tier/limit conversions |
Sequence Diagram
sequenceDiagram
participant User
participant UI as OrganizationSettings
participant API as Billing API
participant Stripe
Note over User,Stripe: Change Plan Flow
User->>UI: Click "Change plan" button
UI->>UI: openChangePlanModal()
UI->>UI: Load current tier from subscription
UI->>User: Show modal with tier selector
User->>UI: Select tier & interval
User->>UI: Click "Update plan" / "Start subscription"
alt Has Active Subscription
UI->>API: changePlan(plan_id, interval, limit)
API->>Stripe: Update subscription with proration
Stripe-->>API: Success
API-->>UI: {ok: true}
UI->>UI: loadSubscription()
UI->>User: Toast: "Plan updated"
else No Active Subscription
UI->>API: createCheckoutSession(plan_id, interval, limit)
API->>Stripe: Create checkout session
Stripe-->>API: {url}
API-->>UI: {url}
UI->>User: Redirect to Stripe checkout
end
Note over User,Stripe: Cancel Subscription Flow
User->>UI: Click "Cancel subscription"
UI->>User: Show cancel modal with options
alt Cancel at Period End
User->>UI: Click "Cancel at period end"
UI->>API: cancelSubscription({at_period_end: true})
API->>Stripe: Set cancel_at_period_end
Stripe-->>API: Success
API-->>UI: {ok: true, at_period_end: true}
UI->>UI: loadSubscription()
UI->>User: Toast: "Subscription will cancel at end of billing period"
UI->>User: Show cancellation notice banner
else Cancel Immediately
User->>UI: Click "Cancel immediately"
UI->>API: cancelSubscription({at_period_end: false})
API->>Stripe: Cancel subscription now
Stripe-->>API: Success
API-->>UI: {ok: true, at_period_end: false}
UI->>UI: loadSubscription()
UI->>User: Toast: "Subscription canceled"
end
…rganizationSettings component
Greptile OverviewGreptile SummaryThis PR implements a comprehensive billing UI redesign with in-app subscription management. The changes include:
The implementation correctly handles the subscription status logic after addressing previous threading feedback. All previous issues around subscription gating, loading states, and invoice rendering have been resolved. Confidence Score: 4/5
|
| Filename | Overview |
|---|---|
| components/settings/OrganizationSettings.tsx | Major UI redesign for billing tab with trial banners, cancel/change plan modals, and usage display; previous threading issues addressed |
| lib/api/billing.ts | Clean API client additions for cancelSubscription and changePlan endpoints with proper typing |
| lib/plans.ts | New shared constants and utilities for plan tiers with sensible defaults |
Sequence Diagram
sequenceDiagram
participant User
participant UI as OrganizationSettings
participant API as Billing API
participant Stripe
Note over User,Stripe: Change Plan Flow
User->>UI: Click "Change plan"
UI->>UI: openChangePlanModal()
UI->>UI: Set tier from current limit
User->>UI: Select tier & interval
User->>UI: Click "Update plan"
alt hasActiveSubscription
UI->>API: changePlan(plan_id, interval, limit)
API->>Stripe: Update subscription
Stripe-->>API: Success
API-->>UI: { ok: true }
UI->>API: getSubscription()
API-->>UI: Updated subscription
else No active subscription
UI->>API: createCheckoutSession(...)
API->>Stripe: Create checkout session
Stripe-->>API: { url }
API-->>UI: Checkout URL
UI->>User: Redirect to Stripe checkout
end
Note over User,Stripe: Cancel Subscription Flow
User->>UI: Click "Cancel subscription"
UI->>UI: Show cancel modal
User->>UI: Choose cancel option
alt Cancel at period end
UI->>API: cancelSubscription({ at_period_end: true })
API->>Stripe: Set cancel_at_period_end
Stripe-->>API: Success
API-->>UI: { ok: true, at_period_end: true }
else Cancel immediately
UI->>API: cancelSubscription({ at_period_end: false })
API->>Stripe: Cancel immediately
Stripe-->>API: Success
API-->>UI: { ok: true, at_period_end: false }
end
UI->>API: getSubscription()
API-->>UI: Updated subscription with cancel_at_period_end
UI->>User: Show cancellation banner
Work Item
PULSE-35
Summary
Changes
cancelSubscription,changePlan;SubscriptionDetailsincludescancel_at_period_end.lib/plans.ts—TRAFFIC_TIERS,PLAN_ID_SOLO, tier index/limit helpers for pricing and billing.Test Plan