@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,12 +4,13 @@ import { useCallback, useState } from "react";
4
4
  import type { CreateCreditCheckoutSessionResponse } from "@stigmer/protos/ai/stigmer/billing/v1/io_pb";
5
5
  import { useStigmer } from "../hooks.js";
6
6
  import { toError } from "../internal/toError.js";
7
+ import { leaveForStripe, type BillingRedirect } from "./redirect.js";
7
8
 
8
9
  /** Parameters for {@link useCreateCheckoutSession}'s `createSession` callback. */
9
10
  export interface CreateCheckoutSessionInput {
10
11
  /** Organization purchasing credits. */
11
12
  readonly orgId: string;
12
- /** Credit pack to purchase (e.g., "starter", "growth", "team"). */
13
+ /** Credit pack to purchase: its id, e.g. "starter", "growth" or "team" (the pack named Scale). */
13
14
  readonly packId: string;
14
15
  /** URL to redirect to after successful payment. */
15
16
  readonly successUrl: string;
@@ -22,9 +23,10 @@ export interface UseCreateCheckoutSessionReturn {
22
23
  /**
23
24
  * Create a Stripe Checkout Session and redirect the user.
24
25
  *
25
- * On success, sets `window.location.href` to the Stripe-hosted
26
- * checkout page URL. The promise resolves with the response before
27
- * the redirect occurs, allowing callers to perform cleanup if needed.
26
+ * On success, leaves for the Stripe-hosted checkout page: through the
27
+ * hook's `redirect.openUrl` when given, else by navigating this window.
28
+ * The promise resolves with the response before the redirect occurs,
29
+ * allowing callers to perform cleanup if needed.
28
30
  */
29
31
  readonly createSession: (
30
32
  input: CreateCheckoutSessionInput,
@@ -60,8 +62,11 @@ export interface UseCreateCheckoutSessionReturn {
60
62
  * };
61
63
  * ```
62
64
  */
63
- export function useCreateCheckoutSession(): UseCreateCheckoutSessionReturn {
65
+ export function useCreateCheckoutSession(
66
+ redirect?: BillingRedirect,
67
+ ): UseCreateCheckoutSessionReturn {
64
68
  const stigmer = useStigmer();
69
+ const openUrl = redirect?.openUrl;
65
70
  const [isSubmitting, setIsSubmitting] = useState(false);
66
71
  const [error, setError] = useState<Error | null>(null);
67
72
 
@@ -83,7 +88,7 @@ export function useCreateCheckoutSession(): UseCreateCheckoutSessionReturn {
83
88
  });
84
89
 
85
90
  if (response.checkoutUrl) {
86
- window.location.href = response.checkoutUrl;
91
+ await leaveForStripe(response.checkoutUrl, { openUrl });
87
92
  }
88
93
 
89
94
  return response;
@@ -94,7 +99,7 @@ export function useCreateCheckoutSession(): UseCreateCheckoutSessionReturn {
94
99
  setIsSubmitting(false);
95
100
  }
96
101
  },
97
- [stigmer],
102
+ [stigmer, openUrl],
98
103
  );
99
104
 
100
105
  return { createSession, isSubmitting, error, clearError };
@@ -0,0 +1,77 @@
1
+ "use client";
2
+
3
+ import { useCallback, useMemo, useState } from "react";
4
+ import { useStigmer } from "../hooks.js";
5
+ import { toError } from "../internal/toError.js";
6
+ import { billingReturnUrl, leaveForStripe, type BillingRedirect } from "./redirect.js";
7
+
8
+ /** Return value of {@link useCreatePaymentMethodSetupSession}. */
9
+ export interface UseCreatePaymentMethodSetupSessionReturn {
10
+ /**
11
+ * Leave for Stripe's hosted page to save a card, with no charge. Stripe
12
+ * returns to the billing page with `?setup=success` (and `&plan=<id>`
13
+ * when `planId` names the plan the person was subscribing to, so the
14
+ * page can reopen that choice), or without a query when they leave.
15
+ * The saved card becomes the organization's default for every invoice.
16
+ */
17
+ readonly addPaymentMethod: (orgId: string, planId?: string) => Promise<void>;
18
+ /** `true` while the setup session is being created. */
19
+ readonly isSubmitting: boolean;
20
+ /** Error from the last failed attempt, or `null` when healthy. */
21
+ readonly error: Error | null;
22
+ /** Reset `error` to `null`. */
23
+ readonly clearError: () => void;
24
+ }
25
+
26
+ /**
27
+ * Behaviour hook that saves a payment method through a Stripe-hosted setup
28
+ * page: the card door every subscription needs before its first period.
29
+ *
30
+ * The caller needs `can_manage_billing` on the organization. Pass the
31
+ * host's {@link BillingRedirect} where the window cannot host Stripe's
32
+ * page itself.
33
+ */
34
+ export function useCreatePaymentMethodSetupSession(
35
+ redirect?: BillingRedirect,
36
+ ): UseCreatePaymentMethodSetupSessionReturn {
37
+ const stigmer = useStigmer();
38
+ const openUrl = redirect?.openUrl;
39
+ const returnUrl = redirect?.returnUrl;
40
+ const [isSubmitting, setIsSubmitting] = useState(false);
41
+ const [error, setError] = useState<Error | null>(null);
42
+
43
+ const addPaymentMethod = useCallback(
44
+ async (orgId: string, planId?: string): Promise<void> => {
45
+ setIsSubmitting(true);
46
+ setError(null);
47
+ try {
48
+ const billingPage = billingReturnUrl({ returnUrl });
49
+ const success = new URLSearchParams({ setup: "success" });
50
+ if (planId !== undefined && planId !== "") {
51
+ success.set("plan", planId);
52
+ }
53
+ const response = await stigmer.billing.createPaymentMethodSetupSession({
54
+ orgId,
55
+ successUrl: `${billingPage}?${success.toString()}`,
56
+ cancelUrl: billingPage,
57
+ });
58
+ if (response.setupUrl) {
59
+ await leaveForStripe(response.setupUrl, { openUrl });
60
+ }
61
+ } catch (e) {
62
+ const err = toError(e);
63
+ setError(err);
64
+ throw err;
65
+ } finally {
66
+ setIsSubmitting(false);
67
+ }
68
+ },
69
+ [stigmer.billing, openUrl, returnUrl],
70
+ );
71
+ const clearError = useCallback(() => setError(null), []);
72
+
73
+ return useMemo(
74
+ () => ({ addPaymentMethod, isSubmitting, error, clearError }),
75
+ [addPaymentMethod, isSubmitting, error, clearError],
76
+ );
77
+ }
@@ -0,0 +1,80 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import {
6
+ GetEntitlementsInputSchema,
7
+ type GetEntitlementsOutput,
8
+ } from "@stigmer/protos/ai/stigmer/billing/subscription/v1/io_pb";
9
+ import type { Feature } from "@stigmer/protos/ai/stigmer/platform/v1/entitlement_pb";
10
+ import { useStigmer } from "../hooks.js";
11
+ import { useFetch } from "../internal/useFetch.js";
12
+
13
+ /** Options for {@link useEntitlements}. */
14
+ export interface UseEntitlementsOptions {
15
+ /** Skip fetching while `false` (stable no-op). Default `true`. */
16
+ readonly enabled?: boolean;
17
+ }
18
+
19
+ /** Return value of {@link useEntitlements}. */
20
+ export interface UseEntitlementsReturn {
21
+ /**
22
+ * What the organization may do right now and the plan that permits it
23
+ * (`planId` empty on Free), or `null` before the first successful fetch.
24
+ */
25
+ readonly entitlements: GetEntitlementsOutput | null;
26
+ /**
27
+ * Whether the organization's plan includes `feature` right now; `null`
28
+ * until the entitlements have loaded, so a caller never refuses on a
29
+ * guess.
30
+ */
31
+ readonly allows: (feature: Feature) => boolean | null;
32
+ /** `true` while the initial fetch is in flight. */
33
+ readonly isLoading: boolean;
34
+ /** Error from the last failed request, or `null` when healthy. */
35
+ readonly error: Error | null;
36
+ /** Discard cached data and re-fetch from the server. */
37
+ readonly refetch: () => void;
38
+ }
39
+
40
+ /**
41
+ * Data hook that resolves what an organization's plan permits right now.
42
+ *
43
+ * The answer is derived by the server on every read: the live
44
+ * subscription's plan, else Free's. A platform-managed organization
45
+ * resolves through its integrator's plan. Every member may read it
46
+ * (`can_view_billing` is granted to viewers).
47
+ *
48
+ * Cloud-only. Self-hosted editions answer from their license, not a plan,
49
+ * so callers gate on the edition before reading (see `UpgradeNotice`).
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * const { allows } = useEntitlements(orgId);
54
+ * if (allows(Feature.teams) === false) return <UpgradeNotice feature={Feature.teams} />;
55
+ * ```
56
+ */
57
+ export function useEntitlements(
58
+ orgId: string | null,
59
+ options?: UseEntitlementsOptions,
60
+ ): UseEntitlementsReturn {
61
+ const stigmer = useStigmer();
62
+ const enabled = (options?.enabled ?? true) && orgId !== null && orgId !== "";
63
+ const { data: entitlements, isLoading, error, refetch } = useFetch(
64
+ enabled && orgId ? () => stigmer.subscription.getEntitlements(create(GetEntitlementsInputSchema, { orgId })) : null,
65
+ [enabled, orgId, stigmer],
66
+ null as GetEntitlementsOutput | null,
67
+ { refetchOnWindowFocus: true },
68
+ );
69
+ const features = entitlements?.entitlements?.features;
70
+ return useMemo(
71
+ () => ({
72
+ entitlements,
73
+ allows: (feature: Feature) => (features === undefined ? null : features.includes(feature)),
74
+ isLoading,
75
+ error,
76
+ refetch,
77
+ }),
78
+ [entitlements, features, isLoading, error, refetch],
79
+ );
80
+ }
@@ -0,0 +1,59 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import {
6
+ GetPeriodEstimateInputSchema,
7
+ type PeriodEstimate,
8
+ } from "@stigmer/protos/ai/stigmer/billing/subscription/v1/io_pb";
9
+ import { useStigmer } from "../hooks.js";
10
+ import { useFetch } from "../internal/useFetch.js";
11
+
12
+ /** Options for {@link usePeriodEstimate}. */
13
+ export interface UsePeriodEstimateOptions {
14
+ /** Skip fetching while `false`: read only while a subscription is live. Default `true`. */
15
+ readonly enabled?: boolean;
16
+ }
17
+
18
+ /** Return value of {@link usePeriodEstimate}. */
19
+ export interface UsePeriodEstimateReturn {
20
+ /** The current period's estimated invoice, or `null` before the first successful fetch. */
21
+ readonly estimate: PeriodEstimate | null;
22
+ /** `true` while the initial fetch is in flight. */
23
+ readonly isLoading: boolean;
24
+ /** Error from the last failed request, or `null` when healthy. */
25
+ readonly error: Error | null;
26
+ /** Discard cached data and re-fetch from the server. */
27
+ readonly refetch: () => void;
28
+ }
29
+
30
+ /**
31
+ * Data hook that estimates what an organization's current subscription
32
+ * period will be invoiced.
33
+ *
34
+ * The estimate is the invoice the period would close with if no further
35
+ * usage occurred: the plan's minimum for the whole period (or its share of
36
+ * the period's provider cost, whichever is greater), less the commission
37
+ * already collected on tokens, plus managed organizations beyond those
38
+ * included. The server rates it by the same rule the monthly close
39
+ * invoices by, so it agrees with the invoice to the cent.
40
+ *
41
+ * The server answers NOT_FOUND when nothing is live to invoice (Free, or a
42
+ * managed organization), so enable it only while a subscription is live.
43
+ *
44
+ * Cloud-only; the caller needs `can_view_billing` on the organization.
45
+ */
46
+ export function usePeriodEstimate(
47
+ orgId: string | null,
48
+ options?: UsePeriodEstimateOptions,
49
+ ): UsePeriodEstimateReturn {
50
+ const stigmer = useStigmer();
51
+ const enabled = (options?.enabled ?? true) && orgId !== null && orgId !== "";
52
+ const { data: estimate, isLoading, error, refetch } = useFetch(
53
+ enabled && orgId ? () => stigmer.subscription.getPeriodEstimate(create(GetPeriodEstimateInputSchema, { orgId })) : null,
54
+ [enabled, orgId, stigmer],
55
+ null as PeriodEstimate | null,
56
+ { refetchOnWindowFocus: true },
57
+ );
58
+ return useMemo(() => ({ estimate, isLoading, error, refetch }), [estimate, isLoading, error, refetch]);
59
+ }
@@ -0,0 +1,65 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import type { Plan } from "@stigmer/protos/ai/stigmer/billing/plan/v1/api_pb";
6
+ import { ListPlansInputSchema } from "@stigmer/protos/ai/stigmer/billing/plan/v1/io_pb";
7
+ import { useStigmer } from "../hooks.js";
8
+ import { useFetch } from "../internal/useFetch.js";
9
+
10
+ /** Options for {@link usePlans}. */
11
+ export interface UsePlansOptions {
12
+ /** Include retired plans beside the ones that can still be bought. Default `false`. */
13
+ readonly includeRetired?: boolean;
14
+ /** Skip fetching while `false` (stable no-op). Default `true`. */
15
+ readonly enabled?: boolean;
16
+ }
17
+
18
+ /** Return value of {@link usePlans}. */
19
+ export interface UsePlansReturn {
20
+ /** The catalog's plans, or `null` before the first successful fetch. */
21
+ readonly plans: readonly Plan[] | null;
22
+ /** `true` while the initial fetch is in flight. */
23
+ readonly isLoading: boolean;
24
+ /** `true` while a background refetch is in flight and stale data is shown. */
25
+ readonly isRefetching: boolean;
26
+ /** Error from the last failed request, or `null` when healthy. */
27
+ readonly error: Error | null;
28
+ /** Discard cached data and re-fetch from the server. */
29
+ readonly refetch: () => void;
30
+ }
31
+
32
+ /**
33
+ * Data hook that lists the Stigmer Cloud plan catalog.
34
+ *
35
+ * Every signed-in caller may read it. By default it lists only the plans
36
+ * that can still be bought; `includeRetired` adds the retired rows an
37
+ * operator audits. Free is not a row: an organization with no live
38
+ * subscription is on Free.
39
+ *
40
+ * Cloud-only: other editions serve no plans.
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * const { plans } = usePlans();
45
+ * ```
46
+ */
47
+ export function usePlans(options?: UsePlansOptions): UsePlansReturn {
48
+ const stigmer = useStigmer();
49
+ const enabled = options?.enabled ?? true;
50
+ const includeRetired = options?.includeRetired ?? false;
51
+ const { data: plans, isLoading, isRefetching, error, refetch } = useFetch(
52
+ enabled
53
+ ? () =>
54
+ stigmer.plan
55
+ .list(create(ListPlansInputSchema, { includeRetired }))
56
+ .then((result): readonly Plan[] => result.entries)
57
+ : null,
58
+ [enabled, includeRetired, stigmer],
59
+ null as readonly Plan[] | null,
60
+ );
61
+ return useMemo(
62
+ () => ({ plans, isLoading, isRefetching, error, refetch }),
63
+ [plans, isLoading, isRefetching, error, refetch],
64
+ );
65
+ }
@@ -0,0 +1,70 @@
1
+ "use client";
2
+
3
+ import { useMemo } from "react";
4
+ import { create } from "@bufbuild/protobuf";
5
+ import { isNotFound } from "@stigmer/sdk";
6
+ import type { Subscription } from "@stigmer/protos/ai/stigmer/billing/subscription/v1/api_pb";
7
+ import { GetSubscriptionForOrganizationInputSchema } from "@stigmer/protos/ai/stigmer/billing/subscription/v1/io_pb";
8
+ import { useStigmer } from "../hooks.js";
9
+ import { useFetch } from "../internal/useFetch.js";
10
+
11
+ /** Return value of {@link useSubscription}. */
12
+ export interface UseSubscriptionReturn {
13
+ /**
14
+ * The organization's subscription as stored; `null` when it never
15
+ * subscribed (it is on Free) or before the first successful fetch.
16
+ * A subscription that ended reads `canceled` with its period in the
17
+ * past; {@link useEntitlements} says what the organization may do now.
18
+ */
19
+ readonly subscription: Subscription | null;
20
+ /** `true` while the initial fetch is in flight. */
21
+ readonly isLoading: boolean;
22
+ /** `true` while a background refetch is in flight and stale data is shown. */
23
+ readonly isRefetching: boolean;
24
+ /** Error from the last failed request, or `null` when healthy. Never NOT_FOUND. */
25
+ readonly error: Error | null;
26
+ /** Discard cached data and re-fetch from the server. */
27
+ readonly refetch: () => void;
28
+ }
29
+
30
+ /**
31
+ * Data hook that reads an organization's subscription.
32
+ *
33
+ * The server answers NOT_FOUND for an organization that never subscribed;
34
+ * this hook reads that as `null`, because never subscribed is a state (the
35
+ * Free plan), not a failure.
36
+ *
37
+ * Pass `null` as `orgId` to skip fetching. Refetches when the window
38
+ * regains focus, so a plan changed in another window, or a card saved in
39
+ * the system browser, shows on return.
40
+ *
41
+ * Cloud-only; the caller needs `can_view_billing` on the organization.
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * const { subscription } = useSubscription(orgId);
46
+ * ```
47
+ */
48
+ export function useSubscription(orgId: string | null): UseSubscriptionReturn {
49
+ const stigmer = useStigmer();
50
+ const { data: subscription, isLoading, isRefetching, error, refetch } = useFetch(
51
+ orgId
52
+ ? () =>
53
+ stigmer.subscription
54
+ .getForOrganization(create(GetSubscriptionForOrganizationInputSchema, { orgId }))
55
+ .catch((err: unknown) => {
56
+ if (isNotFound(err)) {
57
+ return null;
58
+ }
59
+ throw err;
60
+ })
61
+ : null,
62
+ [orgId, stigmer],
63
+ null as Subscription | null,
64
+ { refetchOnWindowFocus: true },
65
+ );
66
+ return useMemo(
67
+ () => ({ subscription, isLoading, isRefetching, error, refetch }),
68
+ [subscription, isLoading, isRefetching, error, refetch],
69
+ );
70
+ }