@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.
Files changed (200) hide show
  1. package/billing/AutoRechargeCard.js +1 -1
  2. package/billing/AutoRechargeCard.js.map +1 -1
  3. package/billing/BillingSection.d.ts +18 -1
  4. package/billing/BillingSection.d.ts.map +1 -1
  5. package/billing/BillingSection.js +21 -11
  6. package/billing/BillingSection.js.map +1 -1
  7. package/billing/ChangePlanDialog.d.ts +46 -0
  8. package/billing/ChangePlanDialog.d.ts.map +1 -0
  9. package/billing/ChangePlanDialog.js +90 -0
  10. package/billing/ChangePlanDialog.js.map +1 -0
  11. package/billing/CreditPackGrid.d.ts +1 -1
  12. package/billing/CreditPackGrid.js +1 -1
  13. package/billing/LowBalanceBanner.js +1 -1
  14. package/billing/LowBalanceBanner.js.map +1 -1
  15. package/billing/PaymentMethodCard.d.ts +9 -1
  16. package/billing/PaymentMethodCard.d.ts.map +1 -1
  17. package/billing/PaymentMethodCard.js +2 -2
  18. package/billing/PaymentMethodCard.js.map +1 -1
  19. package/billing/PlanCard.d.ts +27 -0
  20. package/billing/PlanCard.d.ts.map +1 -0
  21. package/billing/PlanCard.js +68 -0
  22. package/billing/PlanCard.js.map +1 -0
  23. package/billing/PlanPicker.d.ts +34 -0
  24. package/billing/PlanPicker.d.ts.map +1 -0
  25. package/billing/PlanPicker.js +46 -0
  26. package/billing/PlanPicker.js.map +1 -0
  27. package/billing/PlanSection.d.ts +45 -0
  28. package/billing/PlanSection.d.ts.map +1 -0
  29. package/billing/PlanSection.js +136 -0
  30. package/billing/PlanSection.js.map +1 -0
  31. package/billing/UpgradeNotice.d.ts +47 -0
  32. package/billing/UpgradeNotice.d.ts.map +1 -0
  33. package/billing/UpgradeNotice.js +49 -0
  34. package/billing/UpgradeNotice.js.map +1 -0
  35. package/billing/credit-packs.d.ts.map +1 -1
  36. package/billing/credit-packs.js +4 -1
  37. package/billing/credit-packs.js.map +1 -1
  38. package/billing/index.d.ts +26 -1
  39. package/billing/index.d.ts.map +1 -1
  40. package/billing/index.js +12 -0
  41. package/billing/index.js.map +1 -1
  42. package/billing/plan-features.d.ts +60 -0
  43. package/billing/plan-features.d.ts.map +1 -0
  44. package/billing/plan-features.js +128 -0
  45. package/billing/plan-features.js.map +1 -0
  46. package/billing/plan-state.d.ts +43 -0
  47. package/billing/plan-state.d.ts.map +1 -0
  48. package/billing/plan-state.js +69 -0
  49. package/billing/plan-state.js.map +1 -0
  50. package/billing/redirect.d.ts +31 -0
  51. package/billing/redirect.d.ts.map +1 -0
  52. package/billing/redirect.js +30 -0
  53. package/billing/redirect.js.map +1 -0
  54. package/billing/useBillingAccount.d.ts +10 -1
  55. package/billing/useBillingAccount.d.ts.map +1 -1
  56. package/billing/useBillingAccount.js +2 -2
  57. package/billing/useBillingAccount.js.map +1 -1
  58. package/billing/useChangePlan.d.ts +51 -0
  59. package/billing/useChangePlan.d.ts.map +1 -0
  60. package/billing/useChangePlan.js +61 -0
  61. package/billing/useChangePlan.js.map +1 -0
  62. package/billing/useCreateBillingPortalSession.d.ts +6 -3
  63. package/billing/useCreateBillingPortalSession.d.ts.map +1 -1
  64. package/billing/useCreateBillingPortalSession.js +11 -4
  65. package/billing/useCreateBillingPortalSession.js.map +1 -1
  66. package/billing/useCreateCheckoutSession.d.ts +7 -5
  67. package/billing/useCreateCheckoutSession.d.ts.map +1 -1
  68. package/billing/useCreateCheckoutSession.js +5 -3
  69. package/billing/useCreateCheckoutSession.js.map +1 -1
  70. package/billing/useCreatePaymentMethodSetupSession.d.ts +28 -0
  71. package/billing/useCreatePaymentMethodSetupSession.d.ts.map +1 -0
  72. package/billing/useCreatePaymentMethodSetupSession.js +50 -0
  73. package/billing/useCreatePaymentMethodSetupSession.js.map +1 -0
  74. package/billing/useEntitlements.d.ts +46 -0
  75. package/billing/useEntitlements.d.ts.map +1 -0
  76. package/billing/useEntitlements.js +37 -0
  77. package/billing/useEntitlements.js.map +1 -0
  78. package/billing/usePeriodEstimate.d.ts +35 -0
  79. package/billing/usePeriodEstimate.d.ts.map +1 -0
  80. package/billing/usePeriodEstimate.js +29 -0
  81. package/billing/usePeriodEstimate.js.map +1 -0
  82. package/billing/usePlans.d.ts +38 -0
  83. package/billing/usePlans.d.ts.map +1 -0
  84. package/billing/usePlans.js +33 -0
  85. package/billing/usePlans.js.map +1 -0
  86. package/billing/useSubscription.d.ts +39 -0
  87. package/billing/useSubscription.d.ts.map +1 -0
  88. package/billing/useSubscription.js +40 -0
  89. package/billing/useSubscription.js.map +1 -0
  90. package/channel-app/ChannelAppDetailPanel.d.ts +5 -0
  91. package/channel-app/ChannelAppDetailPanel.d.ts.map +1 -1
  92. package/channel-app/ChannelAppDetailPanel.js +9 -3
  93. package/channel-app/ChannelAppDetailPanel.js.map +1 -1
  94. package/channel-app/ChannelAppListPanel.d.ts +13 -4
  95. package/channel-app/ChannelAppListPanel.d.ts.map +1 -1
  96. package/channel-app/ChannelAppListPanel.js +5 -5
  97. package/channel-app/ChannelAppListPanel.js.map +1 -1
  98. package/channel-app/copy.d.ts +15 -0
  99. package/channel-app/copy.d.ts.map +1 -0
  100. package/channel-app/copy.js +15 -0
  101. package/channel-app/copy.js.map +1 -0
  102. package/index.d.ts +4 -2
  103. package/index.d.ts.map +1 -1
  104. package/index.js +4 -1
  105. package/index.js.map +1 -1
  106. package/internal/features.d.ts +11 -0
  107. package/internal/features.d.ts.map +1 -0
  108. package/internal/features.js +42 -0
  109. package/internal/features.js.map +1 -0
  110. package/licenses/license-format.d.ts +5 -3
  111. package/licenses/license-format.d.ts.map +1 -1
  112. package/licenses/license-format.js +10 -19
  113. package/licenses/license-format.js.map +1 -1
  114. package/package.json +5 -5
  115. package/plan-catalog/PlanCatalogConsole.d.ts +22 -0
  116. package/plan-catalog/PlanCatalogConsole.d.ts.map +1 -0
  117. package/plan-catalog/PlanCatalogConsole.js +90 -0
  118. package/plan-catalog/PlanCatalogConsole.js.map +1 -0
  119. package/plan-catalog/PlanCreateForm.d.ts +17 -0
  120. package/plan-catalog/PlanCreateForm.d.ts.map +1 -0
  121. package/plan-catalog/PlanCreateForm.js +52 -0
  122. package/plan-catalog/PlanCreateForm.js.map +1 -0
  123. package/plan-catalog/PlansAccessNotice.d.ts +10 -0
  124. package/plan-catalog/PlansAccessNotice.d.ts.map +1 -0
  125. package/plan-catalog/PlansAccessNotice.js +13 -0
  126. package/plan-catalog/PlansAccessNotice.js.map +1 -0
  127. package/plan-catalog/index.d.ts +8 -0
  128. package/plan-catalog/index.d.ts.map +1 -0
  129. package/plan-catalog/index.js +10 -0
  130. package/plan-catalog/index.js.map +1 -0
  131. package/plan-catalog/plan-draft.d.ts +30 -0
  132. package/plan-catalog/plan-draft.d.ts.map +1 -0
  133. package/plan-catalog/plan-draft.js +91 -0
  134. package/plan-catalog/plan-draft.js.map +1 -0
  135. package/plan-catalog/usePlanMutations.d.ts +42 -0
  136. package/plan-catalog/usePlanMutations.d.ts.map +1 -0
  137. package/plan-catalog/usePlanMutations.js +57 -0
  138. package/plan-catalog/usePlanMutations.js.map +1 -0
  139. package/settings/ChannelAppsSection.d.ts +7 -0
  140. package/settings/ChannelAppsSection.d.ts.map +1 -1
  141. package/settings/ChannelAppsSection.js +15 -2
  142. package/settings/ChannelAppsSection.js.map +1 -1
  143. package/settings/TeamsSection.d.ts.map +1 -1
  144. package/settings/TeamsSection.js +19 -2
  145. package/settings/TeamsSection.js.map +1 -1
  146. package/settings/settings-nav.d.ts.map +1 -1
  147. package/settings/settings-nav.js +7 -1
  148. package/settings/settings-nav.js.map +1 -1
  149. package/settings/useSettingsNavGroups.d.ts.map +1 -1
  150. package/settings/useSettingsNavGroups.js +4 -1
  151. package/settings/useSettingsNavGroups.js.map +1 -1
  152. package/src/billing/AutoRechargeCard.tsx +2 -2
  153. package/src/billing/BillingSection.tsx +68 -11
  154. package/src/billing/ChangePlanDialog.tsx +221 -0
  155. package/src/billing/CreditPackGrid.tsx +1 -1
  156. package/src/billing/LowBalanceBanner.tsx +1 -1
  157. package/src/billing/PaymentMethodCard.tsx +28 -2
  158. package/src/billing/PlanCard.tsx +171 -0
  159. package/src/billing/PlanPicker.tsx +178 -0
  160. package/src/billing/PlanSection.tsx +234 -0
  161. package/src/billing/UpgradeNotice.tsx +95 -0
  162. package/src/billing/credit-packs.ts +4 -1
  163. package/src/billing/index.ts +26 -1
  164. package/src/billing/plan-features.ts +162 -0
  165. package/src/billing/plan-state.ts +92 -0
  166. package/src/billing/redirect.ts +45 -0
  167. package/src/billing/useBillingAccount.ts +12 -0
  168. package/src/billing/useChangePlan.ts +115 -0
  169. package/src/billing/useCreateBillingPortalSession.ts +17 -6
  170. package/src/billing/useCreateCheckoutSession.ts +12 -7
  171. package/src/billing/useCreatePaymentMethodSetupSession.ts +77 -0
  172. package/src/billing/useEntitlements.ts +80 -0
  173. package/src/billing/usePeriodEstimate.ts +59 -0
  174. package/src/billing/usePlans.ts +65 -0
  175. package/src/billing/useSubscription.ts +70 -0
  176. package/src/channel-app/ChannelAppDetailPanel.tsx +179 -158
  177. package/src/channel-app/ChannelAppListPanel.tsx +14 -4
  178. package/src/channel-app/copy.ts +16 -0
  179. package/src/index.ts +54 -0
  180. package/src/internal/features.ts +49 -0
  181. package/src/licenses/license-format.ts +10 -19
  182. package/src/plan-catalog/PlanCatalogConsole.tsx +197 -0
  183. package/src/plan-catalog/PlanCreateForm.tsx +167 -0
  184. package/src/plan-catalog/PlansAccessNotice.tsx +20 -0
  185. package/src/plan-catalog/index.ts +14 -0
  186. package/src/plan-catalog/plan-draft.ts +116 -0
  187. package/src/plan-catalog/usePlanMutations.ts +94 -0
  188. package/src/settings/ChannelAppsSection.tsx +19 -2
  189. package/src/settings/TeamsSection.tsx +20 -2
  190. package/src/settings/settings-nav.ts +7 -0
  191. package/src/settings/useSettingsNavGroups.ts +8 -1
  192. package/src/team/CreateTeamForm.tsx +12 -3
  193. package/src/team/TeamMembersPanel.tsx +6 -2
  194. package/team/CreateTeamForm.d.ts +6 -1
  195. package/team/CreateTeamForm.d.ts.map +1 -1
  196. package/team/CreateTeamForm.js +4 -2
  197. package/team/CreateTeamForm.js.map +1 -1
  198. package/team/TeamMembersPanel.d.ts.map +1 -1
  199. package/team/TeamMembersPanel.js +3 -1
  200. 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":"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"}
