Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions CONTEXT.md
Original file line number Diff line number Diff line change
@@ -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.
27 changes: 5 additions & 22 deletions apps/web/features/dashboard/billing/billing-overview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import {
} from "@/features/dashboard/layout/dashboard-page";
import {
canUsePaidFeatures,
getAdditionalSeatCount,
getBillingCallout,
type BillingCallout,
type WorkspaceBillingEntitlement,
Expand Down Expand Up @@ -126,9 +125,9 @@ function BillingSupportDialog() {
{t("description")}{" "}
<a
className="font-medium text-sidebar-foreground underline decoration-sidebar-foreground/30 underline-offset-2 transition-colors hover:decoration-sidebar-foreground focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
href="mailto:support@easylink.com"
href="mailto:support@easylink.one"
>
support@easylink.com
support@easylink.one
</a>
.
</DialogDescription>
Expand All @@ -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 ??
Expand Down Expand Up @@ -189,13 +187,8 @@ export function BillingOverview({
</p>
<p className="mt-1 text-xs text-muted-foreground">
{t("current.memberCount", {
count: entitlement.billableSeatCount,
count: entitlement.workspaceMemberCount,
})}
{isPlus
? ` · ${t("current.paidSeatCount", {
count: entitlement.paidSeatCapacity,
})}`
: null}
</p>
{effectiveThrough ? (
<p className="mt-1 text-xs text-muted-foreground">
Expand Down Expand Up @@ -252,21 +245,11 @@ export function BillingOverview({
</p>
<p className="mt-1 text-xs text-muted-foreground">
{t("current.memberCount", {
count: entitlement.billableSeatCount,
count: entitlement.workspaceMemberCount,
})}
{isPlus
? ` · ${t("current.paidSeatCount", {
count: entitlement.paidSeatCapacity,
})}`
: null}
</p>
{additionalSeats > 0 || effectiveThrough ? (
{effectiveThrough ? (
<div className="mt-1 space-y-0.5 text-xs text-muted-foreground">
{additionalSeats > 0 ? (
<p>
{t("current.additionalSeats", { count: additionalSeats })}
</p>
) : null}
{effectiveThrough ? (
<p>
{t("current.effectiveThrough", { date: effectiveThrough })}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,10 +157,9 @@ export function BillingPlanCards({
)}
</div>
</div>
<ul className="mt-4 grid gap-2 sm:grid-cols-3">
<ul className="mt-4 grid gap-2 sm:grid-cols-2">
<PlanFeature>{t("plans.plus.features.everything")}</PlanFeature>
<PlanFeature>{t("plans.plus.features.ai")}</PlanFeature>
<PlanFeature>{t("plans.plus.features.seats")}</PlanFeature>
</ul>
</div>
) : null}
Expand Down
10 changes: 1 addition & 9 deletions apps/web/features/dashboard/billing/billing-plans-page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -80,12 +80,7 @@ export function BillingPlansPage({
{
feature: t("plans.comparison.ai"),
free: t("plans.comparison.prepaid"),
plus: t("plans.comparison.monthly"),
},
{
feature: t("plans.comparison.seats"),
free: t("plans.comparison.notIncluded"),
plus: <Included />,
plus: t("plans.comparison.includedAndPrepaid"),
},
],
},
Expand Down Expand Up @@ -150,9 +145,6 @@ export function BillingPlansPage({
<p className="mt-1 text-sm text-foreground">
<span className="font-semibold">
{t("plans.free.price")}
</span>{" "}
<span className="text-xs text-muted-foreground">
{t("plans.perMemberMonth")}
</span>
</p>
</div>
Expand Down
32 changes: 13 additions & 19 deletions apps/web/features/dashboard/billing/model.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ import { describe, expect, test } from "bun:test";
import {
canRequestAi,
canUsePaidFeatures,
getAdditionalSeatCount,
getBillingCallout,
type WorkspaceBillingEntitlement,
} from "./model";

const plusEntitlement: WorkspaceBillingEntitlement = {
plan: "plus",
subscriptionState: "entitled",
billableSeatCount: 3,
paidSeatCapacity: 3,
workspaceMemberCount: 3,
plusEnabled: true,
aiAdmissionAvailable: true,
availableAiCreditUnits: 500_000n,
Expand Down Expand Up @@ -46,22 +44,18 @@ describe("billing presentation model", () => {
).toBe(false);
});

test("reports seats beyond paid capacity without negative counts", () => {
expect(getAdditionalSeatCount(plusEntitlement)).toBe(0);
expect(
getAdditionalSeatCount({
...plusEntitlement,
billableSeatCount: 5,
paidSeatCapacity: 3,
}),
).toBe(2);
expect(
getAdditionalSeatCount({
...plusEntitlement,
billableSeatCount: 1,
paidSeatCapacity: 3,
}),
).toBe(0);
test("uses the accepted workspace-member count as the only member value", () => {
const freeEntitlement: WorkspaceBillingEntitlement = {
...plusEntitlement,
plan: "free",
subscriptionState: "none",
workspaceMemberCount: 8,
plusEnabled: false,
aiAdmissionAvailable: false,
availableAiCreditUnits: 0n,
};

expect(freeEntitlement.workspaceMemberCount).toBe(8);
});

test("maps non-entitled states to an actionable callout", () => {
Expand Down
12 changes: 1 addition & 11 deletions apps/web/features/dashboard/billing/model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ export type SubscriptionState = (typeof subscriptionStates)[number];
export interface WorkspaceBillingEntitlement {
plan: "free" | "plus";
subscriptionState: SubscriptionState;
billableSeatCount: number;
paidSeatCapacity: number;
workspaceMemberCount: number;
plusEnabled: boolean;
aiAdmissionAvailable: boolean;
availableAiCreditUnits: bigint;
Expand All @@ -41,15 +40,6 @@ export function getBillingCallout(
return entitlement.subscriptionState;
}

export function getAdditionalSeatCount(
entitlement: WorkspaceBillingEntitlement,
): number {
return Math.max(
0,
entitlement.billableSeatCount - entitlement.paidSeatCapacity,
);
}

export function canUsePaidFeatures(
entitlement: WorkspaceBillingEntitlement,
): boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ BaseBlocks est un constructeur de sites Fair Source pour les équipes. Créez de
<Card href="/docs/team" title="Équipe">
Configuration de l'espace de travail, rôles et gestion des membres.
</Card>
<Card href="/docs/plans-and-billing" title="Offres et facturation">
Comprendre les offres, les crédits IA, les membres et la facturation.
</Card>
<Card href="/docs/sites/branding" title="Identité visuelle">
Nom et logo du site dans la barre latérale publiée.
</Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ BaseBlocks is a Fair Source site builder for teams. Build internal sites with a
<Card href="/docs/team" title="Team">
Workspace setup, roles, and member management.
</Card>
<Card href="/docs/plans-and-billing" title="Plans and billing">
Understand plans, AI credits, workspace members, and billing.
</Card>
<Card href="/docs/sites/branding" title="Branding">
Site name and logo in the published sidebar.
</Card>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"...editor",
"---Publication---",
"...publishing",
"---Facturation---",
"plans-and-billing",
"---Espace de travail---",
"...team",
"...sites",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"...editor",
"---Publishing---",
"...publishing",
"---Billing---",
"plans-and-billing",
"---Workspace---",
"...team",
"...sites",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Offres et facturation
description: Comprendre les offres BaseBlocks, les membres de l’espace et les crédits IA.
icon: CreditCard
---

import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';

BaseBlocks maintient gratuites les fonctions principales de création de sites, de publication et de collaboration. L’IA utilise des crédits liés à l’utilisation : vous pouvez donc utiliser l’IA avec des crédits prépayés dans l’offre gratuite ou choisir Plus pour obtenir des crédits récurrents inclus.

## Offres

| | Gratuite | Plus |
|---|---|---|
| Prix | 0 $ pour l’espace | 8 $ par membre de l’espace/mois ou 80 $ par membre de l’espace/an |
| Membres | Membres de l’espace illimités | Facturation par membre accepté de l’espace |
| Fonctions principales | Sites, publication et collaboration | Tout le contenu de l’offre gratuite |
| IA | Crédits prépayés | Crédits inclus pour l’espace, plus des recharges prépayées |

<Callout type="info">
Les prix et les offres disponibles peuvent changer. La page Facturation de votre espace affiche les options et le prix actuels.
</Callout>

## Offre gratuite

L’offre gratuite inclut un nombre illimité de membres, de sites, de publications et de fonctionnalités de collaboration. Elle ne nécessite pas d’abonnement.

L’IA est disponible dans l’offre gratuite avec des crédits prépayés. Ajoutez des crédits avant d’utiliser l’IA. Les crédits achetés n’expirent pas et sont partagés par l’espace.

## Offre Plus

Plus est facturée par membre accepté de l’espace. Si huit membres acceptés appartiennent à un espace, l’espace est facturé pour huit membres.

Plus inclut des crédits IA récurrents pour l’espace. Les crédits inclus sont réinitialisés à la fin de chaque période de facturation. Vous pouvez ajouter des crédits prépayés lorsque vous en avez besoin ; les crédits achetés n’expirent pas.

## Membres de l’espace et facturation

Un membre accepté de l’espace est une personne dont l’adhésion à l’espace a été acceptée. Le nombre de membres ne dépend pas du fait que cette personne soit actuellement en ligne ou ait utilisé BaseBlocks récemment.

Tous les rôles de l’espace comptent comme des membres. Les invitations en attente, les invités d’une page et les visiteurs publics ne comptent pas comme des membres de l’espace. Les espaces gratuits peuvent avoir un nombre illimité de membres. Les espaces Plus utilisent le nombre de membres acceptés pour la facturation.

## Pourquoi l’IA est-elle payante ?

Les requêtes IA ont un coût variable lié à leur utilisation. La facturation de l’utilisation de l’IA permet à BaseBlocks de maintenir l’espace de travail principal gratuit tout en vous laissant contrôler votre consommation d’IA.

Vous n’avez pas besoin de Plus pour utiliser l’IA. Choisissez l’offre gratuite avec des crédits prépayés si vous préférez payer selon votre utilisation. Choisissez Plus si les crédits récurrents inclus et la facturation par membre conviennent mieux à votre espace.

## Questions fréquentes

<Accordions type="single">

<Accordion title="Dois-je payer pour les membres de l’espace avec l’offre gratuite ?">

Non. L’offre gratuite inclut un nombre illimité de membres. La facturation par membre s’applique uniquement aux abonnements Plus.

</Accordion>

<Accordion title="Dois-je avoir Plus pour utiliser l’IA ?">

Non. Ajoutez des crédits IA prépayés à un espace gratuit. Plus ajoute des crédits récurrents inclus et prend toujours en charge les recharges prépayées.

</Accordion>

<Accordion title="Les crédits prépayés expirent-ils ?">

Non. Les crédits achetés n’expirent pas. Les crédits Plus inclus sont réinitialisés à la fin de la période de facturation.

</Accordion>

</Accordions>
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Plans and billing
description: Understand BaseBlocks plans, workspace members, and AI credits.
icon: CreditCard
---

import { Accordions, Accordion } from 'fumadocs-ui/components/accordion';

BaseBlocks keeps core site building, publishing, and collaboration free. AI uses usage-based credits, so you can use AI on Free with prepaid credits or choose Plus for recurring included credits.

## Plans

| | Free | Plus |
|---|---|---|
| Price | $0 for the workspace | $8 per workspace member/month or $80 per workspace member/year |
| Members | Unlimited workspace members | Billed per accepted workspace member |
| Core features | Sites, publishing, and collaboration | Everything in Free |
| AI | Prepaid credits | Included credits for the workspace, plus prepaid top-ups |

<Callout type="info">
Prices and available plans can change. The Billing page in your workspace shows the current plan options and price.
</Callout>

## Free

The Free plan includes unlimited workspace members, sites, publishing, and collaboration. It does not require a subscription.

AI is available on Free through prepaid credits. Add credits before using AI. Purchased credits do not expire and are shared by the workspace.

## Plus

Plus is billed per accepted workspace member. If eight accepted members belong to a workspace, the workspace is billed for eight members.

Plus includes recurring AI credits for the workspace. Included credits reset at the end of each billing period. You can add prepaid credits when you need more; purchased credits do not expire.

## Workspace members and billing

An accepted workspace member is a person whose membership in the workspace has been accepted. The member count does not depend on whether that person is currently online or has used BaseBlocks recently.

All workspace roles count as members. Pending invitations, page guests, and public visitors do not count as workspace members. Free workspaces can have unlimited members. Plus workspaces use the accepted member count for billing.

## Why is AI paid?

AI requests have usage-based costs. Charging for AI usage lets BaseBlocks keep the core workspace free while giving you control over how much AI you use.

You do not need Plus to use AI. Choose Free with prepaid credits when you want usage-based spending. Choose Plus when recurring included credits and per-member billing fit your workspace better.

## Frequently asked questions

<Accordions type="single">

<Accordion title="Do I pay for workspace members on Free?">

No. Free includes unlimited workspace members. Member-based billing applies only to Plus subscriptions.

</Accordion>

<Accordion title="Do I need Plus to use AI?">

No. Add prepaid AI credits to a Free workspace. Plus adds recurring included credits and still supports prepaid top-ups.

</Accordion>

<Accordion title="Do prepaid credits expire?">

No. Purchased credits do not expire. Included Plus credits reset at the end of the billing period.

</Accordion>

</Accordions>
Loading