@sudobility/entity_pages 0.0.13 → 0.0.15

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, } from './pages';
package/dist/index.esm.js CHANGED
@@ -1,79 +1,78 @@
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 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, a] = S(!1), [h, f] = S({
10
+ const [p, a] = f(!1), [s, c] = f({
12
11
  displayName: "",
13
12
  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"
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__ */ o("div", { className: "space-y-8", children: [
18
- /* @__PURE__ */ o("div", { className: "flex items-center justify-between", children: [
19
- /* @__PURE__ */ o("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__ */ o(
22
+ /* @__PURE__ */ t(
24
23
  "button",
25
24
  {
26
25
  type: "button",
27
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(re, { 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__ */ o("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__ */ o("form", { onSubmit: async (l) => {
39
- if (l.preventDefault(), p(null), !h.displayName.trim()) {
40
- p("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 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");
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__ */ o("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: h.displayName,
59
- onChange: (l) => f((v) => ({
60
- ...v,
61
- displayName: l.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__ */ o("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: h.description,
74
- onChange: (l) => f((v) => ({
75
- ...v,
76
- description: l.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,47 +80,47 @@ function Ie({
81
80
  }
82
81
  )
83
82
  ] }),
84
- y && /* @__PURE__ */ i("p", { className: "text-sm text-destructive", children: y }),
85
- /* @__PURE__ */ o("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
- a(!1), f({ displayName: "", description: "" }), p(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: m.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: m.isPending ? "Creating..." : "Create"
102
+ children: l.isPending ? "Creating..." : "Create"
104
103
  }
105
104
  )
106
105
  ] })
107
106
  ] })
108
107
  ] }) }),
109
- x.length > 0 && /* @__PURE__ */ o("div", { children: [
110
- /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Personal Workspace" }),
111
- /* @__PURE__ */ i(
112
- B,
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: x,
115
- onSelect: r,
116
- isLoading: s
113
+ entities: b,
114
+ onSelect: n,
115
+ isLoading: r
117
116
  }
118
117
  )
119
118
  ] }),
120
- /* @__PURE__ */ o("div", { children: [
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: [
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",
@@ -130,409 +129,151 @@ function Ie({
130
129
  children: "Create your first organization"
131
130
  }
132
131
  )
133
- ] }) : /* @__PURE__ */ i(
134
- B,
132
+ ] }) : /* @__PURE__ */ e(
133
+ x,
135
134
  {
136
- entities: w,
137
- onSelect: r,
138
- isLoading: s
135
+ entities: v,
136
+ onSelect: n,
137
+ isLoading: r
139
138
  }
140
139
  )
141
140
  ] })
142
141
  ] });
143
142
  }
144
- function Te({
145
- client: c,
146
- entity: r,
147
- currentUserId: t
143
+ function B({
144
+ client: i,
145
+ entity: n,
146
+ currentUserId: p
148
147
  }) {
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) => {
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: r.entitySlug,
156
- memberId: N,
157
- role: g
153
+ await h.mutateAsync({
154
+ entitySlug: n.entitySlug,
155
+ memberId: g,
156
+ role: y
158
157
  });
159
- } catch (M) {
160
- console.error("Failed to update role:", M);
158
+ } catch (w) {
159
+ console.error("Failed to update role:", w);
161
160
  }
162
- }, C = async (N) => {
161
+ }, N = async (g) => {
163
162
  if (confirm("Are you sure you want to remove this member?"))
164
163
  try {
165
- await p.mutateAsync({
166
- entitySlug: r.entitySlug,
167
- memberId: N
164
+ await d.mutateAsync({
165
+ entitySlug: n.entitySlug,
166
+ memberId: g
168
167
  });
169
- } catch (g) {
170
- console.error("Failed to remove member:", g);
168
+ } catch (y) {
169
+ console.error("Failed to remove member:", y);
171
170
  }
172
- }, l = async (N) => {
173
- await m.mutateAsync({
174
- entitySlug: r.entitySlug,
175
- request: N
171
+ }, o = async (g) => {
172
+ await l.mutateAsync({
173
+ entitySlug: n.entitySlug,
174
+ request: g
176
175
  });
177
- }, v = async (N) => {
176
+ }, u = async (g) => {
178
177
  try {
179
- await x.mutateAsync({
180
- entitySlug: r.entitySlug,
181
- invitationId: N
178
+ await b.mutateAsync({
179
+ entitySlug: n.entitySlug,
180
+ invitationId: g
182
181
  });
183
- } catch (g) {
184
- console.error("Failed to cancel invitation:", g);
182
+ } catch (y) {
183
+ console.error("Failed to cancel invitation:", y);
185
184
  }
186
185
  };
187
- return r.entityType === "personal" ? /* @__PURE__ */ o("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__ */ o("div", { className: "space-y-8", children: [
191
- /* @__PURE__ */ o("div", { children: [
192
- /* @__PURE__ */ i("h1", { className: "text-2xl font-bold text-foreground", children: "Members" }),
193
- /* @__PURE__ */ o("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
- r.displayName
194
+ n.displayName
196
195
  ] })
197
196
  ] }),
198
- a && /* @__PURE__ */ o("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
- ae,
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: l,
204
- isSubmitting: m.isPending
202
+ onSubmit: o,
203
+ isSubmitting: l.isPending
205
204
  }
206
205
  )
207
206
  ] }),
208
- a && /* @__PURE__ */ o("div", { children: [
209
- /* @__PURE__ */ i("h2", { className: "text-lg font-semibold mb-3", children: "Pending Invitations" }),
210
- /* @__PURE__ */ i(
211
- $,
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: d,
212
+ invitations: m,
214
213
  mode: "admin",
215
- onCancel: v,
216
- isLoading: s,
214
+ onCancel: u,
215
+ isLoading: r,
217
216
  emptyMessage: "No pending invitations"
218
217
  }
219
218
  )
220
219
  ] }),
221
- /* @__PURE__ */ o("div", { children: [
222
- /* @__PURE__ */ o("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
- h.length,
223
+ s.length,
225
224
  ")"
226
225
  ] }),
227
- /* @__PURE__ */ i(
228
- oe,
226
+ /* @__PURE__ */ e(
227
+ S,
229
228
  {
230
- members: h,
231
- currentUserId: t,
229
+ members: s,
230
+ currentUserId: p,
232
231
  canManage: a,
233
- onRoleChange: w,
234
- onRemove: C,
235
- isLoading: f
232
+ onRoleChange: v,
233
+ onRemove: N,
234
+ isLoading: c
236
235
  }
237
236
  )
238
237
  ] })
239
238
  ] });
