[PULSE-10] Billing usage in dashboard – frontend #9

Merged
uz1mani merged 3 commits from staging into main 2026-02-05 11:06:33 +00:00
uz1mani commented 2026-02-05 10:46:37 +00:00 (Migrated from github.com)

Work Item

PULSE-10

Summary

  • Show current plan and usage (sites, pageviews when limited) in Org Settings → Billing and on the dashboard home.
  • Add Upgrade / Manage billing links that go to existing pricing and org-settings billing; support deep link to Billing tab.

Changes

  • lib/api/billing.ts: Extended SubscriptionDetails with optional sites_count and pageview_usage (for backward compatibility).
  • components/settings/OrganizationSettings.tsx: Use useSearchParams to sync activeTab from ?tab=billing or ?tab=members (initial state + useEffect). Billing tab: in the Current Plan card, added Sites (e.g. 3 or 1/1 for Solo) and Pageviews this period (e.g. X / Y when pageview_limit > 0); grid expanded to five columns (Sites, Pageviews this period, Billing Interval, Pageview Limit, Renews On).
  • app/page.tsx: Renamed “Plan Status” card to “Plan & usage”; shows plan name plus usage line (Sites and Pageviews when available); added Upgrade (→ /pricing) or Manage billing (→ /org-settings?tab=billing) depending on has_payment_method.
  • app/org-settings/page.tsx: Wrapped <OrganizationSettings /> in <Suspense fallback={...}> so useSearchParams() is used inside a Suspense boundary (fixes Next.js prerender for /org-settings).

Test Plan

  • Logged-in user sees plan and usage (sites, pageviews when limited) on dashboard home and in Org Settings → Billing; numbers match backend.
  • “Upgrade” links to /pricing; “Manage billing” links to /org-settings?tab=billing and opens the Billing tab.
  • npm run build passes (including /org-settings with Suspense).
  • Free org: sites count shown; pageview usage “—” when no limit. Solo: sites as “X/1”. Paid: pageviews as “X / Y” when limit > 0.
## Work Item PULSE-10 ## Summary - Show current plan and usage (sites, pageviews when limited) in Org Settings → Billing and on the dashboard home. - Add Upgrade / Manage billing links that go to existing pricing and org-settings billing; support deep link to Billing tab. ## Changes - **lib/api/billing.ts**: Extended `SubscriptionDetails` with optional `sites_count` and `pageview_usage` (for backward compatibility). - **components/settings/OrganizationSettings.tsx**: Use `useSearchParams` to sync `activeTab` from `?tab=billing` or `?tab=members` (initial state + `useEffect`). Billing tab: in the Current Plan card, added **Sites** (e.g. `3` or `1/1` for Solo) and **Pageviews this period** (e.g. `X / Y` when `pageview_limit > 0`); grid expanded to five columns (Sites, Pageviews this period, Billing Interval, Pageview Limit, Renews On). - **app/page.tsx**: Renamed “Plan Status” card to “Plan & usage”; shows plan name plus usage line (Sites and Pageviews when available); added **Upgrade** (→ `/pricing`) or **Manage billing** (→ `/org-settings?tab=billing`) depending on `has_payment_method`. - **app/org-settings/page.tsx**: Wrapped `<OrganizationSettings />` in `<Suspense fallback={...}>` so `useSearchParams()` is used inside a Suspense boundary (fixes Next.js prerender for `/org-settings`). ## Test Plan - [x] Logged-in user sees plan and usage (sites, pageviews when limited) on dashboard home and in Org Settings → Billing; numbers match backend. - [x] “Upgrade” links to `/pricing`; “Manage billing” links to `/org-settings?tab=billing` and opens the Billing tab. - [x] `npm run build` passes (including `/org-settings` with Suspense). - [x] Free org: sites count shown; pageview usage “—” when no limit. Solo: sites as “X/1”. Paid: pageviews as “X / Y” when limit > 0.
greptile-apps[bot] commented 2026-02-05 10:48:58 +00:00 (Migrated from github.com)