1
+ {"version":3,"file":"ChannelAppsSection.js","sourceRoot":"","sources":["../src/settings/ChannelAppsSection.tsx"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TeamsSection.d.ts","sourceRoot":"","sources":["../src/settings/TeamsSection.tsx"],"names":[],"mappings":"AA+BA,oDAAoD;AACpD,wBAAgB,YAAY,4CAgG3B"}
1
+ {"version":3,"file":"TeamsSection.d.ts","sourceRoot":"","sources":["../src/settings/TeamsSection.tsx"],"names":[],"mappings":"AA2CA,oDAAoD;AACpD,wBAAgB,YAAY,4CAsG3B"}
@@ -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 { useResourceAvailable, ApiResourceKind } from "../deployment-mode.js";
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":"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"}
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;AAuB3C,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,gBA8BzC,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"}
@@ -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":"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"}
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,CAmDlE"}
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;IAEzC,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;SACnC,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,CAAC,CAAC,CAAC;AACxF,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
- Purchase a credit pack first to save a card.
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
- Manage credits, purchase credit packs, and view transaction history.
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
- const { createSession, isSubmitting, error: checkoutError, clearError } = useCreateCheckoutSession();
109
- const { openPortal, isLoading: isPortalLoading } = useCreateBillingPortalSession();
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 baseUrl = typeof window !== "undefined" ? window.location.origin : "";
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
+ }