From 47225405a9be29191488bc552ec4336e17033050 Mon Sep 17 00:00:00 2001 From: naaiyy Date: Sat, 22 Aug 2026 03:10:50 +0200 Subject: [PATCH] Refactor billing around workspace members --- CONTEXT.md | 25 +++ .../dashboard/billing/billing-overview.tsx | 27 +-- .../dashboard/billing/billing-plan-cards.tsx | 3 +- .../dashboard/billing/billing-plans-page.tsx | 10 +- .../features/dashboard/billing/model.test.ts | 32 ++- apps/web/features/dashboard/billing/model.ts | 12 +- .../content-pages/content/docs/index.fr.mdx | 3 + .../content-pages/content/docs/index.mdx | 3 + .../content-pages/content/docs/meta.fr.json | 2 + .../content-pages/content/docs/meta.json | 2 + .../content/docs/plans-and-billing.fr.mdx | 70 +++++++ .../content/docs/plans-and-billing.mdx | 70 +++++++ .../marketing/content-pages/source.ts | 2 + packages/backend/convex/_generated/api.d.ts | 2 + .../backend/convex/_generated/server.d.ts | 8 +- .../convex/billing-webhook-model.test.ts | 3 +- packages/backend/convex/billing.ts | 136 +++++++------ .../backend/convex/billingMigrations.test.ts | 87 +++++++++ packages/backend/convex/billingMigrations.ts | 182 ++++++++++++++++++ packages/backend/convex/billingModel.ts | 33 ++-- .../backend/convex/billing_webhook_model.ts | 12 -- packages/backend/convex/schema.ts | 6 +- packages/backend/convex/schema/billing.ts | 9 +- .../backend/convex/workspaceBilling.test.ts | 21 +- packages/backend/convex/workspaceBilling.ts | 18 +- packages/i18n/src/messages/en.json | 20 +- packages/i18n/src/messages/fr.json | 20 +- 27 files changed, 614 insertions(+), 204 deletions(-) create mode 100644 CONTEXT.md create mode 100644 apps/web/features/marketing/content-pages/content/docs/plans-and-billing.fr.mdx create mode 100644 apps/web/features/marketing/content-pages/content/docs/plans-and-billing.mdx create mode 100644 packages/backend/convex/billingMigrations.test.ts create mode 100644 packages/backend/convex/billingMigrations.ts diff --git a/CONTEXT.md b/CONTEXT.md new file mode 100644 index 00000000..42a08ef1 --- /dev/null +++ b/CONTEXT.md @@ -0,0 +1,25 @@ +# BaseBlocks context + +This context defines the product terms that cross the workspace and billing domains. + +## Workspace and billing language + +**Workspace member**: +An accepted member of a workspace. Workspace members are counted from the authentication membership records. +_Avoid_: Active member when describing membership; active means recent product activity. + +**Subscription**: +A recurring relationship between a workspace and a billing provider. +_Avoid_: Plan when referring to the provider relationship. + +**Plan**: +The product access level available to a workspace, such as Free or Plus. +_Avoid_: Seat when referring to product access. + +**Paid seat quantity**: +The quantity sent to the billing provider for a Plus subscription. It is an internal billing value and is not a workspace-membership count. +_Avoid_: Active members, billable members, or seats in normal user-facing copy. + +**AI credits**: +A workspace-level balance that funds AI usage. Included credits and prepaid credits have separate expiration rules. +_Avoid_: AI seats. diff --git a/apps/web/features/dashboard/billing/billing-overview.tsx b/apps/web/features/dashboard/billing/billing-overview.tsx index df1ae8be..9b44d0f5 100644 --- a/apps/web/features/dashboard/billing/billing-overview.tsx +++ b/apps/web/features/dashboard/billing/billing-overview.tsx @@ -26,7 +26,6 @@ import { } from "@/features/dashboard/layout/dashboard-page"; import { canUsePaidFeatures, - getAdditionalSeatCount, getBillingCallout, type BillingCallout, type WorkspaceBillingEntitlement, @@ -126,9 +125,9 @@ function BillingSupportDialog() { {t("description")}{" "} - support@easylink.com + support@easylink.one . @@ -151,7 +150,6 @@ export function BillingOverview({ const t = useTranslations("billing"); const locale = useLocale(); const isPlus = canUsePaidFeatures(entitlement); - const additionalSeats = getAdditionalSeatCount(entitlement); const callout = getBillingCallout(entitlement); const [selectedPlusSku, setSelectedPlusSku] = useState( plusOptions.find((option) => option.recurringInterval === "year")?.sku ?? @@ -189,13 +187,8 @@ export function BillingOverview({

{t("current.memberCount", { - count: entitlement.billableSeatCount, + count: entitlement.workspaceMemberCount, })} - {isPlus - ? ` · ${t("current.paidSeatCount", { - count: entitlement.paidSeatCapacity, - })}` - : null}

{effectiveThrough ? (

@@ -252,21 +245,11 @@ export function BillingOverview({

{t("current.memberCount", { - count: entitlement.billableSeatCount, + count: entitlement.workspaceMemberCount, })} - {isPlus - ? ` · ${t("current.paidSeatCount", { - count: entitlement.paidSeatCapacity, - })}` - : null}

- {additionalSeats > 0 || effectiveThrough ? ( + {effectiveThrough ? (
- {additionalSeats > 0 ? ( -

- {t("current.additionalSeats", { count: additionalSeats })} -

- ) : null} {effectiveThrough ? (

{t("current.effectiveThrough", { date: effectiveThrough })} diff --git a/apps/web/features/dashboard/billing/billing-plan-cards.tsx b/apps/web/features/dashboard/billing/billing-plan-cards.tsx index 9f8d7bd2..0d3cc11b 100644 --- a/apps/web/features/dashboard/billing/billing-plan-cards.tsx +++ b/apps/web/features/dashboard/billing/billing-plan-cards.tsx @@ -157,10 +157,9 @@ export function BillingPlanCards({ )}

-