@arbidocs/blocks 0.5.104 → 0.5.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{DocumentReviewCard-MSHD73S3.cjs → DocumentReviewCard-QVQ2LGFH.cjs} +3 -3
- package/dist/{DocumentReviewCard-MSHD73S3.cjs.map → DocumentReviewCard-QVQ2LGFH.cjs.map} +1 -1
- package/dist/{DocumentReviewCard-WZNBPG72.js → DocumentReviewCard-UOLXZKZE.js} +3 -3
- package/dist/{DocumentReviewCard-WZNBPG72.js.map → DocumentReviewCard-UOLXZKZE.js.map} +1 -1
- package/dist/{FilesViewerDialog-XETOHORQ.js → FilesViewerDialog-5ELQYWAZ.js} +3 -3
- package/dist/{FilesViewerDialog-XETOHORQ.js.map → FilesViewerDialog-5ELQYWAZ.js.map} +1 -1
- package/dist/{FilesViewerDialog-DV2U4USR.cjs → FilesViewerDialog-XNGHHWYS.cjs} +4 -4
- package/dist/{FilesViewerDialog-DV2U4USR.cjs.map → FilesViewerDialog-XNGHHWYS.cjs.map} +1 -1
- package/dist/{WorkspaceTableView-WD754UBK.js → WorkspaceTableView-DIMTW5P5.js} +4 -4
- package/dist/{WorkspaceTableView-WD754UBK.js.map → WorkspaceTableView-DIMTW5P5.js.map} +1 -1
- package/dist/{WorkspaceTableView-3D6SYOWT.cjs → WorkspaceTableView-HRCV6WR7.cjs} +6 -6
- package/dist/{WorkspaceTableView-3D6SYOWT.cjs.map → WorkspaceTableView-HRCV6WR7.cjs.map} +1 -1
- package/dist/{chunk-N77UYRV7.js → chunk-632JXVXY.js} +51 -26
- package/dist/chunk-632JXVXY.js.map +1 -0
- package/dist/{chunk-5R67KX2O.cjs → chunk-6XNWGOS3.cjs} +70 -19
- package/dist/chunk-6XNWGOS3.cjs.map +1 -0
- package/dist/{chunk-TQFDPCWU.js → chunk-7Q6EVFMT.js} +3 -3
- package/dist/{chunk-TQFDPCWU.js.map → chunk-7Q6EVFMT.js.map} +1 -1
- package/dist/{chunk-MMSHLHTA.cjs → chunk-LM5CPXJO.cjs} +51 -26
- package/dist/chunk-LM5CPXJO.cjs.map +1 -0
- package/dist/{chunk-DDIV3MYQ.cjs → chunk-MW7FGIF4.cjs} +25 -59
- package/dist/chunk-MW7FGIF4.cjs.map +1 -0
- package/dist/{chunk-BSPPKX6X.cjs → chunk-OXHAZN43.cjs} +4 -4
- package/dist/{chunk-BSPPKX6X.cjs.map → chunk-OXHAZN43.cjs.map} +1 -1
- package/dist/{chunk-4UOXUWX3.js → chunk-QFKL4HV2.js} +54 -3
- package/dist/chunk-QFKL4HV2.js.map +1 -0
- package/dist/{chunk-FNGFSHVE.js → chunk-TRI3LQVR.js} +24 -50
- package/dist/chunk-TRI3LQVR.js.map +1 -0
- package/dist/connect/index.cjs +1874 -1860
- package/dist/connect/index.cjs.map +1 -1
- package/dist/connect/index.js +1518 -1504
- package/dist/connect/index.js.map +1 -1
- package/dist/connect/viewer.cjs +1 -1
- package/dist/connect/viewer.js +1 -1
- package/dist/connection/billing/AddAddonDialog.d.ts +1 -1
- package/dist/connection/billing/CollaboratorPlan.d.ts +7 -0
- package/dist/connection/billing/ConnectedQuotaDialog.d.ts +2 -2
- package/dist/connection/billing/ConnectedWelcomeDialog.d.ts +2 -2
- package/dist/connection/billing/MonthlySpendCap.d.ts +12 -0
- package/dist/connection/billing/NewProjectFlow.d.ts +0 -7
- package/dist/connection/billing/ProjectBilling.d.ts +1 -1
- package/dist/connection/billing/ProjectDetail.d.ts +1 -1
- package/dist/connection/billing/ProjectPurchaseOptions.d.ts +22 -0
- package/dist/connection/billing/ProjectUsageCard.d.ts +0 -13
- package/dist/connection/billing/ProjectUsageContent.d.ts +3 -3
- package/dist/connection/billing/ReduceAddonDialog.d.ts +2 -2
- package/dist/connection/billing/StripeCheckoutModal.d.ts +5 -2
- package/dist/connection/billing/SubscriptionPlans.d.ts +2 -10
- package/dist/connection/billing/SubscriptionStatus.d.ts +1 -1
- package/dist/connection/billing/SuspendedProjectRecovery.d.ts +8 -0
- package/dist/connection/billing/addon-pricing.d.ts +6 -6
- package/dist/connection/billing/payment-required.d.ts +2 -2
- package/dist/connection/billing/plan-constants.d.ts +6 -4
- package/dist/connection/billing/plan-pricing.d.ts +5 -5
- package/dist/connection/billing/quota-items.d.ts +2 -2
- package/dist/connection/billing/useAddonPurchase.d.ts +1 -1
- package/dist/connection/billing/usePaidWorkspaceMembers.d.ts +397 -0
- package/dist/connection/workspace-list/useConnectedWorkspaceList.d.ts +17 -1
- package/dist/embed/index.cjs +8 -8
- package/dist/embed/index.js +2 -2
- package/dist/settings/PlanCard.d.ts +2 -2
- package/dist/settings/SubscriptionParts.d.ts +3 -1
- package/dist/settings/index.cjs +150 -150
- package/dist/settings/index.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/workflow-canvas/index.cjs +7 -7
- package/dist/workflow-canvas/index.js +2 -2
- package/node_modules/@arbidocs/coedit/package.json +1 -1
- package/package.json +6 -5
- package/src/blocks.css +2 -0
- package/src/connection/billing/project-purchase.css +453 -0
- package/dist/chunk-4UOXUWX3.js.map +0 -1
- package/dist/chunk-5R67KX2O.cjs.map +0 -1
- package/dist/chunk-DDIV3MYQ.cjs.map +0 -1
- package/dist/chunk-FNGFSHVE.js.map +0 -1
- package/dist/chunk-MMSHLHTA.cjs.map +0 -1
- package/dist/chunk-N77UYRV7.js.map +0 -1
- package/dist/connection/billing/CheckoutTotal.d.ts +0 -12
- package/dist/connection/billing/PlanSelector.d.ts +0 -50
- package/dist/connection/billing/billing-country.d.ts +0 -13
- package/dist/connection/billing/useCheckoutQuote.d.ts +0 -30
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
var chunkE5TPRRAW_cjs = require('./chunk-E5TPRRAW.cjs');
|
|
5
|
-
var
|
|
5
|
+
var chunkLM5CPXJO_cjs = require('./chunk-LM5CPXJO.cjs');
|
|
6
6
|
var chunkC4TGESIB_cjs = require('./chunk-C4TGESIB.cjs');
|
|
7
7
|
var chunkLUO46ELJ_cjs = require('./chunk-LUO46ELJ.cjs');
|
|
8
8
|
var react = require('@arbidocs/react');
|
|
@@ -10,7 +10,7 @@ var lucideReact = require('lucide-react');
|
|
|
10
10
|
var jsxRuntime = require('react/jsx-runtime');
|
|
11
11
|
|
|
12
12
|
function useRewardBalance() {
|
|
13
|
-
const { data } = react.useUserSettings();
|
|
13
|
+
const { data } = react.useUserSettings({ refetchOnMount: "always" });
|
|
14
14
|
const ed = data?.extra_discount;
|
|
15
15
|
if (!ed || !ed.amount || ed.amount <= 0) return null;
|
|
16
16
|
return { amount: ed.amount, currency: ed.currency || "gbp" };
|
|
@@ -72,12 +72,12 @@ function blocksLabel(kind, count, addons) {
|
|
|
72
72
|
...legacy > 0 ? [`${legacy} \xD7 ${blockSize(kind, addons, true)}`] : [],
|
|
73
73
|
...current > 0 || legacy === 0 ? [`${current} \xD7 ${blockSize(kind, addons, false)}`] : []
|
|
74
74
|
];
|
|
75
|
-
return `${parts.join(" + ")}${kind === "ai_usage" ? " of AI
|
|
75
|
+
return `${parts.join(" + ")}${kind === "ai_usage" ? " of AI credits" : ""}`;
|
|
76
76
|
}
|
|
77
77
|
function reduceBounds(kind, addons) {
|
|
78
78
|
const current = kind === "ai_usage" ? addons.ai_blocks : addons.storage_blocks;
|
|
79
79
|
const pending = kind === "ai_usage" ? addons.ai_blocks_next : addons.storage_blocks_next;
|
|
80
|
-
const min =
|
|
80
|
+
const min = 0;
|
|
81
81
|
return { min, current, next: pending ?? current };
|
|
82
82
|
}
|
|
83
83
|
function blockMonthlyExVatPence(kind, addons) {
|
|
@@ -87,7 +87,7 @@ function blockPeriodExVatPence(kind, addons) {
|
|
|
87
87
|
return kind === "ai_usage" ? addons.ai_block_period_ex_vat_pence : addons.storage_block_period_ex_vat_pence;
|
|
88
88
|
}
|
|
89
89
|
function blockRenewalShort(kind, addons, isAnnual) {
|
|
90
|
-
return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? "year" : "month"}
|
|
90
|
+
return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? "year" : "month"} ex VAT`;
|
|
91
91
|
}
|
|
92
92
|
var ANNUAL_PREMIUM_AI_NOTE = "No markup on Premium AI on annual plans";
|
|
93
93
|
function addonCharge(kind, addons, isAnnual, periodEnd) {
|
|
@@ -101,15 +101,14 @@ function addonCharge(kind, addons, isAnnual, periodEnd) {
|
|
|
101
101
|
}
|
|
102
102
|
var CAP_REACHED_MESSAGE = "Usage cap reached: no more automatic top-ups this month.";
|
|
103
103
|
var ZERO_BUDGET_MESSAGE = "Automatic top-ups are off (\xA30 usage cap).";
|
|
104
|
-
var
|
|
105
|
-
var USAGE_CAP_HELP = "The most automatic top-ups (AI usage added when it runs low, storage added when an upload needs it) may cost in a month. Adding or removing by hand is never limited by it; the plan and renewals are not counted. \xA30 = no automatic top-ups: AI usage and uploads stop at your allowance unless you add more. No limit = no cap.";
|
|
104
|
+
var USAGE_CAP_HELP = "Your auto-upgrade limit covers extra monthly spend beyond your selected plan and add-ons. \xA30 turns automatic upgrades off. You can adjust usage add-ons with + and \u2212. Additions show the charge before you confirm; reductions apply next billing period, provided your storage and collaborators fit. Prices are ex VAT.";
|
|
106
105
|
function isAddonBudgetZero(addons) {
|
|
107
106
|
return addons.spend_cap_pence === 0;
|
|
108
107
|
}
|
|
109
108
|
function autoRoom(kind, addons) {
|
|
110
109
|
const left = addons.cap_left_pence;
|
|
111
110
|
if (left == null) return addons.spend_cap_pence === 0 ? 0 : null;
|
|
112
|
-
const price =
|
|
111
|
+
const price = blockMonthlyExVatPence(kind, addons);
|
|
113
112
|
if (price <= 0) return 0;
|
|
114
113
|
const blocks = Math.floor(left / price);
|
|
115
114
|
return blocks * (kind === "ai_usage" ? addons.block_ai_gbp : addons.block_storage_gb);
|
|
@@ -122,12 +121,6 @@ function nextRemovedBlockSize(kind, addons) {
|
|
|
122
121
|
}
|
|
123
122
|
return current > 0 ? addons.block_storage_gb : addons.legacy_block_storage_gb ?? 10;
|
|
124
123
|
}
|
|
125
|
-
function parseBudgetPounds(value) {
|
|
126
|
-
if (value.trim() === "") return null;
|
|
127
|
-
const n = Number(value);
|
|
128
|
-
if (!Number.isFinite(n) || n < 0) return void 0;
|
|
129
|
-
return Math.round(n * 100);
|
|
130
|
-
}
|
|
131
124
|
function budgetSavedMessage(capPence) {
|
|
132
125
|
if (capPence == null) return "Usage cap: no limit";
|
|
133
126
|
if (capPence === 0) return "Usage cap: \xA30 (no automatic top-ups)";
|
|
@@ -135,7 +128,7 @@ function budgetSavedMessage(capPence) {
|
|
|
135
128
|
}
|
|
136
129
|
var gbText = (n) => `${Math.round(n * 10) / 10} GB`;
|
|
137
130
|
function stepLabel(kind, size) {
|
|
138
|
-
return kind === "ai_usage" ? `${react.formatGbp(size)} of AI
|
|
131
|
+
return kind === "ai_usage" ? `${react.formatGbp(size)} of AI credits` : gbText(size);
|
|
139
132
|
}
|
|
140
133
|
function nextPeriodBody(kind, keep) {
|
|
141
134
|
return kind === "ai_usage" ? { ai_blocks: keep, storage_blocks: null } : { ai_blocks: null, storage_blocks: keep };
|
|
@@ -155,7 +148,6 @@ var DEFAULT_SPEND_CAP_PENCE = 1e4;
|
|
|
155
148
|
var MIN_AUTO_TOPUP_THRESHOLD_GBP = 1;
|
|
156
149
|
var FREE_AI_GBP = 1;
|
|
157
150
|
var FREE_STORAGE_MB = 50;
|
|
158
|
-
var FREE_COLLABORATORS = 0;
|
|
159
151
|
var UNLIMITED_COLLABORATORS = 999;
|
|
160
152
|
var SUGGESTED_DAILY_CAP_PER_USER = 10;
|
|
161
153
|
|
|
@@ -187,12 +179,12 @@ function getQuotaItems(quotas) {
|
|
|
187
179
|
const aiUsage = quota(quotas?.ai_usage);
|
|
188
180
|
const storage = quota(quotas?.storage_gb);
|
|
189
181
|
const collaborators = quota(quotas?.collaborators);
|
|
190
|
-
const unlimitedCollaborators = collaborators.limit
|
|
182
|
+
const unlimitedCollaborators = collaborators.limit === UNLIMITED_COLLABORATORS;
|
|
191
183
|
return [
|
|
192
184
|
{
|
|
193
185
|
key: "ai-usage",
|
|
194
186
|
icon: lucideReact.PoundSterling,
|
|
195
|
-
label: "AI
|
|
187
|
+
label: "AI credits",
|
|
196
188
|
used: react.formatGbp(aiUsage.used),
|
|
197
189
|
usedOfLimit: `${react.formatGbp(aiUsage.used)} / ${react.formatGbp(aiUsage.limit)}`,
|
|
198
190
|
pct: pctOf(aiUsage.used, aiUsage.limit)
|
|
@@ -230,8 +222,6 @@ function apiErrorDetail(error) {
|
|
|
230
222
|
}
|
|
231
223
|
|
|
232
224
|
// src/connection/billing/tax-quote.ts
|
|
233
|
-
var POSTCODE_COUNTRIES = ["US", "CA"];
|
|
234
|
-
var TAX_LOCATION_INVALID = "TAX_LOCATION_INVALID";
|
|
235
225
|
function money2(pence, currency = "gbp") {
|
|
236
226
|
const symbol = { gbp: "\xA3", usd: "$", eur: "\u20AC" }[currency.toLowerCase()] ?? `${currency} `;
|
|
237
227
|
return `${symbol}${(pence / 100).toLocaleString("en-GB", {
|
|
@@ -265,28 +255,13 @@ function quoteParts(quote) {
|
|
|
265
255
|
function quoteTotalLine(quote) {
|
|
266
256
|
return `Total today ${taxSummary(quoteParts(quote))}`;
|
|
267
257
|
}
|
|
268
|
-
function quoteError(error) {
|
|
269
|
-
const detail = apiErrorDetail(error);
|
|
270
|
-
if (detail && typeof detail === "object") {
|
|
271
|
-
const { code, message } = detail;
|
|
272
|
-
return {
|
|
273
|
-
code: typeof code === "string" ? code : void 0,
|
|
274
|
-
message: typeof message === "string" ? message : void 0
|
|
275
|
-
};
|
|
276
|
-
}
|
|
277
|
-
return { message: typeof detail === "string" ? detail : void 0 };
|
|
278
|
-
}
|
|
279
258
|
|
|
280
259
|
// src/connection/billing/plan-pricing.ts
|
|
281
260
|
var gbp2 = (pence) => formatMoney(pence, "gbp");
|
|
282
261
|
function plusVat(exVatPence, currency = "gbp") {
|
|
283
|
-
return `${formatMoney(exVatPence, currency)}
|
|
262
|
+
return `${formatMoney(exVatPence, currency)} ex VAT`;
|
|
284
263
|
}
|
|
285
264
|
var PRO_PACKS = 1;
|
|
286
|
-
var PLAN_CHOICE_PACKS = {
|
|
287
|
-
free: 0,
|
|
288
|
-
pro: PRO_PACKS
|
|
289
|
-
};
|
|
290
265
|
function planNameForPacks(packs) {
|
|
291
266
|
const n = packs ?? 0;
|
|
292
267
|
if (n <= 0) return "Free";
|
|
@@ -326,11 +301,15 @@ var PAY_TODAY_UNPRICED = "Pay today: the difference for the rest of this period"
|
|
|
326
301
|
function addonPrice(price, kind = "ai_usage") {
|
|
327
302
|
return price?.addon_prices?.find((p) => p.kind === kind);
|
|
328
303
|
}
|
|
329
|
-
function checkoutItemsLabel(aiBlocks, storageBlocks) {
|
|
304
|
+
function checkoutItemsLabel(aiBlocks, storageBlocks, collaboratorBlocks = 0, price) {
|
|
305
|
+
const ai = addonPrice(price, "ai_usage")?.ai_gbp;
|
|
306
|
+
const storage = addonPrice(price, "storage")?.storage_gb;
|
|
307
|
+
const collaborators = price?.addon_prices?.find((p) => p.kind === "collaborators")?.collaborators;
|
|
330
308
|
return [
|
|
331
309
|
"Pro",
|
|
332
|
-
aiBlocks > 0 && `${aiBlocks} \xD7 ${react.formatGbp(
|
|
333
|
-
storageBlocks > 0 && `${storageBlocks} \xD7 ${
|
|
310
|
+
aiBlocks > 0 && (ai != null ? `${aiBlocks} \xD7 ${react.formatGbp(ai)} of AI credits` : `${aiBlocks} AI credits add-ons`),
|
|
311
|
+
storageBlocks > 0 && (storage != null ? `${storageBlocks} \xD7 ${storage} GB` : `${storageBlocks} storage add-ons`),
|
|
312
|
+
collaboratorBlocks > 0 && (collaborators != null ? `${collaboratorBlocks * collaborators} extra collaborators` : `${collaboratorBlocks} collaborator add-ons`)
|
|
334
313
|
].filter(Boolean).join(" + ");
|
|
335
314
|
}
|
|
336
315
|
function packPrice(price, packs = PRO_PACKS) {
|
|
@@ -349,12 +328,6 @@ function calcSavingsPercent(monthlyPrice, annualPrice) {
|
|
|
349
328
|
return savings > 0 ? savings : null;
|
|
350
329
|
}
|
|
351
330
|
var MAX_CHECKOUT_BLOCKS = 100;
|
|
352
|
-
function checkoutBlockPriceText(price, kind) {
|
|
353
|
-
const block = addonPrice(price, kind);
|
|
354
|
-
if (!block) return null;
|
|
355
|
-
const currency = price?.currency ?? "gbp";
|
|
356
|
-
return price?.interval === "year" ? `${formatMoney(block.amount_ex_vat, currency)}/year + VAT each` : `${formatMoney(block.amount_ex_vat, currency)}/month + VAT each`;
|
|
357
|
-
}
|
|
358
331
|
|
|
359
332
|
// src/connection/billing/plan-badge.ts
|
|
360
333
|
function isProProject(project) {
|
|
@@ -365,18 +338,19 @@ function proTierName(project) {
|
|
|
365
338
|
}
|
|
366
339
|
function planName(project) {
|
|
367
340
|
if (project.subscription === "dev") return "Dev";
|
|
341
|
+
if (project.subscription === "suspended") return "Subscription ended";
|
|
368
342
|
if (!isProProject(project)) return "Free";
|
|
369
343
|
return `${proTierName(project)} ${project.plan_interval === "year" ? "annual" : "monthly"}`;
|
|
370
344
|
}
|
|
371
345
|
function planBadgeView(project) {
|
|
372
346
|
const { subscription } = project;
|
|
373
|
-
const label = subscription === "canceled" ? "Cancelling" : subscription === "dev" ? "Dev" : isProProject(project) ? proTierName(project) : "Free";
|
|
374
|
-
const tone = subscription === "dev" ? "dev" : subscription === "canceled" || subscription === "past_due" ? "pending" : isProProject(project) ? "paid" : "free";
|
|
347
|
+
const label = subscription === "suspended" ? "Subscription ended" : subscription === "canceled" ? "Cancelling" : subscription === "dev" ? "Dev" : isProProject(project) ? proTierName(project) : "Free";
|
|
348
|
+
const tone = subscription === "dev" ? "dev" : subscription === "suspended" || subscription === "canceled" || subscription === "past_due" ? "pending" : isProProject(project) ? "paid" : "free";
|
|
375
349
|
return { label, tone };
|
|
376
350
|
}
|
|
377
351
|
function projectGroupBilling(project, { isOwned, stripeEnabled }) {
|
|
378
352
|
if (!project) return {};
|
|
379
|
-
const planBadge = isOwned && stripeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
353
|
+
const planBadge = isOwned && stripeEnabled ? /* @__PURE__ */ jsxRuntime.jsx(chunkLM5CPXJO_cjs.PlanBadge, { ...planBadgeView(project) }) : void 0;
|
|
380
354
|
const quotaItems = project.quotas ? getQuotaItems(project.quotas) : [];
|
|
381
355
|
const readout = project.quotas ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
382
356
|
chunkE5TPRRAW_cjs.ProjectReadout,
|
|
@@ -386,7 +360,7 @@ function projectGroupBilling(project, { isOwned, stripeEnabled }) {
|
|
|
386
360
|
testId: `project-usage-${project.external_id}`,
|
|
387
361
|
// Bare numbers without plans for sale: a glance beside the name, not the Plan & Usage detail.
|
|
388
362
|
node: /* @__PURE__ */ jsxRuntime.jsx(
|
|
389
|
-
|
|
363
|
+
chunkLM5CPXJO_cjs.QuotaMeter,
|
|
390
364
|
{
|
|
391
365
|
items: quotaItems,
|
|
392
366
|
size: "sm",
|
|
@@ -427,21 +401,16 @@ exports.ANNUAL_PREMIUM_AI_NOTE = ANNUAL_PREMIUM_AI_NOTE;
|
|
|
427
401
|
exports.CAP_REACHED_MESSAGE = CAP_REACHED_MESSAGE;
|
|
428
402
|
exports.DEFAULT_SPEND_CAP_PENCE = DEFAULT_SPEND_CAP_PENCE;
|
|
429
403
|
exports.FREE_AI_GBP = FREE_AI_GBP;
|
|
430
|
-
exports.FREE_COLLABORATORS = FREE_COLLABORATORS;
|
|
431
404
|
exports.FREE_STORAGE_MB = FREE_STORAGE_MB;
|
|
432
405
|
exports.MAX_CHECKOUT_BLOCKS = MAX_CHECKOUT_BLOCKS;
|
|
433
406
|
exports.MIN_AUTO_TOPUP_THRESHOLD_GBP = MIN_AUTO_TOPUP_THRESHOLD_GBP;
|
|
434
407
|
exports.PAY_TODAY_UNPRICED = PAY_TODAY_UNPRICED;
|
|
435
|
-
exports.PLAN_CHOICE_PACKS = PLAN_CHOICE_PACKS;
|
|
436
|
-
exports.POSTCODE_COUNTRIES = POSTCODE_COUNTRIES;
|
|
437
408
|
exports.PRO_AI_GBP = PRO_AI_GBP;
|
|
438
409
|
exports.PRO_PACKS = PRO_PACKS;
|
|
439
410
|
exports.PRO_STORAGE_GB = PRO_STORAGE_GB;
|
|
440
411
|
exports.STORAGE_BLOCK_GB = STORAGE_BLOCK_GB;
|
|
441
412
|
exports.SUGGESTED_DAILY_CAP_PER_USER = SUGGESTED_DAILY_CAP_PER_USER;
|
|
442
|
-
exports.TAX_LOCATION_INVALID = TAX_LOCATION_INVALID;
|
|
443
413
|
exports.USAGE_CAP_HELP = USAGE_CAP_HELP;
|
|
444
|
-
exports.USAGE_CAP_LABEL = USAGE_CAP_LABEL;
|
|
445
414
|
exports.ZERO_BUDGET_MESSAGE = ZERO_BUDGET_MESSAGE;
|
|
446
415
|
exports.addStepCaption = addStepCaption;
|
|
447
416
|
exports.addonCharge = addonCharge;
|
|
@@ -453,7 +422,6 @@ exports.autoRoom = autoRoom;
|
|
|
453
422
|
exports.blocksLabel = blocksLabel;
|
|
454
423
|
exports.budgetSavedMessage = budgetSavedMessage;
|
|
455
424
|
exports.calcSavingsPercent = calcSavingsPercent;
|
|
456
|
-
exports.checkoutBlockPriceText = checkoutBlockPriceText;
|
|
457
425
|
exports.checkoutItemsLabel = checkoutItemsLabel;
|
|
458
426
|
exports.findProPrices = findProPrices;
|
|
459
427
|
exports.formatMoney = formatMoney;
|
|
@@ -466,7 +434,6 @@ exports.money2 = money2;
|
|
|
466
434
|
exports.nextPeriodBody = nextPeriodBody;
|
|
467
435
|
exports.nextRemovedBlockSize = nextRemovedBlockSize;
|
|
468
436
|
exports.packPrice = packPrice;
|
|
469
|
-
exports.parseBudgetPounds = parseBudgetPounds;
|
|
470
437
|
exports.planBadgeView = planBadgeView;
|
|
471
438
|
exports.planName = planName;
|
|
472
439
|
exports.planNameForPacks = planNameForPacks;
|
|
@@ -474,7 +441,6 @@ exports.planPreviewLines = planPreviewLines;
|
|
|
474
441
|
exports.plusVat = plusVat;
|
|
475
442
|
exports.proTierName = proTierName;
|
|
476
443
|
exports.projectGroupBilling = projectGroupBilling;
|
|
477
|
-
exports.quoteError = quoteError;
|
|
478
444
|
exports.quoteParts = quoteParts;
|
|
479
445
|
exports.quoteTotalLine = quoteTotalLine;
|
|
480
446
|
exports.reduceBounds = reduceBounds;
|
|
@@ -485,5 +451,5 @@ exports.totalWithTaxWord = totalWithTaxWord;
|
|
|
485
451
|
exports.useBillingSupport = useBillingSupport;
|
|
486
452
|
exports.useRewardBalance = useRewardBalance;
|
|
487
453
|
exports.useSelfExtId = useSelfExtId;
|
|
488
|
-
//# sourceMappingURL=chunk-
|
|
489
|
-
//# sourceMappingURL=chunk-
|
|
454
|
+
//# sourceMappingURL=chunk-MW7FGIF4.cjs.map
|
|
455
|
+
//# sourceMappingURL=chunk-MW7FGIF4.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/connection/billing/reward-balance.ts","../src/connection/billing/support.ts","../src/connection/billing/addon-pricing.ts","../src/connection/billing/plan-constants.ts","../src/connection/billing/quota-items.ts","../src/connection/billing/api-error.ts","../src/connection/billing/tax-quote.ts","../src/connection/billing/plan-pricing.ts","../src/connection/billing/plan-badge.ts","../src/connection/workspace-list/projectBilling.tsx"],"names":["useUserSettings","useHost","useArbi","formatGbp","PoundSterling","HardDrive","Users","gbp","jsx","PlanBadge","ProjectReadout","QuotaMeter","ProjectBreakdown","UsageMeter"],"mappings":"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","file":"chunk-MW7FGIF4.cjs","sourcesContent":["/**\n * Reward-balance logic (hook + money formatter).\n *\n * A user's reward balance is their available account balance — a single GLOBAL per-user\n * number, the Stripe customer account balance mirrored read-only into\n * `settings.extra_discount`. It auto-applies to the user's NEXT invoice.\n *\n * \"Rewards\" is the broad concept: the balance can be fed by several sources (referral\n * reward is just one). The referral pricing surfaces of the billing kit\n * speak to the referral source specifically; this module owns the balance itself.\n */\n\nimport { useUserSettings } from '@arbidocs/react'\n\nexport interface RewardBalance {\n amount: number // minor units (e.g. pennies) of available account balance\n currency: string\n}\n\n/** The user's available reward balance (all sources), or null if none. */\nexport function useRewardBalance(): RewardBalance | null {\n const { data } = useUserSettings({ refetchOnMount: 'always' })\n // Backend mirrors this read-only as { amount, currency }; schema types it as a loose object.\n const ed = data?.extra_discount as { amount?: number; currency?: string } | null | undefined\n if (!ed || !ed.amount || ed.amount <= 0) return null\n return { amount: ed.amount, currency: ed.currency || 'gbp' }\n}\n\n/** Format minor units as a localized money string, e.g. `£20` (whole) or `£29.99`. */\nexport function formatMoney(cents: number, currency = 'gbp'): string {\n const value = cents / 100\n const isWhole = Number.isInteger(value)\n const c = currency.toLowerCase()\n const symbol =\n c === 'usd' ? '$' : c === 'gbp' ? '£' : c === 'eur' ? '€' : currency.toUpperCase() + ' '\n return `${symbol}${value.toLocaleString('en-US', {\n minimumFractionDigits: isWhole ? 0 : 2,\n maximumFractionDigits: isWhole ? 0 : 2,\n })}`\n}\n","/**\n * What a host adds to the billing kit. Selling plans needs Stripe, which stays the host's: whether\n * this deployment sells plans (a publishable key is configured), and its embedded checkout. It is the\n * host configuration's `billing`; without it nothing is for sale: projects show their limits and\n * usage only.\n */\nimport type { ReactNode } from 'react'\nimport { useArbi } from '@arbidocs/react'\nimport { useHost } from '../../provider/host'\n\nexport interface BillingCheckoutSupport {\n /** Load the host's Stripe (lazily, when a checkout opens); `null` when it cannot. */\n load(): Promise<unknown> | null\n /** Stripe's embedded checkout for a session, on the loaded Stripe. */\n render(stripe: unknown, session: { clientSecret: string; onComplete: () => void }): ReactNode\n}\n\nexport interface BillingSupport {\n /** This deployment sells plans (Stripe is configured). */\n stripeEnabled: boolean\n checkout?: BillingCheckoutSupport\n}\n\nconst NOT_FOR_SALE: BillingSupport = { stripeEnabled: false }\n\nexport function useBillingSupport(): BillingSupport {\n return useHost().billing ?? NOT_FOR_SALE\n}\n\n/** The signed-in user's id (`usr-…`), to tell the projects they own from the others. */\nexport function useSelfExtId(): string | undefined {\n const arbi = useArbi()\n return arbi.isLoggedIn ? (arbi.user.identity()?.externalId ?? undefined) : undefined\n}\n","/** Add-on block price helpers (VAT-inclusive pence from `project.addons`). */\n\nimport { formatGbp } from '@arbidocs/react'\nimport type { components } from '@arbidocs/client'\nimport type { AddonKind } from './useAddonPurchase'\nimport { formatMoney } from './reward-balance'\n\ntype ProjectAddons = components['schemas']['ProjectAddons']\n\nconst gbp = (pence: number) => formatMoney(pence, 'gbp')\n\n/** \"3 Nov 2026\". */\nexport function formatPeriodEnd(unix: number | null | undefined): string | null {\n if (!unix) return null\n return new Date(unix * 1000).toLocaleDateString('en-GB', {\n day: 'numeric',\n month: 'short',\n year: 'numeric',\n })\n}\n\n/** What one more block of this kind costs right now, incl. UK VAT. */\nexport function addonPricePence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage' ? addons.ai_block_price_pence : addons.storage_block_price_pence\n}\n\n/** The same price ex VAT. */\nfunction addonPriceExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_price_ex_vat_pence\n : addons.storage_block_price_ex_vat_pence\n}\n\n/**\n * The PUT /addons/settings body (the monthly usage cap and the automatic add-on trigger) from the current add-on\n * state with the given changes applied.\n */\nexport function addonSettingsBody(\n addons: ProjectAddons,\n change: Partial<{\n spend_cap_pence: number | null\n auto_topup_threshold_gbp: number\n }> = {}\n) {\n return {\n spend_cap_pence: addons.spend_cap_pence ?? null,\n auto_topup_threshold_gbp: addons.auto_topup_threshold_gbp,\n ...change,\n }\n}\n\n/**\n * Of `count` blocks of a kind, how many are on an earlier catalogue's price (legacy, at their old size: £10 of AI\n * usage or 10 GB) and how many on today's. A reduction removes today's blocks first, so the legacy ones are the\n * last kept.\n */\nexport function splitBlocks(\n kind: AddonKind,\n count: number,\n addons: ProjectAddons\n): { legacy: number; current: number } {\n const granted =\n (kind === 'ai_usage' ? addons.legacy_ai_blocks : addons.legacy_storage_blocks) ?? 0\n const legacy = Math.max(0, Math.min(granted, count))\n return { legacy, current: count - legacy }\n}\n\n/** One block's size: \"£25 of AI credits\" / \"25 GB\" (legacy: \"£10\" / \"10 GB\"). */\nfunction blockSize(kind: AddonKind, addons: ProjectAddons, legacy: boolean): string {\n if (kind === 'ai_usage') {\n return formatGbp(legacy ? (addons.legacy_block_ai_gbp ?? 10) : addons.block_ai_gbp)\n }\n return `${legacy ? (addons.legacy_block_storage_gb ?? 10) : addons.block_storage_gb} GB`\n}\n\n/**\n * \"2 × £25 of AI credits\", \"1 × £10 + 1 × £25 of AI credits\", \"1 × 10 GB + 2 × 25 GB\": `count` blocks of a kind, the\n * legacy ones (kept last on a reduction) at their old size.\n */\nexport function blocksLabel(kind: AddonKind, count: number, addons: ProjectAddons): string {\n const { legacy, current } = splitBlocks(kind, count, addons)\n const parts = [\n ...(legacy > 0 ? [`${legacy} × ${blockSize(kind, addons, true)}`] : []),\n ...(current > 0 || legacy === 0 ? [`${current} × ${blockSize(kind, addons, false)}`] : []),\n ]\n return `${parts.join(' + ')}${kind === 'ai_usage' ? ' of AI credits' : ''}`\n}\n\n/**\n * Bounds for \"Reduce from next period\": keep `min`..`current` blocks. `next` is what renews\n * now (a pending reduction, or the current count). Storage can't go below what usage needs.\n */\nexport function reduceBounds(\n kind: AddonKind,\n addons: ProjectAddons\n): { min: number; current: number; next: number } {\n const current = kind === 'ai_usage' ? addons.ai_blocks : addons.storage_blocks\n const pending = kind === 'ai_usage' ? addons.ai_blocks_next : addons.storage_blocks_next\n const min = 0\n return { min, current, next: pending ?? current }\n}\n\n/** One block's monthly-equivalent price ex VAT: AI credits and storage are priced separately. */\nfunction blockMonthlyExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_monthly_ex_vat_pence\n : addons.storage_block_monthly_ex_vat_pence\n}\n\n/** What one block renews at per billing period, ex VAT (annual: 12 × the monthly price). */\nfunction blockPeriodExVatPence(kind: AddonKind, addons: ProjectAddons): number {\n return kind === 'ai_usage'\n ? addons.ai_block_period_ex_vat_pence\n : addons.storage_block_period_ex_vat_pence\n}\n\n/** What one add-on renews at: \"£25/month ex VAT\" or \"£225/year ex VAT\". */\nfunction blockRenewalShort(kind: AddonKind, addons: ProjectAddons, isAnnual: boolean): string {\n return `${gbp(blockPeriodExVatPence(kind, addons))}/${isAnnual ? 'year' : 'month'} ex VAT`\n}\n\n/** The extra line annual plans show beside an AI credits add-on or upgrade price. */\nexport const ANNUAL_PREMIUM_AI_NOTE = 'No markup on Premium AI on annual plans'\n\n/** The confirm dialog's figures: charged now (incl. / ex VAT), what it covers until, then what renews. */\nexport function addonCharge(\n kind: AddonKind,\n addons: ProjectAddons,\n isAnnual: boolean,\n periodEnd: number | null | undefined\n): { now: string; nowExVat: string; nowVat: string; until: string | null; then: string } {\n return {\n now: gbp(addonPricePence(kind, addons)),\n nowExVat: gbp(addonPriceExVatPence(kind, addons)),\n nowVat: gbp(addonPricePence(kind, addons) - addonPriceExVatPence(kind, addons)),\n until: isAnnual ? formatPeriodEnd(periodEnd) : null,\n then: blockRenewalShort(kind, addons, isAnnual),\n }\n}\n\n/** Why automatic top-ups stopped (one line; the price of a step is on the Add button). */\nexport const CAP_REACHED_MESSAGE = 'Usage cap reached: no more automatic top-ups this month.'\n\n/** The £0 usage cap: no automatic top-ups (adding by hand still works). */\nexport const ZERO_BUDGET_MESSAGE = 'Automatic top-ups are off (£0 usage cap).'\n\n/** The usage cap's heading wherever it is edited or shown. */\nexport const USAGE_CAP_LABEL = 'Auto-upgrade limit'\n\n/** Automatic-upgrade spending is separate from explicitly selected allowances. */\nexport const USAGE_CAP_HELP =\n 'Your auto-upgrade limit covers extra monthly spend beyond your selected plan and add-ons. ' +\n '£0 turns automatic upgrades off. You can adjust usage add-ons with + and −. ' +\n 'Additions show the charge before you confirm; reductions apply next billing period, ' +\n 'provided your storage and collaborators fit. Prices are ex VAT.'\n\n/** A £0 cap turns automatic top-ups off (manual adds still work). */\nexport function isAddonBudgetZero(addons: Pick<ProjectAddons, 'spend_cap_pence'>): boolean {\n return addons.spend_cap_pence === 0\n}\n\n/**\n * How much more of a resource automatic top-ups may add this month before the usage cap stops them, in the\n * resource's unit (£ of AI credits / GB): the whole blocks the cap left pays for at today's ex-VAT price. `null` =\n * no limit; 0 with a £0 cap or when not even one block fits.\n */\nexport function autoRoom(kind: AddonKind, addons: ProjectAddons): number | null {\n const left = addons.cap_left_pence\n if (left == null) return addons.spend_cap_pence === 0 ? 0 : null\n const price = blockMonthlyExVatPence(kind, addons)\n if (price <= 0) return 0\n const blocks = Math.floor(left / price)\n return blocks * (kind === 'ai_usage' ? addons.block_ai_gbp : addons.block_storage_gb)\n}\n\n/** The size of the block a one-step reduction removes from what renews (today's blocks go first). */\nexport function nextRemovedBlockSize(kind: AddonKind, addons: ProjectAddons): number {\n const { next } = reduceBounds(kind, addons)\n const { current } = splitBlocks(kind, next, addons)\n if (kind === 'ai_usage') {\n return current > 0 ? addons.block_ai_gbp : (addons.legacy_block_ai_gbp ?? 10)\n }\n return current > 0 ? addons.block_storage_gb : (addons.legacy_block_storage_gb ?? 10)\n}\n\n/**\n * Pounds typed into a usage cap field → pence. '' → the default (null); invalid → undefined.\n * Equal to the default is also sent as null (\"keep the default\").\n */\nexport function parseBudgetPounds(value: string): number | null | undefined {\n if (value.trim() === '') return null\n const n = Number(value)\n if (!Number.isFinite(n) || n < 0) return undefined\n return Math.round(n * 100)\n}\n\n/** Toast after saving the usage cap. */\nexport function budgetSavedMessage(capPence: number | null): string {\n if (capPence == null) return 'Usage cap: no limit'\n if (capPence === 0) return 'Usage cap: £0 (no automatic top-ups)'\n return `Usage cap: ${gbp(capPence)} a month`\n}\n\nconst gbText = (n: number) => `${Math.round(n * 10) / 10} GB`\n\n/** \"£25 of AI credits\" / \"25 GB\". */\nexport function stepLabel(kind: AddonKind, size: number): string {\n return kind === 'ai_usage' ? `${formatGbp(size)} of AI credits` : gbText(size)\n}\n\n/** The PUT /addons/next-period body that renews `keep` blocks of `kind` (the other kind unchanged). */\nexport function nextPeriodBody(kind: AddonKind, keep: number) {\n return kind === 'ai_usage'\n ? { ai_blocks: keep, storage_blocks: null }\n : { ai_blocks: null, storage_blocks: keep }\n}\n\n/**\n * The \"+\" step's price line, ex VAT: \"£25/month · today £25\" (annual: \"£18.75/month · today £150\");\n * storage, charged for the rest of the period: \"£25/month · prorated today £8.33\".\n */\nexport function addStepCaption(kind: AddonKind, addons: ProjectAddons): string {\n const now = gbp(addonPriceExVatPence(kind, addons))\n const today = kind === 'storage' ? `prorated today ${now}` : `today ${now}`\n return `${gbp(blockMonthlyExVatPence(kind, addons))}/month · ${today}`\n}\n","/** Pro's allowance per month (one licence) — must match the backend. Legacy multi-pack plans get it per pack.\n * The live numbers for an existing project come from `project.addons`; these are only for\n * describing the plan before a project exists (new-project dialog, upgrade picker). */\nexport const PRO_AI_GBP = 10\nexport const PRO_STORAGE_GB = 5\nexport const PRO_COLLABORATORS = 1\nexport const COLLABORATOR_BLOCK_SIZE = 5\n\n/**\n * Add-on block sizes (£25 of AI credits or 25 GB), the same on every plan. Their prices come from the\n * products API (`addon_prices`) before a project exists, and from `project.addons` after.\n */\nexport const ADDON_BLOCK_AI_GBP = 25\nexport const STORAGE_BLOCK_GB = 25\n\n/** Default monthly usage cap for a new Pro project (pence); it limits automatic top-ups only. 0 = no automatic\n * top-ups, null = no limit. */\nexport const DEFAULT_SPEND_CAP_PENCE = 10000\n\n/** The automatic add-on triggers when less than this much AI credits (£) remains; the backend minimum. */\nexport const MIN_AUTO_TOPUP_THRESHOLD_GBP = 1\n\n/** Free-tier quotas — must match backend FREE_* constants in src/constants.py\n * (FREE_AI_GBP=1, FREE_STORAGE_GB=0.05 → 50 MB, FREE_COLLABORATORS=0). */\nexport const FREE_AI_GBP = 1\nexport const FREE_STORAGE_MB = 50\nexport const FREE_COLLABORATORS = 0\n\n/** Collaborator limits at or above this are \"unlimited\" (the backend reports 999 for dev). */\nexport const UNLIMITED_COLLABORATORS = 999\n\n/**\n * Suggested initial daily per-user AI credits cap (£), pre-filled when creating\n * a new project. The user can clear it to create a project with no cap.\n * Backend treats null as \"no enforcement\" — there is no server-side default.\n */\nexport const SUGGESTED_DAILY_CAP_PER_USER = 10\n","/**\n * Quota display — single source of truth.\n *\n * Builds the icon / label / formatted used-of-limit strings for a project's\n * quotas (AI credits in £, storage, collaborators). Shared by QuotaBadges and the\n * project tooltip so the numbers and units can never disagree (storage in GB on\n * plans of 1 GB or more, MB below).\n */\n\nimport { HardDrive, PoundSterling, Users } from 'lucide-react'\nimport { formatGbp } from '@arbidocs/react'\nimport type { QuotaMeterItem } from '../../settings'\nimport type { components } from '@arbidocs/client'\nimport { UNLIMITED_COLLABORATORS } from './plan-constants'\n\ntype Quotas = components['schemas']['ProjectResponse']['quotas']\n\nconst fmt = (v: number) => Math.round(v).toLocaleString('en-US')\n\n/** \"May 31, 2025\" — the one format every AI-usage-reset mention uses. */\nexport function formatResetDate(unixTimestamp: number | null | undefined): string | null {\n if (!unixTimestamp) return null\n const date = new Date(unixTimestamp * 1000)\n return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })\n}\n\n/**\n * When the project's AI credits allowance next resets, as a unix timestamp.\n * Pro projects report it directly (`addons.ai_usage_reset_at` — monthly, even on an\n * annual plan); monthly subscriptions otherwise reset with the billing period\n * (`current_period_end`); free projects reset on the backend-stamped signup\n * anniversary (`quotas.budget_reset_at`). Only a FUTURE date is returned — a stale\n * past stamp would read as a bug, not a promise.\n */\nexport function aiUsageResetTimestamp(project: {\n quotas?: { budget_reset_at?: number | null } | null\n current_period_end?: number | null\n plan_interval?: string | null\n addons?: { ai_usage_reset_at?: number | null } | null\n}): number | null {\n // An annual period end is a year away — never a monthly AI credits reset.\n const periodEnd = project.plan_interval === 'year' ? null : project.current_period_end\n const ts =\n project.addons?.ai_usage_reset_at ?? periodEnd ?? project.quotas?.budget_reset_at ?? null\n return ts && ts * 1000 > Date.now() ? ts : null\n}\n\n// Defensive: a quota field may be missing on partial/empty payloads — treat it\n// as 0 / 0 rather than crashing the whole row.\nconst quota = (q?: { used?: number; limit?: number }) => ({\n used: q?.used ?? 0,\n limit: q?.limit ?? 0,\n})\n/** GB from 1 GB plans up (\"2.4 GB\", \"5 GB\"), MB below (\"50 MB\"); `unit: false` drops the unit. */\nfunction storageText(gbValue: number, limitGb: number, unit = true): string {\n if (limitGb >= 1) {\n const n = Math.round(gbValue * 10) / 10\n return `${n.toLocaleString('en-US')}${unit ? ' GB' : ''}`\n }\n return `${fmt(gbValue * 1000)}${unit ? ' MB' : ''}`\n}\nconst pctOf = (used: number, limit: number) => (limit > 0 ? (used / limit) * 100 : 0)\n\nexport function getQuotaItems(quotas: Quotas): QuotaMeterItem[] {\n const aiUsage = quota(quotas?.ai_usage)\n const storage = quota(quotas?.storage_gb)\n const collaborators = quota(quotas?.collaborators)\n const unlimitedCollaborators = collaborators.limit === UNLIMITED_COLLABORATORS\n return [\n {\n key: 'ai-usage',\n icon: PoundSterling,\n label: 'AI credits',\n used: formatGbp(aiUsage.used),\n usedOfLimit: `${formatGbp(aiUsage.used)} / ${formatGbp(aiUsage.limit)}`,\n pct: pctOf(aiUsage.used, aiUsage.limit),\n },\n {\n key: 'storage',\n icon: HardDrive,\n label: 'Storage',\n used: storageText(storage.used, storage.limit),\n usedOfLimit: `${storageText(storage.used, storage.limit, false)} / ${storageText(\n storage.limit,\n storage.limit\n )}`,\n pct: pctOf(storage.used, storage.limit),\n },\n {\n key: 'collaborators',\n icon: Users,\n label: 'Collaborators',\n used: fmt(collaborators.used),\n usedOfLimit: unlimitedCollaborators\n ? `${fmt(collaborators.used)} / Unlimited`\n : `${fmt(collaborators.used)} / ${fmt(collaborators.limit)}`,\n // Zero seats (Free) IS the cap: 0 / 0 shows a full destructive bar,\n // because you can't add anyone — the truth the upgrade door answers.\n // (Only when the quota is actually present; a missing field on a\n // partial payload stays an empty bar, not a false alarm.)\n pct: unlimitedCollaborators\n ? 0\n : quotas?.collaborators && collaborators.limit === 0\n ? 100\n : pctOf(collaborators.used, collaborators.limit),\n },\n ]\n}\n","/**\n * The `detail` of a refused billing call: the response body's `detail` (a string, or the\n * `{code, message}` of a payment refusal), whether the error is an SDK `ArbiApiError` (body in\n * `apiError`) or the body itself.\n */\nexport function apiErrorDetail(error: unknown): unknown {\n const body = (error as { apiError?: unknown } | null)?.apiError ?? error\n return (body as { detail?: unknown } | null)?.detail\n}\n","/**\n * Tax-inclusive totals from the backend's Stripe Tax quotes (`TaxQuoteResponse`, plan-change\n * previews, add-on quotes): \"£48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\", \"£40.00 · no VAT\n * (reverse charge)\", \"£40.00 · no tax\".\n */\n\nimport type { components } from '@arbidocs/client'\nimport { apiErrorDetail } from './api-error'\n\nexport type TaxQuote = components['schemas']['TaxQuoteResponse']\n\n/** Countries whose tax depends on the postcode: the field is shown up front. */\nexport const POSTCODE_COUNTRIES: readonly string[] = ['US', 'CA']\n\n/** The quote error that asks for a ZIP / postal code. */\nexport const TAX_LOCATION_INVALID = 'TAX_LOCATION_INVALID'\n\n/** \"£42.00\" — always pence, so the parts of a total add up on screen. */\nexport function money2(pence: number, currency = 'gbp'): string {\n const symbol = { gbp: '£', usd: '$', eur: '€' }[currency.toLowerCase()] ?? `${currency} `\n return `${symbol}${(pence / 100).toLocaleString('en-GB', {\n minimumFractionDigits: 2,\n maximumFractionDigits: 2,\n })}`\n}\n\nexport interface TaxParts {\n subtotal: number\n tax: number\n total: number\n /** \"VAT 20%\"; null when no tax is due. */\n label?: string | null\n reverseCharge?: boolean\n}\n\n/** \"VAT\" for a VAT label, else \"tax\". */\nfunction taxKind(label: string | null | undefined): string {\n return /^vat\\b/i.test(label ?? '') ? 'VAT' : 'tax'\n}\n\n/** What follows the total: \"incl. VAT (£40.00 + £8.00 VAT 20%)\", \"· no VAT (reverse charge)\", \"· no tax\". */\nexport function taxDetail({ subtotal, tax, label, reverseCharge }: TaxParts): string {\n if (reverseCharge) return '· no VAT (reverse charge)'\n if (tax <= 0) return '· no tax'\n return `incl. ${taxKind(label)} (${money2(subtotal)} + ${money2(tax)} ${label ?? 'tax'})`\n}\n\n/** \"£48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\", \"£40.00 · no VAT (reverse charge)\", \"£40.00 · no tax\". */\nexport function taxSummary(parts: TaxParts): string {\n return `${money2(parts.total)} ${taxDetail(parts)}`\n}\n\n/** \"£21.00 incl. VAT\" / \"£17.50\" — a total with only the word for its tax. */\nexport function totalWithTaxWord({ total, tax, label }: TaxParts): string {\n return tax > 0 ? `${money2(total)} incl. ${taxKind(label)}` : money2(total)\n}\n\n/** The parts of a quote response. */\nexport function quoteParts(quote: TaxQuote): TaxParts {\n return {\n subtotal: quote.subtotal_pence,\n tax: quote.tax_pence,\n total: quote.total_pence,\n label: quote.tax_label,\n reverseCharge: quote.reverse_charge,\n }\n}\n\n/** \"Total today £48.00 incl. VAT (£40.00 + £8.00 VAT 20%)\". */\nexport function quoteTotalLine(quote: TaxQuote): string {\n return `Total today ${taxSummary(quoteParts(quote))}`\n}\n\n/** The API error code (`detail.code`) and message, if any. */\nexport function quoteError(error: unknown): { code?: string; message?: string } {\n const detail = apiErrorDetail(error)\n if (detail && typeof detail === 'object') {\n const { code, message } = detail as { code?: unknown; message?: unknown }\n return {\n code: typeof code === 'string' ? code : undefined,\n message: typeof message === 'string' ? message : undefined,\n }\n }\n return { message: typeof detail === 'string' ? detail : undefined }\n}\n","/**\n * Pro plan price helpers. Prices come from Stripe via GET /v1/user/products (source of truth).\n * One plan is sold: Pro, one licence (£10 of AI credits + 5 GB a month). Each price lists\n * `pack_prices` for that one licence — `amount` incl. UK VAT and `amount_ex_vat` — and\n * `addon_prices` (one add-on block on that interval). Display them as-is.\n * Legacy subscriptions with several packs keep working; their name and price come from the project.\n */\n\nimport type { components } from '@arbidocs/client'\nimport { formatGbp } from '@arbidocs/react'\nimport { formatMoney } from './reward-balance'\nimport { formatPeriodEnd } from './addon-pricing'\nimport { taxSummary } from './tax-quote'\n\ntype PlanChangePreview = components['schemas']['PlanChangePreview']\ntype ProductPrice = components['schemas']['ProductPrice']\ntype ProductResponse = components['schemas']['ProductResponse']\ntype PackPrice = components['schemas']['PackPrice']\ntype AddonPrice = components['schemas']['AddonPrice']\n\nexport type PlanInterval = 'month' | 'year'\n\nconst gbp = (pence: number) => formatMoney(pence, 'gbp')\n\n/** The headline price: \"£40 ex VAT\". */\nexport function plusVat(exVatPence: number, currency = 'gbp'): string {\n return `${formatMoney(exVatPence, currency)} ex VAT`\n}\n\n/** The one licence Pro sells. Other pack counts exist only on legacy subscriptions (displayed, not sold). */\nexport const PRO_PACKS = 1\n\n/** A plan the pickers offer. */\nexport type PlanChoice = 'free' | 'pro'\n\n/** Packs per plan choice (0 = Free). */\nexport const PLAN_CHOICE_PACKS: Record<PlanChoice, number> = {\n free: 0,\n pro: PRO_PACKS,\n}\n\n/** \"Free\" (0), \"Pro\" (1), otherwise \"Pro ×N\" (a legacy multi-pack plan; the API's own name wins). */\nexport function planNameForPacks(packs: number | null | undefined): string {\n const n = packs ?? 0\n if (n <= 0) return 'Free'\n if (n === PRO_PACKS) return 'Pro'\n return `Pro ×${n}`\n}\n\n/** The plan-change panel's fixed lines, each with a test id. */\nexport interface PlanChangeLine {\n id: 'pay-today' | 'from' | 'current-until' | 'no-change' | 'awaiting-bank'\n text: string\n}\n\n/**\n * What a plan change costs, from the backend's Stripe invoice preview (taxed for the customer's\n * address), as fixed lines:\n * - \"Pay today £42.00 incl. VAT (£35.00 + £7.00 VAT 20%)\" (\"Pay today £0\" for a change that waits)\n * - \"From 16 Nov 2026: £210.00 incl. VAT (£175.00 + £35.00 VAT 20%) a month\"\n * - \"Current plan until 16 Nov 2026\" for a change that waits for the period end.\n * \"No change\" when the selection is what the project already has; \"Waiting for your bank\" while\n * an earlier upgrade payment awaits 3-D Secure.\n */\nexport function planPreviewLines(preview: PlanChangePreview): PlanChangeLine[] {\n if (preview.unchanged) return [{ id: 'no-change', text: 'No change' }]\n const date = formatPeriodEnd(preview.next_renewal_at)\n const cadence = preview.interval === 'year' ? 'year' : 'month'\n const lines: PlanChangeLine[] = [\n preview.awaiting_confirmation\n ? { id: 'awaiting-bank', text: 'Waiting for your bank' }\n : {\n id: 'pay-today',\n text: preview.effective_now\n ? `Pay today ${taxSummary({\n subtotal: preview.due_now_ex_vat_pence,\n tax: preview.due_now_tax_pence,\n total: preview.due_now_pence,\n label: preview.tax_label,\n })}`\n : `Pay today ${gbp(0)}`,\n },\n {\n id: 'from',\n text: `${date ? `From ${date}` : 'From renewal'}: ${taxSummary({\n subtotal: preview.next_renewal_ex_vat_pence,\n tax: preview.next_renewal_tax_pence,\n total: preview.next_renewal_pence,\n label: preview.tax_label,\n })} a ${cadence}`,\n },\n ]\n if (!preview.effective_now) {\n lines.push({ id: 'current-until', text: `Current plan until ${date ?? 'renewal'}` })\n }\n return lines\n}\n\n/** Stand-in for \"Pay today\" when the preview failed for an upgrade. */\nexport const PAY_TODAY_UNPRICED = 'Pay today: the difference for the rest of this period'\n\n/** One add-on block's price on this interval's plan price (`addon_prices`), if listed. */\nexport function addonPrice(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage' = 'ai_usage'\n): AddonPrice | undefined {\n return price?.addon_prices?.find((p) => p.kind === kind)\n}\n\n/** Per add-on block of `kind` per month, ex VAT (annual plans: the monthly equivalent). */\nexport function addonMonthlyExVat(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage' = 'ai_usage'\n): number | undefined {\n return addonPrice(price, kind)?.monthly_ex_vat\n}\n\n/** What a checkout buys: \"Pro\", \"Pro + 2 × £25 of AI credits + 1 × 25 GB\". */\nexport function checkoutItemsLabel(\n aiBlocks: number,\n storageBlocks: number,\n collaboratorBlocks = 0,\n price?: ProductPrice\n): string {\n const ai = addonPrice(price, 'ai_usage')?.ai_gbp\n const storage = addonPrice(price, 'storage')?.storage_gb\n const collaborators = price?.addon_prices?.find((p) => p.kind === 'collaborators')?.collaborators\n return [\n 'Pro',\n aiBlocks > 0 &&\n (ai != null\n ? `${aiBlocks} × ${formatGbp(ai)} of AI credits`\n : `${aiBlocks} AI credits add-ons`),\n storageBlocks > 0 &&\n (storage != null ? `${storageBlocks} × ${storage} GB` : `${storageBlocks} storage add-ons`),\n collaboratorBlocks > 0 &&\n (collaborators != null\n ? `${collaboratorBlocks * collaborators} extra collaborators`\n : `${collaboratorBlocks} collaborator add-ons`),\n ]\n .filter(Boolean)\n .join(' + ')\n}\n\n/** The price of `packs` packs on this interval's price, if Stripe lists it (only one licence is listed). */\nexport function packPrice(\n price: ProductPrice | undefined,\n packs: number = PRO_PACKS\n): PackPrice | undefined {\n return price?.pack_prices?.find((p) => p.packs === packs)\n}\n\nexport interface ProPrices {\n monthly?: ProductPrice\n annual?: ProductPrice\n}\n\n/** The Pro product's monthly and annual prices (default price per interval wins). */\nexport function findProPrices(products: ProductResponse[] | undefined): ProPrices {\n const prices = products?.[0]?.prices ?? []\n const pick = (interval: PlanInterval) =>\n prices.find((p) => p.interval === interval && p.default_price) ??\n prices.find((p) => p.interval === interval)\n return { monthly: pick('month'), annual: pick('year') }\n}\n\n/** Annual savings vs 12 × monthly, as a whole percent (e.g. £360 vs £480 → 25). */\nexport function calcSavingsPercent(\n monthlyPrice: ProductPrice | undefined,\n annualPrice: ProductPrice | undefined\n): number | null {\n const monthly = packPrice(monthlyPrice)?.amount ?? monthlyPrice?.amount\n const annual = packPrice(annualPrice)?.amount ?? annualPrice?.amount\n if (monthly == null || annual == null || monthly === 0) return null\n const savings = Math.round(((monthly * 12 - annual) / (monthly * 12)) * 100)\n return savings > 0 ? savings : null\n}\n\n/** Most add-on blocks of each kind a checkout may include (the backend's limit). */\nexport const MAX_CHECKOUT_BLOCKS = 100\n\n/** \"£25/month ex VAT each\", annual: \"£225/year ex VAT each\": one block on the chosen interval. */\nexport function checkoutBlockPriceText(\n price: ProductPrice | undefined,\n kind: 'ai_usage' | 'storage'\n): string | null {\n const block = addonPrice(price, kind)\n if (!block) return null\n const currency = price?.currency ?? 'gbp'\n return price?.interval === 'year'\n ? `${formatMoney(block.amount_ex_vat, currency)}/year ex VAT each`\n : `${formatMoney(block.amount_ex_vat, currency)}/month ex VAT each`\n}\n","import type { PlanBadgeProps } from '../../settings'\nimport type { components } from '@arbidocs/client'\nimport { planNameForPacks } from './plan-pricing'\n\ntype ProjectBillingFields = Pick<\n components['schemas']['ProjectResponse'],\n 'subscription' | 'plan_interval'\n> &\n Partial<Pick<components['schemas']['ProjectResponse'], 'packs' | 'addons'>>\n\n/** A project is on Pro when it has a billing interval (null = Free or checkout pending). */\nexport function isProProject(project: Pick<ProjectBillingFields, 'plan_interval'>): boolean {\n return project.plan_interval != null\n}\n\n/** \"Pro\", or a legacy multi-pack plan's name (\"Pro ×N\") — the API's name when it sends one (owner view). */\nexport function proTierName(project: ProjectBillingFields): string {\n return project.addons?.plan_name || planNameForPacks(Math.max(project.packs ?? 1, 1))\n}\n\n/** \"Free\", \"Pro monthly\", \"Pro annual\", \"Pro ×3 monthly\" (legacy) or \"Dev\" — the full plan name. */\nexport function planName(project: ProjectBillingFields): string {\n if (project.subscription === 'dev') return 'Dev'\n if (project.subscription === 'suspended') return 'Subscription ended'\n if (!isProProject(project)) return 'Free'\n return `${proTierName(project)} ${project.plan_interval === 'year' ? 'annual' : 'monthly'}`\n}\n\n/**\n * Maps a project's billing state to the `PlanBadge` block's label + tone.\n * Pro reads as accent, Free/dev as neutral, payment trouble or cancelling as pending.\n */\nexport function planBadgeView(\n project: ProjectBillingFields\n): Pick<PlanBadgeProps, 'label' | 'tone'> {\n const { subscription } = project\n const label =\n subscription === 'suspended'\n ? 'Subscription ended'\n : subscription === 'canceled'\n ? 'Cancelling'\n : subscription === 'dev'\n ? 'Dev'\n : isProProject(project)\n ? proTierName(project)\n : 'Free'\n\n const tone =\n subscription === 'dev'\n ? 'dev'\n : subscription === 'suspended' || subscription === 'canceled' || subscription === 'past_due'\n ? 'pending'\n : isProProject(project)\n ? 'paid'\n : 'free'\n\n return { label, tone }\n}\n","/**\n * What a project's group in the workspace list says about the project itself: its plan badge (own\n * projects, where plans are sold), the usage readout beside its name, and the plan and limits\n * breakdown behind the name. One source for the cards' bands and the table's headings.\n */\nimport type { ReactNode } from 'react'\nimport type { LucideIcon } from 'lucide-react'\nimport type { components } from '@arbidocs/client'\nimport { UsageMeter } from '../../components/UsageMeter'\nimport { PlanBadge, QuotaMeter } from '../../settings'\nimport { ProjectBreakdown, ProjectReadout } from '../../home/ProjectBandParts'\nimport { planBadgeView } from '../billing/plan-badge'\nimport { getQuotaItems } from '../billing/quota-items'\n\ntype Project = components['schemas']['ProjectResponse']\n\nexport interface ProjectGroupBilling {\n /** The plan badge: own projects only, where plans are sold. */\n planBadge?: ReactNode\n /** Plan and usage, right after the name (projects with quotas). */\n readout?: ReactNode\n /** The plan and limits panel behind the name; meters for own projects only. */\n breakdown?: ReactNode\n}\n\nexport function projectGroupBilling(\n project: Project | undefined,\n { isOwned, stripeEnabled }: { isOwned: boolean; stripeEnabled: boolean }\n): ProjectGroupBilling {\n if (!project) return {}\n const planBadge = isOwned && stripeEnabled ? <PlanBadge {...planBadgeView(project)} /> : undefined\n const quotaItems = project.quotas ? getQuotaItems(project.quotas) : []\n const readout = project.quotas ? (\n <ProjectReadout\n plan={\n planBadge ? { badge: planBadge, testId: `project-tier-${project.external_id}` } : undefined\n }\n usage={{\n testId: `project-usage-${project.external_id}`,\n // Bare numbers without plans for sale: a glance beside the name, not the Plan & Usage detail.\n node: (\n <QuotaMeter\n items={quotaItems}\n size=\"sm\"\n showTooltips={false}\n showBars={stripeEnabled}\n showPercent={false}\n />\n ),\n }}\n />\n ) : (\n planBadge\n )\n const breakdown = (\n <ProjectBreakdown\n name={project.name}\n badge={planBadge}\n meters={\n isOwned\n ? quotaItems.map(({ key, icon, label, usedOfLimit, pct }) => (\n <UsageMeter\n key={key}\n icon={icon as LucideIcon}\n label={label}\n value={usedOfLimit}\n pct={pct}\n />\n ))\n : undefined\n }\n dailyCap={{\n label: 'Daily cap',\n value:\n project.daily_cap_per_user == null\n ? 'No limit'\n : project.daily_cap_per_user.toLocaleString('en-US'),\n }}\n />\n )\n return { planBadge, readout, breakdown }\n}\n"]}
|
|
@@ -9,7 +9,7 @@ var chunkJNVJQCGF_cjs = require('./chunk-JNVJQCGF.cjs');
|
|
|
9
9
|
var chunk5OUOPKGE_cjs = require('./chunk-5OUOPKGE.cjs');
|
|
10
10
|
var chunk53RXGFJ6_cjs = require('./chunk-53RXGFJ6.cjs');
|
|
11
11
|
var chunkQN5GTCPB_cjs = require('./chunk-QN5GTCPB.cjs');
|
|
12
|
-
var
|
|
12
|
+
var chunkLM5CPXJO_cjs = require('./chunk-LM5CPXJO.cjs');
|
|
13
13
|
var chunkJH6JTI76_cjs = require('./chunk-JH6JTI76.cjs');
|
|
14
14
|
var chunkZ3LFF3YT_cjs = require('./chunk-Z3LFF3YT.cjs');
|
|
15
15
|
var chunkNSD37OF5_cjs = require('./chunk-NSD37OF5.cjs');
|
|
@@ -688,7 +688,7 @@ function ConnectedThreadTaskBar({
|
|
|
688
688
|
] }),
|
|
689
689
|
error && /* @__PURE__ */ jsxRuntime.jsx("span", { role: "alert", className: "text-xs text-destructive", children: error }),
|
|
690
690
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
691
|
-
|
|
691
|
+
chunkLM5CPXJO_cjs.RenameDialog,
|
|
692
692
|
{
|
|
693
693
|
open: renameOpen,
|
|
694
694
|
onClose: () => setRenameOpen(false),
|
|
@@ -2502,5 +2502,5 @@ exports.useRequiredLabels = useRequiredLabels;
|
|
|
2502
2502
|
exports.useTaskPeople = useTaskPeople;
|
|
2503
2503
|
exports.useWorkflowsPageState = useWorkflowsPageState;
|
|
2504
2504
|
exports.workflowRunView = workflowRunView;
|
|
2505
|
-
//# sourceMappingURL=chunk-
|
|
2506
|
-
//# sourceMappingURL=chunk-
|
|
2505
|
+
//# sourceMappingURL=chunk-OXHAZN43.cjs.map
|
|
2506
|
+
//# sourceMappingURL=chunk-OXHAZN43.cjs.map
|