@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 +1 -1
- package/dist/index.esm.js +153 -412
- package/dist/index.umd.js +1 -1
- package/dist/pages/index.d.ts +0 -1
- package/package.json +4 -13
- package/dist/pages/EntitySubscriptionsPage.d.ts +0 -106
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,
|
|
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
|
|
2
|
-
import { useState as
|
|
3
|
-
import { Plus as
|
|
4
|
-
import { EntityList as
|
|
5
|
-
import { useEntities as
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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 [
|
|
10
|
+
const [p, a] = f(!1), [s, c] = f({
|
|
12
11
|
displayName: "",
|
|
13
12
|
description: ""
|
|
14
|
-
}), [
|
|
15
|
-
(
|
|
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__ */
|
|
18
|
-
/* @__PURE__ */
|
|
19
|
-
/* @__PURE__ */
|
|
20
|
-
/* @__PURE__ */
|
|
21
|
-
/* @__PURE__ */
|
|
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__ */
|
|
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__ */
|
|
31
|
-
/* @__PURE__ */
|
|
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
|
-
|
|
37
|
-
/* @__PURE__ */
|
|
38
|
-
/* @__PURE__ */
|
|
39
|
-
if (
|
|
40
|
-
|
|
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
|
|
45
|
-
displayName:
|
|
46
|
-
description:
|
|
47
|
-
}), a(!1),
|
|
48
|
-
} catch (
|
|
49
|
-
|
|
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__ */
|
|
53
|
-
/* @__PURE__ */
|
|
54
|
-
/* @__PURE__ */
|
|
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:
|
|
59
|
-
onChange: (
|
|
60
|
-
...
|
|
61
|
-
displayName:
|
|
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__ */
|
|
69
|
-
/* @__PURE__ */
|
|
70
|
-
/* @__PURE__ */
|
|
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:
|
|
74
|
-
onChange: (
|
|
75
|
-
...
|
|
76
|
-
description:
|
|
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
|
-
|
|
85
|
-
/* @__PURE__ */
|
|
86
|
-
/* @__PURE__ */
|
|
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),
|
|
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__ */
|
|
96
|
+
/* @__PURE__ */ e(
|
|
98
97
|
"button",
|
|
99
98
|
{
|
|
100
99
|
type: "submit",
|
|
101
|
-
disabled:
|
|
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:
|
|
102
|
+
children: l.isPending ? "Creating..." : "Create"
|
|
104
103
|
}
|
|
105
104
|
)
|
|
106
105
|
] })
|
|
107
106
|
] })
|
|
108
107
|
] }) }),
|
|
109
|
-
|
|
110
|
-
/* @__PURE__ */
|
|
111
|
-
/* @__PURE__ */
|
|
112
|
-
|
|
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:
|
|
115
|
-
onSelect:
|
|
116
|
-
isLoading:
|
|
113
|
+
entities: b,
|
|
114
|
+
onSelect: n,
|
|
115
|
+
isLoading: r
|
|
117
116
|
}
|
|
118
117
|
)
|
|
119
118
|
] }),
|
|
120
|
-
/* @__PURE__ */
|
|
121
|
-
/* @__PURE__ */
|
|
122
|
-
|
|
123
|
-
/* @__PURE__ */
|
|
124
|
-
/* @__PURE__ */
|
|
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__ */
|
|
134
|
-
|
|
132
|
+
] }) : /* @__PURE__ */ e(
|
|
133
|
+
x,
|
|
135
134
|
{
|
|
136
|
-
entities:
|
|
137
|
-
onSelect:
|
|
138
|
-
isLoading:
|
|
135
|
+
entities: v,
|
|
136
|
+
onSelect: n,
|
|
137
|
+
isLoading: r
|
|
139
138
|
}
|
|
140
139
|
)
|
|
141
140
|
] })
|
|
142
141
|
] });
|
|
143
142
|
}
|
|
144
|
-
function
|
|
145
|
-
client:
|
|
146
|
-
entity:
|
|
147
|
-
currentUserId:
|
|
143
|
+
function B({
|
|
144
|
+
client: i,
|
|
145
|
+
entity: n,
|
|
146
|
+
currentUserId: p
|
|
148
147
|
}) {
|
|
149
|
-
const a =
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
),
|
|
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
|
|
155
|
-
entitySlug:
|
|
156
|
-
memberId:
|
|
157
|
-
role:
|
|
153
|
+
await h.mutateAsync({
|
|
154
|
+
entitySlug: n.entitySlug,
|
|
155
|
+
memberId: g,
|
|
156
|
+
role: y
|
|
158
157
|
});
|
|
159
|
-
} catch (
|
|
160
|
-
console.error("Failed to update role:",
|
|
158
|
+
} catch (w) {
|
|
159
|
+
console.error("Failed to update role:", w);
|
|
161
160
|
}
|
|
162
|
-
},
|
|
161
|
+
}, N = async (g) => {
|
|
163
162
|
if (confirm("Are you sure you want to remove this member?"))
|
|
164
163
|
try {
|
|
165
|
-
await
|
|
166
|
-
entitySlug:
|
|
167
|
-
memberId:
|
|
164
|
+
await d.mutateAsync({
|
|
165
|
+
entitySlug: n.entitySlug,
|
|
166
|
+
memberId: g
|
|
168
167
|
});
|
|
169
|
-
} catch (
|
|
170
|
-
console.error("Failed to remove member:",
|
|
168
|
+
} catch (y) {
|
|
169
|
+
console.error("Failed to remove member:", y);
|
|
171
170
|
}
|
|
172
|
-
},
|
|
173
|
-
await
|
|
174
|
-
entitySlug:
|
|
175
|
-
request:
|
|
171
|
+
}, o = async (g) => {
|
|
172
|
+
await l.mutateAsync({
|
|
173
|
+
entitySlug: n.entitySlug,
|
|
174
|
+
request: g
|
|
176
175
|
});
|
|
177
|
-
},
|
|
176
|
+
}, u = async (g) => {
|
|
178
177
|
try {
|
|
179
|
-
await
|
|
180
|
-
entitySlug:
|
|
181
|
-
invitationId:
|
|
178
|
+
await b.mutateAsync({
|
|
179
|
+
entitySlug: n.entitySlug,
|
|
180
|
+
invitationId: g
|
|
182
181
|
});
|
|
183
|
-
} catch (
|
|
184
|
-
console.error("Failed to cancel invitation:",
|
|
182
|
+
} catch (y) {
|
|
183
|
+
console.error("Failed to cancel invitation:", y);
|
|
185
184
|
}
|
|
186
185
|
};
|
|
187
|
-
return
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
-
/* @__PURE__ */
|
|
190
|
-
] }) : /* @__PURE__ */
|
|
191
|
-
/* @__PURE__ */
|
|
192
|
-
/* @__PURE__ */
|
|
193
|
-
/* @__PURE__ */
|
|
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
|
-
|
|
194
|
+
n.displayName
|
|
196
195
|
] })
|
|
197
196
|
] }),
|
|
198
|
-
a && /* @__PURE__ */
|
|
199
|
-
/* @__PURE__ */
|
|
200
|
-
/* @__PURE__ */
|
|
201
|
-
|
|
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:
|
|
204
|
-
isSubmitting:
|
|
202
|
+
onSubmit: o,
|
|
203
|
+
isSubmitting: l.isPending
|
|
205
204
|
}
|
|
206
205
|
)
|
|
207
206
|
] }),
|
|
208
|
-
a && /* @__PURE__ */
|
|
209
|
-
/* @__PURE__ */
|
|
210
|
-
/* @__PURE__ */
|
|
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:
|
|
212
|
+
invitations: m,
|
|
214
213
|
mode: "admin",
|
|
215
|
-
onCancel:
|
|
216
|
-
isLoading:
|
|
214
|
+
onCancel: u,
|
|
215
|
+
isLoading: r,
|
|
217
216
|
emptyMessage: "No pending invitations"
|
|
218
217
|
}
|
|
219
218
|
)
|
|
220
219
|
] }),
|
|
221
|
-
/* @__PURE__ */
|
|
222
|
-
/* @__PURE__ */
|
|
220
|
+
/* @__PURE__ */ t("div", { children: [
|
|
221
|
+
/* @__PURE__ */ t("h2", { className: "text-lg font-semibold mb-3", children: [
|
|
223
222
|
"Current Members (",
|
|
224
|
-
|
|
223
|
+
s.length,
|
|
225
224
|
")"
|
|
226
225
|
] }),
|
|
227
|
-
/* @__PURE__ */
|
|
228
|
-
|
|
226
|
+
/* @__PURE__ */ e(
|
|
227
|
+
S,
|
|
229
228
|
{
|
|
230
|
-
members:
|
|
231
|
-
currentUserId:
|
|
229
|
+
members: s,
|
|
230
|
+
currentUserId: p,
|
|
232
231
|
canManage: a,
|
|
233
|
-
onRoleChange:
|
|
234
|
-
onRemove:
|
|
235
|
-
isLoading:
|
|
232
|
+
onRoleChange: v,
|
|
233
|
+
onRemove: N,
|
|
234
|
+
isLoading: c
|
|
236
235
|
}
|
|
237
236
|
)
|
|
238
237
|
] })
|
|
239
238
|
] });
|
|
240
239
|
}
|
|
241
|
-
function
|
|
242
|
-
client:
|
|
243
|
-
onInvitationAccepted:
|
|
240
|
+
function G({
|
|
241
|
+
client: i,
|
|
242
|
+
onInvitationAccepted: n
|
|
244
243
|
}) {
|
|
245
|
-
const { data:
|
|
244
|
+
const { data: p = [], isLoading: a } = R(i), s = j(i), c = O(i), h = async (r) => {
|
|
246
245
|
try {
|
|
247
|
-
await
|
|
248
|
-
} catch (
|
|
249
|
-
console.error("Failed to accept invitation:",
|
|
246
|
+
await s.mutateAsync(r), n?.();
|
|
247
|
+
} catch (l) {
|
|
248
|
+
console.error("Failed to accept invitation:", l);
|
|
250
249
|
}
|
|
251
|
-
},
|
|
250
|
+
}, d = async (r) => {
|
|
252
251
|
try {
|
|
253
|
-
await
|
|
254
|
-
} catch (
|
|
255
|
-
console.error("Failed to decline invitation:",
|
|
252
|
+
await c.mutateAsync(r);
|
|
253
|
+
} catch (l) {
|
|
254
|
+
console.error("Failed to decline invitation:", l);
|
|
256
255
|
}
|
|
257
|
-
},
|
|
258
|
-
return /* @__PURE__ */
|
|
259
|
-
/* @__PURE__ */
|
|
260
|
-
/* @__PURE__ */
|
|
261
|
-
/* @__PURE__ */
|
|
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__ */
|
|
264
|
-
|
|
262
|
+
/* @__PURE__ */ e(
|
|
263
|
+
C,
|
|
265
264
|
{
|
|
266
|
-
invitations:
|
|
265
|
+
invitations: p,
|
|
267
266
|
mode: "user",
|
|
268
|
-
onAccept:
|
|
269
|
-
onDecline:
|
|
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
|
-
|
|
535
|
-
|
|
536
|
-
|
|
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"})}));
|
package/dist/pages/index.d.ts
CHANGED
|
@@ -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.
|
|
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/
|
|
46
|
-
"@sudobility/
|
|
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.
|
|
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;
|