Greptile Overview

Greptile Summary

This PR adds billing usage information (sites count and pageviews) to both the dashboard home page and Organization Settings → Billing tab. The changes properly handle optional fields for backward compatibility and implement deep linking to the Billing tab via ?tab=billing query parameter.

Key Changes:

  • Extended SubscriptionDetails API interface with optional sites_count and pageview_usage fields
  • Dashboard "Plan Status" renamed to "Plan & usage" with usage metrics and action links (Upgrade/Manage billing)
  • Organization Settings Billing tab displays Sites and Pageviews in a five-column grid layout
  • Solo plan shows "X / 1" format for sites when count > 0, otherwise shows plain count
  • Pageview usage only displayed when pageview_limit > 0 (shows "Unlimited" otherwise)
  • Wrapped OrganizationSettings in Suspense boundary to fix Next.js prerender with useSearchParams()
  • Fixed useEffect infinite loop with proper guard condition

Implementation Quality:

  • Backward compatibility maintained with optional fields
  • Proper null/undefined checks with typeof guards
  • Consistent formatting across dashboard and settings
  • Deep linking properly syncs URL params with component state
  • Previous review feedback addressed (useEffect guard, Solo 0 sites display)

Confidence Score: 5/5

  • This PR is safe to merge with no critical issues found
  • All changes are well-implemented with proper type safety, backward compatibility, null checks, and follow React best practices. Previous feedback has been addressed. The Suspense boundary fix ensures Next.js SSR compatibility, and the display logic correctly handles edge cases (Solo 0 sites, unlimited pageviews).
  • No files require special attention

Important Files Changed

Filename Overview
lib/api/billing.ts Added optional sites_count and pageview_usage fields to SubscriptionDetails interface for backward compatibility
app/org-settings/page.tsx Wrapped OrganizationSettings in Suspense boundary to support useSearchParams() during Next.js SSR prerendering
components/settings/OrganizationSettings.tsx Added useSearchParams for tab deep linking, displayed Sites and Pageviews usage in billing section with proper Solo/Free formatting
app/page.tsx Updated Plan Status card to show usage (sites, pageviews) and added Upgrade/Manage billing links with proper routing

Sequence Diagram

sequenceDiagram
    participant User
    participant Dashboard as Dashboard (page.tsx)
    participant OrgSettings as Org Settings (OrganizationSettings.tsx)
    participant API as Billing API (billing.ts)
    participant Backend as Backend Server

    Note over User,Backend: User views dashboard
    User->>Dashboard: Navigate to /
    Dashboard->>API: getSubscription()
    API->>Backend: GET /api/billing/subscription
    Backend-->>API: SubscriptionDetails (with sites_count, pageview_usage)
    API-->>Dashboard: subscription data
    Dashboard->>Dashboard: Display plan name, sites count, pageviews
    Dashboard->>Dashboard: Show "Upgrade" or "Manage billing" link

    Note over User,Backend: User navigates to billing settings
    User->>OrgSettings: Navigate to /org-settings?tab=billing
    OrgSettings->>OrgSettings: useSearchParams() reads ?tab=billing
    OrgSettings->>OrgSettings: setActiveTab('billing')
    OrgSettings->>API: getSubscription()
    API->>Backend: GET /api/billing/subscription
    Backend-->>API: SubscriptionDetails (with sites_count, pageview_usage)
    API-->>OrgSettings: subscription data
    OrgSettings->>OrgSettings: Display Sites (X or X/1 for Solo)
    OrgSettings->>OrgSettings: Display Pageviews this period (X/Y when limit > 0)
    OrgSettings->>OrgSettings: Display "Unlimited" when pageview_limit = 0

    Note over User,Backend: User clicks "Manage billing"
    User->>Dashboard: Click "Manage billing" link
    Dashboard->>OrgSettings: Navigate to /org-settings?tab=billing
    OrgSettings->>OrgSettings: Suspense boundary renders
    OrgSettings->>OrgSettings: useSearchParams() sync activeTab
    OrgSettings->>OrgSettings: Display Billing tab