240
239
  }
241
- function Fe({
242
- client: c,
243
- onInvitationAccepted: r
240
+ function G({
241
+ client: i,
242
+ onInvitationAccepted: n
244
243
  }) {
245
- const { data: t = [], isLoading: a } = ge(c), h = fe(c), f = pe(c), y = async (s) => {
244
+ const { data: p = [], isLoading: a } = R(i), s = j(i), c = O(i), h = async (r) => {
246
245
  try {
247
- await h.mutateAsync(s), r?.();
248
- } catch (m) {
249
- console.error("Failed to accept invitation:", m);
246
+ await s.mutateAsync(r), n?.();
247
+ } catch (l) {
248
+ console.error("Failed to accept invitation:", l);
250
249
  }
251
- }, p = async (s) => {
250
+ }, d = async (r) => {
252
251
  try {
253
- await f.mutateAsync(s);
254
- } catch (m) {
255
- console.error("Failed to decline invitation:", m);
252
+ await c.mutateAsync(r);
253
+ } catch (l) {
254
+ console.error("Failed to decline invitation:", l);
256
255
  }
257
- }, d = t.filter((s) => s.status === "pending").length;
258
- return /* @__PURE__ */ o("div", { className: "space-y-6", children: [
259
- /* @__PURE__ */ o("div", { children: [
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" })
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
- $,
262
+ /* @__PURE__ */ e(
263
+ C,
265
264
  {
266
- invitations: t,
265
+ invitations: p,
267
266
  mode: "user",
268
- onAccept: y,
269
- onDecline: p,
267
+ onAccept: h,
268
+ onDecline: d,
270
269
  isLoading: a,
271
270
  emptyMessage: "You don't have any pending invitations"
272
271
  }
273
272
  )
274
273
  ] });
275
274
  }
276
- const L = {
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: c,
286
- rateLimitsConfig: r,
287
- labels: t,
288
- formatters: a,
289
- onPurchaseSuccess: h,
290
- onRestoreSuccess: f,
291
- onError: y,
292
- onWarning: p
293
- }) {
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) => {
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();
315
- try {
316
- await w(g) && (h?.(), M(null));
317
- } catch (e) {
318
- y?.(
319
- t.errorTitle,
320
- e instanceof Error ? e.message : t.purchaseError
321
- );
322
- } finally {
323
- A(!1);
324
- }
325
- }
326
- }, H = async () => {
327
- E(!0), l();
328
- try {
329
- await C() ? f?.() : p?.(t.errorTitle, t.restoreNoPurchases);
330
- } catch (e) {
331
- y?.(
332
- t.errorTitle,
333
- e instanceof Error ? e.message : t.restoreError
334
- );
335
- } finally {
336
- E(!1);
337
- }
338
- }, V = P((e) => e ? new Intl.DateTimeFormat(void 0, {
339
- year: "numeric",
340
- month: "long",
341
- day: "numeric"
342
- }).format(e) : "", []), G = P(
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
- ), K = P(
346
- (e) => {
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);
350
- },
351
- [a]
352
- ), k = P(
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");
359
- },
360
- [r]
361
- ), b = P(
362
- (e) => e === null ? t.unlimited : e.toLocaleString(),
363
- [t.unlimited]
364
- ), D = P(
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;
376
- },
377
- [k, b, a, t.unlimitedRequests]
378
- ), J = P(
379
- (e) => D(e),
380
- [D]
381
- ), Q = P(() => {
382
- const e = [...t.freeTierFeatures];
383
- if (r?.tiers) {
384
- const n = r.tiers.find(
385
- (u) => u.entitlement === "none"
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))
393
- ));
394
- }
395
- return e;
396
- }, [r, b, a, t.freeTierFeatures]), X = P(
397
- (e) => {
398
- const n = L[e];
399
- if (!n) return;
400
- const u = d.find(
401
- (F) => F.identifier === e
402
- );
403
- if (!u) return;
404
- const z = Object.entries(L).find(
405
- ([F, te]) => te === n && F.includes("monthly")
406
- )?.[0];
407
- if (!z) return;
408
- const _ = d.find(
409
- (F) => F.identifier === z
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);
416
- },
417
- [d]
418
- ), Z = [
419
- { value: "monthly", label: t.billingMonthly },
420
- { value: "yearly", label: t.billingYearly }
421
- ];
422
- return /* @__PURE__ */ i(
423
- ve,
424
- {
425
- title: t.title,
426
- error: x,
427
- currentStatusLabel: t.currentStatusLabel,
428
- currentStatus: {
429
- isActive: s?.isActive ?? !1,
430
- activeContent: s?.isActive ? {
431
- title: t.statusActive,
432
- fields: [
433
- {
434
- label: t.labelPlan,
435
- value: s.productIdentifier || t.labelPremium
436
- },
437
- {
438
- label: t.labelExpires,
439
- value: V(
440
- s.expirationDate
441
- )
442
- },
443
- {
444
- label: t.labelWillRenew,
445
- value: s.willRenew ? t.yes : t.no
446
- },
447
- ...r ? [
448
- {
449
- label: t.labelMonthlyUsage,
450
- value: `${r.currentUsage.monthly.toLocaleString()} / ${b(r.currentLimits.monthly)}`
451
- },
452
- {
453
- label: t.labelDailyUsage,
454
- value: `${r.currentUsage.daily.toLocaleString()} / ${b(r.currentLimits.daily)}`
455
- }
456
- ] : []
457
- ]
458
- } : void 0,
459
- inactiveContent: s?.isActive ? void 0 : {
460
- title: t.statusInactive,
461
- message: t.statusInactiveMessage
462
- }
463
- },
464
- aboveProducts: !m && d.length > 0 ? /* @__PURE__ */ i("div", { className: "flex justify-center mb-6", children: /* @__PURE__ */ i(
465
- be,
466
- {
467
- options: Z,
468
- value: v,
469
- onChange: U
470
- }
471
- ) }) : null,
472
- primaryAction: {
473
- label: I ? t.buttonPurchasing : t.buttonSubscribe,
474
- onClick: q,
475
- disabled: !g || I || T,
476
- loading: I
477
- },
478
- secondaryAction: {
479
- label: T ? t.buttonRestoring : t.buttonRestore,
480
- onClick: H,
481
- disabled: I || T,
482
- loading: T
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: [
485
- /* @__PURE__ */ i(
486
- O,
487
- {
488
- id: "free",
489
- title: t.freeTierTitle,
490
- price: t.freeTierPrice,
491
- periodLabel: t.periodMonth,
492
- features: Q(),
493
- isSelected: !s?.isActive && g === null,
494
- onSelect: () => M(null),
495
- topBadge: s?.isActive ? void 0 : {
496
- text: t.currentPlanBadge,
497
- color: "green"
498
- },
499
- disabled: I || T,
500
- hideSelectionIndicator: !0
501
- },
502
- "free"
503
- ),
504
- R.map((e) => /* @__PURE__ */ i(
505
- O,
506
- {
507
- id: e.identifier,
508
- title: e.title,
509
- price: e.priceString,
510
- periodLabel: G(e.period),
511
- features: J(e.identifier),
512
- isSelected: g === e.identifier,
513
- onSelect: () => M(e.identifier),
514
- isBestValue: e.identifier.includes("pro"),
515
- discountBadge: e.period?.includes("Y") ? (() => {
516
- const n = X(
517
- e.identifier
518
- );
519
- return n && n > 0 ? {
520
- text: a.formatSavePercent(n),
521
- isBestValue: !0
522
- } : void 0;
523
- })() : void 0,
524
- introPriceNote: e.freeTrialPeriod ? K(e.freeTrialPeriod) : e.introPrice ? a.formatIntroNote(e.introPrice) : void 0,
525
- disabled: I || T
526
- },
527
- e.identifier
528
- ))
529
- ] })
530
- }
531
- );
532
- }
533
275
  export {
534
- Ie as EntityListPage,
535
- Ce as EntitySubscriptionsPage,
536
- Fe as InvitationsPage,
537
- Te as MembersManagementPage
276
+ U as EntityListPage,
277
+ G as InvitationsPage,
278
+ B as MembersManagementPage
538
279
  };
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(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,4 +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';
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.15",
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.9",
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.10",
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.9",
59
- "@sudobility/subscription-components": "^1.0.13",
60
- "@sudobility/types": "^1.9.43",
51
+ "@sudobility/entity_client": "^0.0.10",
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,106 +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
- /** All localized labels */
91
- labels: SubscriptionPageLabels;
92
- /** Formatter functions for dynamic strings */
93
- formatters: SubscriptionPageFormatters;
94
- /** Called when purchase succeeds */
95
- onPurchaseSuccess?: () => void;
96
- /** Called when restore succeeds */
97
- onRestoreSuccess?: () => void;
98
- /** Called on error */
99
- onError?: (title: string, message: string) => void;
100
- /** Called on warning */
101
- onWarning?: (title: string, message: string) => void;
102
- }
103
- /**
104
- * Page for managing entity subscriptions.
105
- */
106
- export declare function EntitySubscriptionsPage({ subscription, rateLimitsConfig, labels, formatters, onPurchaseSuccess, onRestoreSuccess, onError, onWarning, }: EntitySubscriptionsPageProps): import("react/jsx-runtime").JSX.Element;