@sudobility/entity_pages 0.0.14 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -20,4 +20,4 @@
20
20
  * }
21
21
  * ```
22
22
  */
23
- export { EntityListPage, type EntityListPageProps, MembersManagementPage, type MembersManagementPageProps, InvitationsPage, type InvitationsPageProps, EntitySubscriptionsPage, type EntitySubscriptionsPageProps, type SubscriptionProduct, type CurrentSubscription, type SubscriptionContextValue, type SubscriptionPageLabels, type SubscriptionPageFormatters, EntityPricingPage, type EntityPricingPageProps, type PricingProduct, type FAQItem, type PricingPageLabels, type PricingPageFormatters, type EntitlementMap, type EntitlementLevels, } from './pages';
23
+ export { EntityListPage, type EntityListPageProps, MembersManagementPage, type MembersManagementPageProps, InvitationsPage, type InvitationsPageProps, } from './pages';
package/dist/index.esm.js CHANGED
@@ -1,79 +1,78 @@
1
- import { jsxs as s, jsx as i, Fragment as ne } from "react/jsx-runtime";
2
- import { useState as E, useEffect as re, useCallback as x } from "react";
3
- import { Plus as ae } from "lucide-react";
4
- import { EntityList as G, InvitationForm as oe, InvitationList as K, MemberList as le } from "@sudobility/entity-components";
5
- import { useEntities as se, useCreateEntity as ce, useEntityMembers as de, useUpdateMemberRole as ue, useRemoveMember as me, useEntityInvitations as he, useCreateInvitation as ye, useCancelInvitation as fe, useMyInvitations as ge, useAcceptInvitation as pe, useDeclineInvitation as ve } from "@sudobility/entity_client";
6
- import { SubscriptionLayout as xe, SubscriptionTile as W, SegmentedControl as J } from "@sudobility/subscription-components";
7
- function Te({
8
- client: u,
9
- onSelectEntity: a
1
+ import { jsxs as t, jsx as e } from "react/jsx-runtime";
2
+ import { useState as f } from "react";
3
+ import { Plus as I } from "lucide-react";
4
+ import { EntityList as x, InvitationForm as M, InvitationList as C, MemberList as S } from "@sudobility/entity-components";
5
+ import { useEntities as z, useCreateEntity as k, useEntityMembers as L, useUpdateMemberRole as A, useRemoveMember as F, useEntityInvitations as E, useCreateInvitation as P, useCancelInvitation as D, useMyInvitations as R, useAcceptInvitation as j, useDeclineInvitation as O } from "@sudobility/entity_client";
6
+ function U({
7
+ client: i,
8
+ onSelectEntity: n
10
9
  }) {
11
- const [t, l] = E(!1), [o, b] = E({
10
+ const [p, a] = f(!1), [s, c] = f({
12
11
  displayName: "",
13
12
  description: ""
14
- }), [y, N] = E(null), { data: h = [], isLoading: c } = se(u), f = ce(u), S = h.filter((d) => d.entityType === "personal"), T = h.filter(
15
- (d) => d.entityType === "organization"
13
+ }), [h, d] = f(null), { data: m = [], isLoading: r } = z(i), l = k(i), b = m.filter((o) => o.entityType === "personal"), v = m.filter(
14
+ (o) => o.entityType === "organization"
16
15
  );
17
- return /* @__PURE__ */ s("div", { className: "space-y-8", children: [
18
- /* @__PURE__ */ s("div", { className: "flex items-center justify-between", children: [
19
- /* @__PURE__ */ s("div", { children: [
20
- /* @__PURE__ */ i("h1", { className: "text-2xl font-bold text-foreground", children: "Workspaces" }),
21
- /* @__PURE__ */ i("p", { className: "text-muted-foreground", children: "Manage your personal and organization workspaces" })
16
+ return /* @__PURE__ */ t("div", { className: "space-y-8", children: [
17
+ /* @__PURE__ */ t("div", { className: "flex items-center justify-between", children: [
18
+ /* @__PURE__ */ t("div", { children: [
19
+ /* @__PURE__ */ e("h1", { className: "text-2xl font-bold text-foreground", children: "Workspaces" }),
20
+ /* @__PURE__ */ e("p", { className: "text-muted-foreground", children: "Manage your personal and organization workspaces" })
22
21
  ] }),
23
- /* @__PURE__ */ s(
22
+ /* @__PURE__ */ t(
24
23
  "button",
25
24
  {
26
25
  type: "button",
27
- onClick: () => l(!0),
26
+ onClick: () => a(!0),
28
27
  className: "flex items-center gap-2 px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors",
29
28
  children: [
30
- /* @__PURE__ */ i(ae, { className: "h-4 w-4" }),
31
- /* @__PURE__ */ i("span", { children: "New Organization" })
29
+ /* @__PURE__ */ e(I, { className: "h-4 w-4" }),
30
+ /* @__PURE__ */ e("span", { children: "New Organization" })
32
31
  ]
33
32
  }
34
33
  )
35
34
  ] }),
36
- t && /* @__PURE__ */ i("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ s("div", { className: "w-full max-w-md rounded-lg bg-background p-6 shadow-lg", children: [
37
- /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-4", children: "Create Organization" }),
38
- /* @__PURE__ */ s("form", { onSubmit: async (d) => {
39
- if (d.preventDefault(), N(null), !o.displayName.trim()) {
40
- N("Display name is required");
35
+ p && /* @__PURE__ */ e("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ t("div", { className: "w-full max-w-md rounded-lg bg-background p-6 shadow-lg", children: [
36
+ /* @__PURE__ */ e("h2", { className: "text-lg font-semibold mb-4", children: "Create Organization" }),
37
+ /* @__PURE__ */ t("form", { onSubmit: async (o) => {
38
+ if (o.preventDefault(), d(null), !s.displayName.trim()) {
39
+ d("Display name is required");
41
40
  return;
42
41
  }
43
42
  try {
44
- await f.mutateAsync({
45
- displayName: o.displayName.trim(),
46
- description: o.description.trim() || void 0
47
- }), l(!1), b({ displayName: "", description: "" });
48
- } catch (g) {
49
- N(g.message || "Failed to create organization");
43
+ await l.mutateAsync({
44
+ displayName: s.displayName.trim(),
45
+ description: s.description.trim() || void 0
46
+ }), a(!1), c({ displayName: "", description: "" });
47
+ } catch (u) {
48
+ d(u.message || "Failed to create organization");
50
49
  }
51
50
  }, className: "space-y-4", children: [
52
- /* @__PURE__ */ s("div", { children: [
53
- /* @__PURE__ */ i("label", { className: "block text-sm font-medium mb-1", children: "Display Name" }),
54
- /* @__PURE__ */ i(
51
+ /* @__PURE__ */ t("div", { children: [
52
+ /* @__PURE__ */ e("label", { className: "block text-sm font-medium mb-1", children: "Display Name" }),
53
+ /* @__PURE__ */ e(
55
54
  "input",
56
55
  {
57
56
  type: "text",
58
- value: o.displayName,
59
- onChange: (d) => b((g) => ({
60
- ...g,
61
- displayName: d.target.value
57
+ value: s.displayName,
58
+ onChange: (o) => c((u) => ({
59
+ ...u,
60
+ displayName: o.target.value
62
61
  })),
63
62
  placeholder: "My Organization",
64
63
  className: "w-full px-3 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
65
64
  }
66
65
  )
67
66
  ] }),
68
- /* @__PURE__ */ s("div", { children: [
69
- /* @__PURE__ */ i("label", { className: "block text-sm font-medium mb-1", children: "Description (optional)" }),
70
- /* @__PURE__ */ i(
67
+ /* @__PURE__ */ t("div", { children: [
68
+ /* @__PURE__ */ e("label", { className: "block text-sm font-medium mb-1", children: "Description (optional)" }),
69
+ /* @__PURE__ */ e(
71
70
  "textarea",
72
71
  {
73
- value: o.description,
74
- onChange: (d) => b((g) => ({
75
- ...g,
76
- description: d.target.value
72
+ value: s.description,
73
+ onChange: (o) => c((u) => ({
74
+ ...u,
75
+ description: o.target.value
77
76
  })),
78
77
  placeholder: "What is this organization for?",
79
78
  rows: 3,
@@ -81,633 +80,200 @@ function Te({
81
80
  }
82
81
  )
83
82
  ] }),
84
- y && /* @__PURE__ */ i("p", { className: "text-sm text-destructive", children: y }),
85
- /* @__PURE__ */ s("div", { className: "flex justify-end gap-2", children: [
86
- /* @__PURE__ */ i(
83
+ h && /* @__PURE__ */ e("p", { className: "text-sm text-destructive", children: h }),
84
+ /* @__PURE__ */ t("div", { className: "flex justify-end gap-2", children: [
85
+ /* @__PURE__ */ e(
87
86
  "button",
88
87
  {
89
88
  type: "button",
90
89
  onClick: () => {
91
- l(!1), b({ displayName: "", description: "" }), N(null);
90
+ a(!1), c({ displayName: "", description: "" }), d(null);
92
91
  },
93
92
  className: "px-4 py-2 rounded-lg border hover:bg-muted transition-colors",
94
93
  children: "Cancel"
95
94
  }
96
95
  ),
97
- /* @__PURE__ */ i(
96
+ /* @__PURE__ */ e(
98
97
  "button",
99
98
  {
100
99
  type: "submit",
101
- disabled: f.isPending,
100
+ disabled: l.isPending,
102
101
  className: "px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50",
103
- children: f.isPending ? "Creating..." : "Create"
102
+ children: l.isPending ? "Creating..." : "Create"
104
103
  }
105
104
  )
106
105
  ] })
107
106
  ] })
108
107
  ] }) }),
109
- S.length > 0 && /* @__PURE__ */ s("div", { children: [
110
- /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Personal Workspace" }),
111
- /* @__PURE__ */ i(
112
- G,
108
+ b.length > 0 && /* @__PURE__ */ t("div", { children: [
109
+ /* @__PURE__ */ e("h2", { className: "text-lg font-semibold mb-3", children: "Personal Workspace" }),
110
+ /* @__PURE__ */ e(
111
+ x,
113
112
  {
114
- entities: S,
115
- onSelect: a,
116
- isLoading: c
113
+ entities: b,
114
+ onSelect: n,
115
+ isLoading: r
117
116
  }
118
117
  )
119
118
  ] }),
120
- /* @__PURE__ */ s("div", { children: [
121
- /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Organizations" }),
122
- T.length === 0 && !c ? /* @__PURE__ */ s("div", { className: "text-center py-8 text-muted-foreground border border-dashed rounded-lg", children: [
123
- /* @__PURE__ */ i("p", { children: "No organizations yet" }),
124
- /* @__PURE__ */ i(
119
+ /* @__PURE__ */ t("div", { children: [
120
+ /* @__PURE__ */ e("h2", { className: "text-lg font-semibold mb-3", children: "Organizations" }),
121
+ v.length === 0 && !r ? /* @__PURE__ */ t("div", { className: "text-center py-8 text-muted-foreground border border-dashed rounded-lg", children: [
122
+ /* @__PURE__ */ e("p", { children: "No organizations yet" }),
123
+ /* @__PURE__ */ e(
125
124
  "button",
126
125
  {
127
126
  type: "button",
128
- onClick: () => l(!0),
127
+ onClick: () => a(!0),
129
128
  className: "mt-2 text-primary hover:underline",
130
129
  children: "Create your first organization"
131
130
  }
132
131
  )
133
- ] }) : /* @__PURE__ */ i(
134
- G,
132
+ ] }) : /* @__PURE__ */ e(
133
+ x,
135
134
  {
136
- entities: T,
137
- onSelect: a,
138
- isLoading: c
135
+ entities: v,
136
+ onSelect: n,
137
+ isLoading: r
139
138
  }
140
139
  )
141
140
  ] })
142
141
  ] });
143
142
  }
144
- function Fe({
145
- client: u,
146
- entity: a,
147
- currentUserId: t
143
+ function B({
144
+ client: i,
145
+ entity: n,
146
+ currentUserId: p
148
147
  }) {
149
- const l = a.userRole === "admin", { data: o = [], isLoading: b } = de(
150
- u,
151
- a.entitySlug
152
- ), y = ue(u), N = me(u), { data: h = [], isLoading: c } = he(u, l ? a.entitySlug : null), f = ye(u), S = fe(u), T = async (w, P) => {
148
+ const a = n.userRole === "admin", { data: s = [], isLoading: c } = L(
149
+ i,
150
+ n.entitySlug
151
+ ), h = A(i), d = F(i), { data: m = [], isLoading: r } = E(i, a ? n.entitySlug : null), l = P(i), b = D(i), v = async (g, y) => {
153
152
  try {
154
- await y.mutateAsync({
155
- entitySlug: a.entitySlug,
156
- memberId: w,
157
- role: P
153
+ await h.mutateAsync({
154
+ entitySlug: n.entitySlug,
155
+ memberId: g,
156
+ role: y
158
157
  });
159
- } catch (F) {
160
- console.error("Failed to update role:", F);
158
+ } catch (w) {
159
+ console.error("Failed to update role:", w);
161
160
  }
162
- }, R = async (w) => {
161
+ }, N = async (g) => {
163
162
  if (confirm("Are you sure you want to remove this member?"))
164
163
  try {
165
- await N.mutateAsync({
166
- entitySlug: a.entitySlug,
167
- memberId: w
164
+ await d.mutateAsync({
165
+ entitySlug: n.entitySlug,
166
+ memberId: g
168
167
  });
169
- } catch (P) {
170
- console.error("Failed to remove member:", P);
168
+ } catch (y) {
169
+ console.error("Failed to remove member:", y);
171
170
  }
172
- }, d = async (w) => {
173
- await f.mutateAsync({
174
- entitySlug: a.entitySlug,
175
- request: w
171
+ }, o = async (g) => {
172
+ await l.mutateAsync({
173
+ entitySlug: n.entitySlug,
174
+ request: g
176
175
  });
177
- }, g = async (w) => {
176
+ }, u = async (g) => {
178
177
  try {
179
- await S.mutateAsync({
180
- entitySlug: a.entitySlug,
181
- invitationId: w
178
+ await b.mutateAsync({
179
+ entitySlug: n.entitySlug,
180
+ invitationId: g
182
181
  });
183
- } catch (P) {
184
- console.error("Failed to cancel invitation:", P);
182
+ } catch (y) {
183
+ console.error("Failed to cancel invitation:", y);
185
184
  }
186
185
  };
187
- return a.entityType === "personal" ? /* @__PURE__ */ s("div", { className: "text-center py-12 text-muted-foreground", children: [
188
- /* @__PURE__ */ i("p", { children: "Personal workspaces cannot have additional members." }),
189
- /* @__PURE__ */ i("p", { className: "mt-2", children: "Create an organization to collaborate with others." })
190
- ] }) : /* @__PURE__ */ s("div", { className: "space-y-8", children: [
191
- /* @__PURE__ */ s("div", { children: [
192
- /* @__PURE__ */ i("h1", { className: "text-2xl font-bold text-foreground", children: "Members" }),
193
- /* @__PURE__ */ s("p", { className: "text-muted-foreground", children: [
186
+ return n.entityType === "personal" ? /* @__PURE__ */ t("div", { className: "text-center py-12 text-muted-foreground", children: [
187
+ /* @__PURE__ */ e("p", { children: "Personal workspaces cannot have additional members." }),
188
+ /* @__PURE__ */ e("p", { className: "mt-2", children: "Create an organization to collaborate with others." })
189
+ ] }) : /* @__PURE__ */ t("div", { className: "space-y-8", children: [
190
+ /* @__PURE__ */ t("div", { children: [
191
+ /* @__PURE__ */ e("h1", { className: "text-2xl font-bold text-foreground", children: "Members" }),
192
+ /* @__PURE__ */ t("p", { className: "text-muted-foreground", children: [
194
193
  "Manage members and invitations for ",
195
- a.displayName
194
+ n.displayName
196
195
  ] })
197
196
  ] }),
198
- l && /* @__PURE__ */ s("div", { className: "rounded-lg border p-4", children: [
199
- /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-4", children: "Invite Members" }),
200
- /* @__PURE__ */ i(
201
- oe,
197
+ a && /* @__PURE__ */ t("div", { className: "rounded-lg border p-4", children: [
198
+ /* @__PURE__ */ e("h2", { className: "text-lg font-semibold mb-4", children: "Invite Members" }),
199
+ /* @__PURE__ */ e(
200
+ M,
202
201
  {
203
- onSubmit: d,
204
- isSubmitting: f.isPending
202
+ onSubmit: o,
203
+ isSubmitting: l.isPending
205
204
  }
206
205
  )
207
206
  ] }),
208
- l && /* @__PURE__ */ s("div", { children: [
209
- /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Pending Invitations" }),
210
- /* @__PURE__ */ i(
211
- K,
207
+ a && /* @__PURE__ */ t("div", { children: [
208
+ /* @__PURE__ */ e("h2", { className: "text-lg font-semibold mb-3", children: "Pending Invitations" }),
209
+ /* @__PURE__ */ e(
210
+ C,
212
211
  {
213
- invitations: h,
212
+ invitations: m,
214
213
  mode: "admin",
215
- onCancel: g,
216
- isLoading: c,
214
+ onCancel: u,
215
+ isLoading: r,
217
216
  emptyMessage: "No pending invitations"
218
217
  }
219
218
  )
220
219
  ] }),
221
- /* @__PURE__ */ s("div", { children: [
222
- /* @__PURE__ */ s("h2", { className: "text-lg font-semibold mb-3", children: [
220
+ /* @__PURE__ */ t("div", { children: [
221
+ /* @__PURE__ */ t("h2", { className: "text-lg font-semibold mb-3", children: [
223
222
  "Current Members (",
224
- o.length,
223
+ s.length,
225
224
  ")"
226
225
  ] }),
227
- /* @__PURE__ */ i(
228
- le,
226
+ /* @__PURE__ */ e(
227
+ S,
229
228
  {
230
- members: o,
231
- currentUserId: t,
232
- canManage: l,
233
- onRoleChange: T,
234
- onRemove: R,
235
- isLoading: b
229
+ members: s,
230
+ currentUserId: p,
231
+ canManage: a,
232
+ onRoleChange: v,
233
+ onRemove: N,
234
+ isLoading: c
236
235
  }
237
236
  )
238
237
  ] })
239
238
  ] });
240
239
  }
241
- function Le({
242
- client: u,
243
- onInvitationAccepted: a
240
+ function G({
241
+ client: i,
242
+ onInvitationAccepted: n
244
243
  }) {
245
- const { data: t = [], isLoading: l } = ge(u), o = pe(u), b = ve(u), y = async (c) => {
244
+ const { data: p = [], isLoading: a } = R(i), s = j(i), c = O(i), h = async (r) => {
246
245
  try {
247
- await o.mutateAsync(c), a?.();
248
- } catch (f) {
249
- console.error("Failed to accept invitation:", f);
246
+ await s.mutateAsync(r), n?.();
247
+ } catch (l) {
248
+ console.error("Failed to accept invitation:", l);
250
249
  }
251
- }, N = async (c) => {
250
+ }, d = async (r) => {
252
251
  try {
253
- await b.mutateAsync(c);
254
- } catch (f) {
255
- console.error("Failed to decline invitation:", f);
252
+ await c.mutateAsync(r);
253
+ } catch (l) {
254
+ console.error("Failed to decline invitation:", l);
256
255
  }
257
- }, h = t.filter((c) => c.status === "pending").length;
258
- return /* @__PURE__ */ s("div", { className: "space-y-6", children: [
259
- /* @__PURE__ */ s("div", { children: [
260
- /* @__PURE__ */ i("h1", { className: "text-2xl font-bold text-foreground", children: "Invitations" }),
261
- /* @__PURE__ */ i("p", { className: "text-muted-foreground", children: h > 0 ? `You have ${h} pending invitation${h > 1 ? "s" : ""}` : "No pending invitations" })
256
+ }, m = p.filter((r) => r.status === "pending").length;
257
+ return /* @__PURE__ */ t("div", { className: "space-y-6", children: [
258
+ /* @__PURE__ */ t("div", { children: [
259
+ /* @__PURE__ */ e("h1", { className: "text-2xl font-bold text-foreground", children: "Invitations" }),
260
+ /* @__PURE__ */ e("p", { className: "text-muted-foreground", children: m > 0 ? `You have ${m} pending invitation${m > 1 ? "s" : ""}` : "No pending invitations" })
262
261
  ] }),
263
- /* @__PURE__ */ i(
264
- K,
262
+ /* @__PURE__ */ e(
263
+ C,
265
264
  {
266
- invitations: t,
265
+ invitations: p,
267
266
  mode: "user",
268
- onAccept: y,
269
- onDecline: N,
270
- isLoading: l,
267
+ onAccept: h,
268
+ onDecline: d,
269
+ isLoading: a,
271
270
  emptyMessage: "You don't have any pending invitations"
272
271
  }
273
272
  )
274
273
  ] });
275
274
  }
276
- const O = {
277
- ultra_yearly: "bandwidth_ultra",
278
- ultra_monthly: "bandwidth_ultra",
279
- pro_yearly: "bandwidth_pro",
280
- pro_monthly: "bandwidth_pro",
281
- dev_yearly: "bandwidth_dev",
282
- dev_monthly: "bandwidth_dev"
283
- };
284
- function Ce({
285
- subscription: u,
286
- rateLimitsConfig: a,
287
- labels: t,
288
- formatters: l,
289
- onPurchaseSuccess: o,
290
- onRestoreSuccess: b,
291
- onError: y,
292
- onWarning: N
293
- }) {
294
- const {
295
- products: h,
296
- currentSubscription: c,
297
- isLoading: f,
298
- error: S,
299
- purchase: T,
300
- restore: R,
301
- clearError: d
302
- } = u, [g, w] = E("monthly"), [P, F] = E(null), [L, z] = E(!1), [C, n] = E(!1);
303
- re(() => {
304
- S && (y?.(t.errorTitle, S), d());
305
- }, [S, d, t.errorTitle, y]);
306
- const m = h.filter((e) => {
307
- if (!e.period) return !1;
308
- const r = e.period.includes("Y") || e.period.includes("year");
309
- return g === "yearly" ? r : !r;
310
- }).sort((e, r) => parseFloat(e.price) - parseFloat(r.price)), B = (e) => {
311
- w(e), F(null);
312
- }, M = async () => {
313
- if (P) {
314
- z(!0), d();
315
- try {
316
- await T(P) && (o?.(), F(null));
317
- } catch (e) {
318
- y?.(
319
- t.errorTitle,
320
- e instanceof Error ? e.message : t.purchaseError
321
- );
322
- } finally {
323
- z(!1);
324
- }
325
- }
326
- }, I = async () => {
327
- n(!0), d();
328
- try {
329
- await R() ? b?.() : N?.(t.errorTitle, t.restoreNoPurchases);
330
- } catch (e) {
331
- y?.(
332
- t.errorTitle,
333
- e instanceof Error ? e.message : t.restoreError
334
- );
335
- } finally {
336
- n(!1);
337
- }
338
- }, k = x((e) => e ? new Intl.DateTimeFormat(void 0, {
339
- year: "numeric",
340
- month: "long",
341
- day: "numeric"
342
- }).format(e) : "", []), A = x(
343
- (e) => e ? e.includes("Y") || e.includes("year") ? t.periodYear : e.includes("M") || e.includes("month") ? t.periodMonth : e.includes("W") || e.includes("week") ? t.periodWeek : "" : "",
344
- [t]
345
- ), Y = x(
346
- (e) => {
347
- if (!e) return;
348
- const r = parseInt(e.replace(/\D/g, "") || "1", 10);
349
- return e.includes("W") ? l.formatTrialWeeks(r) : e.includes("M") ? l.formatTrialMonths(r) : l.formatTrialDays(r);
350
- },
351
- [l]
352
- ), _ = x(
353
- (e) => {
354
- if (!a?.tiers) return;
355
- const r = O[e];
356
- return r ? a.tiers.find(
357
- (v) => v.entitlement === r
358
- ) : a.tiers.find((v) => v.entitlement === "none");
359
- },
360
- [a]
361
- ), p = x(
362
- (e) => e === null ? t.unlimited : e.toLocaleString(),
363
- [t.unlimited]
364
- ), j = x(
365
- (e) => {
366
- const r = _(e);
367
- if (!r) return [];
368
- const v = [];
369
- return r.limits.hourly !== null && v.push(
370
- l.formatHourlyLimit(p(r.limits.hourly))
371
- ), r.limits.daily !== null && v.push(
372
- l.formatDailyLimit(p(r.limits.daily))
373
- ), r.limits.monthly !== null && v.push(
374
- l.formatMonthlyLimit(p(r.limits.monthly))
375
- ), r.limits.hourly === null && r.limits.daily === null && r.limits.monthly === null && v.push(t.unlimitedRequests), v;
376
- },
377
- [_, p, l, t.unlimitedRequests]
378
- ), Q = x(
379
- (e) => j(e),
380
- [j]
381
- ), X = x(() => {
382
- const e = [...t.freeTierFeatures];
383
- if (a?.tiers) {
384
- const r = a.tiers.find(
385
- (v) => v.entitlement === "none"
386
- );
387
- r && (r.limits.hourly !== null && e.push(
388
- l.formatHourlyLimit(p(r.limits.hourly))
389
- ), r.limits.daily !== null && e.push(
390
- l.formatDailyLimit(p(r.limits.daily))
391
- ), r.limits.monthly !== null && e.push(
392
- l.formatMonthlyLimit(p(r.limits.monthly))
393
- ));
394
- }
395
- return e;
396
- }, [a, p, l, t.freeTierFeatures]), Z = x(
397
- (e) => {
398
- const r = O[e];
399
- if (!r) return;
400
- const v = h.find(
401
- (D) => D.identifier === e
402
- );
403
- if (!v) return;
404
- const U = Object.entries(O).find(
405
- ([D, ie]) => ie === r && D.includes("monthly")
406
- )?.[0];
407
- if (!U) return;
408
- const $ = h.find(
409
- (D) => D.identifier === U
410
- );
411
- if (!$) return;
412
- const V = parseFloat(v.price), q = parseFloat($.price);
413
- if (q <= 0 || V <= 0) return;
414
- const H = q * 12, te = (H - V) / H * 100;
415
- return Math.round(te);
416
- },
417
- [h]
418
- ), ee = [
419
- { value: "monthly", label: t.billingMonthly },
420
- { value: "yearly", label: t.billingYearly }
421
- ];
422
- return /* @__PURE__ */ i(
423
- xe,
424
- {
425
- title: t.title,
426
- error: S,
427
- currentStatusLabel: t.currentStatusLabel,
428
- currentStatus: {
429
- isActive: c?.isActive ?? !1,
430
- activeContent: c?.isActive ? {
431
- title: t.statusActive,
432
- fields: [
433
- {
434
- label: t.labelPlan,
435
- value: c.productIdentifier || t.labelPremium
436
- },
437
- {
438
- label: t.labelExpires,
439
- value: k(
440
- c.expirationDate
441
- )
442
- },
443
- {
444
- label: t.labelWillRenew,
445
- value: c.willRenew ? t.yes : t.no
446
- },
447
- ...a ? [
448
- {
449
- label: t.labelMonthlyUsage,
450
- value: `${a.currentUsage.monthly.toLocaleString()} / ${p(a.currentLimits.monthly)}`
451
- },
452
- {
453
- label: t.labelDailyUsage,
454
- value: `${a.currentUsage.daily.toLocaleString()} / ${p(a.currentLimits.daily)}`
455
- }
456
- ] : []
457
- ]
458
- } : void 0,
459
- inactiveContent: c?.isActive ? void 0 : {
460
- title: t.statusInactive,
461
- message: t.statusInactiveMessage
462
- }
463
- },
464
- aboveProducts: !f && h.length > 0 ? /* @__PURE__ */ i("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ i(
465
- J,
466
- {
467
- options: ee,
468
- value: g,
469
- onChange: B
470
- }
471
- ) }) : null,
472
- primaryAction: {
473
- label: L ? t.buttonPurchasing : t.buttonSubscribe,
474
- onClick: M,
475
- disabled: !P || L || C,
476
- loading: L
477
- },
478
- secondaryAction: {
479
- label: C ? t.buttonRestoring : t.buttonRestore,
480
- onClick: I,
481
- disabled: L || C,
482
- loading: C
483
- },
484
- children: f ? /* @__PURE__ */ i("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ i("div", { className: "animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600" }) }) : h.length === 0 ? /* @__PURE__ */ i("div", { className: "text-center py-12 text-theme-text-secondary", children: t.noProducts }) : m.length === 0 ? /* @__PURE__ */ i("div", { className: "text-center py-12 text-theme-text-secondary", children: t.noProductsForPeriod }) : /* @__PURE__ */ s(ne, { children: [
485
- /* @__PURE__ */ i(
486
- W,
487
- {
488
- id: "free",
489
- title: t.freeTierTitle,
490
- price: t.freeTierPrice,
491
- periodLabel: t.periodMonth,
492
- features: X(),
493
- isSelected: !c?.isActive && P === null,
494
- onSelect: () => F(null),
495
- topBadge: c?.isActive ? void 0 : {
496
- text: t.currentPlanBadge,
497
- color: "green"
498
- },
499
- disabled: L || C,
500
- hideSelectionIndicator: !0
501
- },
502
- "free"
503
- ),
504
- m.map((e) => /* @__PURE__ */ i(
505
- W,
506
- {
507
- id: e.identifier,
508
- title: e.title,
509
- price: e.priceString,
510
- periodLabel: A(e.period),
511
- features: Q(e.identifier),
512
- isSelected: P === e.identifier,
513
- onSelect: () => F(e.identifier),
514
- isBestValue: e.identifier.includes("pro"),
515
- discountBadge: e.period?.includes("Y") ? (() => {
516
- const r = Z(
517
- e.identifier
518
- );
519
- return r && r > 0 ? {
520
- text: l.formatSavePercent(r),
521
- isBestValue: !0
522
- } : void 0;
523
- })() : void 0,
524
- introPriceNote: e.freeTrialPeriod ? Y(e.freeTrialPeriod) : e.introPrice ? l.formatIntroNote(e.introPrice) : void 0,
525
- disabled: L || C
526
- },
527
- e.identifier
528
- ))
529
- ] })
530
- }
531
- );
532
- }
533
- function Ie({
534
- products: u,
535
- isAuthenticated: a,
536
- hasActiveSubscription: t,
537
- currentProductIdentifier: l,
538
- labels: o,
539
- formatters: b,
540
- entitlementMap: y,
541
- entitlementLevels: N,
542
- onPlanClick: h,
543
- onFreePlanClick: c,
544
- faqItems: f,
545
- className: S
546
- }) {
547
- const [T, R] = E("monthly"), d = x(
548
- (n) => {
549
- const m = y[n];
550
- return m ? N[m] ?? 0 : 0;
551
- },
552
- [y, N]
553
- ), g = l ? d(l) : 0, w = u.filter((n) => {
554
- if (!n.period) return !1;
555
- const m = n.period.includes("Y") || n.period.includes("year");
556
- return T === "yearly" ? m : !m;
557
- }).sort((n, m) => parseFloat(n.price) - parseFloat(m.price)), P = x(
558
- (n) => n ? n.includes("Y") || n.includes("year") ? o.periodYear : n.includes("M") || n.includes("month") ? o.periodMonth : n.includes("W") || n.includes("week") ? o.periodWeek : "" : "",
559
- [o]
560
- ), F = x(
561
- (n) => {
562
- const m = y[n];
563
- if (!m) return;
564
- const B = u.find(
565
- (p) => p.identifier === n
566
- );
567
- if (!B) return;
568
- const M = Object.entries(y).find(
569
- ([p, j]) => j === m && p.includes("monthly")
570
- )?.[0];
571
- if (!M) return;
572
- const I = u.find(
573
- (p) => p.identifier === M
574
- );
575
- if (!I) return;
576
- const k = parseFloat(B.price), A = parseFloat(I.price);
577
- if (A <= 0 || k <= 0) return;
578
- const Y = A * 12, _ = (Y - k) / Y * 100;
579
- return Math.round(_);
580
- },
581
- [u, y]
582
- ), L = [
583
- { value: "monthly", label: o.billingMonthly },
584
- { value: "yearly", label: o.billingYearly }
585
- ], z = x(
586
- (n) => !a || !t ? !1 : d(n) === g && g > 0,
587
- [a, t, d, g]
588
- ), C = x(
589
- (n) => d(n) > g,
590
- [d, g]
591
- );
592
- return /* @__PURE__ */ s("div", { className: S, children: [
593
- /* @__PURE__ */ i("section", { className: "py-16 px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ s("div", { className: "max-w-4xl mx-auto text-center", children: [
594
- /* @__PURE__ */ i("h1", { className: "text-4xl sm:text-5xl font-bold text-theme-text-primary mb-4", children: o.title }),
595
- /* @__PURE__ */ i("p", { className: "text-lg text-theme-text-secondary", children: o.subtitle })
596
- ] }) }),
597
- /* @__PURE__ */ i("section", { className: "pb-20 px-4 sm:px-6 lg:px-8", children: /* @__PURE__ */ s("div", { className: "max-w-6xl mx-auto", children: [
598
- /* @__PURE__ */ i("div", { className: "flex justify-center mb-8", children: /* @__PURE__ */ i(
599
- J,
600
- {
601
- options: L,
602
- value: T,
603
- onChange: R
604
- }
605
- ) }),
606
- /* @__PURE__ */ s(
607
- "div",
608
- {
609
- style: {
610
- display: "grid",
611
- gridTemplateColumns: "repeat(auto-fit, minmax(min(100%, 280px), 1fr))",
612
- gap: "1.5rem"
613
- },
614
- children: [
615
- /* @__PURE__ */ i(
616
- W,
617
- {
618
- id: "free",
619
- title: o.freeTierTitle,
620
- price: o.freeTierPrice,
621
- periodLabel: o.periodMonth,
622
- features: o.freeTierFeatures,
623
- isSelected: !1,
624
- onSelect: () => {
625
- },
626
- topBadge: a && !t ? {
627
- text: o.currentPlanBadge,
628
- color: "green"
629
- } : void 0,
630
- ctaButton: (
631
- // Not logged in: show "Try it for Free"
632
- // Logged in on free plan: no CTA (current plan)
633
- // Logged in with subscription: no CTA (can't downgrade here)
634
- a ? void 0 : {
635
- label: o.ctaTryFree,
636
- onClick: c
637
- }
638
- ),
639
- hideSelectionIndicator: a
640
- }
641
- ),
642
- w.map((n) => {
643
- const m = z(n.identifier), B = C(n.identifier);
644
- let M;
645
- a ? m ? M = void 0 : B && (M = {
646
- label: o.ctaUpgrade,
647
- onClick: () => h(n.identifier)
648
- }) : M = {
649
- label: o.ctaLogIn,
650
- onClick: () => h(n.identifier)
651
- };
652
- let I;
653
- return m ? I = {
654
- text: o.currentPlanBadge,
655
- color: "green"
656
- } : n.identifier.includes("pro") && (I = {
657
- text: o.mostPopularBadge,
658
- color: "yellow"
659
- }), /* @__PURE__ */ i(
660
- W,
661
- {
662
- id: n.identifier,
663
- title: n.title,
664
- price: n.priceString,
665
- periodLabel: P(n.period),
666
- features: b.getProductFeatures(n.identifier),
667
- isSelected: !1,
668
- onSelect: () => {
669
- },
670
- isBestValue: n.identifier.includes("pro"),
671
- topBadge: I,
672
- discountBadge: n.period?.includes("Y") ? (() => {
673
- const k = F(
674
- n.identifier
675
- );
676
- return k && k > 0 ? {
677
- text: b.formatSavePercent(k),
678
- isBestValue: !0
679
- } : void 0;
680
- })() : void 0,
681
- ctaButton: M,
682
- hideSelectionIndicator: !M
683
- },
684
- n.identifier
685
- );
686
- })
687
- ]
688
- }
689
- )
690
- ] }) }),
691
- f && f.length > 0 && /* @__PURE__ */ i("section", { className: "py-20 px-4 sm:px-6 lg:px-8 bg-theme-bg-secondary", children: /* @__PURE__ */ s("div", { className: "max-w-3xl mx-auto", children: [
692
- /* @__PURE__ */ i("h2", { className: "text-3xl font-bold text-theme-text-primary text-center mb-12", children: o.faqTitle }),
693
- /* @__PURE__ */ i("div", { className: "space-y-6", children: f.map((n, m) => /* @__PURE__ */ s(
694
- "div",
695
- {
696
- className: "bg-theme-bg-primary p-6 rounded-xl border border-theme-border",
697
- children: [
698
- /* @__PURE__ */ i("h3", { className: "text-lg font-semibold text-theme-text-primary mb-2", children: n.question }),
699
- /* @__PURE__ */ i("p", { className: "text-theme-text-secondary", children: n.answer })
700
- ]
701
- },
702
- m
703
- )) })
704
- ] }) })
705
- ] });
706
- }
707
275
  export {
708
- Te as EntityListPage,
709
- Ie as EntityPricingPage,
710
- Ce as EntitySubscriptionsPage,
711
- Le as InvitationsPage,
712
- Fe as MembersManagementPage
276
+ U as EntityListPage,
277
+ G as InvitationsPage,
278
+ B as MembersManagementPage
713
279
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(m,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("react/jsx-runtime"),require("react"),require("lucide-react"),require("@sudobility/entity-components"),require("@sudobility/entity_client"),require("@sudobility/subscription-components")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","lucide-react","@sudobility/entity-components","@sudobility/entity_client","@sudobility/subscription-components"],e):(m=typeof globalThis<"u"?globalThis:m||self,e(m.EntityPages={},m.jsxRuntime,m.React,m.LucideReact,m.SudobilityEntityComponents,m.SudobilityEntityClient,m.SudobilitySubscriptionComponents))})(this,(function(m,e,l,J,z,S,B){"use strict";function Q({client:u,onSelectEntity:a}){const[i,o]=l.useState(!1),[s,x]=l.useState({displayName:"",description:""}),[f,N]=l.useState(null),{data:h=[],isLoading:c}=S.useEntities(u),g=S.useCreateEntity(u),w=h.filter(d=>d.entityType==="personal"),k=h.filter(d=>d.entityType==="organization"),D=async d=>{if(d.preventDefault(),N(null),!s.displayName.trim()){N("Display name is required");return}try{await g.mutateAsync({displayName:s.displayName.trim(),description:s.description.trim()||void 0}),o(!1),x({displayName:"",description:""})}catch(p){N(p.message||"Failed to create organization")}};return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-foreground",children:"Workspaces"}),e.jsx("p",{className:"text-muted-foreground",children:"Manage your personal and organization workspaces"})]}),e.jsxs("button",{type:"button",onClick:()=>o(!0),className:"flex items-center gap-2 px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors",children:[e.jsx(J.Plus,{className:"h-4 w-4"}),e.jsx("span",{children:"New Organization"})]})]}),i&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/50",children:e.jsxs("div",{className:"w-full max-w-md rounded-lg bg-background p-6 shadow-lg",children:[e.jsx("h2",{className:"text-lg font-semibold mb-4",children:"Create Organization"}),e.jsxs("form",{onSubmit:D,className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1",children:"Display Name"}),e.jsx("input",{type:"text",value:s.displayName,onChange:d=>x(p=>({...p,displayName:d.target.value})),placeholder:"My Organization",className:"w-full px-3 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1",children:"Description (optional)"}),e.jsx("textarea",{value:s.description,onChange:d=>x(p=>({...p,description:d.target.value})),placeholder:"What is this organization for?",rows:3,className:"w-full px-3 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary resize-none"})]}),f&&e.jsx("p",{className:"text-sm text-destructive",children:f}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx("button",{type:"button",onClick:()=>{o(!1),x({displayName:"",description:""}),N(null)},className:"px-4 py-2 rounded-lg border hover:bg-muted transition-colors",children:"Cancel"}),e.jsx("button",{type:"submit",disabled:g.isPending,className:"px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50",children:g.isPending?"Creating...":"Create"})]})]})]})}),w.length>0&&e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Personal Workspace"}),e.jsx(z.EntityList,{entities:w,onSelect:a,isLoading:c})]}),e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Organizations"}),k.length===0&&!c?e.jsxs("div",{className:"text-center py-8 text-muted-foreground border border-dashed rounded-lg",children:[e.jsx("p",{children:"No organizations yet"}),e.jsx("button",{type:"button",onClick:()=>o(!0),className:"mt-2 text-primary hover:underline",children:"Create your first organization"})]}):e.jsx(z.EntityList,{entities:k,onSelect:a,isLoading:c})]})]})}function X({client:u,entity:a,currentUserId:i}){const o=a.userRole==="admin",{data:s=[],isLoading:x}=S.useEntityMembers(u,a.entitySlug),f=S.useUpdateMemberRole(u),N=S.useRemoveMember(u),{data:h=[],isLoading:c}=S.useEntityInvitations(u,o?a.entitySlug:null),g=S.useCreateInvitation(u),w=S.useCancelInvitation(u),k=async(T,P)=>{try{await f.mutateAsync({entitySlug:a.entitySlug,memberId:T,role:P})}catch(C){console.error("Failed to update role:",C)}},D=async T=>{if(confirm("Are you sure you want to remove this member?"))try{await N.mutateAsync({entitySlug:a.entitySlug,memberId:T})}catch(P){console.error("Failed to remove member:",P)}},d=async T=>{await g.mutateAsync({entitySlug:a.entitySlug,request:T})},p=async T=>{try{await w.mutateAsync({entitySlug:a.entitySlug,invitationId:T})}catch(P){console.error("Failed to cancel invitation:",P)}};return a.entityType==="personal"?e.jsxs("div",{className:"text-center py-12 text-muted-foreground",children:[e.jsx("p",{children:"Personal workspaces cannot have additional members."}),e.jsx("p",{className:"mt-2",children:"Create an organization to collaborate with others."})]}):e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-foreground",children:"Members"}),e.jsxs("p",{className:"text-muted-foreground",children:["Manage members and invitations for ",a.displayName]})]}),o&&e.jsxs("div",{className:"rounded-lg border p-4",children:[e.jsx("h2",{className:"text-lg font-semibold mb-4",children:"Invite Members"}),e.jsx(z.InvitationForm,{onSubmit:d,isSubmitting:g.isPending})]}),o&&e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Pending Invitations"}),e.jsx(z.InvitationList,{invitations:h,mode:"admin",onCancel:p,isLoading:c,emptyMessage:"No pending invitations"})]}),e.jsxs("div",{children:[e.jsxs("h2",{className:"text-lg font-semibold mb-3",children:["Current Members (",s.length,")"]}),e.jsx(z.MemberList,{members:s,currentUserId:i,canManage:o,onRoleChange:k,onRemove:D,isLoading:x})]})]})}function Z({client:u,onInvitationAccepted:a}){const{data:i=[],isLoading:o}=S.useMyInvitations(u),s=S.useAcceptInvitation(u),x=S.useDeclineInvitation(u),f=async c=>{try{await s.mutateAsync(c),a?.()}catch(g){console.error("Failed to accept invitation:",g)}},N=async c=>{try{await x.mutateAsync(c)}catch(g){console.error("Failed to decline invitation:",g)}},h=i.filter(c=>c.status==="pending").length;return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-foreground",children:"Invitations"}),e.jsx("p",{className:"text-muted-foreground",children:h>0?`You have ${h} pending invitation${h>1?"s":""}`:"No pending invitations"})]}),e.jsx(z.InvitationList,{invitations:i,mode:"user",onAccept:f,onDecline:N,isLoading:o,emptyMessage:"You don't have any pending invitations"})]})}const _={ultra_yearly:"bandwidth_ultra",ultra_monthly:"bandwidth_ultra",pro_yearly:"bandwidth_pro",pro_monthly:"bandwidth_pro",dev_yearly:"bandwidth_dev",dev_monthly:"bandwidth_dev"};function R({subscription:u,rateLimitsConfig:a,labels:i,formatters:o,onPurchaseSuccess:s,onRestoreSuccess:x,onError:f,onWarning:N}){const{products:h,currentSubscription:c,isLoading:g,error:w,purchase:k,restore:D,clearError:d}=u,[p,T]=l.useState("monthly"),[P,C]=l.useState(null),[L,j]=l.useState(!1),[F,n]=l.useState(!1);l.useEffect(()=>{w&&(f?.(i.errorTitle,w),d())},[w,d,i.errorTitle,f]);const y=h.filter(t=>{if(!t.period)return!1;const r=t.period.includes("Y")||t.period.includes("year");return p==="yearly"?r:!r}).sort((t,r)=>parseFloat(t.price)-parseFloat(r.price)),A=t=>{T(t),C(null)},M=async()=>{if(P){j(!0),d();try{await k(P)&&(s?.(),C(null))}catch(t){f?.(i.errorTitle,t instanceof Error?t.message:i.purchaseError)}finally{j(!1)}}},E=async()=>{n(!0),d();try{await D()?x?.():N?.(i.errorTitle,i.restoreNoPurchases)}catch(t){f?.(i.errorTitle,t instanceof Error?t.message:i.restoreError)}finally{n(!1)}},I=l.useCallback(t=>t?new Intl.DateTimeFormat(void 0,{year:"numeric",month:"long",day:"numeric"}).format(t):"",[]),W=l.useCallback(t=>t?t.includes("Y")||t.includes("year")?i.periodYear:t.includes("M")||t.includes("month")?i.periodMonth:t.includes("W")||t.includes("week")?i.periodWeek:"":"",[i]),q=l.useCallback(t=>{if(!t)return;const r=parseInt(t.replace(/\D/g,"")||"1",10);return t.includes("W")?o.formatTrialWeeks(r):t.includes("M")?o.formatTrialMonths(r):o.formatTrialDays(r)},[o]),O=l.useCallback(t=>{if(!a?.tiers)return;const r=_[t];return r?a.tiers.find(b=>b.entitlement===r):a.tiers.find(b=>b.entitlement==="none")},[a]),v=l.useCallback(t=>t===null?i.unlimited:t.toLocaleString(),[i.unlimited]),U=l.useCallback(t=>{const r=O(t);if(!r)return[];const b=[];return r.limits.hourly!==null&&b.push(o.formatHourlyLimit(v(r.limits.hourly))),r.limits.daily!==null&&b.push(o.formatDailyLimit(v(r.limits.daily))),r.limits.monthly!==null&&b.push(o.formatMonthlyLimit(v(r.limits.monthly))),r.limits.hourly===null&&r.limits.daily===null&&r.limits.monthly===null&&b.push(i.unlimitedRequests),b},[O,v,o,i.unlimitedRequests]),te=l.useCallback(t=>U(t),[U]),ie=l.useCallback(()=>{const t=[...i.freeTierFeatures];if(a?.tiers){const r=a.tiers.find(b=>b.entitlement==="none");r&&(r.limits.hourly!==null&&t.push(o.formatHourlyLimit(v(r.limits.hourly))),r.limits.daily!==null&&t.push(o.formatDailyLimit(v(r.limits.daily))),r.limits.monthly!==null&&t.push(o.formatMonthlyLimit(v(r.limits.monthly))))}return t},[a,v,o,i.freeTierFeatures]),ne=l.useCallback(t=>{const r=_[t];if(!r)return;const b=h.find(Y=>Y.identifier===t);if(!b)return;const $=Object.entries(_).find(([Y,se])=>se===r&&Y.includes("monthly"))?.[0];if(!$)return;const V=h.find(Y=>Y.identifier===$);if(!V)return;const H=parseFloat(b.price),G=parseFloat(V.price);if(G<=0||H<=0)return;const K=G*12,ae=(K-H)/K*100;return Math.round(ae)},[h]),re=[{value:"monthly",label:i.billingMonthly},{value:"yearly",label:i.billingYearly}];return e.jsx(B.SubscriptionLayout,{title:i.title,error:w,currentStatusLabel:i.currentStatusLabel,currentStatus:{isActive:c?.isActive??!1,activeContent:c?.isActive?{title:i.statusActive,fields:[{label:i.labelPlan,value:c.productIdentifier||i.labelPremium},{label:i.labelExpires,value:I(c.expirationDate)},{label:i.labelWillRenew,value:c.willRenew?i.yes:i.no},...a?[{label:i.labelMonthlyUsage,value:`${a.currentUsage.monthly.toLocaleString()} / ${v(a.currentLimits.monthly)}`},{label:i.labelDailyUsage,value:`${a.currentUsage.daily.toLocaleString()} / ${v(a.currentLimits.daily)}`}]:[]]}:void 0,inactiveContent:c?.isActive?void 0:{title:i.statusInactive,message:i.statusInactiveMessage}},aboveProducts:!g&&h.length>0?e.jsx("div",{className:"flex justify-center mb-6",children:e.jsx(B.SegmentedControl,{options:re,value:p,onChange:A})}):null,primaryAction:{label:L?i.buttonPurchasing:i.buttonSubscribe,onClick:M,disabled:!P||L||F,loading:L},secondaryAction:{label:F?i.buttonRestoring:i.buttonRestore,onClick:E,disabled:L||F,loading:F},children:g?e.jsx("div",{className:"flex items-center justify-center py-12",children:e.jsx("div",{className:"animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"})}):h.length===0?e.jsx("div",{className:"text-center py-12 text-theme-text-secondary",children:i.noProducts}):y.length===0?e.jsx("div",{className:"text-center py-12 text-theme-text-secondary",children:i.noProductsForPeriod}):e.jsxs(e.Fragment,{children:[e.jsx(B.SubscriptionTile,{id:"free",title:i.freeTierTitle,price:i.freeTierPrice,periodLabel:i.periodMonth,features:ie(),isSelected:!c?.isActive&&P===null,onSelect:()=>C(null),topBadge:c?.isActive?void 0:{text:i.currentPlanBadge,color:"green"},disabled:L||F,hideSelectionIndicator:!0},"free"),y.map(t=>e.jsx(B.SubscriptionTile,{id:t.identifier,title:t.title,price:t.priceString,periodLabel:W(t.period),features:te(t.identifier),isSelected:P===t.identifier,onSelect:()=>C(t.identifier),isBestValue:t.identifier.includes("pro"),discountBadge:t.period?.includes("Y")?(()=>{const r=ne(t.identifier);return r&&r>0?{text:o.formatSavePercent(r),isBestValue:!0}:void 0})():void 0,introPriceNote:t.freeTrialPeriod?q(t.freeTrialPeriod):t.introPrice?o.formatIntroNote(t.introPrice):void 0,disabled:L||F},t.identifier))]})})}function ee({products:u,isAuthenticated:a,hasActiveSubscription:i,currentProductIdentifier:o,labels:s,formatters:x,entitlementMap:f,entitlementLevels:N,onPlanClick:h,onFreePlanClick:c,faqItems:g,className:w}){const[k,D]=l.useState("monthly"),d=l.useCallback(n=>{const y=f[n];return y?N[y]??0:0},[f,N]),p=o?d(o):0,T=u.filter(n=>{if(!n.period)return!1;const y=n.period.includes("Y")||n.period.includes("year");return k==="yearly"?y:!y}).sort((n,y)=>parseFloat(n.price)-parseFloat(y.price)),P=l.useCallback(n=>n?n.includes("Y")||n.includes("year")?s.periodYear:n.includes("M")||n.includes("month")?s.periodMonth:n.includes("W")||n.includes("week")?s.periodWeek:"":"",[s]),C=l.useCallback(n=>{const y=f[n];if(!y)return;const A=u.find(v=>v.identifier===n);if(!A)return;const M=Object.entries(f).find(([v,U])=>U===y&&v.includes("monthly"))?.[0];if(!M)return;const E=u.find(v=>v.identifier===M);if(!E)return;const I=parseFloat(A.price),W=parseFloat(E.price);if(W<=0||I<=0)return;const q=W*12,O=(q-I)/q*100;return Math.round(O)},[u,f]),L=[{value:"monthly",label:s.billingMonthly},{value:"yearly",label:s.billingYearly}],j=l.useCallback(n=>!a||!i?!1:d(n)===p&&p>0,[a,i,d,p]),F=l.useCallback(n=>d(n)>p,[d,p]);return e.jsxs("div",{className:w,children:[e.jsx("section",{className:"py-16 px-4 sm:px-6 lg:px-8",children:e.jsxs("div",{className:"max-w-4xl mx-auto text-center",children:[e.jsx("h1",{className:"text-4xl sm:text-5xl font-bold text-theme-text-primary mb-4",children:s.title}),e.jsx("p",{className:"text-lg text-theme-text-secondary",children:s.subtitle})]})}),e.jsx("section",{className:"pb-20 px-4 sm:px-6 lg:px-8",children:e.jsxs("div",{className:"max-w-6xl mx-auto",children:[e.jsx("div",{className:"flex justify-center mb-8",children:e.jsx(B.SegmentedControl,{options:L,value:k,onChange:D})}),e.jsxs("div",{style:{display:"grid",gridTemplateColumns:"repeat(auto-fit, minmax(min(100%, 280px), 1fr))",gap:"1.5rem"},children:[e.jsx(B.SubscriptionTile,{id:"free",title:s.freeTierTitle,price:s.freeTierPrice,periodLabel:s.periodMonth,features:s.freeTierFeatures,isSelected:!1,onSelect:()=>{},topBadge:a&&!i?{text:s.currentPlanBadge,color:"green"}:void 0,ctaButton:a?void 0:{label:s.ctaTryFree,onClick:c},hideSelectionIndicator:a}),T.map(n=>{const y=j(n.identifier),A=F(n.identifier);let M;a?y?M=void 0:A&&(M={label:s.ctaUpgrade,onClick:()=>h(n.identifier)}):M={label:s.ctaLogIn,onClick:()=>h(n.identifier)};let E;return y?E={text:s.currentPlanBadge,color:"green"}:n.identifier.includes("pro")&&(E={text:s.mostPopularBadge,color:"yellow"}),e.jsx(B.SubscriptionTile,{id:n.identifier,title:n.title,price:n.priceString,periodLabel:P(n.period),features:x.getProductFeatures(n.identifier),isSelected:!1,onSelect:()=>{},isBestValue:n.identifier.includes("pro"),topBadge:E,discountBadge:n.period?.includes("Y")?(()=>{const I=C(n.identifier);return I&&I>0?{text:x.formatSavePercent(I),isBestValue:!0}:void 0})():void 0,ctaButton:M,hideSelectionIndicator:!M},n.identifier)})]})]})}),g&&g.length>0&&e.jsx("section",{className:"py-20 px-4 sm:px-6 lg:px-8 bg-theme-bg-secondary",children:e.jsxs("div",{className:"max-w-3xl mx-auto",children:[e.jsx("h2",{className:"text-3xl font-bold text-theme-text-primary text-center mb-12",children:s.faqTitle}),e.jsx("div",{className:"space-y-6",children:g.map((n,y)=>e.jsxs("div",{className:"bg-theme-bg-primary p-6 rounded-xl border border-theme-border",children:[e.jsx("h3",{className:"text-lg font-semibold text-theme-text-primary mb-2",children:n.question}),e.jsx("p",{className:"text-theme-text-secondary",children:n.answer})]},y))})]})})]})}m.EntityListPage=Q,m.EntityPricingPage=ee,m.EntitySubscriptionsPage=R,m.InvitationsPage=Z,m.MembersManagementPage=X,Object.defineProperty(m,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(a,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("react/jsx-runtime"),require("react"),require("lucide-react"),require("@sudobility/entity-components"),require("@sudobility/entity_client")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react","lucide-react","@sudobility/entity-components","@sudobility/entity_client"],e):(a=typeof globalThis<"u"?globalThis:a||self,e(a.EntityPages={},a.jsxRuntime,a.React,a.LucideReact,a.SudobilityEntityComponents,a.SudobilityEntityClient))})(this,(function(a,e,x,w,h,s){"use strict";function C({client:n,onSelectEntity:t}){const[y,i]=x.useState(!1),[d,c]=x.useState({displayName:"",description:""}),[b,g]=x.useState(null),{data:m=[],isLoading:r}=s.useEntities(n),l=s.useCreateEntity(n),f=m.filter(o=>o.entityType==="personal"),N=m.filter(o=>o.entityType==="organization"),S=async o=>{if(o.preventDefault(),g(null),!d.displayName.trim()){g("Display name is required");return}try{await l.mutateAsync({displayName:d.displayName.trim(),description:d.description.trim()||void 0}),i(!1),c({displayName:"",description:""})}catch(u){g(u.message||"Failed to create organization")}};return e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{className:"flex items-center justify-between",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-foreground",children:"Workspaces"}),e.jsx("p",{className:"text-muted-foreground",children:"Manage your personal and organization workspaces"})]}),e.jsxs("button",{type:"button",onClick:()=>i(!0),className:"flex items-center gap-2 px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors",children:[e.jsx(w.Plus,{className:"h-4 w-4"}),e.jsx("span",{children:"New Organization"})]})]}),y&&e.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/50",children:e.jsxs("div",{className:"w-full max-w-md rounded-lg bg-background p-6 shadow-lg",children:[e.jsx("h2",{className:"text-lg font-semibold mb-4",children:"Create Organization"}),e.jsxs("form",{onSubmit:S,className:"space-y-4",children:[e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1",children:"Display Name"}),e.jsx("input",{type:"text",value:d.displayName,onChange:o=>c(u=>({...u,displayName:o.target.value})),placeholder:"My Organization",className:"w-full px-3 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary"})]}),e.jsxs("div",{children:[e.jsx("label",{className:"block text-sm font-medium mb-1",children:"Description (optional)"}),e.jsx("textarea",{value:d.description,onChange:o=>c(u=>({...u,description:o.target.value})),placeholder:"What is this organization for?",rows:3,className:"w-full px-3 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary resize-none"})]}),b&&e.jsx("p",{className:"text-sm text-destructive",children:b}),e.jsxs("div",{className:"flex justify-end gap-2",children:[e.jsx("button",{type:"button",onClick:()=>{i(!1),c({displayName:"",description:""}),g(null)},className:"px-4 py-2 rounded-lg border hover:bg-muted transition-colors",children:"Cancel"}),e.jsx("button",{type:"submit",disabled:l.isPending,className:"px-4 py-2 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50",children:l.isPending?"Creating...":"Create"})]})]})]})}),f.length>0&&e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Personal Workspace"}),e.jsx(h.EntityList,{entities:f,onSelect:t,isLoading:r})]}),e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Organizations"}),N.length===0&&!r?e.jsxs("div",{className:"text-center py-8 text-muted-foreground border border-dashed rounded-lg",children:[e.jsx("p",{children:"No organizations yet"}),e.jsx("button",{type:"button",onClick:()=>i(!0),className:"mt-2 text-primary hover:underline",children:"Create your first organization"})]}):e.jsx(h.EntityList,{entities:N,onSelect:t,isLoading:r})]})]})}function M({client:n,entity:t,currentUserId:y}){const i=t.userRole==="admin",{data:d=[],isLoading:c}=s.useEntityMembers(n,t.entitySlug),b=s.useUpdateMemberRole(n),g=s.useRemoveMember(n),{data:m=[],isLoading:r}=s.useEntityInvitations(n,i?t.entitySlug:null),l=s.useCreateInvitation(n),f=s.useCancelInvitation(n),N=async(p,v)=>{try{await b.mutateAsync({entitySlug:t.entitySlug,memberId:p,role:v})}catch(L){console.error("Failed to update role:",L)}},S=async p=>{if(confirm("Are you sure you want to remove this member?"))try{await g.mutateAsync({entitySlug:t.entitySlug,memberId:p})}catch(v){console.error("Failed to remove member:",v)}},o=async p=>{await l.mutateAsync({entitySlug:t.entitySlug,request:p})},u=async p=>{try{await f.mutateAsync({entitySlug:t.entitySlug,invitationId:p})}catch(v){console.error("Failed to cancel invitation:",v)}};return t.entityType==="personal"?e.jsxs("div",{className:"text-center py-12 text-muted-foreground",children:[e.jsx("p",{children:"Personal workspaces cannot have additional members."}),e.jsx("p",{className:"mt-2",children:"Create an organization to collaborate with others."})]}):e.jsxs("div",{className:"space-y-8",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-foreground",children:"Members"}),e.jsxs("p",{className:"text-muted-foreground",children:["Manage members and invitations for ",t.displayName]})]}),i&&e.jsxs("div",{className:"rounded-lg border p-4",children:[e.jsx("h2",{className:"text-lg font-semibold mb-4",children:"Invite Members"}),e.jsx(h.InvitationForm,{onSubmit:o,isSubmitting:l.isPending})]}),i&&e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Pending Invitations"}),e.jsx(h.InvitationList,{invitations:m,mode:"admin",onCancel:u,isLoading:r,emptyMessage:"No pending invitations"})]}),e.jsxs("div",{children:[e.jsxs("h2",{className:"text-lg font-semibold mb-3",children:["Current Members (",d.length,")"]}),e.jsx(h.MemberList,{members:d,currentUserId:y,canManage:i,onRoleChange:N,onRemove:S,isLoading:c})]})]})}function I({client:n,onInvitationAccepted:t}){const{data:y=[],isLoading:i}=s.useMyInvitations(n),d=s.useAcceptInvitation(n),c=s.useDeclineInvitation(n),b=async r=>{try{await d.mutateAsync(r),t?.()}catch(l){console.error("Failed to accept invitation:",l)}},g=async r=>{try{await c.mutateAsync(r)}catch(l){console.error("Failed to decline invitation:",l)}},m=y.filter(r=>r.status==="pending").length;return e.jsxs("div",{className:"space-y-6",children:[e.jsxs("div",{children:[e.jsx("h1",{className:"text-2xl font-bold text-foreground",children:"Invitations"}),e.jsx("p",{className:"text-muted-foreground",children:m>0?`You have ${m} pending invitation${m>1?"s":""}`:"No pending invitations"})]}),e.jsx(h.InvitationList,{invitations:y,mode:"user",onAccept:b,onDecline:g,isLoading:i,emptyMessage:"You don't have any pending invitations"})]})}a.EntityListPage=C,a.InvitationsPage=I,a.MembersManagementPage=M,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
@@ -4,5 +4,3 @@
4
4
  export { EntityListPage, type EntityListPageProps } from './EntityListPage';
5
5
  export { MembersManagementPage, type MembersManagementPageProps, } from './MembersManagementPage';
6
6
  export { InvitationsPage, type InvitationsPageProps } from './InvitationsPage';
7
- export { EntitySubscriptionsPage, type EntitySubscriptionsPageProps, type SubscriptionProduct, type CurrentSubscription, type SubscriptionContextValue, type SubscriptionPageLabels, type SubscriptionPageFormatters, } from './EntitySubscriptionsPage';
8
- export { EntityPricingPage, type EntityPricingPageProps, type PricingProduct, type FAQItem, type PricingPageLabels, type PricingPageFormatters, type EntitlementMap, type EntitlementLevels, } from './EntityPricingPage';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sudobility/entity_pages",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Page containers for entity/organization management",
5
5
  "type": "module",
6
6
  "main": "dist/index.umd.js",
@@ -42,22 +42,13 @@
42
42
  "react": "^18.0.0 || ^19.0.0",
43
43
  "react-dom": "^18.0.0 || ^19.0.0",
44
44
  "@tanstack/react-query": "^5.0.0",
45
- "@sudobility/types": "^1.9.43",
46
- "@sudobility/entity_client": "^0.0.10",
47
- "@sudobility/entity-components": "^1.0.4",
48
- "@sudobility/subscription-components": "^1.0.13"
49
- },
50
- "peerDependenciesMeta": {
51
- "@sudobility/subscription-components": {
52
- "optional": true
53
- }
45
+ "@sudobility/entity_client": "^0.0.11",
46
+ "@sudobility/entity-components": "^1.0.4"
54
47
  },
55
48
  "devDependencies": {
56
49
  "@eslint/js": "^9.38.0",
57
50
  "@sudobility/entity-components": "^1.0.4",
58
- "@sudobility/entity_client": "^0.0.10",
59
- "@sudobility/subscription-components": "^1.0.13",
60
- "@sudobility/types": "^1.9.43",
51
+ "@sudobility/entity_client": "^0.0.11",
61
52
  "@tanstack/react-query": "^5.0.0",
62
53
  "@testing-library/dom": "^10.4.0",
63
54
  "@testing-library/jest-dom": "^6.4.2",
@@ -1,86 +0,0 @@
1
- /**
2
- * @fileoverview Entity Pricing Page
3
- * @description Public pricing page for displaying subscription options
4
- */
5
- /** Product from subscription provider */
6
- export interface PricingProduct {
7
- identifier: string;
8
- title: string;
9
- price: string;
10
- priceString: string;
11
- period?: string;
12
- }
13
- /** FAQ item */
14
- export interface FAQItem {
15
- question: string;
16
- answer: string;
17
- }
18
- /** All localized labels for the pricing page */
19
- export interface PricingPageLabels {
20
- title: string;
21
- subtitle: string;
22
- periodYear: string;
23
- periodMonth: string;
24
- periodWeek: string;
25
- billingMonthly: string;
26
- billingYearly: string;
27
- freeTierTitle: string;
28
- freeTierPrice: string;
29
- freeTierFeatures: string[];
30
- currentPlanBadge: string;
31
- mostPopularBadge: string;
32
- ctaLogIn: string;
33
- ctaTryFree: string;
34
- ctaUpgrade: string;
35
- faqTitle: string;
36
- }
37
- /** Formatter functions for dynamic strings */
38
- export interface PricingPageFormatters {
39
- /** Format savings badge: "Save 20%" */
40
- formatSavePercent: (percent: number) => string;
41
- /** Get features for a product by its identifier */
42
- getProductFeatures: (productId: string) => string[];
43
- }
44
- /** Package ID to entitlement mapping */
45
- export interface EntitlementMap {
46
- [packageId: string]: string;
47
- }
48
- /** Entitlement to level mapping for comparing plan tiers */
49
- export interface EntitlementLevels {
50
- [entitlement: string]: number;
51
- }
52
- export interface EntityPricingPageProps {
53
- /** Available subscription products */
54
- products: PricingProduct[];
55
- /** Whether user is authenticated */
56
- isAuthenticated: boolean;
57
- /** Whether user has an active subscription */
58
- hasActiveSubscription: boolean;
59
- /** Current subscription product identifier (if any) */
60
- currentProductIdentifier?: string;
61
- /** Entity ID used for subscription (the selected entity's ID when logged in) */
62
- subscriptionUserId?: string;
63
- /** All localized labels */
64
- labels: PricingPageLabels;
65
- /** Formatter functions */
66
- formatters: PricingPageFormatters;
67
- /** Package ID to entitlement mapping for calculating savings */
68
- entitlementMap: EntitlementMap;
69
- /** Entitlement to level mapping for comparing tiers (higher = better) */
70
- entitlementLevels: EntitlementLevels;
71
- /** Called when user clicks on a plan */
72
- onPlanClick: (planIdentifier: string) => void;
73
- /** Called when user clicks on free plan */
74
- onFreePlanClick: () => void;
75
- /** Optional FAQ items */
76
- faqItems?: FAQItem[];
77
- /** Optional className for the container */
78
- className?: string;
79
- }
80
- /**
81
- * Public pricing page for displaying subscription options.
82
- * - Non-authenticated: Free tile shows "Try it for Free", paid tiles show "Log in to Continue"
83
- * - Authenticated on free: Free tile shows "Current Plan" badge (no CTA), paid tiles show "Upgrade"
84
- * - Authenticated with subscription: Current plan shows badge (no CTA), higher tiers show "Upgrade"
85
- */
86
- export declare function EntityPricingPage({ products, isAuthenticated, hasActiveSubscription, currentProductIdentifier, labels, formatters, entitlementMap, entitlementLevels, onPlanClick, onFreePlanClick, faqItems, className, }: EntityPricingPageProps): import("react/jsx-runtime").JSX.Element;
@@ -1,108 +0,0 @@
1
- import { RateLimitsConfigData } from '@sudobility/types';
2
- /** Product from subscription provider */
3
- export interface SubscriptionProduct {
4
- identifier: string;
5
- title: string;
6
- price: string;
7
- priceString: string;
8
- period?: string;
9
- freeTrialPeriod?: string;
10
- introPrice?: string;
11
- }
12
- /** Current subscription state */
13
- export interface CurrentSubscription {
14
- isActive: boolean;
15
- productIdentifier?: string;
16
- expirationDate?: Date;
17
- willRenew?: boolean;
18
- }
19
- /** Subscription context value passed from consumer */
20
- export interface SubscriptionContextValue {
21
- products: SubscriptionProduct[];
22
- currentSubscription: CurrentSubscription | null;
23
- isLoading: boolean;
24
- error: string | null;
25
- purchase: (productId: string) => Promise<boolean>;
26
- restore: () => Promise<boolean>;
27
- clearError: () => void;
28
- }
29
- /** All localized labels for the subscription page */
30
- export interface SubscriptionPageLabels {
31
- title: string;
32
- errorTitle: string;
33
- purchaseError: string;
34
- restoreError: string;
35
- restoreNoPurchases: string;
36
- periodYear: string;
37
- periodMonth: string;
38
- periodWeek: string;
39
- billingMonthly: string;
40
- billingYearly: string;
41
- unlimited: string;
42
- unlimitedRequests: string;
43
- currentStatusLabel: string;
44
- statusActive: string;
45
- statusInactive: string;
46
- statusInactiveMessage: string;
47
- labelPlan: string;
48
- labelPremium: string;
49
- labelExpires: string;
50
- labelWillRenew: string;
51
- labelMonthlyUsage: string;
52
- labelDailyUsage: string;
53
- yes: string;
54
- no: string;
55
- buttonSubscribe: string;
56
- buttonPurchasing: string;
57
- buttonRestore: string;
58
- buttonRestoring: string;
59
- noProducts: string;
60
- noProductsForPeriod: string;
61
- freeTierTitle: string;
62
- freeTierPrice: string;
63
- freeTierFeatures: string[];
64
- currentPlanBadge: string;
65
- }
66
- /** Formatter functions for dynamic strings */
67
- export interface SubscriptionPageFormatters {
68
- /** Format rate limit: "1,000 requests/hour" */
69
- formatHourlyLimit: (limit: string) => string;
70
- /** Format rate limit: "10,000 requests/day" */
71
- formatDailyLimit: (limit: string) => string;
72
- /** Format rate limit: "100,000 requests/month" */
73
- formatMonthlyLimit: (limit: string) => string;
74
- /** Format trial period: "7 days free trial" */
75
- formatTrialDays: (count: number) => string;
76
- /** Format trial period: "2 weeks free trial" */
77
- formatTrialWeeks: (count: number) => string;
78
- /** Format trial period: "1 month free trial" */
79
- formatTrialMonths: (count: number) => string;
80
- /** Format savings badge: "Save 20%" */
81
- formatSavePercent: (percent: number) => string;
82
- /** Format intro price note */
83
- formatIntroNote: (price: string) => string;
84
- }
85
- export interface EntitySubscriptionsPageProps {
86
- /** Subscription context value */
87
- subscription: SubscriptionContextValue;
88
- /** Rate limit configuration */
89
- rateLimitsConfig?: RateLimitsConfigData | null;
90
- /** Entity ID used for subscription (the selected entity's ID when logged in) */
91
- subscriptionUserId?: string;
92
- /** All localized labels */
93
- labels: SubscriptionPageLabels;
94
- /** Formatter functions for dynamic strings */
95
- formatters: SubscriptionPageFormatters;
96
- /** Called when purchase succeeds */
97
- onPurchaseSuccess?: () => void;
98
- /** Called when restore succeeds */
99
- onRestoreSuccess?: () => void;
100
- /** Called on error */
101
- onError?: (title: string, message: string) => void;
102
- /** Called on warning */
103
- onWarning?: (title: string, message: string) => void;
104
- }
105
- /**
106
- * Page for managing entity subscriptions.
107
- */
108
- export declare function EntitySubscriptionsPage({ subscription, rateLimitsConfig, labels, formatters, onPurchaseSuccess, onRestoreSuccess, onError, onWarning, }: EntitySubscriptionsPageProps): import("react/jsx-runtime").JSX.Element;