<h2>Greptile Overview</h2> <h3>Greptile Summary</h3> This PR adds billing usage information (sites count and pageviews) to both the dashboard home page and Organization Settings → Billing tab. The changes properly handle optional fields for backward compatibility and implement deep linking to the Billing tab via `?tab=billing` query parameter. **Key Changes:** - Extended `SubscriptionDetails` API interface with optional `sites_count` and `pageview_usage` fields - Dashboard "Plan Status" renamed to "Plan & usage" with usage metrics and action links (Upgrade/Manage billing) - Organization Settings Billing tab displays Sites and Pageviews in a five-column grid layout - Solo plan shows "X / 1" format for sites when count > 0, otherwise shows plain count - Pageview usage only displayed when `pageview_limit > 0` (shows "Unlimited" otherwise) - Wrapped `OrganizationSettings` in Suspense boundary to fix Next.js prerender with `useSearchParams()` - Fixed `useEffect` infinite loop with proper guard condition **Implementation Quality:** - Backward compatibility maintained with optional fields - Proper null/undefined checks with `typeof` guards - Consistent formatting across dashboard and settings - Deep linking properly syncs URL params with component state - Previous review feedback addressed (useEffect guard, Solo 0 sites display) <h3>Confidence Score: 5/5</h3> - This PR is safe to merge with no critical issues found - All changes are well-implemented with proper type safety, backward compatibility, null checks, and follow React best practices. Previous feedback has been addressed. The Suspense boundary fix ensures Next.js SSR compatibility, and the display logic correctly handles edge cases (Solo 0 sites, unlimited pageviews). - No files require special attention <details><summary><h3>Important Files Changed</h3></summary> | Filename | Overview | |----------|----------| | lib/api/billing.ts | Added optional sites_count and pageview_usage fields to SubscriptionDetails interface for backward compatibility | | app/org-settings/page.tsx | Wrapped OrganizationSettings in Suspense boundary to support useSearchParams() during Next.js SSR prerendering | | components/settings/OrganizationSettings.tsx | Added useSearchParams for tab deep linking, displayed Sites and Pageviews usage in billing section with proper Solo/Free formatting | | app/page.tsx | Updated Plan Status card to show usage (sites, pageviews) and added Upgrade/Manage billing links with proper routing | </details> </details> <h3>Sequence Diagram</h3> ```mermaid sequenceDiagram participant User participant Dashboard as Dashboard (page.tsx) participant OrgSettings as Org Settings (OrganizationSettings.tsx) participant API as Billing API (billing.ts) participant Backend as Backend Server Note over User,Backend: User views dashboard User->>Dashboard: Navigate to / Dashboard->>API: getSubscription() API->>Backend: GET /api/billing/subscription Backend-->>API: SubscriptionDetails (with sites_count, pageview_usage) API-->>Dashboard: subscription data Dashboard->>Dashboard: Display plan name, sites count, pageviews Dashboard->>Dashboard: Show "Upgrade" or "Manage billing" link Note over User,Backend: User navigates to billing settings User->>OrgSettings: Navigate to /org-settings?tab=billing OrgSettings->>OrgSettings: useSearchParams() reads ?tab=billing OrgSettings->>OrgSettings: setActiveTab('billing') OrgSettings->>API: getSubscription() API->>Backend: GET /api/billing/subscription Backend-->>API: SubscriptionDetails (with sites_count, pageview_usage) API-->>OrgSettings: subscription data OrgSettings->>OrgSettings: Display Sites (X or X/1 for Solo) OrgSettings->>OrgSettings: Display Pageviews this period (X/Y when limit > 0) OrgSettings->>OrgSettings: Display "Unlimited" when pageview_limit = 0 Note over User,Backend: User clicks "Manage billing" User->>Dashboard: Click "Manage billing" link Dashboard->>OrgSettings: Navigate to /org-settings?tab=billing OrgSettings->>OrgSettings: Suspense boundary renders OrgSettings->>OrgSettings: useSearchParams() sync activeTab OrgSettings->>OrgSettings: Display Billing tab ```
greptile-apps[bot] (Migrated from github.com) reviewed 2026-02-05 10:49:03 +00:00
greptile-apps[bot] (Migrated from github.com) left a comment

