@sudobility/entity_pages 0.0.13 → 0.0.14

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, } from './pages';
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';
package/dist/index.esm.js CHANGED
@@ -1,79 +1,79 @@
1
- import { jsxs as o, jsx as i, Fragment as ie } from "react/jsx-runtime";
2
- import { useState as S, useEffect as ne, useCallback as P } from "react";
3
- import { Plus as re } from "lucide-react";
4
- import { EntityList as B, InvitationForm as ae, InvitationList as $, MemberList as oe } from "@sudobility/entity-components";
5
- import { useEntities as se, useCreateEntity as le, useEntityMembers as ce, useUpdateMemberRole as de, useRemoveMember as ue, useEntityInvitations as me, useCreateInvitation as he, useCancelInvitation as ye, useMyInvitations as ge, useAcceptInvitation as fe, useDeclineInvitation as pe } from "@sudobility/entity_client";
6
- import { SubscriptionLayout as ve, SubscriptionTile as O, SegmentedControl as be } from "@sudobility/subscription-components";
7
- function Ie({
8
- client: c,
9
- onSelectEntity: r
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
10
10
  }) {
11
- const [t, a] = S(!1), [h, f] = S({
11
+ const [t, l] = E(!1), [o, b] = E({
12
12
  displayName: "",
13
13
  description: ""
14
- }), [y, p] = S(null), { data: d = [], isLoading: s } = se(c), m = le(c), x = d.filter((l) => l.entityType === "personal"), w = d.filter(
15
- (l) => l.entityType === "organization"
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"
16
16
  );
17
- return /* @__PURE__ */ o("div", { className: "space-y-8", children: [
18
- /* @__PURE__ */ o("div", { className: "flex items-center justify-between", children: [
19
- /* @__PURE__ */ o("div", { children: [
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
20
  /* @__PURE__ */ i("h1", { className: "text-2xl font-bold text-foreground", children: "Workspaces" }),
21
21
  /* @__PURE__ */ i("p", { className: "text-muted-foreground", children: "Manage your personal and organization workspaces" })
22
22
  ] }),
23
- /* @__PURE__ */ o(
23
+ /* @__PURE__ */ s(
24
24
  "button",
25
25
  {
26
26
  type: "button",
27
- onClick: () => a(!0),
27
+ onClick: () => l(!0),
28
28
  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
29
  children: [
30
- /* @__PURE__ */ i(re, { className: "h-4 w-4" }),
30
+ /* @__PURE__ */ i(ae, { className: "h-4 w-4" }),
31
31
  /* @__PURE__ */ i("span", { children: "New Organization" })
32
32
  ]
33
33
  }
34
34
  )
35
35
  ] }),
36
- t && /* @__PURE__ */ i("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/50", children: /* @__PURE__ */ o("div", { className: "w-full max-w-md rounded-lg bg-background p-6 shadow-lg", children: [
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
37
  /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-4", children: "Create Organization" }),
38
- /* @__PURE__ */ o("form", { onSubmit: async (l) => {
39
- if (l.preventDefault(), p(null), !h.displayName.trim()) {
40
- p("Display name is required");
38
+ /* @__PURE__ */ s("form", { onSubmit: async (d) => {
39
+ if (d.preventDefault(), N(null), !o.displayName.trim()) {
40
+ N("Display name is required");
41
41
  return;
42
42
  }
43
43
  try {
44
- await m.mutateAsync({
45
- displayName: h.displayName.trim(),
46
- description: h.description.trim() || void 0
47
- }), a(!1), f({ displayName: "", description: "" });
48
- } catch (v) {
49
- p(v.message || "Failed to create organization");
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");
50
50
  }
51
51
  }, className: "space-y-4", children: [
52
- /* @__PURE__ */ o("div", { children: [
52
+ /* @__PURE__ */ s("div", { children: [
53
53
  /* @__PURE__ */ i("label", { className: "block text-sm font-medium mb-1", children: "Display Name" }),
54
54
  /* @__PURE__ */ i(
55
55
  "input",
56
56
  {
57
57
  type: "text",
58
- value: h.displayName,
59
- onChange: (l) => f((v) => ({
60
- ...v,
61
- displayName: l.target.value
58
+ value: o.displayName,
59
+ onChange: (d) => b((g) => ({
60
+ ...g,
61
+ displayName: d.target.value
62
62
  })),
63
63
  placeholder: "My Organization",
64
64
  className: "w-full px-3 py-2 rounded-lg border bg-background focus:outline-none focus:ring-2 focus:ring-primary"
65
65
  }
66
66
  )
67
67
  ] }),
68
- /* @__PURE__ */ o("div", { children: [
68
+ /* @__PURE__ */ s("div", { children: [
69
69
  /* @__PURE__ */ i("label", { className: "block text-sm font-medium mb-1", children: "Description (optional)" }),
70
70
  /* @__PURE__ */ i(
71
71
  "textarea",
72
72
  {
73
- value: h.description,
74
- onChange: (l) => f((v) => ({
75
- ...v,
76
- description: l.target.value
73
+ value: o.description,
74
+ onChange: (d) => b((g) => ({
75
+ ...g,
76
+ description: d.target.value
77
77
  })),
78
78
  placeholder: "What is this organization for?",
79
79
  rows: 3,
@@ -82,13 +82,13 @@ function Ie({
82
82
  )
83
83
  ] }),
84
84
  y && /* @__PURE__ */ i("p", { className: "text-sm text-destructive", children: y }),
85
- /* @__PURE__ */ o("div", { className: "flex justify-end gap-2", children: [
85
+ /* @__PURE__ */ s("div", { className: "flex justify-end gap-2", children: [
86
86
  /* @__PURE__ */ i(
87
87
  "button",
88
88
  {
89
89
  type: "button",
90
90
  onClick: () => {
91
- a(!1), f({ displayName: "", description: "" }), p(null);
91
+ l(!1), b({ displayName: "", description: "" }), N(null);
92
92
  },
93
93
  className: "px-4 py-2 rounded-lg border hover:bg-muted transition-colors",
94
94
  children: "Cancel"
@@ -98,182 +98,182 @@ function Ie({
98
98
  "button",
99
99
  {
100
100
  type: "submit",
101
- disabled: m.isPending,
101
+ disabled: f.isPending,
102
102
  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: m.isPending ? "Creating..." : "Create"
103
+ children: f.isPending ? "Creating..." : "Create"
104
104
  }
105
105
  )
106
106
  ] })
107
107
  ] })
108
108
  ] }) }),
109
- x.length > 0 && /* @__PURE__ */ o("div", { children: [
109
+ S.length > 0 && /* @__PURE__ */ s("div", { children: [
110
110
  /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Personal Workspace" }),
111
111
  /* @__PURE__ */ i(
112
- B,
112
+ G,
113
113
  {
114
- entities: x,
115
- onSelect: r,
116
- isLoading: s
114
+ entities: S,
115
+ onSelect: a,
116
+ isLoading: c
117
117
  }
118
118
  )
119
119
  ] }),
120
- /* @__PURE__ */ o("div", { children: [
120
+ /* @__PURE__ */ s("div", { children: [
121
121
  /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Organizations" }),
122
- w.length === 0 && !s ? /* @__PURE__ */ o("div", { className: "text-center py-8 text-muted-foreground border border-dashed rounded-lg", children: [
122
+ T.length === 0 && !c ? /* @__PURE__ */ s("div", { className: "text-center py-8 text-muted-foreground border border-dashed rounded-lg", children: [
123
123
  /* @__PURE__ */ i("p", { children: "No organizations yet" }),
124
124
  /* @__PURE__ */ i(
125
125
  "button",
126
126
  {
127
127
  type: "button",
128
- onClick: () => a(!0),
128
+ onClick: () => l(!0),
129
129
  className: "mt-2 text-primary hover:underline",
130
130
  children: "Create your first organization"
131
131
  }
132
132
  )
133
133
  ] }) : /* @__PURE__ */ i(
134
- B,
134
+ G,
135
135
  {
136
- entities: w,
137
- onSelect: r,
138
- isLoading: s
136
+ entities: T,
137
+ onSelect: a,
138
+ isLoading: c
139
139
  }
140
140
  )
141
141
  ] })
142
142
  ] });
143
143
  }
144
- function Te({
145
- client: c,
146
- entity: r,
144
+ function Fe({
145
+ client: u,
146
+ entity: a,
147
147
  currentUserId: t
148
148
  }) {
149
- const a = r.userRole === "admin", { data: h = [], isLoading: f } = ce(
150
- c,
151
- r.entitySlug
152
- ), y = de(c), p = ue(c), { data: d = [], isLoading: s } = me(c, a ? r.entitySlug : null), m = he(c), x = ye(c), w = async (N, g) => {
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) => {
153
153
  try {
154
154
  await y.mutateAsync({
155
- entitySlug: r.entitySlug,
156
- memberId: N,
157
- role: g
155
+ entitySlug: a.entitySlug,
156
+ memberId: w,
157
+ role: P
158
158
  });
159
- } catch (M) {
160
- console.error("Failed to update role:", M);
159
+ } catch (F) {
160
+ console.error("Failed to update role:", F);
161
161
  }
162
- }, C = async (N) => {
162
+ }, R = async (w) => {
163
163
  if (confirm("Are you sure you want to remove this member?"))
164
164
  try {
165
- await p.mutateAsync({
166
- entitySlug: r.entitySlug,
167
- memberId: N
165
+ await N.mutateAsync({
166
+ entitySlug: a.entitySlug,
167
+ memberId: w
168
168
  });
169
- } catch (g) {
170
- console.error("Failed to remove member:", g);
169
+ } catch (P) {
170
+ console.error("Failed to remove member:", P);
171
171
  }
172
- }, l = async (N) => {
173
- await m.mutateAsync({
174
- entitySlug: r.entitySlug,
175
- request: N
172
+ }, d = async (w) => {
173
+ await f.mutateAsync({
174
+ entitySlug: a.entitySlug,
175
+ request: w
176
176
  });
177
- }, v = async (N) => {
177
+ }, g = async (w) => {
178
178
  try {
179
- await x.mutateAsync({
180
- entitySlug: r.entitySlug,
181
- invitationId: N
179
+ await S.mutateAsync({
180
+ entitySlug: a.entitySlug,
181
+ invitationId: w
182
182
  });
183
- } catch (g) {
184
- console.error("Failed to cancel invitation:", g);
183
+ } catch (P) {
184
+ console.error("Failed to cancel invitation:", P);
185
185
  }
186
186
  };
187
- return r.entityType === "personal" ? /* @__PURE__ */ o("div", { className: "text-center py-12 text-muted-foreground", children: [
187
+ return a.entityType === "personal" ? /* @__PURE__ */ s("div", { className: "text-center py-12 text-muted-foreground", children: [
188
188
  /* @__PURE__ */ i("p", { children: "Personal workspaces cannot have additional members." }),
189
189
  /* @__PURE__ */ i("p", { className: "mt-2", children: "Create an organization to collaborate with others." })
190
- ] }) : /* @__PURE__ */ o("div", { className: "space-y-8", children: [
191
- /* @__PURE__ */ o("div", { children: [
190
+ ] }) : /* @__PURE__ */ s("div", { className: "space-y-8", children: [
191
+ /* @__PURE__ */ s("div", { children: [
192
192
  /* @__PURE__ */ i("h1", { className: "text-2xl font-bold text-foreground", children: "Members" }),
193
- /* @__PURE__ */ o("p", { className: "text-muted-foreground", children: [
193
+ /* @__PURE__ */ s("p", { className: "text-muted-foreground", children: [
194
194
  "Manage members and invitations for ",
195
- r.displayName
195
+ a.displayName
196
196
  ] })
197
197
  ] }),
198
- a && /* @__PURE__ */ o("div", { className: "rounded-lg border p-4", children: [
198
+ l && /* @__PURE__ */ s("div", { className: "rounded-lg border p-4", children: [
199
199
  /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-4", children: "Invite Members" }),
200
200
  /* @__PURE__ */ i(
201
- ae,
201
+ oe,
202
202
  {
203
- onSubmit: l,
204
- isSubmitting: m.isPending
203
+ onSubmit: d,
204
+ isSubmitting: f.isPending
205
205
  }
206
206
  )
207
207
  ] }),
208
- a && /* @__PURE__ */ o("div", { children: [
208
+ l && /* @__PURE__ */ s("div", { children: [
209
209
  /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Pending Invitations" }),
210
210
  /* @__PURE__ */ i(
211
- $,
211
+ K,
212
212
  {
213
- invitations: d,
213
+ invitations: h,
214
214
  mode: "admin",
215
- onCancel: v,
216
- isLoading: s,
215
+ onCancel: g,
216
+ isLoading: c,
217
217
  emptyMessage: "No pending invitations"
218
218
  }
219
219
  )
220
220
  ] }),
221
- /* @__PURE__ */ o("div", { children: [
222
- /* @__PURE__ */ o("h2", { className: "text-lg font-semibold mb-3", children: [
221
+ /* @__PURE__ */ s("div", { children: [
222
+ /* @__PURE__ */ s("h2", { className: "text-lg font-semibold mb-3", children: [
223
223
  "Current Members (",
224
- h.length,
224
+ o.length,
225
225
  ")"
226
226
  ] }),
227
227
  /* @__PURE__ */ i(
228
- oe,
228
+ le,
229
229
  {
230
- members: h,
230
+ members: o,
231
231
  currentUserId: t,
232
- canManage: a,
233
- onRoleChange: w,
234
- onRemove: C,
235
- isLoading: f
232
+ canManage: l,
233
+ onRoleChange: T,
234
+ onRemove: R,
235
+ isLoading: b
236
236
  }
237
237
  )
238
238
  ] })
239
239
  ] });
240
240
  }
241
- function Fe({
242
- client: c,
243
- onInvitationAccepted: r
241
+ function Le({
242
+ client: u,
243
+ onInvitationAccepted: a
244
244
  }) {
245
- const { data: t = [], isLoading: a } = ge(c), h = fe(c), f = pe(c), y = async (s) => {
245
+ const { data: t = [], isLoading: l } = ge(u), o = pe(u), b = ve(u), y = async (c) => {
246
246
  try {
247
- await h.mutateAsync(s), r?.();
248
- } catch (m) {
249
- console.error("Failed to accept invitation:", m);
247
+ await o.mutateAsync(c), a?.();
248
+ } catch (f) {
249
+ console.error("Failed to accept invitation:", f);
250
250
  }
251
- }, p = async (s) => {
251
+ }, N = async (c) => {
252
252
  try {
253
- await f.mutateAsync(s);
254
- } catch (m) {
255
- console.error("Failed to decline invitation:", m);
253
+ await b.mutateAsync(c);
254
+ } catch (f) {
255
+ console.error("Failed to decline invitation:", f);
256
256
  }
257
- }, d = t.filter((s) => s.status === "pending").length;
258
- return /* @__PURE__ */ o("div", { className: "space-y-6", children: [
259
- /* @__PURE__ */ o("div", { children: [
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
260
  /* @__PURE__ */ i("h1", { className: "text-2xl font-bold text-foreground", children: "Invitations" }),
261
- /* @__PURE__ */ i("p", { className: "text-muted-foreground", children: d > 0 ? `You have ${d} pending invitation${d > 1 ? "s" : ""}` : "No pending invitations" })
261
+ /* @__PURE__ */ i("p", { className: "text-muted-foreground", children: h > 0 ? `You have ${h} pending invitation${h > 1 ? "s" : ""}` : "No pending invitations" })
262
262
  ] }),
263
263
  /* @__PURE__ */ i(
264
- $,
264
+ K,
265
265
  {
266
266
  invitations: t,
267
267
  mode: "user",
268
268
  onAccept: y,
269
- onDecline: p,
270
- isLoading: a,
269
+ onDecline: N,
270
+ isLoading: l,
271
271
  emptyMessage: "You don't have any pending invitations"
272
272
  }
273
273
  )
274
274
  ] });
275
275
  }
276
- const L = {
276
+ const O = {
277
277
  ultra_yearly: "bandwidth_ultra",
278
278
  ultra_monthly: "bandwidth_ultra",
279
279
  pro_yearly: "bandwidth_pro",
@@ -282,247 +282,247 @@ const L = {
282
282
  dev_monthly: "bandwidth_dev"
283
283
  };
284
284
  function Ce({
285
- subscription: c,
286
- rateLimitsConfig: r,
285
+ subscription: u,
286
+ rateLimitsConfig: a,
287
287
  labels: t,
288
- formatters: a,
289
- onPurchaseSuccess: h,
290
- onRestoreSuccess: f,
288
+ formatters: l,
289
+ onPurchaseSuccess: o,
290
+ onRestoreSuccess: b,
291
291
  onError: y,
292
- onWarning: p
292
+ onWarning: N
293
293
  }) {
294
294
  const {
295
- products: d,
296
- currentSubscription: s,
297
- isLoading: m,
298
- error: x,
299
- purchase: w,
300
- restore: C,
301
- clearError: l
302
- } = c, [v, N] = S("monthly"), [g, M] = S(null), [I, A] = S(!1), [T, E] = S(!1);
303
- ne(() => {
304
- x && (y?.(t.errorTitle, x), l());
305
- }, [x, l, t.errorTitle, y]);
306
- const R = d.filter((e) => {
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
307
  if (!e.period) return !1;
308
- const n = e.period.includes("Y") || e.period.includes("year");
309
- return v === "yearly" ? n : !n;
310
- }).sort((e, n) => parseFloat(e.price) - parseFloat(n.price)), U = (e) => {
311
- N(e), M(null);
312
- }, q = async () => {
313
- if (g) {
314
- A(!0), l();
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
315
  try {
316
- await w(g) && (h?.(), M(null));
316
+ await T(P) && (o?.(), F(null));
317
317
  } catch (e) {
318
318
  y?.(
319
319
  t.errorTitle,
320
320
  e instanceof Error ? e.message : t.purchaseError
321
321
  );
322
322
  } finally {
323
- A(!1);
323
+ z(!1);
324
324
  }
325
325
  }
326
- }, H = async () => {
327
- E(!0), l();
326
+ }, I = async () => {
327
+ n(!0), d();
328
328
  try {
329
- await C() ? f?.() : p?.(t.errorTitle, t.restoreNoPurchases);
329
+ await R() ? b?.() : N?.(t.errorTitle, t.restoreNoPurchases);
330
330
  } catch (e) {
331
331
  y?.(
332
332
  t.errorTitle,
333
333
  e instanceof Error ? e.message : t.restoreError
334
334
  );
335
335
  } finally {
336
- E(!1);
336
+ n(!1);
337
337
  }
338
- }, V = P((e) => e ? new Intl.DateTimeFormat(void 0, {
338
+ }, k = x((e) => e ? new Intl.DateTimeFormat(void 0, {
339
339
  year: "numeric",
340
340
  month: "long",
341
341
  day: "numeric"
342
- }).format(e) : "", []), G = P(
342
+ }).format(e) : "", []), A = x(
343
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
344
  [t]
345
- ), K = P(
345
+ ), Y = x(
346
346
  (e) => {
347
347
  if (!e) return;
348
- const n = parseInt(e.replace(/\D/g, "") || "1", 10);
349
- return e.includes("W") ? a.formatTrialWeeks(n) : e.includes("M") ? a.formatTrialMonths(n) : a.formatTrialDays(n);
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
350
  },
351
- [a]
352
- ), k = P(
351
+ [l]
352
+ ), _ = x(
353
353
  (e) => {
354
- if (!r?.tiers) return;
355
- const n = L[e];
356
- return n ? r.tiers.find(
357
- (u) => u.entitlement === n
358
- ) : r.tiers.find((u) => u.entitlement === "none");
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
359
  },
360
- [r]
361
- ), b = P(
360
+ [a]
361
+ ), p = x(
362
362
  (e) => e === null ? t.unlimited : e.toLocaleString(),
363
363
  [t.unlimited]
364
- ), D = P(
364
+ ), j = x(
365
365
  (e) => {
366
- const n = k(e);
367
- if (!n) return [];
368
- const u = [];
369
- return n.limits.hourly !== null && u.push(
370
- a.formatHourlyLimit(b(n.limits.hourly))
371
- ), n.limits.daily !== null && u.push(
372
- a.formatDailyLimit(b(n.limits.daily))
373
- ), n.limits.monthly !== null && u.push(
374
- a.formatMonthlyLimit(b(n.limits.monthly))
375
- ), n.limits.hourly === null && n.limits.daily === null && n.limits.monthly === null && u.push(t.unlimitedRequests), u;
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
376
  },
377
- [k, b, a, t.unlimitedRequests]
378
- ), J = P(
379
- (e) => D(e),
380
- [D]
381
- ), Q = P(() => {
377
+ [_, p, l, t.unlimitedRequests]
378
+ ), Q = x(
379
+ (e) => j(e),
380
+ [j]
381
+ ), X = x(() => {
382
382
  const e = [...t.freeTierFeatures];
383
- if (r?.tiers) {
384
- const n = r.tiers.find(
385
- (u) => u.entitlement === "none"
383
+ if (a?.tiers) {
384
+ const r = a.tiers.find(
385
+ (v) => v.entitlement === "none"
386
386
  );
387
- n && (n.limits.hourly !== null && e.push(
388
- a.formatHourlyLimit(b(n.limits.hourly))
389
- ), n.limits.daily !== null && e.push(
390
- a.formatDailyLimit(b(n.limits.daily))
391
- ), n.limits.monthly !== null && e.push(
392
- a.formatMonthlyLimit(b(n.limits.monthly))
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
393
  ));
394
394
  }
395
395
  return e;
396
- }, [r, b, a, t.freeTierFeatures]), X = P(
396
+ }, [a, p, l, t.freeTierFeatures]), Z = x(
397
397
  (e) => {
398
- const n = L[e];
399
- if (!n) return;
400
- const u = d.find(
401
- (F) => F.identifier === e
398
+ const r = O[e];
399
+ if (!r) return;
400
+ const v = h.find(
401
+ (D) => D.identifier === e
402
402
  );
403
- if (!u) return;
404
- const z = Object.entries(L).find(
405
- ([F, te]) => te === n && F.includes("monthly")
403
+ if (!v) return;
404
+ const U = Object.entries(O).find(
405
+ ([D, ie]) => ie === r && D.includes("monthly")
406
406
  )?.[0];
407
- if (!z) return;
408
- const _ = d.find(
409
- (F) => F.identifier === z
407
+ if (!U) return;
408
+ const $ = h.find(
409
+ (D) => D.identifier === U
410
410
  );
411
- if (!_) return;
412
- const Y = parseFloat(u.price), j = parseFloat(_.price);
413
- if (j <= 0 || Y <= 0) return;
414
- const W = j * 12, ee = (W - Y) / W * 100;
415
- return Math.round(ee);
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
416
  },
417
- [d]
418
- ), Z = [
417
+ [h]
418
+ ), ee = [
419
419
  { value: "monthly", label: t.billingMonthly },
420
420
  { value: "yearly", label: t.billingYearly }
421
421
  ];
422
422
  return /* @__PURE__ */ i(
423
- ve,
423
+ xe,
424
424
  {
425
425
  title: t.title,
426
- error: x,
426
+ error: S,
427
427
  currentStatusLabel: t.currentStatusLabel,
428
428
  currentStatus: {
429
- isActive: s?.isActive ?? !1,
430
- activeContent: s?.isActive ? {
429
+ isActive: c?.isActive ?? !1,
430
+ activeContent: c?.isActive ? {
431
431
  title: t.statusActive,
432
432
  fields: [
433
433
  {
434
434
  label: t.labelPlan,
435
- value: s.productIdentifier || t.labelPremium
435
+ value: c.productIdentifier || t.labelPremium
436
436
  },
437
437
  {
438
438
  label: t.labelExpires,
439
- value: V(
440
- s.expirationDate
439
+ value: k(
440
+ c.expirationDate
441
441
  )
442
442
  },
443
443
  {
444
444
  label: t.labelWillRenew,
445
- value: s.willRenew ? t.yes : t.no
445
+ value: c.willRenew ? t.yes : t.no
446
446
  },
447
- ...r ? [
447
+ ...a ? [
448
448
  {
449
449
  label: t.labelMonthlyUsage,
450
- value: `${r.currentUsage.monthly.toLocaleString()} / ${b(r.currentLimits.monthly)}`
450
+ value: `${a.currentUsage.monthly.toLocaleString()} / ${p(a.currentLimits.monthly)}`
451
451
  },
452
452
  {
453
453
  label: t.labelDailyUsage,
454
- value: `${r.currentUsage.daily.toLocaleString()} / ${b(r.currentLimits.daily)}`
454
+ value: `${a.currentUsage.daily.toLocaleString()} / ${p(a.currentLimits.daily)}`
455
455
  }
456
456
  ] : []
457
457
  ]
458
458
  } : void 0,
459
- inactiveContent: s?.isActive ? void 0 : {
459
+ inactiveContent: c?.isActive ? void 0 : {
460
460
  title: t.statusInactive,
461
461
  message: t.statusInactiveMessage
462
462
  }
463
463
  },
464
- aboveProducts: !m && d.length > 0 ? /* @__PURE__ */ i("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ i(
465
- be,
464
+ aboveProducts: !f && h.length > 0 ? /* @__PURE__ */ i("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ i(
465
+ J,
466
466
  {
467
- options: Z,
468
- value: v,
469
- onChange: U
467
+ options: ee,
468
+ value: g,
469
+ onChange: B
470
470
  }
471
471
  ) }) : null,
472
472
  primaryAction: {
473
- label: I ? t.buttonPurchasing : t.buttonSubscribe,
474
- onClick: q,
475
- disabled: !g || I || T,
476
- loading: I
473
+ label: L ? t.buttonPurchasing : t.buttonSubscribe,
474
+ onClick: M,
475
+ disabled: !P || L || C,
476
+ loading: L
477
477
  },
478
478
  secondaryAction: {
479
- label: T ? t.buttonRestoring : t.buttonRestore,
480
- onClick: H,
481
- disabled: I || T,
482
- loading: T
479
+ label: C ? t.buttonRestoring : t.buttonRestore,
480
+ onClick: I,
481
+ disabled: L || C,
482
+ loading: C
483
483
  },
484
- children: m ? /* @__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" }) }) : d.length === 0 ? /* @__PURE__ */ i("div", { className: "text-center py-12 text-theme-text-secondary", children: t.noProducts }) : R.length === 0 ? /* @__PURE__ */ i("div", { className: "text-center py-12 text-theme-text-secondary", children: t.noProductsForPeriod }) : /* @__PURE__ */ o(ie, { children: [
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
485
  /* @__PURE__ */ i(
486
- O,
486
+ W,
487
487
  {
488
488
  id: "free",
489
489
  title: t.freeTierTitle,
490
490
  price: t.freeTierPrice,
491
491
  periodLabel: t.periodMonth,
492
- features: Q(),
493
- isSelected: !s?.isActive && g === null,
494
- onSelect: () => M(null),
495
- topBadge: s?.isActive ? void 0 : {
492
+ features: X(),
493
+ isSelected: !c?.isActive && P === null,
494
+ onSelect: () => F(null),
495
+ topBadge: c?.isActive ? void 0 : {
496
496
  text: t.currentPlanBadge,
497
497
  color: "green"
498
498
  },
499
- disabled: I || T,
499
+ disabled: L || C,
500
500
  hideSelectionIndicator: !0
501
501
  },
502
502
  "free"
503
503
  ),
504
- R.map((e) => /* @__PURE__ */ i(
505
- O,
504
+ m.map((e) => /* @__PURE__ */ i(
505
+ W,
506
506
  {
507
507
  id: e.identifier,
508
508
  title: e.title,
509
509
  price: e.priceString,
510
- periodLabel: G(e.period),
511
- features: J(e.identifier),
512
- isSelected: g === e.identifier,
513
- onSelect: () => M(e.identifier),
510
+ periodLabel: A(e.period),
511
+ features: Q(e.identifier),
512
+ isSelected: P === e.identifier,
513
+ onSelect: () => F(e.identifier),
514
514
  isBestValue: e.identifier.includes("pro"),
515
515
  discountBadge: e.period?.includes("Y") ? (() => {
516
- const n = X(
516
+ const r = Z(
517
517
  e.identifier
518
518
  );
519
- return n && n > 0 ? {
520
- text: a.formatSavePercent(n),
519
+ return r && r > 0 ? {
520
+ text: l.formatSavePercent(r),
521
521
  isBestValue: !0
522
522
  } : void 0;
523
523
  })() : void 0,
524
- introPriceNote: e.freeTrialPeriod ? K(e.freeTrialPeriod) : e.introPrice ? a.formatIntroNote(e.introPrice) : void 0,
525
- disabled: I || T
524
+ introPriceNote: e.freeTrialPeriod ? Y(e.freeTrialPeriod) : e.introPrice ? l.formatIntroNote(e.introPrice) : void 0,
525
+ disabled: L || C
526
526
  },
527
527
  e.identifier
528
528
  ))
@@ -530,9 +530,184 @@ function Ce({
530
530
  }
531
531
  );
532
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
+ }
533
707
  export {
534
- Ie as EntityListPage,
708
+ Te as EntityListPage,
709
+ Ie as EntityPricingPage,
535
710
  Ce as EntitySubscriptionsPage,
536
- Fe as InvitationsPage,
537
- Te as MembersManagementPage
711
+ Le as InvitationsPage,
712
+ Fe as MembersManagementPage
538
713
  };
package/dist/index.umd.js CHANGED
@@ -1 +1 @@
1
- (function(c,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):(c=typeof globalThis<"u"?globalThis:c||self,e(c.EntityPages={},c.jsxRuntime,c.React,c.LucideReact,c.SudobilityEntityComponents,c.SudobilityEntityClient,c.SudobilitySubscriptionComponents))})(this,(function(c,e,o,$,w,g,C){"use strict";function U({client:d,onSelectEntity:r}){const[i,a]=o.useState(!1),[m,v]=o.useState({displayName:"",description:""}),[f,b]=o.useState(null),{data:u=[],isLoading:s}=g.useEntities(d),h=g.useCreateEntity(d),S=u.filter(l=>l.entityType==="personal"),M=u.filter(l=>l.entityType==="organization"),k=async l=>{if(l.preventDefault(),b(null),!m.displayName.trim()){b("Display name is required");return}try{await h.mutateAsync({displayName:m.displayName.trim(),description:m.description.trim()||void 0}),a(!1),v({displayName:"",description:""})}catch(N){b(N.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:()=>a(!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($.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:k,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:m.displayName,onChange:l=>v(N=>({...N,displayName:l.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:m.description,onChange:l=>v(N=>({...N,description:l.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:()=>{a(!1),v({displayName:"",description:""}),b(null)},className:"px-4 py-2 rounded-lg border hover:bg-muted transition-colors",children:"Cancel"}),e.jsx("button",{type:"submit",disabled:h.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:h.isPending?"Creating...":"Create"})]})]})]})}),S.length>0&&e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Personal Workspace"}),e.jsx(w.EntityList,{entities:S,onSelect:r,isLoading:s})]}),e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Organizations"}),M.length===0&&!s?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:()=>a(!0),className:"mt-2 text-primary hover:underline",children:"Create your first organization"})]}):e.jsx(w.EntityList,{entities:M,onSelect:r,isLoading:s})]})]})}function H({client:d,entity:r,currentUserId:i}){const a=r.userRole==="admin",{data:m=[],isLoading:v}=g.useEntityMembers(d,r.entitySlug),f=g.useUpdateMemberRole(d),b=g.useRemoveMember(d),{data:u=[],isLoading:s}=g.useEntityInvitations(d,a?r.entitySlug:null),h=g.useCreateInvitation(d),S=g.useCancelInvitation(d),M=async(P,p)=>{try{await f.mutateAsync({entitySlug:r.entitySlug,memberId:P,role:p})}catch(T){console.error("Failed to update role:",T)}},k=async P=>{if(confirm("Are you sure you want to remove this member?"))try{await b.mutateAsync({entitySlug:r.entitySlug,memberId:P})}catch(p){console.error("Failed to remove member:",p)}},l=async P=>{await h.mutateAsync({entitySlug:r.entitySlug,request:P})},N=async P=>{try{await S.mutateAsync({entitySlug:r.entitySlug,invitationId:P})}catch(p){console.error("Failed to cancel invitation:",p)}};return r.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 ",r.displayName]})]}),a&&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(w.InvitationForm,{onSubmit:l,isSubmitting:h.isPending})]}),a&&e.jsxs("div",{children:[e.jsx("h2",{className:"text-lg font-semibold mb-3",children:"Pending Invitations"}),e.jsx(w.InvitationList,{invitations:u,mode:"admin",onCancel:N,isLoading:s,emptyMessage:"No pending invitations"})]}),e.jsxs("div",{children:[e.jsxs("h2",{className:"text-lg font-semibold mb-3",children:["Current Members (",m.length,")"]}),e.jsx(w.MemberList,{members:m,currentUserId:i,canManage:a,onRoleChange:M,onRemove:k,isLoading:v})]})]})}function V({client:d,onInvitationAccepted:r}){const{data:i=[],isLoading:a}=g.useMyInvitations(d),m=g.useAcceptInvitation(d),v=g.useDeclineInvitation(d),f=async s=>{try{await m.mutateAsync(s),r?.()}catch(h){console.error("Failed to accept invitation:",h)}},b=async s=>{try{await v.mutateAsync(s)}catch(h){console.error("Failed to decline invitation:",h)}},u=i.filter(s=>s.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:u>0?`You have ${u} pending invitation${u>1?"s":""}`:"No pending invitations"})]}),e.jsx(w.InvitationList,{invitations:i,mode:"user",onAccept:f,onDecline:b,isLoading:a,emptyMessage:"You don't have any pending invitations"})]})}const F={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 G({subscription:d,rateLimitsConfig:r,labels:i,formatters:a,onPurchaseSuccess:m,onRestoreSuccess:v,onError:f,onWarning:b}){const{products:u,currentSubscription:s,isLoading:h,error:S,purchase:M,restore:k,clearError:l}=d,[N,P]=o.useState("monthly"),[p,T]=o.useState(null),[I,A]=o.useState(!1),[L,D]=o.useState(!1);o.useEffect(()=>{S&&(f?.(i.errorTitle,S),l())},[S,l,i.errorTitle,f]);const z=u.filter(t=>{if(!t.period)return!1;const n=t.period.includes("Y")||t.period.includes("year");return N==="yearly"?n:!n}).sort((t,n)=>parseFloat(t.price)-parseFloat(n.price)),K=t=>{P(t),T(null)},J=async()=>{if(p){A(!0),l();try{await M(p)&&(m?.(),T(null))}catch(t){f?.(i.errorTitle,t instanceof Error?t.message:i.purchaseError)}finally{A(!1)}}},Q=async()=>{D(!0),l();try{await k()?v?.():b?.(i.errorTitle,i.restoreNoPurchases)}catch(t){f?.(i.errorTitle,t instanceof Error?t.message:i.restoreError)}finally{D(!1)}},X=o.useCallback(t=>t?new Intl.DateTimeFormat(void 0,{year:"numeric",month:"long",day:"numeric"}).format(t):"",[]),Z=o.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]),R=o.useCallback(t=>{if(!t)return;const n=parseInt(t.replace(/\D/g,"")||"1",10);return t.includes("W")?a.formatTrialWeeks(n):t.includes("M")?a.formatTrialMonths(n):a.formatTrialDays(n)},[a]),j=o.useCallback(t=>{if(!r?.tiers)return;const n=F[t];return n?r.tiers.find(y=>y.entitlement===n):r.tiers.find(y=>y.entitlement==="none")},[r]),x=o.useCallback(t=>t===null?i.unlimited:t.toLocaleString(),[i.unlimited]),q=o.useCallback(t=>{const n=j(t);if(!n)return[];const y=[];return n.limits.hourly!==null&&y.push(a.formatHourlyLimit(x(n.limits.hourly))),n.limits.daily!==null&&y.push(a.formatDailyLimit(x(n.limits.daily))),n.limits.monthly!==null&&y.push(a.formatMonthlyLimit(x(n.limits.monthly))),n.limits.hourly===null&&n.limits.daily===null&&n.limits.monthly===null&&y.push(i.unlimitedRequests),y},[j,x,a,i.unlimitedRequests]),ee=o.useCallback(t=>q(t),[q]),te=o.useCallback(()=>{const t=[...i.freeTierFeatures];if(r?.tiers){const n=r.tiers.find(y=>y.entitlement==="none");n&&(n.limits.hourly!==null&&t.push(a.formatHourlyLimit(x(n.limits.hourly))),n.limits.daily!==null&&t.push(a.formatDailyLimit(x(n.limits.daily))),n.limits.monthly!==null&&t.push(a.formatMonthlyLimit(x(n.limits.monthly))))}return t},[r,x,a,i.freeTierFeatures]),ie=o.useCallback(t=>{const n=F[t];if(!n)return;const y=u.find(E=>E.identifier===t);if(!y)return;const Y=Object.entries(F).find(([E,ae])=>ae===n&&E.includes("monthly"))?.[0];if(!Y)return;const W=u.find(E=>E.identifier===Y);if(!W)return;const O=parseFloat(y.price),B=parseFloat(W.price);if(B<=0||O<=0)return;const _=B*12,re=(_-O)/_*100;return Math.round(re)},[u]),ne=[{value:"monthly",label:i.billingMonthly},{value:"yearly",label:i.billingYearly}];return e.jsx(C.SubscriptionLayout,{title:i.title,error:S,currentStatusLabel:i.currentStatusLabel,currentStatus:{isActive:s?.isActive??!1,activeContent:s?.isActive?{title:i.statusActive,fields:[{label:i.labelPlan,value:s.productIdentifier||i.labelPremium},{label:i.labelExpires,value:X(s.expirationDate)},{label:i.labelWillRenew,value:s.willRenew?i.yes:i.no},...r?[{label:i.labelMonthlyUsage,value:`${r.currentUsage.monthly.toLocaleString()} / ${x(r.currentLimits.monthly)}`},{label:i.labelDailyUsage,value:`${r.currentUsage.daily.toLocaleString()} / ${x(r.currentLimits.daily)}`}]:[]]}:void 0,inactiveContent:s?.isActive?void 0:{title:i.statusInactive,message:i.statusInactiveMessage}},aboveProducts:!h&&u.length>0?e.jsx("div",{className:"flex justify-center mb-6",children:e.jsx(C.SegmentedControl,{options:ne,value:N,onChange:K})}):null,primaryAction:{label:I?i.buttonPurchasing:i.buttonSubscribe,onClick:J,disabled:!p||I||L,loading:I},secondaryAction:{label:L?i.buttonRestoring:i.buttonRestore,onClick:Q,disabled:I||L,loading:L},children:h?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"})}):u.length===0?e.jsx("div",{className:"text-center py-12 text-theme-text-secondary",children:i.noProducts}):z.length===0?e.jsx("div",{className:"text-center py-12 text-theme-text-secondary",children:i.noProductsForPeriod}):e.jsxs(e.Fragment,{children:[e.jsx(C.SubscriptionTile,{id:"free",title:i.freeTierTitle,price:i.freeTierPrice,periodLabel:i.periodMonth,features:te(),isSelected:!s?.isActive&&p===null,onSelect:()=>T(null),topBadge:s?.isActive?void 0:{text:i.currentPlanBadge,color:"green"},disabled:I||L,hideSelectionIndicator:!0},"free"),z.map(t=>e.jsx(C.SubscriptionTile,{id:t.identifier,title:t.title,price:t.priceString,periodLabel:Z(t.period),features:ee(t.identifier),isSelected:p===t.identifier,onSelect:()=>T(t.identifier),isBestValue:t.identifier.includes("pro"),discountBadge:t.period?.includes("Y")?(()=>{const n=ie(t.identifier);return n&&n>0?{text:a.formatSavePercent(n),isBestValue:!0}:void 0})():void 0,introPriceNote:t.freeTrialPeriod?R(t.freeTrialPeriod):t.introPrice?a.formatIntroNote(t.introPrice):void 0,disabled:I||L},t.identifier))]})})}c.EntityListPage=U,c.EntitySubscriptionsPage=G,c.InvitationsPage=V,c.MembersManagementPage=H,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
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"})}));
@@ -0,0 +1,86 @@
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;
@@ -87,6 +87,8 @@ export interface EntitySubscriptionsPageProps {
87
87
  subscription: SubscriptionContextValue;
88
88
  /** Rate limit configuration */
89
89
  rateLimitsConfig?: RateLimitsConfigData | null;
90
+ /** Entity ID used for subscription (the selected entity's ID when logged in) */
91
+ subscriptionUserId?: string;
90
92
  /** All localized labels */
91
93
  labels: SubscriptionPageLabels;
92
94
  /** Formatter functions for dynamic strings */
@@ -5,3 +5,4 @@ export { EntityListPage, type EntityListPageProps } from './EntityListPage';
5
5
  export { MembersManagementPage, type MembersManagementPageProps, } from './MembersManagementPage';
6
6
  export { InvitationsPage, type InvitationsPageProps } from './InvitationsPage';
7
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.13",
3
+ "version": "0.0.14",
4
4
  "description": "Page containers for entity/organization management",
5
5
  "type": "module",
6
6
  "main": "dist/index.umd.js",
@@ -43,7 +43,7 @@
43
43
  "react-dom": "^18.0.0 || ^19.0.0",
44
44
  "@tanstack/react-query": "^5.0.0",
45
45
  "@sudobility/types": "^1.9.43",
46
- "@sudobility/entity_client": "^0.0.9",
46
+ "@sudobility/entity_client": "^0.0.10",
47
47
  "@sudobility/entity-components": "^1.0.4",
48
48
  "@sudobility/subscription-components": "^1.0.13"
49
49
  },
@@ -55,7 +55,7 @@
55
55
  "devDependencies": {
56
56
  "@eslint/js": "^9.38.0",
57
57
  "@sudobility/entity-components": "^1.0.4",
58
- "@sudobility/entity_client": "^0.0.9",
58
+ "@sudobility/entity_client": "^0.0.10",
59
59
  "@sudobility/subscription-components": "^1.0.13",
60
60
  "@sudobility/types": "^1.9.43",
61
61
  "@tanstack/react-query": "^5.0.0",