@stigmer/react 3.39.1-dev.20260928194002 → 3.40.1
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/billing/AutoRechargeCard.js +1 -1
- package/billing/AutoRechargeCard.js.map +1 -1
- package/billing/BillingSection.d.ts +18 -1
- package/billing/BillingSection.d.ts.map +1 -1
- package/billing/BillingSection.js +21 -11
- package/billing/BillingSection.js.map +1 -1
- package/billing/ChangePlanDialog.d.ts +46 -0
- package/billing/ChangePlanDialog.d.ts.map +1 -0
- package/billing/ChangePlanDialog.js +90 -0
- package/billing/ChangePlanDialog.js.map +1 -0
- package/billing/CreditPackGrid.d.ts +1 -1
- package/billing/CreditPackGrid.js +1 -1
- package/billing/LowBalanceBanner.js +1 -1
- package/billing/LowBalanceBanner.js.map +1 -1
- package/billing/PaymentMethodCard.d.ts +9 -1
- package/billing/PaymentMethodCard.d.ts.map +1 -1
- package/billing/PaymentMethodCard.js +2 -2
- package/billing/PaymentMethodCard.js.map +1 -1
- package/billing/PlanCard.d.ts +27 -0
- package/billing/PlanCard.d.ts.map +1 -0
- package/billing/PlanCard.js +68 -0
- package/billing/PlanCard.js.map +1 -0
- package/billing/PlanPicker.d.ts +34 -0
- package/billing/PlanPicker.d.ts.map +1 -0
- package/billing/PlanPicker.js +46 -0
- package/billing/PlanPicker.js.map +1 -0
- package/billing/PlanSection.d.ts +45 -0
- package/billing/PlanSection.d.ts.map +1 -0
- package/billing/PlanSection.js +136 -0
- package/billing/PlanSection.js.map +1 -0
- package/billing/UpgradeNotice.d.ts +47 -0
- package/billing/UpgradeNotice.d.ts.map +1 -0
- package/billing/UpgradeNotice.js +49 -0
- package/billing/UpgradeNotice.js.map +1 -0
- package/billing/credit-packs.d.ts.map +1 -1
- package/billing/credit-packs.js +4 -1
- package/billing/credit-packs.js.map +1 -1
- package/billing/index.d.ts +26 -1
- package/billing/index.d.ts.map +1 -1
- package/billing/index.js +12 -0
- package/billing/index.js.map +1 -1
- package/billing/plan-features.d.ts +60 -0
- package/billing/plan-features.d.ts.map +1 -0
- package/billing/plan-features.js +128 -0
- package/billing/plan-features.js.map +1 -0
- package/billing/plan-state.d.ts +43 -0
- package/billing/plan-state.d.ts.map +1 -0
- package/billing/plan-state.js +69 -0
- package/billing/plan-state.js.map +1 -0
- package/billing/redirect.d.ts +31 -0
- package/billing/redirect.d.ts.map +1 -0
- package/billing/redirect.js +30 -0
- package/billing/redirect.js.map +1 -0
- package/billing/useBillingAccount.d.ts +10 -1
- package/billing/useBillingAccount.d.ts.map +1 -1
- package/billing/useBillingAccount.js +2 -2
- package/billing/useBillingAccount.js.map +1 -1
- package/billing/useChangePlan.d.ts +51 -0
- package/billing/useChangePlan.d.ts.map +1 -0
- package/billing/useChangePlan.js +61 -0
- package/billing/useChangePlan.js.map +1 -0
- package/billing/useCreateBillingPortalSession.d.ts +6 -3
- package/billing/useCreateBillingPortalSession.d.ts.map +1 -1
- package/billing/useCreateBillingPortalSession.js +11 -4
- package/billing/useCreateBillingPortalSession.js.map +1 -1
- package/billing/useCreateCheckoutSession.d.ts +7 -5
- package/billing/useCreateCheckoutSession.d.ts.map +1 -1
- package/billing/useCreateCheckoutSession.js +5 -3
- package/billing/useCreateCheckoutSession.js.map +1 -1
- package/billing/useCreatePaymentMethodSetupSession.d.ts +28 -0
- package/billing/useCreatePaymentMethodSetupSession.d.ts.map +1 -0
- package/billing/useCreatePaymentMethodSetupSession.js +50 -0
- package/billing/useCreatePaymentMethodSetupSession.js.map +1 -0
- package/billing/useEntitlements.d.ts +46 -0
- package/billing/useEntitlements.d.ts.map +1 -0
- package/billing/useEntitlements.js +37 -0
- package/billing/useEntitlements.js.map +1 -0
- package/billing/usePeriodEstimate.d.ts +35 -0
- package/billing/usePeriodEstimate.d.ts.map +1 -0
- package/billing/usePeriodEstimate.js +29 -0
- package/billing/usePeriodEstimate.js.map +1 -0
- package/billing/usePlans.d.ts +38 -0
- package/billing/usePlans.d.ts.map +1 -0
- package/billing/usePlans.js +33 -0
- package/billing/usePlans.js.map +1 -0
- package/billing/useSubscription.d.ts +39 -0
- package/billing/useSubscription.d.ts.map +1 -0
- package/billing/useSubscription.js +40 -0
- package/billing/useSubscription.js.map +1 -0
- package/channel-app/ChannelAppDetailPanel.d.ts +5 -0
- package/channel-app/ChannelAppDetailPanel.d.ts.map +1 -1
- package/channel-app/ChannelAppDetailPanel.js +9 -3
- package/channel-app/ChannelAppDetailPanel.js.map +1 -1
- package/channel-app/ChannelAppListPanel.d.ts +13 -4
- package/channel-app/ChannelAppListPanel.d.ts.map +1 -1
- package/channel-app/ChannelAppListPanel.js +5 -5
- package/channel-app/ChannelAppListPanel.js.map +1 -1
- package/channel-app/copy.d.ts +15 -0
- package/channel-app/copy.d.ts.map +1 -0
- package/channel-app/copy.js +15 -0
- package/channel-app/copy.js.map +1 -0
- package/index.d.ts +4 -2
- package/index.d.ts.map +1 -1
- package/index.js +4 -1
- package/index.js.map +1 -1
- package/internal/features.d.ts +11 -0
- package/internal/features.d.ts.map +1 -0
- package/internal/features.js +42 -0
- package/internal/features.js.map +1 -0
- package/licenses/license-format.d.ts +5 -3
- package/licenses/license-format.d.ts.map +1 -1
- package/licenses/license-format.js +10 -19
- package/licenses/license-format.js.map +1 -1
- package/package.json +5 -5
- package/plan-catalog/PlanCatalogConsole.d.ts +22 -0
- package/plan-catalog/PlanCatalogConsole.d.ts.map +1 -0
- package/plan-catalog/PlanCatalogConsole.js +90 -0
- package/plan-catalog/PlanCatalogConsole.js.map +1 -0
- package/plan-catalog/PlanCreateForm.d.ts +17 -0
- package/plan-catalog/PlanCreateForm.d.ts.map +1 -0
- package/plan-catalog/PlanCreateForm.js +52 -0
- package/plan-catalog/PlanCreateForm.js.map +1 -0
- package/plan-catalog/PlansAccessNotice.d.ts +10 -0
- package/plan-catalog/PlansAccessNotice.d.ts.map +1 -0
- package/plan-catalog/PlansAccessNotice.js +13 -0
- package/plan-catalog/PlansAccessNotice.js.map +1 -0
- package/plan-catalog/index.d.ts +8 -0
- package/plan-catalog/index.d.ts.map +1 -0
- package/plan-catalog/index.js +10 -0
- package/plan-catalog/index.js.map +1 -0
- package/plan-catalog/plan-draft.d.ts +30 -0
- package/plan-catalog/plan-draft.d.ts.map +1 -0
- package/plan-catalog/plan-draft.js +91 -0
- package/plan-catalog/plan-draft.js.map +1 -0
- package/plan-catalog/usePlanMutations.d.ts +42 -0
- package/plan-catalog/usePlanMutations.d.ts.map +1 -0
- package/plan-catalog/usePlanMutations.js +57 -0
- package/plan-catalog/usePlanMutations.js.map +1 -0
- package/settings/ChannelAppsSection.d.ts +7 -0
- package/settings/ChannelAppsSection.d.ts.map +1 -1
- package/settings/ChannelAppsSection.js +15 -2
- package/settings/ChannelAppsSection.js.map +1 -1
- package/settings/TeamsSection.d.ts.map +1 -1
- package/settings/TeamsSection.js +19 -2
- package/settings/TeamsSection.js.map +1 -1
- package/settings/settings-nav.d.ts.map +1 -1
- package/settings/settings-nav.js +7 -1
- package/settings/settings-nav.js.map +1 -1
- package/settings/useSettingsNavGroups.d.ts.map +1 -1
- package/settings/useSettingsNavGroups.js +4 -1
- package/settings/useSettingsNavGroups.js.map +1 -1
- package/src/billing/AutoRechargeCard.tsx +2 -2
- package/src/billing/BillingSection.tsx +68 -11
- package/src/billing/ChangePlanDialog.tsx +221 -0
- package/src/billing/CreditPackGrid.tsx +1 -1
- package/src/billing/LowBalanceBanner.tsx +1 -1
- package/src/billing/PaymentMethodCard.tsx +28 -2
- package/src/billing/PlanCard.tsx +171 -0
- package/src/billing/PlanPicker.tsx +178 -0
- package/src/billing/PlanSection.tsx +234 -0
- package/src/billing/UpgradeNotice.tsx +95 -0
- package/src/billing/credit-packs.ts +4 -1
- package/src/billing/index.ts +26 -1
- package/src/billing/plan-features.ts +162 -0
- package/src/billing/plan-state.ts +92 -0
- package/src/billing/redirect.ts +45 -0
- package/src/billing/useBillingAccount.ts +12 -0
- package/src/billing/useChangePlan.ts +115 -0
- package/src/billing/useCreateBillingPortalSession.ts +17 -6
- package/src/billing/useCreateCheckoutSession.ts +12 -7
- package/src/billing/useCreatePaymentMethodSetupSession.ts +77 -0
- package/src/billing/useEntitlements.ts +80 -0
- package/src/billing/usePeriodEstimate.ts +59 -0
- package/src/billing/usePlans.ts +65 -0
- package/src/billing/useSubscription.ts +70 -0
- package/src/channel-app/ChannelAppDetailPanel.tsx +179 -158
- package/src/channel-app/ChannelAppListPanel.tsx +14 -4
- package/src/channel-app/copy.ts +16 -0
- package/src/index.ts +54 -0
- package/src/internal/features.ts +49 -0
- package/src/licenses/license-format.ts +10 -19
- package/src/plan-catalog/PlanCatalogConsole.tsx +197 -0
- package/src/plan-catalog/PlanCreateForm.tsx +167 -0
- package/src/plan-catalog/PlansAccessNotice.tsx +20 -0
- package/src/plan-catalog/index.ts +14 -0
- package/src/plan-catalog/plan-draft.ts +116 -0
- package/src/plan-catalog/usePlanMutations.ts +94 -0
- package/src/settings/ChannelAppsSection.tsx +19 -2
- package/src/settings/TeamsSection.tsx +20 -2
- package/src/settings/settings-nav.ts +7 -0
- package/src/settings/useSettingsNavGroups.ts +8 -1
- package/src/team/CreateTeamForm.tsx +12 -3
- package/src/team/TeamMembersPanel.tsx +6 -2
- package/team/CreateTeamForm.d.ts +6 -1
- package/team/CreateTeamForm.d.ts.map +1 -1
- package/team/CreateTeamForm.js +4 -2
- package/team/CreateTeamForm.js.map +1 -1
- package/team/TeamMembersPanel.d.ts.map +1 -1
- package/team/TeamMembersPanel.js +3 -1
- package/team/TeamMembersPanel.js.map +1 -1
|
@@ -4,9 +4,11 @@ import { useCallback, useId, useRef, useState } from "react";
|
|
|
4
4
|
import { ChannelAppListPanel } from "../channel-app/ChannelAppListPanel.js";
|
|
5
5
|
import { CreateChannelAppForm, } from "../channel-app/CreateChannelAppForm.js";
|
|
6
6
|
import { ChannelAppDetailPanel } from "../channel-app/ChannelAppDetailPanel.js";
|
|
7
|
+
import { CHANNEL_APPS_MANAGED_BY_ADMINS } from "../channel-app/copy.js";
|
|
7
8
|
import { useDeploymentMode } from "../deployment-mode.js";
|
|
9
|
+
import { useCheckPermission } from "../iam-policy/useCheckPermission.js";
|
|
8
10
|
import { CloudFeatureNotice } from "../internal/CloudFeatureNotice.js";
|
|
9
|
-
import { useActiveOrgSlug } from "../organization/OrgProvider.js";
|
|
11
|
+
import { useActiveOrgId, useActiveOrgSlug } from "../organization/OrgProvider.js";
|
|
10
12
|
/**
|
|
11
13
|
* Settings section for the organization's channel apps — customer-owned
|
|
12
14
|
* provider apps (Slack apps, Meta WhatsApp apps) that agent channels
|
|
@@ -17,13 +19,24 @@ import { useActiveOrgSlug } from "../organization/OrgProvider.js";
|
|
|
17
19
|
*
|
|
18
20
|
* After registering an app here, the connect dialog on any agent's
|
|
19
21
|
* Channels tab offers it as the serving app.
|
|
22
|
+
*
|
|
23
|
+
* "New channel app" is offered once the server confirms the caller holds
|
|
24
|
+
* `can_create_channel_app` on the organization. The list shows only the
|
|
25
|
+
* apps the caller may view (their creator, anyone granted viewer on one,
|
|
26
|
+
* and the organization's admins), so a caller who may not create channel
|
|
27
|
+
* apps is told that the organization's admins manage them rather than that
|
|
28
|
+
* none is registered.
|
|
20
29
|
*/
|
|
21
30
|
export function ChannelAppsSection() {
|
|
22
31
|
const headingId = useId();
|
|
23
32
|
const org = useActiveOrgSlug();
|
|
33
|
+
const orgId = useActiveOrgId();
|
|
24
34
|
// Channel installs (the consumer of these credentials) are cloud-only;
|
|
25
35
|
// gate the whole section the way the Channels tab gates connects.
|
|
26
36
|
const installsAvailable = useDeploymentMode() === "cloud";
|
|
37
|
+
const createCheck = useCheckPermission(installsAvailable && orgId ? { kind: "organization", id: orgId } : null, "can_create_channel_app");
|
|
38
|
+
const canCreate = !createCheck.isLoading && createCheck.allowed;
|
|
39
|
+
const deniedCreate = !createCheck.isLoading && !createCheck.allowed;
|
|
27
40
|
const [flow, setFlow] = useState({ phase: "idle" });
|
|
28
41
|
const listRefetchRef = useRef(null);
|
|
29
42
|
const handleRefetchRef = useCallback((refetch) => {
|
|
@@ -44,6 +57,6 @@ export function ChannelAppsSection() {
|
|
|
44
57
|
listRefetchRef.current?.();
|
|
45
58
|
setFlow({ phase: "idle" });
|
|
46
59
|
}, []);
|
|
47
|
-
return (_jsxs("section", { "aria-labelledby": headingId, children: [_jsxs("div", { className: "stg:mb-3 stg:flex stg:items-center stg:justify-between", children: [_jsx("h2", { id: headingId, className: "stg:text-foreground stg:text-sm stg:font-semibold", children: "Channel Apps" }), installsAvailable && org && flow.phase === "idle" && (_jsx("button", { type: "button", onClick: () => setFlow({ phase: "creating" }), className: "stg:text-primary stg:hover:text-foreground stg:text-xs stg:font-medium stg:transition-colors", children: "+ New channel app" }))] }), _jsx("p", { className: "stg:text-muted-foreground stg:mb-4 stg:text-xs", children: "Your own provider apps for serving agent channels. A Slack app gives the bot your name and icon, and lets multiple agents serve the same workspace \u2014 one per app. A Meta app is how WhatsApp channels connect \u2014 every WhatsApp channel installs through one." }), !installsAvailable ? (_jsx(CloudFeatureNotice, { children: "Channel apps are not available in local mode. Channel installs require Stigmer Cloud." })) : flow.phase === "creating" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(CreateChannelAppForm, { org: org, onCreated: handleCreated, onCancel: () => setFlow({ phase: "idle" }) }) })) : flow.phase === "editing" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(ChannelAppDetailPanel, { channelApp: flow.channelApp, createHandoff: flow.createHandoff, onUpdated: handleUpdated, onDeleted: handleDeleted, onBack: () => setFlow({ phase: "idle" }) }) })) : (_jsx(ChannelAppListPanel, { org: org, onEdit: (app) => setFlow({ phase: "editing", channelApp: app }), onRefetchRef: handleRefetchRef }))] }));
|
|
60
|
+
return (_jsxs("section", { "aria-labelledby": headingId, children: [_jsxs("div", { className: "stg:mb-3 stg:flex stg:items-center stg:justify-between", children: [_jsx("h2", { id: headingId, className: "stg:text-foreground stg:text-sm stg:font-semibold", children: "Channel Apps" }), installsAvailable && org && canCreate && flow.phase === "idle" && (_jsx("button", { type: "button", onClick: () => setFlow({ phase: "creating" }), className: "stg:text-primary stg:hover:text-foreground stg:text-xs stg:font-medium stg:transition-colors", children: "+ New channel app" }))] }), _jsx("p", { className: "stg:text-muted-foreground stg:mb-4 stg:text-xs", children: "Your own provider apps for serving agent channels. A Slack app gives the bot your name and icon, and lets multiple agents serve the same workspace \u2014 one per app. A Meta app is how WhatsApp channels connect \u2014 every WhatsApp channel installs through one." }), !installsAvailable ? (_jsx(CloudFeatureNotice, { children: "Channel apps are not available in local mode. Channel installs require Stigmer Cloud." })) : flow.phase === "creating" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(CreateChannelAppForm, { org: org, onCreated: handleCreated, onCancel: () => setFlow({ phase: "idle" }) }) })) : flow.phase === "editing" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(ChannelAppDetailPanel, { channelApp: flow.channelApp, createHandoff: flow.createHandoff, onUpdated: handleUpdated, onDeleted: handleDeleted, onBack: () => setFlow({ phase: "idle" }) }) })) : (_jsx(ChannelAppListPanel, { org: org, onEdit: (app) => setFlow({ phase: "editing", channelApp: app }), onRefetchRef: handleRefetchRef, emptyState: deniedCreate ? CHANNEL_APPS_MANAGED_BY_ADMINS : undefined }))] }));
|
|
48
61
|
}
|
|
49
62
|
//# sourceMappingURL=ChannelAppsSection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChannelAppsSection.js","sourceRoot":"","sources":["../src/settings/ChannelAppsSection.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EACL,oBAAoB,GAErB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"ChannelAppsSection.js","sourceRoot":"","sources":["../src/settings/ChannelAppsSection.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EACL,oBAAoB,GAErB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EAAE,qBAAqB,EAAE,MAAM,yCAAyC,CAAC;AAChF,OAAO,EAAE,8BAA8B,EAAE,MAAM,wBAAwB,CAAC;AACxE,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAC;AAgBlF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,UAAU,kBAAkB;IAChC,MAAM,SAAS,GAAG,KAAK,EAAE,CAAC;IAC1B,MAAM,GAAG,GAAG,gBAAgB,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;IAC/B,uEAAuE;IACvE,kEAAkE;IAClE,MAAM,iBAAiB,GAAG,iBAAiB,EAAE,KAAK,OAAO,CAAC;IAC1D,MAAM,WAAW,GAAG,kBAAkB,CACpC,iBAAiB,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,EACvE,wBAAwB,CACzB,CAAC;IACF,MAAM,SAAS,GAAG,CAAC,WAAW,CAAC,SAAS,IAAI,WAAW,CAAC,OAAO,CAAC;IAChE,MAAM,YAAY,GAAG,CAAC,WAAW,CAAC,SAAS,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC;IAEpE,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAY,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC/D,MAAM,cAAc,GAAG,MAAM,CAAsB,IAAI,CAAC,CAAC;IAEzD,MAAM,gBAAgB,GAAG,WAAW,CAAC,CAAC,OAAmB,EAAE,EAAE;QAC3D,cAAc,CAAC,OAAO,GAAG,OAAO,CAAC;IACnC,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,aAAa,GAAG,WAAW,CAC/B,CAAC,GAAe,EAAE,aAAsC,EAAE,EAAE;QAC1D,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,oEAAoE;QACpE,iEAAiE;QACjE,iDAAiD;QACjD,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,aAAa,EAAE,CAAC,CAAC;IAChE,CAAC,EACD,EAAE,CACH,CAAC;IAEF,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,aAAa,GAAG,WAAW,CAAC,GAAG,EAAE;QACrC,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC7B,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CACL,sCAA0B,SAAS,aACjC,eAAK,SAAS,EAAC,wDAAwD,aACrE,aACE,EAAE,EAAE,SAAS,EACb,SAAS,EAAC,mDAAmD,6BAG1D,EAEJ,iBAAiB,IAAI,GAAG,IAAI,SAAS,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,CACjE,iBACE,IAAI,EAAC,QAAQ,EACb,OAAO,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,EAC7C,SAAS,EAAC,8FAA8F,kCAGjG,CACV,IACG,EACN,YAAG,SAAS,EAAC,gDAAgD,uRAKzD,EAEH,CAAC,iBAAiB,CAAC,CAAC,CAAC,CACpB,KAAC,kBAAkB,wGAGE,CACtB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,CAC9B,cAAK,SAAS,EAAC,iEAAiE,YAC9E,KAAC,oBAAoB,IACnB,GAAG,EAAE,GAAG,EACR,SAAS,EAAE,aAAa,EACxB,QAAQ,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,GAC1C,GACE,CACP,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAC7B,cAAK,SAAS,EAAC,iEAAiE,YAC9E,KAAC,qBAAqB,IACpB,UAAU,EAAE,IAAI,CAAC,UAAU,EAC3B,aAAa,EAAE,IAAI,CAAC,aAAa,EACjC,SAAS,EAAE,aAAa,EACxB,SAAS,EAAE,aAAa,EACxB,MAAM,EAAE,GAAG,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,GACxC,GACE,CACP,CAAC,CAAC,CAAC,CACF,KAAC,mBAAmB,IAClB,GAAG,EAAE,GAAG,EACR,MAAM,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,OAAO,CAAC,EAAE,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,EAAE,CAAC,EAC/D,YAAY,EAAE,gBAAgB,EAC9B,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,SAAS,GACrE,CACH,IACO,CACX,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamsSection.d.ts","sourceRoot":"","sources":["../src/settings/TeamsSection.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"TeamsSection.d.ts","sourceRoot":"","sources":["../src/settings/TeamsSection.tsx"],"names":[],"mappings":"AA2CA,oDAAoD;AACpD,wBAAgB,YAAY,4CAsG3B"}
|
package/settings/TeamsSection.js
CHANGED
|
@@ -12,9 +12,21 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
12
12
|
*
|
|
13
13
|
* The section owns the list's data and passes it down, so a create, an edit
|
|
14
14
|
* or a delete refreshes the list here without a child reaching up.
|
|
15
|
+
*
|
|
16
|
+
* On Stigmer Cloud, teams are a plan feature: where the organization's plan
|
|
17
|
+
* lacks them, the section names the cheapest plan that includes them and
|
|
18
|
+
* links to the plans in place of "New team", and its existing teams stay
|
|
19
|
+
* readable and usable. Only Cloud reads
|
|
20
|
+
* the plan; on Enterprise a license never lists teams and every
|
|
21
|
+
* organization has them, so the read would refuse Enterprise its own teams.
|
|
15
22
|
*/
|
|
16
23
|
import { useCallback, useId, useState } from "react";
|
|
17
|
-
import {
|
|
24
|
+
import { Feature } from "@stigmer/protos/ai/stigmer/platform/v1/entitlement_pb";
|
|
25
|
+
import { UpgradeNotice } from "../billing/UpgradeNotice.js";
|
|
26
|
+
import { lowestPlanWith } from "../billing/plan-features.js";
|
|
27
|
+
import { useEntitlements } from "../billing/useEntitlements.js";
|
|
28
|
+
import { usePlans } from "../billing/usePlans.js";
|
|
29
|
+
import { useDeploymentMode, useResourceAvailable, ApiResourceKind } from "../deployment-mode.js";
|
|
18
30
|
import { CloudFeatureNotice } from "../internal/CloudFeatureNotice.js";
|
|
19
31
|
import { PermissionGate } from "../iam-policy/PermissionGate.js";
|
|
20
32
|
import { useOrg } from "../organization/OrgProvider.js";
|
|
@@ -29,6 +41,11 @@ export function TeamsSection() {
|
|
|
29
41
|
const teamsServed = useResourceAvailable(ApiResourceKind.team);
|
|
30
42
|
const orgSlug = activeOrg?.metadata?.slug ?? "";
|
|
31
43
|
const orgId = activeOrg?.metadata?.id ?? "";
|
|
44
|
+
const onCloud = useDeploymentMode() === "cloud";
|
|
45
|
+
const plan = useEntitlements(orgId || null, { enabled: onCloud && teamsServed });
|
|
46
|
+
const planLacksTeams = plan.allows(Feature.teams) === false;
|
|
47
|
+
const catalog = usePlans({ enabled: planLacksTeams });
|
|
48
|
+
const unlockingPlanName = lowestPlanWith(catalog.plans ?? [], Feature.teams)?.metadata?.name;
|
|
32
49
|
const list = useTeamList(teamsServed && orgSlug ? orgSlug : null);
|
|
33
50
|
const { refetch } = list;
|
|
34
51
|
const [flow, setFlow] = useState({ phase: "idle" });
|
|
@@ -44,6 +61,6 @@ export function TeamsSection() {
|
|
|
44
61
|
refetch();
|
|
45
62
|
setFlow({ phase: "idle" });
|
|
46
63
|
}, [refetch]);
|
|
47
|
-
return (_jsxs("section", { "aria-labelledby": headingId, children: [_jsxs("div", { className: "stg:mb-3 stg:flex stg:items-center stg:justify-between", children: [_jsx("h2", { id: headingId, className: "stg:text-foreground stg:text-sm stg:font-semibold", children: "Teams" }), teamsServed && orgId && flow.phase === "idle" && (_jsx(PermissionGate, { resource: { kind: "organization", id: orgId }, relation: "can_create_team", children: _jsx("button", { type: "button", onClick: () => setFlow({ phase: "creating" }), className: "stg:text-primary stg:hover:text-foreground stg:text-xs stg:font-medium stg:transition-colors", children: "+ New team" }) }))] }), _jsx("p", { className: "stg:text-muted-foreground stg:mb-4 stg:text-xs", children: "Teams group people so agents, workflows and other resources can be shared with everyone in the team at once. Adding someone to a team gives them everything shared with it. Removing them from the team, or their leaving the organization, takes that access away." }), !teamsServed ? (_jsx(CloudFeatureNotice, { children: "Teams are available in Stigmer Enterprise and Cloud. This edition shares resources with people and with the whole organization." })) : !orgSlug ? (_jsx("p", { className: "stg:text-muted-foreground stg:py-4 stg:text-center stg:text-xs", children: "Select an organization to manage teams." })) : flow.phase === "creating" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(CreateTeamForm, { org: orgSlug, onCreated: handleCreated, onCancel: () => setFlow({ phase: "idle" }) }) })) : flow.phase === "viewing" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(TeamDetailPanel, { team: flow.team, orgId: orgId, onUpdated: handleUpdated, onDeleted: handleDeleted, onBack: () => setFlow({ phase: "idle" }) }, flow.team.metadata?.id) })) : (_jsx(TeamListPanel, { teams: list.teams, isLoading: list.isLoading, error: list.error, onOpen: (team) => setFlow({ phase: "viewing", team }) }))] }));
|
|
64
|
+
return (_jsxs("section", { "aria-labelledby": headingId, children: [_jsxs("div", { className: "stg:mb-3 stg:flex stg:items-center stg:justify-between", children: [_jsx("h2", { id: headingId, className: "stg:text-foreground stg:text-sm stg:font-semibold", children: "Teams" }), teamsServed && orgId && flow.phase === "idle" && !planLacksTeams && (_jsx(PermissionGate, { resource: { kind: "organization", id: orgId }, relation: "can_create_team", children: _jsx("button", { type: "button", onClick: () => setFlow({ phase: "creating" }), className: "stg:text-primary stg:hover:text-foreground stg:text-xs stg:font-medium stg:transition-colors", children: "+ New team" }) }))] }), _jsx("p", { className: "stg:text-muted-foreground stg:mb-4 stg:text-xs", children: "Teams group people so agents, workflows and other resources can be shared with everyone in the team at once. Adding someone to a team gives them everything shared with it. Removing them from the team, or their leaving the organization, takes that access away." }), teamsServed && orgId && planLacksTeams && _jsx(UpgradeNotice, { feature: Feature.teams, unlockingPlanName: unlockingPlanName, className: "stg:mb-3" }), !teamsServed ? (_jsx(CloudFeatureNotice, { children: "Teams are available in Stigmer Enterprise and Cloud. This edition shares resources with people and with the whole organization." })) : !orgSlug ? (_jsx("p", { className: "stg:text-muted-foreground stg:py-4 stg:text-center stg:text-xs", children: "Select an organization to manage teams." })) : flow.phase === "creating" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(CreateTeamForm, { org: orgSlug, onCreated: handleCreated, onCancel: () => setFlow({ phase: "idle" }) }) })) : flow.phase === "viewing" ? (_jsx("div", { className: "stg:border-border stg:bg-card stg:rounded-lg stg:border stg:p-4", children: _jsx(TeamDetailPanel, { team: flow.team, orgId: orgId, onUpdated: handleUpdated, onDeleted: handleDeleted, onBack: () => setFlow({ phase: "idle" }) }, flow.team.metadata?.id) })) : (_jsx(TeamListPanel, { teams: list.teams, isLoading: list.isLoading, error: list.error, onOpen: (team) => setFlow({ phase: "viewing", team }) }))] }));
|
|
48
65
|
}
|
|
49
66
|
//# sourceMappingURL=TeamsSection.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamsSection.js","sourceRoot":"","sources":["../src/settings/TeamsSection.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAEb
|
|
1
|
+
{"version":3,"file":"TeamsSection.js","sourceRoot":"","sources":["../src/settings/TeamsSection.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-nav.d.ts","sourceRoot":"","sources":["../src/settings/settings-nav.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"settings-nav.d.ts","sourceRoot":"","sources":["../src/settings/settings-nav.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAwB3C,sDAAsD;AACtD,MAAM,WAAW,eAAe;IAC9B,yCAAyC;IACzC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oDAAoD;IACpD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;QAC3B,mDAAmD;QACnD,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;IACH;;;;;;OAMG;IACH,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;CACtC;AAED,kEAAkE;AAClE,MAAM,WAAW,gBAAgB;IAC/B,sDAAsD;IACtD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gDAAgD;IAChD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,yCAAyC;IACzC,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;CAC5C;AAED,2DAA2D;AAC3D,eAAO,MAAM,mBAAmB,EAAE,SAAS,gBAAgB,EA8D1D,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,2BAA2B,EAAE,gBAoCzC,CAAC"}
|
package/settings/settings-nav.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Activity, AppWindow, BarChart3, Box, Brain, Building2, CreditCard, FileBadge, KeyRound, Link, MessageSquare, MousePointerClick, Plug, Scale, ShieldCheck, SlidersHorizontal, UserCog, Users, UsersRound, } from "lucide-react";
|
|
1
|
+
import { Activity, AppWindow, BarChart3, Box, Brain, Building2, CreditCard, FileBadge, Layers, KeyRound, Link, MessageSquare, MousePointerClick, Plug, Scale, ShieldCheck, SlidersHorizontal, UserCog, Users, UsersRound, } from "lucide-react";
|
|
2
2
|
/** Canonical settings sidebar/grouped navigation model. */
|
|
3
3
|
export const SETTINGS_NAV_GROUPS = [
|
|
4
4
|
{
|
|
@@ -102,6 +102,12 @@ export const PLATFORM_SETTINGS_NAV_GROUP = {
|
|
|
102
102
|
icon: FileBadge,
|
|
103
103
|
requiredPermission: "can_issue_license",
|
|
104
104
|
},
|
|
105
|
+
{
|
|
106
|
+
href: "/settings/plans",
|
|
107
|
+
label: "Plans",
|
|
108
|
+
icon: Layers,
|
|
109
|
+
requiredPermission: "can_manage_plans",
|
|
110
|
+
},
|
|
105
111
|
],
|
|
106
112
|
};
|
|
107
113
|
//# sourceMappingURL=settings-nav.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-nav.js","sourceRoot":"","sources":["../src/settings/settings-nav.ts"],"names":[],"mappings":"AACA,OAAO,EACL,QAAQ,EACR,SAAS,EACT,SAAS,EACT,GAAG,EACH,KAAK,EACL,SAAS,EACT,UAAU,EACV,SAAS,EACT,QAAQ,EACR,IAAI,EACJ,aAAa,EACb,iBAAiB,EACjB,IAAI,EACJ,KAAK,EACL,WAAW,EACX,iBAAiB,EACjB,OAAO,EACP,KAAK,EACL,UAAU,GACX,MAAM,cAAc,CAAC;AAiCtB,2DAA2D;AAC3D,MAAM,CAAC,MAAM,mBAAmB,GAAgC;IAC9D;QACE,KAAK,EAAE,cAAc;QACrB,WAAW,EACT,uEAAuE;QACzE,KAAK,EAAE;YACL,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE;YACxE;gBACE,IAAI,EAAE,2BAA2B;gBACjC,KAAK,EAAE,aAAa;gBACpB,IAAI,EAAE,iBAAiB;aACxB;YACD,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE;YAC5D,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE;YAC7D,EAAE,IAAI,EAAE,uBAAuB,EAAE,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,IAAI,EAAE;YACnE;gBACE,IAAI,EAAE,8BAA8B;gBACpC,KAAK,EAAE,oBAAoB;gBAC3B,IAAI,EAAE,WAAW;aAClB;SACF;KACF;IACD;QACE,KAAK,EAAE,eAAe;QACtB,WAAW,EACT,mFAAmF;QACrF,KAAK,EAAE;YACL,EAAE,IAAI,EAAE,oBAAoB,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE;YACjE;gBACE,IAAI,EAAE,4BAA4B;gBAClC,KAAK,EAAE,kBAAkB;gBACzB,IAAI,EAAE,IAAI;aACX;YACD,EAAE,IAAI,EAAE,wBAAwB,EAAE,KAAK,EAAE,cAAc,EAAE,IAAI,EAAE,GAAG,EAAE;YACpE,EAAE,IAAI,EAAE,sBAAsB,EAAE,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE;YACtE;gBACE,IAAI,EAAE,wBAAwB;gBAC9B,KAAK,EAAE,cAAc;gBACrB,IAAI,EAAE,aAAa;aACpB;SACF;KACF;IACD;QACE,KAAK,EAAE,iBAAiB;QACxB,WAAW,EAAE,sCAAsC;QACnD,KAAK,EAAE;YACL,EAAE,IAAI,EAAE,mBAAmB,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE;YACjE,EAAE,IAAI,EAAE,iBAAiB,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE;SAC7D;KACF;IACD;QACE,KAAK,EAAE,SAAS;QAChB,WAAW,EAAE,0DAA0D;QACvE,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,+BAA+B;gBACrC,KAAK,EAAE,aAAa;gBACpB,IAAI,EAAE,OAAO;aACd;YACD,EAAE,IAAI,EAAE,kBAAkB,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE;SAC3D;KACF;CACF,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAqB;IAC3D,KAAK,EAAE,UAAU;IACjB,WAAW,EACT,kEAAkE;IACpE,KAAK,EAAE;QACL;YACE,IAAI,EAAE,8BAA8B;YACpC,KAAK,EAAE,oBAAoB;YAC3B,IAAI,EAAE,KAAK;YACX,kBAAkB,EAAE,0BAA0B;SAC/C;QACD;YACE,IAAI,EAAE,2BAA2B;YACjC,KAAK,EAAE,iBAAiB;YACxB,IAAI,EAAE,iBAAiB;YACvB,kBAAkB,EAAE,4BAA4B;SACjD;QACD;YACE,IAAI,EAAE,6BAA6B;YACnC,KAAK,EAAE,mBAAmB;YAC1B,IAAI,EAAE,QAAQ;YACd,kBAAkB,EAAE,4BAA4B;SACjD;QACD;YACE,IAAI,EAAE,oBAAoB;YAC1B,KAAK,EAAE,UAAU;YACjB,IAAI,EAAE,SAAS;YACf,kBAAkB,EAAE,mBAAmB;SACxC;KACF;CACF,CAAC"}
|
|
1
|
+
{"version":3,"file":"settings-nav.js","sourceRoot":"","sources":["../src/settings/settings-nav.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSettingsNavGroups.d.ts","sourceRoot":"","sources":["../src/settings/useSettingsNavGroups.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAS3B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,IAAI,SAAS,gBAAgB,EAAE,
|
|
1
|
+
{"version":3,"file":"useSettingsNavGroups.d.ts","sourceRoot":"","sources":["../src/settings/useSettingsNavGroups.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,mBAAmB,CAAC;AAS3B;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,oBAAoB,IAAI,SAAS,gBAAgB,EAAE,CA0DlE"}
|
|
@@ -39,16 +39,19 @@ export function useSettingsNavGroups() {
|
|
|
39
39
|
const cursorAccounts = useCheckPermission(PLATFORM_RESOURCE, "can_manage_cursor_accounts", { fail: "closed" });
|
|
40
40
|
const providerStanding = useCheckPermission(PLATFORM_RESOURCE, "can_view_provider_standing", { fail: "closed" });
|
|
41
41
|
const licenses = useCheckPermission(PLATFORM_RESOURCE, "can_issue_license", { fail: "closed" });
|
|
42
|
+
const plans = useCheckPermission(PLATFORM_RESOURCE, "can_manage_plans", { fail: "closed" });
|
|
42
43
|
const pricingAllowed = pricing.allowed;
|
|
43
44
|
const cursorAccountsAllowed = cursorAccounts.allowed;
|
|
44
45
|
const providerStandingAllowed = providerStanding.allowed;
|
|
45
46
|
const licensesAllowed = licenses.allowed;
|
|
47
|
+
const plansAllowed = plans.allowed;
|
|
46
48
|
return useMemo(() => {
|
|
47
49
|
const verdicts = {
|
|
48
50
|
can_manage_model_pricing: pricingAllowed,
|
|
49
51
|
can_manage_cursor_accounts: cursorAccountsAllowed,
|
|
50
52
|
can_view_provider_standing: providerStandingAllowed,
|
|
51
53
|
can_issue_license: licensesAllowed,
|
|
54
|
+
can_manage_plans: plansAllowed,
|
|
52
55
|
};
|
|
53
56
|
const visibleItems = PLATFORM_SETTINGS_NAV_GROUP.items.filter(
|
|
54
57
|
// Fail closed on both axes: an item with no declared permission or
|
|
@@ -62,6 +65,6 @@ export function useSettingsNavGroups() {
|
|
|
62
65
|
...SETTINGS_NAV_GROUPS,
|
|
63
66
|
{ ...PLATFORM_SETTINGS_NAV_GROUP, items: visibleItems },
|
|
64
67
|
];
|
|
65
|
-
}, [pricingAllowed, cursorAccountsAllowed, providerStandingAllowed, licensesAllowed]);
|
|
68
|
+
}, [pricingAllowed, cursorAccountsAllowed, providerStandingAllowed, licensesAllowed, plansAllowed]);
|
|
66
69
|
}
|
|
67
70
|
//# sourceMappingURL=useSettingsNavGroups.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSettingsNavGroups.js","sourceRoot":"","sources":["../src/settings/useSettingsNavGroups.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAW,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,kBAAkB,CAChC,iBAAiB,EACjB,0BAA0B,EAC1B,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IACF,MAAM,cAAc,GAAG,kBAAkB,CACvC,iBAAiB,EACjB,4BAA4B,EAC5B,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IACF,MAAM,gBAAgB,GAAG,kBAAkB,CACzC,iBAAiB,EACjB,4BAA4B,EAC5B,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IACF,MAAM,QAAQ,GAAG,kBAAkB,CACjC,iBAAiB,EACjB,mBAAmB,EACnB,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IAEF,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IACvC,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,CAAC;IACrD,MAAM,uBAAuB,GAAG,gBAAgB,CAAC,OAAO,CAAC;IACzD,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"useSettingsNavGroups.js","sourceRoot":"","sources":["../src/settings/useSettingsNavGroups.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EACL,2BAA2B,EAC3B,mBAAmB,GAEpB,MAAM,mBAAmB,CAAC;AAE3B;;;;GAIG;AACH,MAAM,iBAAiB,GAAG,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,SAAS,EAAW,CAAC;AAEvE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,MAAM,UAAU,oBAAoB;IAClC,MAAM,OAAO,GAAG,kBAAkB,CAChC,iBAAiB,EACjB,0BAA0B,EAC1B,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IACF,MAAM,cAAc,GAAG,kBAAkB,CACvC,iBAAiB,EACjB,4BAA4B,EAC5B,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IACF,MAAM,gBAAgB,GAAG,kBAAkB,CACzC,iBAAiB,EACjB,4BAA4B,EAC5B,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IACF,MAAM,QAAQ,GAAG,kBAAkB,CACjC,iBAAiB,EACjB,mBAAmB,EACnB,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IACF,MAAM,KAAK,GAAG,kBAAkB,CAC9B,iBAAiB,EACjB,kBAAkB,EAClB,EAAE,IAAI,EAAE,QAAQ,EAAE,CACnB,CAAC;IAEF,MAAM,cAAc,GAAG,OAAO,CAAC,OAAO,CAAC;IACvC,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,CAAC;IACrD,MAAM,uBAAuB,GAAG,gBAAgB,CAAC,OAAO,CAAC;IACzD,MAAM,eAAe,GAAG,QAAQ,CAAC,OAAO,CAAC;IACzC,MAAM,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC;IAEnC,OAAO,OAAO,CAAC,GAAG,EAAE;QAClB,MAAM,QAAQ,GAA4B;YACxC,wBAAwB,EAAE,cAAc;YACxC,0BAA0B,EAAE,qBAAqB;YACjD,0BAA0B,EAAE,uBAAuB;YACnD,iBAAiB,EAAE,eAAe;YAClC,gBAAgB,EAAE,YAAY;SAC/B,CAAC;QAEF,MAAM,YAAY,GAAG,2BAA2B,CAAC,KAAK,CAAC,MAAM;QAC3D,mEAAmE;QACnE,+DAA+D;QAC/D,CAAC,IAAI,EAAE,EAAE,CACP,IAAI,CAAC,kBAAkB,KAAK,SAAS;YACrC,QAAQ,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,IAAI,CAC7C,CAAC;QAEF,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC9B,OAAO,mBAAmB,CAAC;QAC7B,CAAC;QACD,OAAO;YACL,GAAG,mBAAmB;YACtB,EAAE,GAAG,2BAA2B,EAAE,KAAK,EAAE,YAAY,EAAE;SACxD,CAAC;IACJ,CAAC,EAAE,CAAC,cAAc,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,eAAe,EAAE,YAAY,CAAC,CAAC,CAAC;AACtG,CAAC"}
|
|
@@ -161,8 +161,8 @@ export function AutoRechargeCard({
|
|
|
161
161
|
|
|
162
162
|
{!hasPaymentMethod && (
|
|
163
163
|
<p className="stg:mt-3 stg:text-xs stg:text-muted-foreground">
|
|
164
|
-
A saved payment method is required to enable auto-recharge.
|
|
165
|
-
|
|
164
|
+
A saved payment method is required to enable auto-recharge. Add
|
|
165
|
+
one under Payment Method.
|
|
166
166
|
</p>
|
|
167
167
|
)}
|
|
168
168
|
|
|
@@ -4,6 +4,7 @@ import { useCallback, useId, useState } from "react";
|
|
|
4
4
|
import { cn } from "@stigmer/theme";
|
|
5
5
|
import { getUserMessage } from "@stigmer/sdk";
|
|
6
6
|
import { BillingAccountStatus } from "@stigmer/protos/ai/stigmer/billing/v1/enum_pb";
|
|
7
|
+
import { ManagementMode } from "@stigmer/protos/ai/stigmer/tenancy/organization/v1/enum_pb";
|
|
7
8
|
import { useDeploymentMode } from "../deployment-mode.js";
|
|
8
9
|
import { CloudFeatureNotice } from "../internal/CloudFeatureNotice.js";
|
|
9
10
|
import { useOrg } from "../organization/OrgProvider.js";
|
|
@@ -16,6 +17,9 @@ import { AutoRechargeCard } from "./AutoRechargeCard.js";
|
|
|
16
17
|
import { CreditPackGrid } from "./CreditPackGrid.js";
|
|
17
18
|
import { CreditLedgerTable } from "./CreditLedgerTable.js";
|
|
18
19
|
import { LowBalanceBanner } from "./LowBalanceBanner.js";
|
|
20
|
+
import { PlanSection } from "./PlanSection.js";
|
|
21
|
+
import { billingReturnUrl, type BillingRedirect } from "./redirect.js";
|
|
22
|
+
import { useCreatePaymentMethodSetupSession } from "./useCreatePaymentMethodSetupSession.js";
|
|
19
23
|
|
|
20
24
|
/** Props for {@link BillingSection}. */
|
|
21
25
|
export interface BillingSectionProps {
|
|
@@ -29,6 +33,22 @@ export interface BillingSectionProps {
|
|
|
29
33
|
readonly checkoutSuccess?: boolean;
|
|
30
34
|
/** Callback to dismiss the checkout success banner. */
|
|
31
35
|
readonly onDismissCheckoutSuccess?: () => void;
|
|
36
|
+
/**
|
|
37
|
+
* The plan the person was choosing when they left to save a card: the
|
|
38
|
+
* `plan` query Stripe returns with after `?setup=success`. The plan
|
|
39
|
+
* section reopens that choice for an explicit confirm.
|
|
40
|
+
*/
|
|
41
|
+
readonly resumePlanId?: string;
|
|
42
|
+
/** Called once the resumed plan choice is taken up, so the host can clear its query. */
|
|
43
|
+
readonly onResumeHandled?: () => void;
|
|
44
|
+
/**
|
|
45
|
+
* The host's seam for Stripe-hosted pages (checkout, card setup and the
|
|
46
|
+
* billing portal). Absent, this window navigates to them and Stripe
|
|
47
|
+
* returns to this origin's `/settings/billing`. A desktop host opens the
|
|
48
|
+
* system browser and names the web console's billing page instead; its
|
|
49
|
+
* reads refresh when the window regains focus.
|
|
50
|
+
*/
|
|
51
|
+
readonly redirect?: BillingRedirect;
|
|
32
52
|
/** Additional CSS class names. */
|
|
33
53
|
readonly className?: string;
|
|
34
54
|
}
|
|
@@ -51,12 +71,16 @@ export interface BillingSectionProps {
|
|
|
51
71
|
export function BillingSection({
|
|
52
72
|
checkoutSuccess,
|
|
53
73
|
onDismissCheckoutSuccess,
|
|
74
|
+
resumePlanId,
|
|
75
|
+
onResumeHandled,
|
|
76
|
+
redirect,
|
|
54
77
|
className,
|
|
55
78
|
}: BillingSectionProps) {
|
|
56
79
|
const headingId = useId();
|
|
57
80
|
const { activeOrg } = useOrg();
|
|
58
81
|
const mode = useDeploymentMode();
|
|
59
82
|
const orgId = activeOrg?.metadata?.id ?? "";
|
|
83
|
+
const managed = activeOrg?.spec?.managementMode === ManagementMode.platform_managed;
|
|
60
84
|
|
|
61
85
|
return (
|
|
62
86
|
<section aria-labelledby={headingId} className={className}>
|
|
@@ -67,7 +91,7 @@ export function BillingSection({
|
|
|
67
91
|
Billing
|
|
68
92
|
</h2>
|
|
69
93
|
<p className="stg:text-muted-foreground stg:mb-4 stg:text-xs">
|
|
70
|
-
|
|
94
|
+
Your plan, credits, payment method and transaction history.
|
|
71
95
|
</p>
|
|
72
96
|
|
|
73
97
|
{mode === "local" ? (
|
|
@@ -83,8 +107,12 @@ export function BillingSection({
|
|
|
83
107
|
) : (
|
|
84
108
|
<BillingContent
|
|
85
109
|
orgId={orgId}
|
|
110
|
+
managed={managed}
|
|
86
111
|
checkoutSuccess={checkoutSuccess}
|
|
87
112
|
onDismissCheckoutSuccess={onDismissCheckoutSuccess}
|
|
113
|
+
resumePlanId={resumePlanId}
|
|
114
|
+
onResumeHandled={onResumeHandled}
|
|
115
|
+
redirect={redirect}
|
|
88
116
|
/>
|
|
89
117
|
)}
|
|
90
118
|
</section>
|
|
@@ -97,16 +125,28 @@ export function BillingSection({
|
|
|
97
125
|
|
|
98
126
|
function BillingContent({
|
|
99
127
|
orgId,
|
|
128
|
+
managed,
|
|
100
129
|
checkoutSuccess,
|
|
101
130
|
onDismissCheckoutSuccess,
|
|
131
|
+
resumePlanId,
|
|
132
|
+
onResumeHandled,
|
|
133
|
+
redirect,
|
|
102
134
|
}: {
|
|
103
135
|
orgId: string;
|
|
136
|
+
managed: boolean;
|
|
104
137
|
checkoutSuccess?: boolean;
|
|
105
138
|
onDismissCheckoutSuccess?: () => void;
|
|
139
|
+
resumePlanId?: string;
|
|
140
|
+
onResumeHandled?: () => void;
|
|
141
|
+
redirect?: BillingRedirect;
|
|
106
142
|
}) {
|
|
107
|
-
const { account, isLoading, error, refetch } = useBillingAccount(orgId
|
|
108
|
-
|
|
109
|
-
|
|
143
|
+
const { account, isLoading, error, refetch } = useBillingAccount(orgId, {
|
|
144
|
+
refetchOnWindowFocus: redirect?.openUrl !== undefined,
|
|
145
|
+
});
|
|
146
|
+
const { createSession, isSubmitting, error: checkoutError, clearError } = useCreateCheckoutSession(redirect);
|
|
147
|
+
const { openPortal, isLoading: isPortalLoading } = useCreateBillingPortalSession(redirect);
|
|
148
|
+
const setup = useCreatePaymentMethodSetupSession(redirect);
|
|
149
|
+
const returnUrl = redirect?.returnUrl;
|
|
110
150
|
const [purchasingPackId, setPurchasingPackId] = useState<string | null>(null);
|
|
111
151
|
|
|
112
152
|
const handlePurchase = useCallback(
|
|
@@ -114,8 +154,7 @@ function BillingContent({
|
|
|
114
154
|
setPurchasingPackId(packId);
|
|
115
155
|
clearError();
|
|
116
156
|
|
|
117
|
-
const
|
|
118
|
-
const billingPath = `${baseUrl}/settings/billing`;
|
|
157
|
+
const billingPath = billingReturnUrl({ returnUrl });
|
|
119
158
|
|
|
120
159
|
createSession({
|
|
121
160
|
orgId,
|
|
@@ -126,7 +165,7 @@ function BillingContent({
|
|
|
126
165
|
setPurchasingPackId(null);
|
|
127
166
|
});
|
|
128
167
|
},
|
|
129
|
-
[orgId, createSession, clearError],
|
|
168
|
+
[orgId, createSession, clearError, returnUrl],
|
|
130
169
|
);
|
|
131
170
|
|
|
132
171
|
if (isLoading) {
|
|
@@ -161,6 +200,8 @@ function BillingContent({
|
|
|
161
200
|
|
|
162
201
|
const isLowBalance =
|
|
163
202
|
balance.availableMicros < account.lowBalanceThresholdMicros;
|
|
203
|
+
const hasPaymentMethod =
|
|
204
|
+
account.defaultPaymentMethod != null && account.defaultPaymentMethod.paymentMethodId !== "";
|
|
164
205
|
|
|
165
206
|
return (
|
|
166
207
|
<div className="stg:space-y-6">
|
|
@@ -173,6 +214,16 @@ function BillingContent({
|
|
|
173
214
|
thresholdMicros={account.lowBalanceThresholdMicros}
|
|
174
215
|
/>
|
|
175
216
|
|
|
217
|
+
<PlanSection
|
|
218
|
+
orgId={orgId}
|
|
219
|
+
managed={managed}
|
|
220
|
+
hasPaymentMethod={hasPaymentMethod}
|
|
221
|
+
onRefreshAccount={refetch}
|
|
222
|
+
redirect={redirect}
|
|
223
|
+
resumePlanId={resumePlanId}
|
|
224
|
+
onResumeHandled={onResumeHandled}
|
|
225
|
+
/>
|
|
226
|
+
|
|
176
227
|
<CreditBalanceCard balance={balance} isLowBalance={isLowBalance} />
|
|
177
228
|
|
|
178
229
|
<PaymentMethodCard
|
|
@@ -180,15 +231,21 @@ function BillingContent({
|
|
|
180
231
|
accountStatus={account.status}
|
|
181
232
|
isPortalLoading={isPortalLoading}
|
|
182
233
|
onManage={() => openPortal(orgId)}
|
|
234
|
+
onAdd={() => {
|
|
235
|
+
setup.addPaymentMethod(orgId).catch(() => undefined);
|
|
236
|
+
}}
|
|
237
|
+
isAdding={setup.isSubmitting}
|
|
183
238
|
/>
|
|
239
|
+
{setup.error && (
|
|
240
|
+
<p className="stg:text-destructive stg:text-xs" role="alert">
|
|
241
|
+
{getUserMessage(setup.error)}
|
|
242
|
+
</p>
|
|
243
|
+
)}
|
|
184
244
|
|
|
185
245
|
<AutoRechargeCard
|
|
186
246
|
orgId={orgId}
|
|
187
247
|
autoRecharge={account.autoRecharge}
|
|
188
|
-
hasPaymentMethod={
|
|
189
|
-
account.defaultPaymentMethod != null &&
|
|
190
|
-
account.defaultPaymentMethod.paymentMethodId !== ""
|
|
191
|
-
}
|
|
248
|
+
hasPaymentMethod={hasPaymentMethod}
|
|
192
249
|
accountStatus={account.status}
|
|
193
250
|
onSaved={refetch}
|
|
194
251
|
/>
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { useId } from "react";
|
|
4
|
+
import { getErrorReason, getUserMessage } from "@stigmer/sdk";
|
|
5
|
+
import type { PlanTerms } from "@stigmer/protos/ai/stigmer/billing/plan/v1/spec_pb";
|
|
6
|
+
import { Button } from "../button/index.js";
|
|
7
|
+
import { DialogShell } from "../internal/DialogShell.js";
|
|
8
|
+
import { formatDay, formatMonthlyMinimum, sharePercent, type OfferedFeature } from "./plan-features.js";
|
|
9
|
+
import type { PlanMove } from "./plan-state.js";
|
|
10
|
+
|
|
11
|
+
/** The reason a plan change is refused for want of a saved card (billing/subscription/v1/command.proto). */
|
|
12
|
+
export const PAYMENT_METHOD_REQUIRED = "PAYMENT_METHOD_REQUIRED";
|
|
13
|
+
|
|
14
|
+
/** Props for {@link ChangePlanDialog}. */
|
|
15
|
+
export interface ChangePlanDialogProps {
|
|
16
|
+
/** The move to confirm; the dialog is open while it is set. */
|
|
17
|
+
readonly move: PlanMove | null;
|
|
18
|
+
/** The display name of a plan by id, for the plan being left. */
|
|
19
|
+
readonly planName: (planId: string) => string;
|
|
20
|
+
/**
|
|
21
|
+
* For a switch, what the plan being left offers that the new one does
|
|
22
|
+
* not (`featuresLost`). Empty, as for an upgrade, the dialog says
|
|
23
|
+
* nothing is lost.
|
|
24
|
+
*/
|
|
25
|
+
readonly losing?: readonly OfferedFeature[];
|
|
26
|
+
/**
|
|
27
|
+
* Whether the organization has a saved card. Every move but a cancel
|
|
28
|
+
* needs one, because every period is collected from it.
|
|
29
|
+
*/
|
|
30
|
+
readonly hasPaymentMethod: boolean;
|
|
31
|
+
/**
|
|
32
|
+
* The card was just saved on Stripe's page and is on its way to the
|
|
33
|
+
* account: say so instead of asking for one again.
|
|
34
|
+
*/
|
|
35
|
+
readonly awaitingPaymentMethod?: boolean;
|
|
36
|
+
/** `true` while the move, or the card page, is in flight. */
|
|
37
|
+
readonly isSubmitting: boolean;
|
|
38
|
+
/** Why the last attempt failed, or `null`. */
|
|
39
|
+
readonly error: Error | null;
|
|
40
|
+
/** Make the move. */
|
|
41
|
+
readonly onConfirm: () => void;
|
|
42
|
+
/** Leave to save a card, returning to this choice. */
|
|
43
|
+
readonly onAddPaymentMethod: () => void;
|
|
44
|
+
/** Close without moving. */
|
|
45
|
+
readonly onClose: () => void;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The confirm step for a plan change. It states exactly what the move does
|
|
50
|
+
* and when money moves: a subscription starts now and every period is
|
|
51
|
+
* collected when it closes; a switch closes this period early, billed for
|
|
52
|
+
* the time it ran; a cancel keeps the plan to its period's end and deletes
|
|
53
|
+
* nothing. Without a saved card it offers the card page first.
|
|
54
|
+
*/
|
|
55
|
+
export function ChangePlanDialog({
|
|
56
|
+
move,
|
|
57
|
+
planName,
|
|
58
|
+
losing = [],
|
|
59
|
+
hasPaymentMethod,
|
|
60
|
+
awaitingPaymentMethod,
|
|
61
|
+
isSubmitting,
|
|
62
|
+
error,
|
|
63
|
+
onConfirm,
|
|
64
|
+
onAddPaymentMethod,
|
|
65
|
+
onClose,
|
|
66
|
+
}: ChangePlanDialogProps) {
|
|
67
|
+
const headingId = useId();
|
|
68
|
+
const needsCard =
|
|
69
|
+
move !== null &&
|
|
70
|
+
move.kind !== "cancel" &&
|
|
71
|
+
(!hasPaymentMethod || getErrorReason(error)?.reason === PAYMENT_METHOD_REQUIRED);
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
<DialogShell
|
|
75
|
+
open={move !== null}
|
|
76
|
+
onOpenChange={(open) => {
|
|
77
|
+
if (!open) onClose();
|
|
78
|
+
}}
|
|
79
|
+
width="md"
|
|
80
|
+
aria-labelledby={headingId}
|
|
81
|
+
>
|
|
82
|
+
{move && (
|
|
83
|
+
<div className="stg:space-y-3 stg:p-5">
|
|
84
|
+
<h2 id={headingId} className="stg:text-sm stg:font-semibold stg:text-foreground">
|
|
85
|
+
{title(move)}
|
|
86
|
+
</h2>
|
|
87
|
+
<div className="stg:space-y-2 stg:text-xs stg:text-muted-foreground">{body(move, planName, losing)}</div>
|
|
88
|
+
|
|
89
|
+
{needsCard && (
|
|
90
|
+
<p className="stg:rounded-md stg:bg-muted-subtle stg:px-3 stg:py-2 stg:text-xs stg:text-foreground" role="status">
|
|
91
|
+
{awaitingPaymentMethod
|
|
92
|
+
? "Your card is being saved. This takes a few seconds; the choice is kept."
|
|
93
|
+
: "A saved card is needed first: every period is collected from it. You come back here after saving it."}
|
|
94
|
+
</p>
|
|
95
|
+
)}
|
|
96
|
+
{error && getErrorReason(error)?.reason !== PAYMENT_METHOD_REQUIRED && (
|
|
97
|
+
<p className="stg:text-xs stg:text-destructive" role="alert">
|
|
98
|
+
{getUserMessage(error)}
|
|
99
|
+
</p>
|
|
100
|
+
)}
|
|
101
|
+
|
|
102
|
+
<div className="stg:flex stg:justify-end stg:gap-2 stg:pt-1">
|
|
103
|
+
<Button variant="ghost" size="sm" onClick={onClose} disabled={isSubmitting}>
|
|
104
|
+
Not now
|
|
105
|
+
</Button>
|
|
106
|
+
{needsCard ? (
|
|
107
|
+
<Button size="sm" onClick={onAddPaymentMethod} disabled={isSubmitting || awaitingPaymentMethod}>
|
|
108
|
+
Add a payment method
|
|
109
|
+
</Button>
|
|
110
|
+
) : (
|
|
111
|
+
<Button
|
|
112
|
+
variant={move.kind === "cancel" ? "destructive" : "primary"}
|
|
113
|
+
size="sm"
|
|
114
|
+
onClick={onConfirm}
|
|
115
|
+
disabled={isSubmitting}
|
|
116
|
+
>
|
|
117
|
+
{confirmLabel(move)}
|
|
118
|
+
</Button>
|
|
119
|
+
)}
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
|
+
</DialogShell>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function title(move: PlanMove): string {
|
|
128
|
+
switch (move.kind) {
|
|
129
|
+
case "subscribe":
|
|
130
|
+
return `Subscribe to ${move.to.metadata?.name ?? "this plan"}`;
|
|
131
|
+
case "switch":
|
|
132
|
+
return `Switch to ${move.to.metadata?.name ?? "this plan"}`;
|
|
133
|
+
case "resume":
|
|
134
|
+
return `Keep ${move.to.metadata?.name ?? "this plan"}`;
|
|
135
|
+
case "cancel":
|
|
136
|
+
return "Move to Free";
|
|
137
|
+
default: {
|
|
138
|
+
const exhaustive: never = move;
|
|
139
|
+
return String(exhaustive);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function body(move: PlanMove, planName: (planId: string) => string, losing: readonly OfferedFeature[]) {
|
|
145
|
+
switch (move.kind) {
|
|
146
|
+
case "subscribe":
|
|
147
|
+
return (
|
|
148
|
+
<>
|
|
149
|
+
<p>
|
|
150
|
+
{move.to.metadata?.name} starts now. Each monthly period is billed when it ends:{" "}
|
|
151
|
+
{periodCharge(move.to.spec?.terms)}
|
|
152
|
+
</p>
|
|
153
|
+
<p>Nothing is charged today.</p>
|
|
154
|
+
</>
|
|
155
|
+
);
|
|
156
|
+
case "switch":
|
|
157
|
+
return (
|
|
158
|
+
<>
|
|
159
|
+
<p>
|
|
160
|
+
The switch takes effect now. This period closes early and is billed for the time it ran on{" "}
|
|
161
|
+
{planName(move.fromPlanId)}; a new monthly period opens on {move.to.metadata?.name} at{" "}
|
|
162
|
+
{formatMonthlyMinimum(move.to.spec?.terms)}.
|
|
163
|
+
</p>
|
|
164
|
+
{losing.length > 0 && (
|
|
165
|
+
<p>
|
|
166
|
+
{move.to.metadata?.name} does not include {listFeatures(losing)}. Everything you built keeps working; only
|
|
167
|
+
creating more is refused.
|
|
168
|
+
</p>
|
|
169
|
+
)}
|
|
170
|
+
</>
|
|
171
|
+
);
|
|
172
|
+
case "resume":
|
|
173
|
+
return <p>The cancellation is withdrawn, and {move.to.metadata?.name} runs on into its next period.</p>;
|
|
174
|
+
case "cancel":
|
|
175
|
+
return (
|
|
176
|
+
<>
|
|
177
|
+
<p>
|
|
178
|
+
{planName(move.fromPlanId)} stays in force until {formatDay(move.endsAt)}, then this organization is on
|
|
179
|
+
Free. The period is billed as usual when it closes.
|
|
180
|
+
</p>
|
|
181
|
+
<p>Nothing you built is deleted. Creating what Free does not include is refused until you subscribe again.</p>
|
|
182
|
+
</>
|
|
183
|
+
);
|
|
184
|
+
default: {
|
|
185
|
+
const exhaustive: never = move;
|
|
186
|
+
return String(exhaustive);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function confirmLabel(move: PlanMove): string {
|
|
192
|
+
switch (move.kind) {
|
|
193
|
+
case "subscribe":
|
|
194
|
+
return "Subscribe";
|
|
195
|
+
case "switch":
|
|
196
|
+
return "Switch now";
|
|
197
|
+
case "resume":
|
|
198
|
+
return "Keep plan";
|
|
199
|
+
case "cancel":
|
|
200
|
+
return "Cancel plan";
|
|
201
|
+
default: {
|
|
202
|
+
const exhaustive: never = move;
|
|
203
|
+
return String(exhaustive);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/** "Teams", "Teams and Managed organizations", "A, B and C". */
|
|
209
|
+
function listFeatures(features: readonly OfferedFeature[]): string {
|
|
210
|
+
const labels = features.map((feature) => feature.label);
|
|
211
|
+
return labels.length <= 1 ? (labels[0] ?? "") : `${labels.slice(0, -1).join(", ")} and ${labels[labels.length - 1]}`;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** What a period of the plan is billed at its end, in the customer's words. */
|
|
215
|
+
function periodCharge(terms: PlanTerms | undefined): string {
|
|
216
|
+
const minimum = formatMonthlyMinimum(terms);
|
|
217
|
+
const percent = sharePercent(terms);
|
|
218
|
+
return percent === ""
|
|
219
|
+
? `${minimum}.`
|
|
220
|
+
: `${minimum}, less the ${percent} commission already paid on that period's usage. Usage itself stays paid from credits.`;
|
|
221
|
+
}
|