4 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

<sub>4 files reviewed, 2 comments</sub> <sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews)</sub>
greptile-apps[bot] (Migrated from github.com) commented 2026-02-05 10:49:02 +00:00

For a Solo plan with 0 sites, this displays "Sites: 0/1". For consistency with free plans and better UX, showing just "0" might be clearer when there are no sites yet.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: app/page.tsx
Line: 305:314

Comment:
For a Solo plan with 0 sites, this displays "Sites: 0/1". For consistency with free plans and better UX, showing just "0" might be clearer when there are no sites yet.

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.
For a Solo plan with 0 sites, this displays "Sites: 0/1". For consistency with free plans and better UX, showing just "0" might be clearer when there are no sites yet. <sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub> <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: app/page.tsx Line: 305:314 Comment: For a Solo plan with 0 sites, this displays "Sites: 0/1". For consistency with free plans and better UX, showing just "0" might be clearer when there are no sites yet. <sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub> How can I resolve this? If you propose a fix, please make it concise. ````` </details>
@@ -135,6 +139,13 @@ export default function OrganizationSettings() {
}
greptile-apps[bot] (Migrated from github.com) commented 2026-02-05 10:49:01 +00:00

This useEffect can cause infinite re-renders if the URL updates programmatically. When setActiveTab is called, if other code updates the URL, it will trigger this effect again. Consider adding a guard or removing this effect since initial state is already set from searchParams in the useState initializer.

Prompt To Fix With AI
This is a comment left during a code review.
Path: components/settings/OrganizationSettings.tsx
Line: 142:147

Comment:
This `useEffect` can cause infinite re-renders if the URL updates programmatically. When `setActiveTab` is called, if other code updates the URL, it will trigger this effect again. Consider adding a guard or removing this effect since initial state is already set from `searchParams` in the `useState` initializer.

How can I resolve this? If you propose a fix, please make it concise.
This `useEffect` can cause infinite re-renders if the URL updates programmatically. When `setActiveTab` is called, if other code updates the URL, it will trigger this effect again. Consider adding a guard or removing this effect since initial state is already set from `searchParams` in the `useState` initializer. <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: components/settings/OrganizationSettings.tsx Line: 142:147 Comment: This `useEffect` can cause infinite re-renders if the URL updates programmatically. When `setActiveTab` is called, if other code updates the URL, it will trigger this effect again. Consider adding a guard or removing this effect since initial state is already set from `searchParams` in the `useState` initializer. How can I resolve this? If you propose a fix, please make it concise. ````` </details>
uz1mani (Migrated from github.com) reviewed 2026-02-05 10:55:33 +00:00
uz1mani (Migrated from github.com) commented 2026-02-05 10:55:33 +00:00

Change: For Solo, show the “X/1” format only when there is at least one site; when there are 0 sites, show just “0” (same style as free plans).
How: Replaced
subscription.plan_id === 'solo' ? ${subscription.sites_count}/1\ : subscription.sites_count
with
subscription.plan_id === 'solo' && subscription.sites_count > 0 ? ${subscription.sites_count}/1\ : subscription.sites_count.
Result: Solo with 0 sites now shows “Sites: 0” instead of “Sites: 0/1”; Solo with 1+ sites still shows “Sites: 1/1”.

Change: For Solo, show the “X/1” format only when there is at least one site; when there are 0 sites, show just “0” (same style as free plans). How: Replaced subscription.plan_id === 'solo' ? \${subscription.sites_count}/1\ : subscription.sites_count with subscription.plan_id === 'solo' && subscription.sites_count > 0 ? \${subscription.sites_count}/1\ : subscription.sites_count. Result: Solo with 0 sites now shows “Sites: 0” instead of “Sites: 0/1”; Solo with 1+ sites still shows “Sites: 1/1”.
uz1mani (Migrated from github.com) reviewed 2026-02-05 10:55:41 +00:00
@@ -135,6 +139,13 @@ export default function OrganizationSettings() {
}
uz1mani (Migrated from github.com) commented 2026-02-05 10:55:41 +00:00

Change: Only call setActiveTab(tab) when the tab from the URL is different from the current activeTab.
How: Added && tab !== activeTab so we don’t set state when the URL and state already match, and added activeTab to the effect dependency array so the guard is correct.
Result: The effect no longer triggers repeated updates when the URL is updated programmatically; it only syncs when the URL tab actually changes (e.g. client-side navigation to ?tab=billing).

Change: Only call setActiveTab(tab) when the tab from the URL is different from the current activeTab. How: Added && tab !== activeTab so we don’t set state when the URL and state already match, and added activeTab to the effect dependency array so the guard is correct. Result: The effect no longer triggers repeated updates when the URL is updated programmatically; it only syncs when the URL tab actually changes (e.g. client-side navigation to ?tab=billing).
greptile-apps[bot] (Migrated from github.com) reviewed 2026-02-05 10:57:50 +00:00
greptile-apps[bot] (Migrated from github.com) left a comment

4 files reviewed, 1 comment

Edit Code Review Agent Settings | Greptile

<sub>4 files reviewed, 1 comment</sub> <sub>[Edit Code Review Agent Settings](https://app.greptile.com/review/github) | [Greptile](https://greptile.com?utm_source=greptile_expert&utm_medium=github&utm_campaign=code_reviews)</sub>
greptile-apps[bot] commented 2026-02-05 10:57:51 +00:00 (Migrated from github.com)
Additional Comments (1)

components/settings/OrganizationSettings.tsx
For free plans with pageview_limit: 0, this displays "0 / month" which is inconsistent with the conditional rendering used in the "Pageviews this period" field above (line 621) which shows "—" when limit is 0.

                            {subscription.pageview_limit > 0 ? `${subscription.pageview_limit.toLocaleString()} / month` : 'Unlimited'}
Prompt To Fix With AI
This is a comment left during a code review.
Path: components/settings/OrganizationSettings.tsx
Line: 635:635

Comment:
For free plans with `pageview_limit: 0`, this displays "0 / month" which is inconsistent with the conditional rendering used in the "Pageviews this period" field above (line 621) which shows "—" when limit is 0.

```suggestion
                            {subscription.pageview_limit > 0 ? `${subscription.pageview_limit.toLocaleString()} / month` : 'Unlimited'}
```

How can I resolve this? If you propose a fix, please make it concise.
<details> <summary>Additional Comments (1)</summary> **`components/settings/OrganizationSettings.tsx`** For free plans with `pageview_limit: 0`, this displays "0 / month" which is inconsistent with the conditional rendering used in the "Pageviews this period" field above (line 621) which shows "—" when limit is 0. ```suggestion {subscription.pageview_limit > 0 ? `${subscription.pageview_limit.toLocaleString()} / month` : 'Unlimited'} ``` <details><summary>Prompt To Fix With AI</summary> `````markdown This is a comment left during a code review. Path: components/settings/OrganizationSettings.tsx Line: 635:635 Comment: For free plans with `pageview_limit: 0`, this displays "0 / month" which is inconsistent with the conditional rendering used in the "Pageviews this period" field above (line 621) which shows "—" when limit is 0. ```suggestion {subscription.pageview_limit > 0 ? `${subscription.pageview_limit.toLocaleString()} / month` : 'Unlimited'} ``` How can I resolve this? If you propose a fix, please make it concise. ````` </details> </details>
Sign in to join this conversation.
No description provided.