@strapi-community/plugin-better-auth-dashboard 1.0.0-alpha.8 → 1.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1685 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
+ const jsxRuntime = require("react/jsx-runtime");
4
+ const designSystem = require("@strapi/design-system");
5
+ const icons = require("@strapi/icons");
6
+ const admin = require("@strapi/strapi/admin");
7
+ const react = require("react");
8
+ const reactQuery = require("react-query");
9
+ const styled = require("styled-components");
10
+ const index = require("./index-BHfYu0uA.js");
11
+ const useModelSchema = require("./useModelSchema-CqOTI7P1.js");
12
+ const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
13
+ const styled__default = /* @__PURE__ */ _interopDefault(styled);
14
+ function UserCombobox({
15
+ label = "User",
16
+ hint,
17
+ placeholder = "Search users by email or name…",
18
+ value,
19
+ onChange,
20
+ error,
21
+ required
22
+ }) {
23
+ const [search, setSearch] = react.useState("");
24
+ const { data, isLoading } = reactQuery.useQuery({
25
+ queryKey: ["user-combobox-search", search],
26
+ queryFn: async () => {
27
+ const whereClause = search ? JSON.stringify([
28
+ {
29
+ field: "email",
30
+ operator: "contains",
31
+ value: search,
32
+ connector: "OR"
33
+ },
34
+ {
35
+ field: "name",
36
+ operator: "contains",
37
+ value: search,
38
+ connector: "OR"
39
+ }
40
+ ]) : void 0;
41
+ const result = await index.client.dash.listUsers({
42
+ query: {
43
+ limit: search ? 100 : 20,
44
+ offset: 0,
45
+ sortBy: "createdAt",
46
+ sortOrder: "desc",
47
+ ...whereClause ? { where: whereClause } : {}
48
+ }
49
+ });
50
+ return result.data?.users ?? [];
51
+ },
52
+ keepPreviousData: true
53
+ });
54
+ const users = data ?? [];
55
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { hint, error, style: { width: "100%" }, children: [
56
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: label }),
57
+ /* @__PURE__ */ jsxRuntime.jsx(
58
+ designSystem.Combobox,
59
+ {
60
+ placeholder,
61
+ value,
62
+ onChange: (val) => onChange(val),
63
+ onInputChange: (e) => setSearch(e.target.value),
64
+ loading: isLoading,
65
+ required,
66
+ children: users.map((user) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ComboboxOption, { value: user.id, children: [
67
+ user.name,
68
+ " — ",
69
+ user.email
70
+ ] }, user.id))
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
74
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
75
+ ] });
76
+ }
77
+ function CreateOrganizationDialog({ teamsEnabled, onClose }) {
78
+ const qc = reactQuery.useQueryClient();
79
+ const { toggleNotification } = admin.useNotification();
80
+ const [name, setName] = react.useState("");
81
+ const [slug, setSlug] = react.useState("");
82
+ const [logo, setLogo] = react.useState("");
83
+ const [ownerId, setOwnerId] = react.useState("");
84
+ const [skipDefaultTeam, setSkipDefaultTeam] = react.useState(false);
85
+ const [defaultTeamName, setDefaultTeamName] = react.useState("");
86
+ const createMutation = reactQuery.useMutation({
87
+ mutationFn: async () => {
88
+ const result = await index.client.dash.organization.create(
89
+ {
90
+ name,
91
+ slug,
92
+ ...logo ? { logo } : {},
93
+ ...teamsEnabled && !skipDefaultTeam && defaultTeamName ? { defaultTeamName } : {}
94
+ },
95
+ useModelSchema.withContext({ userId: ownerId, skipDefaultTeam }, index.getAuthHeaders())
96
+ );
97
+ if (result.error)
98
+ throw new Error(result.error.message ?? "Create failed");
99
+ return result.data;
100
+ },
101
+ onSuccess: () => {
102
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
103
+ toggleNotification({
104
+ type: "success",
105
+ message: "Organization created successfully"
106
+ });
107
+ onClose();
108
+ },
109
+ onError: (err) => {
110
+ toggleNotification({
111
+ type: "danger",
112
+ message: err.message ?? "Failed to create organization"
113
+ });
114
+ }
115
+ });
116
+ const handleNameChange = (e) => {
117
+ const val = e.target.value;
118
+ setName(val);
119
+ if (!slug || slug === slugify(name)) {
120
+ setSlug(slugify(val));
121
+ }
122
+ };
123
+ const footer = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
124
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: onClose, children: "Cancel" }),
125
+ /* @__PURE__ */ jsxRuntime.jsx(
126
+ designSystem.Button,
127
+ {
128
+ loading: createMutation.isLoading,
129
+ disabled: !name || !slug || !ownerId,
130
+ onClick: () => createMutation.mutate(),
131
+ "data-testid": "create-org-submit",
132
+ children: "Create organization"
133
+ }
134
+ )
135
+ ] });
136
+ return /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.Drawer, { title: "Create organization", footer, onClose, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, children: [
137
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
138
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Details" }),
139
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { width: "100%" }, required: true, children: [
140
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Name" }),
141
+ /* @__PURE__ */ jsxRuntime.jsx(
142
+ designSystem.TextInput,
143
+ {
144
+ name: "name",
145
+ value: name,
146
+ onChange: handleNameChange,
147
+ placeholder: "Acme Corp"
148
+ }
149
+ )
150
+ ] }),
151
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { width: "100%" }, hint: "Used in URLs", required: true, children: [
152
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Slug" }),
153
+ /* @__PURE__ */ jsxRuntime.jsx(
154
+ designSystem.TextInput,
155
+ {
156
+ name: "slug",
157
+ value: slug,
158
+ onChange: (e) => setSlug(e.target.value),
159
+ placeholder: "acme-corp"
160
+ }
161
+ ),
162
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
163
+ ] }),
164
+ /* @__PURE__ */ jsxRuntime.jsx(
165
+ useModelSchema.MediaPickerField,
166
+ {
167
+ label: "Logo URL",
168
+ name: "logo",
169
+ value: logo,
170
+ onChange: setLogo,
171
+ placeholder: "https://example.com/logo.png",
172
+ hint: "Optional — publicly accessible image URL"
173
+ }
174
+ )
175
+ ] }),
176
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
177
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Owner" }),
178
+ /* @__PURE__ */ jsxRuntime.jsx(
179
+ UserCombobox,
180
+ {
181
+ label: "Owner",
182
+ hint: "The user who will own and manage this organization",
183
+ value: ownerId,
184
+ onChange: setOwnerId,
185
+ required: true
186
+ }
187
+ )
188
+ ] }),
189
+ teamsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
190
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Default team" }),
191
+ /* @__PURE__ */ jsxRuntime.jsx(
192
+ designSystem.Checkbox,
193
+ {
194
+ name: "skipDefaultTeam",
195
+ checked: skipDefaultTeam,
196
+ onCheckedChange: (checked) => setSkipDefaultTeam(checked),
197
+ children: "Don't create a default team"
198
+ }
199
+ ),
200
+ !skipDefaultTeam && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { width: "100%" }, children: [
201
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Default team name" }),
202
+ /* @__PURE__ */ jsxRuntime.jsx(
203
+ designSystem.TextInput,
204
+ {
205
+ name: "defaultTeamName",
206
+ value: defaultTeamName,
207
+ onChange: (e) => setDefaultTeamName(e.target.value),
208
+ placeholder: "Leave blank to use the system default"
209
+ }
210
+ )
211
+ ] }),
212
+ skipDefaultTeam && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "Members can be added to teams manually after the organization is created." }) })
213
+ ] })
214
+ ] }) });
215
+ }
216
+ function slugify(str) {
217
+ return str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
218
+ }
219
+ const ExpandableRow = styled__default.default.div`
220
+ width: 100%;
221
+ background: white;
222
+ border: 1px solid #dcdce4;
223
+ border-radius: 8px;
224
+ overflow: hidden;
225
+ `;
226
+ const ExpandableRowHeader = styled__default.default.div`
227
+ display: flex;
228
+ align-items: center;
229
+ justify-content: space-between;
230
+ padding: 10px 14px;
231
+ gap: 12px;
232
+ cursor: pointer;
233
+ &:hover {
234
+ background: #fafafe;
235
+ }
236
+ `;
237
+ const ExpandableRowBody = styled__default.default.div`
238
+ padding: 12px 14px;
239
+ border-top: 1px solid #eaeaef;
240
+ background: #fafafe;
241
+ display: flex;
242
+ flex-direction: column;
243
+ gap: 10px;
244
+ `;
245
+ const STANDARD_ORG_FIELDS = /* @__PURE__ */ new Set([
246
+ "id",
247
+ "name",
248
+ "slug",
249
+ "logo",
250
+ "metadata",
251
+ "memberCount",
252
+ "createdAt",
253
+ "updatedAt"
254
+ ]);
255
+ function OrganizationDetail({
256
+ organizationId,
257
+ teamsEnabled,
258
+ onClose
259
+ }) {
260
+ const schemaQuery = useModelSchema.useModelSchema("organization");
261
+ const qc = reactQuery.useQueryClient();
262
+ const { toggleNotification } = admin.useNotification();
263
+ const { get, put } = admin.useFetchClient();
264
+ const orgQuery = reactQuery.useQuery({
265
+ queryKey: ["dash-org", organizationId],
266
+ queryFn: async () => {
267
+ const result = await index.client.dash.organization[organizationId](
268
+ {},
269
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
270
+ );
271
+ if (result.error)
272
+ throw new Error(result.error.message ?? "Failed to load org");
273
+ return result.data;
274
+ }
275
+ });
276
+ const membersQuery = reactQuery.useQuery({
277
+ queryKey: ["dash-org-members", organizationId],
278
+ queryFn: async () => {
279
+ const result = await index.client.dash.organization[organizationId].members({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
280
+ if (result.error) throw new Error(result.error.message ?? "Failed");
281
+ return result.data ?? [];
282
+ }
283
+ });
284
+ const teamsQuery = reactQuery.useQuery({
285
+ queryKey: ["dash-org-teams", organizationId],
286
+ queryFn: async () => {
287
+ if (!teamsEnabled) return [];
288
+ const result = await index.client.dash.organization[organizationId].teams({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
289
+ if (result.error) throw new Error(result.error.message ?? "Failed");
290
+ return result.data ?? [];
291
+ },
292
+ enabled: teamsEnabled
293
+ });
294
+ const ssoQuery = reactQuery.useQuery({
295
+ queryKey: ["dash-org-sso", organizationId],
296
+ queryFn: async () => {
297
+ const result = await index.client.dash.organization[organizationId].ssoProviders({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
298
+ if (result.error) throw new Error(result.error.message ?? "Failed");
299
+ return result.data ?? [];
300
+ }
301
+ });
302
+ const invitationsQuery = reactQuery.useQuery({
303
+ queryKey: ["dash-org-invitations", organizationId],
304
+ queryFn: async () => {
305
+ const result = await index.client.dash.organization[organizationId].invitations({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
306
+ if (result.error) throw new Error(result.error.message ?? "Failed");
307
+ return result.data ?? [];
308
+ }
309
+ });
310
+ const strapiOrgQuery = reactQuery.useQuery({
311
+ queryKey: ["dash-strapi-org", organizationId],
312
+ enabled: !!orgQuery.data,
313
+ queryFn: async () => {
314
+ const { data } = await get(
315
+ `/better-auth-dashboard/db?uid=plugin::better-auth.organization&filters[id][$eq]=${organizationId}&pagination[pageSize]=1`
316
+ );
317
+ return data.results?.[0] ?? null;
318
+ }
319
+ });
320
+ const [activeTab, setActiveTab] = react.useState("details");
321
+ const [editName, setEditName] = react.useState(void 0);
322
+ const [editSlug, setEditSlug] = react.useState(void 0);
323
+ const [editLogo, setEditLogo] = react.useState(void 0);
324
+ const [editExtra, setEditExtra] = react.useState({});
325
+ const [confirmRemoveMemberId, setConfirmRemoveMemberId] = react.useState(null);
326
+ const [confirmDeleteTeamId, setConfirmDeleteTeamId] = react.useState(
327
+ null
328
+ );
329
+ const [confirmDeleteSsoId, setConfirmDeleteSsoId] = react.useState(
330
+ null
331
+ );
332
+ const [inviteEmail, setInviteEmail] = react.useState("");
333
+ const [inviteRole, setInviteRole] = react.useState("member");
334
+ const [confirmCancelInvitationId, setConfirmCancelInvitationId] = react.useState(null);
335
+ const handleExtraChange = (name, value) => {
336
+ setEditExtra((prev) => ({ ...prev, [name]: value }));
337
+ };
338
+ const org = orgQuery.data;
339
+ const extraData = {
340
+ ...strapiOrgQuery.data ?? {},
341
+ ...editExtra
342
+ };
343
+ const updateOrgMutation = reactQuery.useMutation({
344
+ mutationFn: async () => {
345
+ const body = { ...editExtra };
346
+ if (editName !== void 0) body.name = editName;
347
+ if (editSlug !== void 0) body.slug = editSlug;
348
+ if (editLogo !== void 0) body.logo = editLogo;
349
+ const documentId = strapiOrgQuery.data?.documentId;
350
+ if (!documentId)
351
+ throw new Error("Could not resolve documentId for organization");
352
+ await put(
353
+ `/better-auth-dashboard/db/${documentId}?uid=plugin::better-auth.organization`,
354
+ body
355
+ );
356
+ },
357
+ onSuccess: async () => {
358
+ await qc.invalidateQueries({
359
+ queryKey: ["dash-strapi-org", organizationId]
360
+ });
361
+ qc.invalidateQueries({ queryKey: ["dash-org", organizationId] });
362
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
363
+ qc.invalidateQueries({ queryKey: ["dash-strapi-org", organizationId] });
364
+ setEditName(void 0);
365
+ setEditSlug(void 0);
366
+ setEditLogo(void 0);
367
+ setEditExtra({});
368
+ toggleNotification({
369
+ type: "success",
370
+ message: "Organization updated successfully"
371
+ });
372
+ onClose();
373
+ },
374
+ onError: (err) => {
375
+ toggleNotification({
376
+ type: "danger",
377
+ message: err.message ?? "Update failed"
378
+ });
379
+ }
380
+ });
381
+ const [addUserId, setAddUserId] = react.useState("");
382
+ const [addRole, setAddRole] = react.useState("member");
383
+ const addMemberMutation = reactQuery.useMutation({
384
+ mutationFn: async () => {
385
+ const result = await index.client.dash.organization.addMember(
386
+ { userId: addUserId, role: addRole },
387
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
388
+ );
389
+ if (result.error)
390
+ throw new Error(result.error.message ?? "Add member failed");
391
+ return result.data;
392
+ },
393
+ onSuccess: () => {
394
+ qc.invalidateQueries({ queryKey: ["dash-org-members", organizationId] });
395
+ setAddUserId("");
396
+ setAddRole("member");
397
+ toggleNotification({ type: "success", message: "Member added" });
398
+ },
399
+ onError: (err) => {
400
+ toggleNotification({
401
+ type: "danger",
402
+ message: err.message ?? "Failed to add member"
403
+ });
404
+ }
405
+ });
406
+ const removeMemberMutation = reactQuery.useMutation({
407
+ mutationFn: async (memberId) => {
408
+ const result = await index.client.dash.organization.removeMember(
409
+ { memberId },
410
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
411
+ );
412
+ if (result.error) throw new Error(result.error.message ?? "Failed");
413
+ },
414
+ onSuccess: () => {
415
+ setConfirmRemoveMemberId(null);
416
+ qc.invalidateQueries({ queryKey: ["dash-org-members", organizationId] });
417
+ toggleNotification({ type: "success", message: "Member removed" });
418
+ },
419
+ onError: (err) => {
420
+ toggleNotification({
421
+ type: "danger",
422
+ message: err.message ?? "Failed to remove member"
423
+ });
424
+ }
425
+ });
426
+ const updateRoleMutation = reactQuery.useMutation({
427
+ mutationFn: async ({
428
+ memberId,
429
+ role
430
+ }) => {
431
+ const result = await index.client.dash.organization.updateMemberRole(
432
+ { memberId, role },
433
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
434
+ );
435
+ if (result.error) throw new Error(result.error.message ?? "Failed");
436
+ return result.data;
437
+ },
438
+ onSuccess: () => {
439
+ qc.invalidateQueries({ queryKey: ["dash-org-members", organizationId] });
440
+ toggleNotification({ type: "success", message: "Role updated" });
441
+ },
442
+ onError: (err) => {
443
+ toggleNotification({
444
+ type: "danger",
445
+ message: err.message ?? "Failed to update role"
446
+ });
447
+ }
448
+ });
449
+ const [newTeamName, setNewTeamName] = react.useState("");
450
+ const createTeamMutation = reactQuery.useMutation({
451
+ mutationFn: async () => {
452
+ const result = await index.client.dash.organization.createTeam(
453
+ { name: newTeamName },
454
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
455
+ );
456
+ if (result.error)
457
+ throw new Error(result.error.message ?? "Create team failed");
458
+ return result.data;
459
+ },
460
+ onSuccess: () => {
461
+ qc.invalidateQueries({ queryKey: ["dash-org-teams", organizationId] });
462
+ setNewTeamName("");
463
+ toggleNotification({ type: "success", message: "Team created" });
464
+ },
465
+ onError: (err) => {
466
+ toggleNotification({
467
+ type: "danger",
468
+ message: err.message ?? "Failed to create team"
469
+ });
470
+ }
471
+ });
472
+ const deleteTeamMutation = reactQuery.useMutation({
473
+ mutationFn: async (teamId) => {
474
+ const result = await index.client.dash.organization.deleteTeam(
475
+ { teamId },
476
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
477
+ );
478
+ if (result.error) throw new Error(result.error.message ?? "Failed");
479
+ },
480
+ onSuccess: () => {
481
+ setConfirmDeleteTeamId(null);
482
+ qc.invalidateQueries({ queryKey: ["dash-org-teams", organizationId] });
483
+ toggleNotification({ type: "success", message: "Team deleted" });
484
+ },
485
+ onError: (err) => {
486
+ toggleNotification({
487
+ type: "danger",
488
+ message: err.message ?? "Failed to delete team"
489
+ });
490
+ }
491
+ });
492
+ const deleteSsoMutation = reactQuery.useMutation({
493
+ mutationFn: async (providerId) => {
494
+ const result = await index.client.dash.organization[organizationId].ssoProvider.delete(
495
+ { providerId },
496
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
497
+ );
498
+ if (result.error) throw new Error(result.error.message ?? "Failed");
499
+ },
500
+ onSuccess: () => {
501
+ setConfirmDeleteSsoId(null);
502
+ qc.invalidateQueries({ queryKey: ["dash-org-sso", organizationId] });
503
+ toggleNotification({ type: "success", message: "SSO provider deleted" });
504
+ },
505
+ onError: (err) => {
506
+ toggleNotification({
507
+ type: "danger",
508
+ message: err.message ?? "Failed to delete SSO provider"
509
+ });
510
+ }
511
+ });
512
+ const inviteMemberMutation = reactQuery.useMutation({
513
+ mutationFn: async () => {
514
+ const result = await index.client.dash.organization.inviteMember(
515
+ { email: inviteEmail, role: inviteRole, invitedBy: "" },
516
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
517
+ );
518
+ if (result.error) throw new Error(result.error.message ?? "Failed");
519
+ return result.data;
520
+ },
521
+ onSuccess: () => {
522
+ qc.invalidateQueries({
523
+ queryKey: ["dash-org-invitations", organizationId]
524
+ });
525
+ setInviteEmail("");
526
+ setInviteRole("member");
527
+ toggleNotification({ type: "success", message: "Invitation sent" });
528
+ },
529
+ onError: (err) => {
530
+ toggleNotification({
531
+ type: "danger",
532
+ message: err.message ?? "Failed to invite"
533
+ });
534
+ }
535
+ });
536
+ const cancelInvitationMutation = reactQuery.useMutation({
537
+ mutationFn: async (invitationId) => {
538
+ const result = await index.client.dash.organization.cancelInvitation(
539
+ { invitationId },
540
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
541
+ );
542
+ if (result.error) throw new Error(result.error.message ?? "Failed");
543
+ },
544
+ onSuccess: () => {
545
+ setConfirmCancelInvitationId(null);
546
+ qc.invalidateQueries({
547
+ queryKey: ["dash-org-invitations", organizationId]
548
+ });
549
+ toggleNotification({ type: "success", message: "Invitation cancelled" });
550
+ },
551
+ onError: (err) => {
552
+ toggleNotification({
553
+ type: "danger",
554
+ message: err.message ?? "Failed to cancel"
555
+ });
556
+ }
557
+ });
558
+ const resendInvitationMutation = reactQuery.useMutation({
559
+ mutationFn: async (invitationId) => {
560
+ const result = await index.client.dash.organization.resendInvitation(
561
+ { invitationId },
562
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
563
+ );
564
+ if (result.error) throw new Error(result.error.message ?? "Failed");
565
+ },
566
+ onSuccess: () => {
567
+ toggleNotification({ type: "success", message: "Invitation resent" });
568
+ },
569
+ onError: (err) => {
570
+ toggleNotification({
571
+ type: "danger",
572
+ message: err.message ?? "Failed to resend"
573
+ });
574
+ }
575
+ });
576
+ const members = membersQuery.data ?? [];
577
+ const teams = teamsQuery.data ?? [];
578
+ const ssoProviders = ssoQuery.data ?? [];
579
+ const invitations = invitationsQuery.data ?? [];
580
+ const hasOrgEdits = editName !== void 0 || editSlug !== void 0 || editLogo !== void 0 || Object.keys(editExtra).length > 0;
581
+ const customFields = Object.entries(schemaQuery.data ?? {}).filter(([name]) => !STANDARD_ORG_FIELDS.has(name)).map(([name, attribute]) => ({ name, attribute }));
582
+ const detailsFooter = activeTab === "details" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
583
+ /* @__PURE__ */ jsxRuntime.jsx(
584
+ designSystem.Button,
585
+ {
586
+ variant: "tertiary",
587
+ onClick: () => {
588
+ setEditName(void 0);
589
+ setEditSlug(void 0);
590
+ setEditLogo(void 0);
591
+ setEditExtra({});
592
+ },
593
+ children: "Discard"
594
+ }
595
+ ),
596
+ /* @__PURE__ */ jsxRuntime.jsx(
597
+ designSystem.Button,
598
+ {
599
+ disabled: !hasOrgEdits,
600
+ loading: updateOrgMutation.isLoading,
601
+ onClick: () => updateOrgMutation.mutate(),
602
+ "data-testid": "save-org-btn",
603
+ children: "Save changes"
604
+ }
605
+ )
606
+ ] }) : void 0;
607
+ return /* @__PURE__ */ jsxRuntime.jsxs(
608
+ useModelSchema.Drawer,
609
+ {
610
+ title: org?.name ?? "Organization",
611
+ footer: detailsFooter,
612
+ onClose,
613
+ "data-testid": "org-detail-drawer",
614
+ children: [
615
+ orgQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", children: "Loading…" }) : orgQuery.isError ? /* @__PURE__ */ jsxRuntime.jsx(
616
+ designSystem.Alert,
617
+ {
618
+ closeLabel: "Close",
619
+ title: "Failed to load organization",
620
+ variant: "danger",
621
+ children: orgQuery.error instanceof Error ? orgQuery.error.message : "An error occurred"
622
+ }
623
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Root, { defaultValue: "details", onValueChange: setActiveTab, children: [
624
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.List, { children: [
625
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "details", children: "Details" }),
626
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Trigger, { value: "members", "data-testid": "org-members-tab", children: [
627
+ "Members (",
628
+ members.length,
629
+ ")"
630
+ ] }),
631
+ teamsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Trigger, { value: "teams", children: [
632
+ "Teams (",
633
+ teams.length,
634
+ ")"
635
+ ] }),
636
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Trigger, { value: "invitations", children: [
637
+ "Invitations (",
638
+ invitations.length,
639
+ ")"
640
+ ] }),
641
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Trigger, { value: "sso", children: [
642
+ "SSO (",
643
+ ssoProviders.length,
644
+ ")"
645
+ ] })
646
+ ] }),
647
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "details", children: /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.EditLayout, { children: [
648
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, children: [
649
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
650
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Details" }),
651
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { width: "100%" }, children: [
652
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Name" }),
653
+ /* @__PURE__ */ jsxRuntime.jsx(
654
+ designSystem.TextInput,
655
+ {
656
+ value: editName ?? org?.name ?? "",
657
+ onChange: (e) => setEditName(e.target.value),
658
+ "data-testid": "org-name-input"
659
+ }
660
+ )
661
+ ] }),
662
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { hint: "Used in URLs", style: { width: "100%" }, children: [
663
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Slug" }),
664
+ /* @__PURE__ */ jsxRuntime.jsx(
665
+ designSystem.TextInput,
666
+ {
667
+ value: editSlug ?? org?.slug ?? "",
668
+ onChange: (e) => setEditSlug(e.target.value),
669
+ "data-testid": "org-slug-input"
670
+ }
671
+ ),
672
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
673
+ ] }),
674
+ /* @__PURE__ */ jsxRuntime.jsx(
675
+ useModelSchema.MediaPickerField,
676
+ {
677
+ label: "Logo URL",
678
+ name: "logo",
679
+ value: editLogo ?? org?.logo ?? "",
680
+ onChange: setEditLogo,
681
+ placeholder: "https://example.com/logo.png",
682
+ hint: "Optional — publicly accessible image URL"
683
+ }
684
+ )
685
+ ] }),
686
+ /* @__PURE__ */ jsxRuntime.jsx(
687
+ useModelSchema.CustomFieldsSection,
688
+ {
689
+ fields: customFields,
690
+ data: extraData,
691
+ onChange: handleExtraChange
692
+ }
693
+ )
694
+ ] }),
695
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.EditSidebar, { children: [
696
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
697
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Details" }),
698
+ /* @__PURE__ */ jsxRuntime.jsxs(
699
+ "div",
700
+ {
701
+ style: {
702
+ display: "grid",
703
+ gridTemplateColumns: "1fr 1fr",
704
+ gap: 12
705
+ },
706
+ children: [
707
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.MetaItem, { style: { gridColumn: "1 / -1" }, children: [
708
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaKey, { children: "Organization ID" }),
709
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MonoChip, { children: org?.id })
710
+ ] }),
711
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.MetaItem, { children: [
712
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaKey, { children: "Members" }),
713
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaVal, { children: org?.memberCount ?? 0 })
714
+ ] }),
715
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.MetaItem, { children: [
716
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaKey, { children: "Created" }),
717
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaVal, { children: org?.createdAt ? new Date(org.createdAt).toLocaleDateString() : "—" })
718
+ ] })
719
+ ]
720
+ }
721
+ )
722
+ ] }),
723
+ /* @__PURE__ */ jsxRuntime.jsx(
724
+ useModelSchema.EditViewSidePanels,
725
+ {
726
+ model: "plugin::better-auth.organization",
727
+ documentId: strapiOrgQuery.data?.documentId,
728
+ document: strapiOrgQuery.data ?? void 0
729
+ }
730
+ )
731
+ ] })
732
+ ] }) }),
733
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "members", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, paddingTop: 6, children: [
734
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
735
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Add member" }),
736
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 3, children: [
737
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 8, children: /* @__PURE__ */ jsxRuntime.jsx(
738
+ UserCombobox,
739
+ {
740
+ label: "User",
741
+ value: addUserId,
742
+ onChange: setAddUserId
743
+ }
744
+ ) }),
745
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
746
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Role" }),
747
+ /* @__PURE__ */ jsxRuntime.jsxs(
748
+ designSystem.SingleSelect,
749
+ {
750
+ value: addRole,
751
+ onChange: (role) => setAddRole(String(role)),
752
+ "aria-label": "Member role",
753
+ children: [
754
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "member", children: "Member" }),
755
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "admin", children: "Admin" }),
756
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "owner", children: "Owner" })
757
+ ]
758
+ }
759
+ )
760
+ ] }) })
761
+ ] }),
762
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
763
+ designSystem.Button,
764
+ {
765
+ size: "S",
766
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
767
+ disabled: !addUserId,
768
+ loading: addMemberMutation.isLoading,
769
+ onClick: () => addMemberMutation.mutate(),
770
+ "data-testid": "add-member-btn",
771
+ children: "Add member"
772
+ }
773
+ ) })
774
+ ] }),
775
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
776
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.SectionLabel, { children: [
777
+ "Members (",
778
+ members.length,
779
+ ")"
780
+ ] }),
781
+ membersQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", children: "Loading…" }) : members.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "No members yet." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: members.map((member) => /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.AccountRow, { "data-testid": "member-row", children: [
782
+ /* @__PURE__ */ jsxRuntime.jsxs(
783
+ designSystem.Flex,
784
+ {
785
+ direction: "column",
786
+ gap: 1,
787
+ alignItems: "flex-start",
788
+ style: { flex: 1 },
789
+ children: [
790
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: member.user?.name ?? "Unknown" }),
791
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: member.user?.email })
792
+ ]
793
+ }
794
+ ),
795
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
796
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { style: { width: 110 }, children: /* @__PURE__ */ jsxRuntime.jsxs(
797
+ designSystem.SingleSelect,
798
+ {
799
+ value: member.role,
800
+ onChange: (role) => updateRoleMutation.mutate({
801
+ memberId: member.id,
802
+ role: String(role)
803
+ }),
804
+ size: "S",
805
+ "aria-label": "Member role",
806
+ children: [
807
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "member", children: "Member" }),
808
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "admin", children: "Admin" }),
809
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "owner", children: "Owner" })
810
+ ]
811
+ }
812
+ ) }),
813
+ /* @__PURE__ */ jsxRuntime.jsx(
814
+ designSystem.IconButton,
815
+ {
816
+ label: "Remove member",
817
+ onClick: () => setConfirmRemoveMemberId(member.id),
818
+ "data-testid": "remove-member-btn",
819
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
820
+ }
821
+ )
822
+ ] })
823
+ ] }, member.id)) })
824
+ ] })
825
+ ] }) }),
826
+ teamsEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "teams", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, paddingTop: 6, children: [
827
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
828
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Create team" }),
829
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", children: [
830
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
831
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Team name" }),
832
+ /* @__PURE__ */ jsxRuntime.jsx(
833
+ designSystem.TextInput,
834
+ {
835
+ value: newTeamName,
836
+ onChange: (e) => setNewTeamName(e.target.value),
837
+ placeholder: "e.g. Engineering"
838
+ }
839
+ )
840
+ ] }) }),
841
+ /* @__PURE__ */ jsxRuntime.jsx(
842
+ designSystem.Button,
843
+ {
844
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
845
+ disabled: !newTeamName,
846
+ loading: createTeamMutation.isLoading,
847
+ onClick: () => createTeamMutation.mutate(),
848
+ children: "Create"
849
+ }
850
+ )
851
+ ] })
852
+ ] }),
853
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
854
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.SectionLabel, { children: [
855
+ "Teams (",
856
+ teams.length,
857
+ ")"
858
+ ] }),
859
+ teamsQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", children: "Loading…" }) : teams.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "No teams yet." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: teams.map((team) => /* @__PURE__ */ jsxRuntime.jsx(
860
+ TeamRow,
861
+ {
862
+ team,
863
+ organizationId,
864
+ onDelete: () => setConfirmDeleteTeamId(team.id)
865
+ },
866
+ team.id
867
+ )) })
868
+ ] })
869
+ ] }) }),
870
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "sso", children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 5, paddingTop: 6, children: /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
871
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "SSO providers" }),
872
+ ssoQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", children: "Loading…" }) : ssoProviders.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "No SSO providers configured." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: ssoProviders.map((provider) => /* @__PURE__ */ jsxRuntime.jsxs(
873
+ useModelSchema.AccountRow,
874
+ {
875
+ "data-testid": "sso-provider-row",
876
+ children: [
877
+ /* @__PURE__ */ jsxRuntime.jsxs(
878
+ designSystem.Flex,
879
+ {
880
+ direction: "column",
881
+ gap: 1,
882
+ alignItems: "flex-start",
883
+ style: { flex: 1 },
884
+ children: [
885
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.ProviderBadge, { children: provider.providerId }),
886
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: provider.domain }),
887
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
888
+ "Issuer: ",
889
+ provider.issuer
890
+ ] })
891
+ ]
892
+ }
893
+ ),
894
+ /* @__PURE__ */ jsxRuntime.jsx(
895
+ designSystem.IconButton,
896
+ {
897
+ label: "Delete SSO provider",
898
+ onClick: () => setConfirmDeleteSsoId(provider.providerId),
899
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
900
+ }
901
+ )
902
+ ]
903
+ },
904
+ provider.id
905
+ )) })
906
+ ] }) }) }),
907
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "invitations", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, paddingTop: 6, children: [
908
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
909
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: "Invite member by email" }),
910
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 3, children: [
911
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 8, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
912
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Email address" }),
913
+ /* @__PURE__ */ jsxRuntime.jsx(
914
+ designSystem.TextInput,
915
+ {
916
+ type: "email",
917
+ value: inviteEmail,
918
+ onChange: (e) => setInviteEmail(e.target.value),
919
+ placeholder: "user@example.com"
920
+ }
921
+ )
922
+ ] }) }),
923
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
924
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Role" }),
925
+ /* @__PURE__ */ jsxRuntime.jsxs(
926
+ designSystem.SingleSelect,
927
+ {
928
+ value: inviteRole,
929
+ onChange: (v) => setInviteRole(String(v)),
930
+ "aria-label": "Invite role",
931
+ children: [
932
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "member", children: "Member" }),
933
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "admin", children: "Admin" }),
934
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SingleSelectOption, { value: "owner", children: "Owner" })
935
+ ]
936
+ }
937
+ )
938
+ ] }) })
939
+ ] }),
940
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
941
+ designSystem.Button,
942
+ {
943
+ size: "S",
944
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
945
+ disabled: !inviteEmail,
946
+ loading: inviteMemberMutation.isLoading,
947
+ onClick: () => inviteMemberMutation.mutate(),
948
+ children: "Send invitation"
949
+ }
950
+ ) })
951
+ ] }),
952
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
953
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.SectionLabel, { children: [
954
+ "Invitations (",
955
+ invitations.length,
956
+ ")"
957
+ ] }),
958
+ invitationsQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral500", children: "Loading…" }) : invitations.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "No invitations yet." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: invitations.map((inv) => {
959
+ const statusColor = {
960
+ pending: "#f59e0b",
961
+ accepted: "#5cb176",
962
+ rejected: "#d02b20",
963
+ canceled: "#8e8ea9"
964
+ };
965
+ const color = statusColor[inv.status] ?? "#8e8ea9";
966
+ const isPending = inv.status === "pending";
967
+ return /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.AccountRow, { children: [
968
+ /* @__PURE__ */ jsxRuntime.jsxs(
969
+ designSystem.Flex,
970
+ {
971
+ direction: "column",
972
+ gap: 1,
973
+ alignItems: "flex-start",
974
+ style: { flex: 1 },
975
+ children: [
976
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: inv.user?.name ?? inv.email }),
977
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
978
+ inv.email,
979
+ " · role: ",
980
+ inv.role
981
+ ] }),
982
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
983
+ "Expires",
984
+ " ",
985
+ new Date(inv.expiresAt).toLocaleDateString()
986
+ ] })
987
+ ]
988
+ }
989
+ ),
990
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
991
+ /* @__PURE__ */ jsxRuntime.jsx(
992
+ useModelSchema.MonoChip,
993
+ {
994
+ style: {
995
+ color,
996
+ borderColor: color,
997
+ background: `${color}18`
998
+ },
999
+ children: inv.status
1000
+ }
1001
+ ),
1002
+ isPending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1003
+ /* @__PURE__ */ jsxRuntime.jsx(
1004
+ designSystem.Button,
1005
+ {
1006
+ size: "S",
1007
+ variant: "secondary",
1008
+ loading: resendInvitationMutation.isLoading,
1009
+ onClick: () => resendInvitationMutation.mutate(inv.id),
1010
+ children: "Resend"
1011
+ }
1012
+ ),
1013
+ /* @__PURE__ */ jsxRuntime.jsx(
1014
+ designSystem.IconButton,
1015
+ {
1016
+ label: "Cancel invitation",
1017
+ onClick: () => setConfirmCancelInvitationId(inv.id),
1018
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1019
+ }
1020
+ )
1021
+ ] })
1022
+ ] })
1023
+ ] }, inv.id);
1024
+ }) })
1025
+ ] })
1026
+ ] }) })
1027
+ ] }),
1028
+ confirmRemoveMemberId && /* @__PURE__ */ jsxRuntime.jsx(
1029
+ useModelSchema.ConfirmDialog,
1030
+ {
1031
+ title: "Remove member",
1032
+ message: "Are you sure you want to remove this member from the organization?",
1033
+ confirmLabel: "Remove",
1034
+ loading: removeMemberMutation.isLoading,
1035
+ onConfirm: () => removeMemberMutation.mutate(confirmRemoveMemberId),
1036
+ onCancel: () => setConfirmRemoveMemberId(null)
1037
+ }
1038
+ ),
1039
+ confirmDeleteTeamId && /* @__PURE__ */ jsxRuntime.jsx(
1040
+ useModelSchema.ConfirmDialog,
1041
+ {
1042
+ title: "Delete team",
1043
+ message: "Are you sure you want to delete this team? All team members will be removed. This action cannot be undone.",
1044
+ confirmLabel: "Delete",
1045
+ loading: deleteTeamMutation.isLoading,
1046
+ onConfirm: () => deleteTeamMutation.mutate(confirmDeleteTeamId),
1047
+ onCancel: () => setConfirmDeleteTeamId(null)
1048
+ }
1049
+ ),
1050
+ confirmDeleteSsoId && /* @__PURE__ */ jsxRuntime.jsx(
1051
+ useModelSchema.ConfirmDialog,
1052
+ {
1053
+ title: "Delete SSO provider",
1054
+ message: "Are you sure you want to delete this SSO provider? Members using this provider will need to authenticate differently.",
1055
+ confirmLabel: "Delete",
1056
+ loading: deleteSsoMutation.isLoading,
1057
+ onConfirm: () => deleteSsoMutation.mutate(confirmDeleteSsoId),
1058
+ onCancel: () => setConfirmDeleteSsoId(null)
1059
+ }
1060
+ ),
1061
+ confirmCancelInvitationId && /* @__PURE__ */ jsxRuntime.jsx(
1062
+ useModelSchema.ConfirmDialog,
1063
+ {
1064
+ title: "Cancel invitation",
1065
+ message: "Are you sure you want to cancel this invitation? The invite link will no longer work.",
1066
+ confirmLabel: "Cancel invitation",
1067
+ loading: cancelInvitationMutation.isLoading,
1068
+ onConfirm: () => cancelInvitationMutation.mutate(confirmCancelInvitationId),
1069
+ onCancel: () => setConfirmCancelInvitationId(null)
1070
+ }
1071
+ )
1072
+ ]
1073
+ }
1074
+ );
1075
+ }
1076
+ function TeamRow({
1077
+ team,
1078
+ organizationId,
1079
+ onDelete
1080
+ }) {
1081
+ const qc = reactQuery.useQueryClient();
1082
+ const [expanded, setExpanded] = react.useState(false);
1083
+ const [confirmRemoveTeamMemberId, setConfirmRemoveTeamMemberId] = react.useState(null);
1084
+ const teamMembersQuery = reactQuery.useQuery({
1085
+ queryKey: ["dash-team-members", organizationId, team.id],
1086
+ queryFn: async () => {
1087
+ const result = await index.client.dash.organization[
1088
+ // @ts-expect-error
1089
+ organizationId
1090
+ ].teams[team.id].members(
1091
+ { params: { orgId: organizationId, teamId: team.id } },
1092
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1093
+ );
1094
+ if (result.error) throw new Error(result.error.message ?? "Failed");
1095
+ return result.data ?? [];
1096
+ },
1097
+ enabled: expanded
1098
+ });
1099
+ const [addUserId, setAddUserId] = react.useState("");
1100
+ const addTeamMemberMutation = reactQuery.useMutation({
1101
+ mutationFn: async () => {
1102
+ const result = await index.client.dash.organization.addTeamMember(
1103
+ { teamId: team.id, userId: addUserId },
1104
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1105
+ );
1106
+ if (result.error) throw new Error(result.error.message ?? "Failed");
1107
+ return result.data;
1108
+ },
1109
+ onSuccess: () => {
1110
+ qc.invalidateQueries({
1111
+ queryKey: ["dash-team-members", organizationId, team.id]
1112
+ });
1113
+ setAddUserId("");
1114
+ }
1115
+ });
1116
+ const removeTeamMemberMutation = reactQuery.useMutation({
1117
+ mutationFn: async (userId) => {
1118
+ const result = await index.client.dash.organization.removeTeamMember(
1119
+ { teamId: team.id, userId },
1120
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1121
+ );
1122
+ if (result.error) throw new Error(result.error.message ?? "Failed");
1123
+ },
1124
+ onSuccess: () => {
1125
+ setConfirmRemoveTeamMemberId(null);
1126
+ qc.invalidateQueries({
1127
+ queryKey: ["dash-team-members", organizationId, team.id]
1128
+ });
1129
+ }
1130
+ });
1131
+ return /* @__PURE__ */ jsxRuntime.jsxs(ExpandableRow, { "data-testid": "team-row", children: [
1132
+ /* @__PURE__ */ jsxRuntime.jsxs(ExpandableRowHeader, { onClick: () => setExpanded((e) => !e), children: [
1133
+ /* @__PURE__ */ jsxRuntime.jsxs(
1134
+ designSystem.Flex,
1135
+ {
1136
+ direction: "column",
1137
+ gap: 1,
1138
+ alignItems: "flex-start",
1139
+ style: { flex: 1 },
1140
+ children: [
1141
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: team.name }),
1142
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: [
1143
+ expanded ? "Collapse" : "Expand",
1144
+ " ·",
1145
+ " ",
1146
+ teamMembersQuery.data?.length ?? "—",
1147
+ " members"
1148
+ ] })
1149
+ ]
1150
+ }
1151
+ ),
1152
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1153
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral400", children: expanded ? "▲" : "▼" }),
1154
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { onClick: (e) => e.stopPropagation(), children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.IconButton, { label: "Delete team", onClick: onDelete, children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {}) }) })
1155
+ ] })
1156
+ ] }),
1157
+ expanded && /* @__PURE__ */ jsxRuntime.jsxs(ExpandableRowBody, { children: [
1158
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", children: [
1159
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { style: { flex: 1 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1160
+ UserCombobox,
1161
+ {
1162
+ label: "Add member to team",
1163
+ value: addUserId,
1164
+ onChange: setAddUserId
1165
+ }
1166
+ ) }),
1167
+ /* @__PURE__ */ jsxRuntime.jsx(
1168
+ designSystem.Button,
1169
+ {
1170
+ size: "S",
1171
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1172
+ disabled: !addUserId,
1173
+ loading: addTeamMemberMutation.isLoading,
1174
+ onClick: () => addTeamMemberMutation.mutate(),
1175
+ children: "Add"
1176
+ }
1177
+ )
1178
+ ] }),
1179
+ teamMembersQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "Loading…" }) : (teamMembersQuery.data ?? []).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: "No members in this team." }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: (teamMembersQuery.data ?? []).map((tm) => /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.AccountRow, { children: [
1180
+ /* @__PURE__ */ jsxRuntime.jsxs(
1181
+ designSystem.Flex,
1182
+ {
1183
+ direction: "column",
1184
+ gap: 1,
1185
+ alignItems: "flex-start",
1186
+ style: { flex: 1 },
1187
+ children: [
1188
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: tm.user?.name ?? tm.userId }),
1189
+ tm.user?.email && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: tm.user.email })
1190
+ ]
1191
+ }
1192
+ ),
1193
+ /* @__PURE__ */ jsxRuntime.jsx(
1194
+ designSystem.IconButton,
1195
+ {
1196
+ label: "Remove from team",
1197
+ onClick: () => setConfirmRemoveTeamMemberId(tm.userId),
1198
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1199
+ }
1200
+ )
1201
+ ] }, tm.id)) })
1202
+ ] }),
1203
+ confirmRemoveTeamMemberId && /* @__PURE__ */ jsxRuntime.jsx(
1204
+ useModelSchema.ConfirmDialog,
1205
+ {
1206
+ title: "Remove from team",
1207
+ message: "Are you sure you want to remove this member from the team?",
1208
+ confirmLabel: "Remove",
1209
+ loading: removeTeamMemberMutation.isLoading,
1210
+ onConfirm: () => removeTeamMemberMutation.mutate(confirmRemoveTeamMemberId),
1211
+ onCancel: () => setConfirmRemoveTeamMemberId(null)
1212
+ }
1213
+ )
1214
+ ] });
1215
+ }
1216
+ const PAGE_SIZE = 25;
1217
+ const fadeUp = styled.keyframes`
1218
+ from { opacity: 0; transform: translateY(6px); }
1219
+ to { opacity: 1; transform: translateY(0); }
1220
+ `;
1221
+ const Wrap = styled__default.default.div`
1222
+ padding: 28px 32px;
1223
+ background: #f6f6f9;
1224
+ min-height: 100%;
1225
+ display: flex;
1226
+ flex-direction: column;
1227
+ gap: 20px;
1228
+ `;
1229
+ const PageHeader = styled__default.default.div`
1230
+ display: flex;
1231
+ justify-content: space-between;
1232
+ align-items: flex-start;
1233
+ `;
1234
+ const TitleBlock = styled__default.default.div`
1235
+ display: flex;
1236
+ flex-direction: column;
1237
+ gap: 4px;
1238
+ `;
1239
+ const PageTitle = styled__default.default.h1`
1240
+ margin: 0;
1241
+ font-size: 22px;
1242
+ font-weight: 800;
1243
+ color: #32324d;
1244
+ letter-spacing: -0.03em;
1245
+ `;
1246
+ const PageSubtitle = styled__default.default.p`
1247
+ margin: 0;
1248
+ font-size: 12px;
1249
+ color: #8e8ea9;
1250
+ `;
1251
+ const TableCard = styled__default.default.div`
1252
+ background: #ffffff;
1253
+ border: 1px solid #eaeaef;
1254
+ border-radius: 10px;
1255
+ overflow: hidden;
1256
+ `;
1257
+ const Table = styled__default.default.table`
1258
+ width: 100%;
1259
+ border-collapse: collapse;
1260
+ `;
1261
+ const TH = styled__default.default.th`
1262
+ text-align: left;
1263
+ padding: 10px 14px;
1264
+ font-size: 10px;
1265
+ font-weight: 700;
1266
+ color: #8e8ea9;
1267
+ text-transform: uppercase;
1268
+ letter-spacing: 0.08em;
1269
+ border-bottom: 1px solid #eaeaef;
1270
+ background: #fafafa;
1271
+ white-space: nowrap;
1272
+ &:first-child { padding-left: 20px; }
1273
+ &:last-child { padding-right: 20px; }
1274
+ `;
1275
+ const THCheck = styled__default.default(TH)`
1276
+ width: 44px;
1277
+ `;
1278
+ const THActions = styled__default.default(TH)`
1279
+ width: 80px;
1280
+ `;
1281
+ const TD = styled__default.default.td`
1282
+ padding: 11px 14px;
1283
+ font-size: 12px;
1284
+ color: #32324d;
1285
+ border-bottom: 1px solid #f5f5f9;
1286
+ vertical-align: middle;
1287
+ &:first-child { padding-left: 20px; }
1288
+ &:last-child { padding-right: 20px; }
1289
+ `;
1290
+ const TDCheck = styled__default.default(TD)`
1291
+ width: 44px;
1292
+ `;
1293
+ const TDActions = styled__default.default(TD)`
1294
+ width: 80px;
1295
+ `;
1296
+ const TR = styled__default.default.tr`
1297
+ animation: ${fadeUp} 280ms ease both;
1298
+ animation-delay: ${(p) => (p.$i ?? 0) * 25}ms;
1299
+ background: ${(p) => p.$selected ? "#f0f0ff" : "transparent"};
1300
+ transition: background 120ms ease;
1301
+ &:hover td { background: ${(p) => p.$selected ? "#e8e8ff" : "#fafafe"}; }
1302
+ &:last-child td { border-bottom: none; }
1303
+ `;
1304
+ const SlugChip = styled__default.default.span`
1305
+ display: inline-block;
1306
+ background: #f5f5f9;
1307
+ color: #666687;
1308
+ border: 1px solid #eaeaef;
1309
+ border-radius: 6px;
1310
+ padding: 2px 6px;
1311
+ font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, monospace;
1312
+ font-size: 11px;
1313
+ `;
1314
+ const CountChip = styled__default.default.span`
1315
+ display: inline-flex;
1316
+ align-items: center;
1317
+ padding: 2px 8px;
1318
+ border-radius: 20px;
1319
+ font-size: 10px;
1320
+ font-weight: 700;
1321
+ background: #f0f0ff;
1322
+ color: #4945ff;
1323
+ `;
1324
+ const OrgName = styled__default.default.span`
1325
+ font-size: 12px;
1326
+ font-weight: 600;
1327
+ color: #32324d;
1328
+ `;
1329
+ const DateText = styled__default.default.span`
1330
+ font-size: 11px;
1331
+ color: #b8b8c7;
1332
+ font-variant-numeric: tabular-nums;
1333
+ `;
1334
+ const Toolbar = styled__default.default.div`
1335
+ display: flex;
1336
+ align-items: flex-end;
1337
+ gap: 12px;
1338
+ `;
1339
+ const OrgLogoFallback = styled__default.default.div`
1340
+ width: 30px;
1341
+ height: 30px;
1342
+ border-radius: 7px;
1343
+ background: ${(p) => p.$bg};
1344
+ color: ${(p) => p.$fg};
1345
+ display: inline-flex;
1346
+ align-items: center;
1347
+ justify-content: center;
1348
+ font-size: 11px;
1349
+ font-weight: 700;
1350
+ flex-shrink: 0;
1351
+ user-select: none;
1352
+ `;
1353
+ const ORG_COLORS = [
1354
+ { bg: "#EAF5FF", fg: "#0C75AF" },
1355
+ { bg: "#F0F0FF", fg: "#4945FF" },
1356
+ { bg: "#EAFBE7", fg: "#328048" },
1357
+ { bg: "#FFF3D3", fg: "#8E6A00" },
1358
+ { bg: "#FCECEA", fg: "#D02B20" },
1359
+ { bg: "#F6ECFC", fg: "#8312D1" }
1360
+ ];
1361
+ function OrgAvatar({ name, logo }) {
1362
+ if (logo) {
1363
+ return /* @__PURE__ */ jsxRuntime.jsx(
1364
+ designSystem.Box,
1365
+ {
1366
+ tag: "img",
1367
+ src: logo,
1368
+ alt: "",
1369
+ style: {
1370
+ width: 30,
1371
+ height: 30,
1372
+ borderRadius: 7,
1373
+ objectFit: "cover",
1374
+ flexShrink: 0
1375
+ }
1376
+ }
1377
+ );
1378
+ }
1379
+ const color = ORG_COLORS[(name.charCodeAt(0) || 0) % ORG_COLORS.length];
1380
+ const initials = name.slice(0, 2).toUpperCase();
1381
+ return /* @__PURE__ */ jsxRuntime.jsx(OrgLogoFallback, { $bg: color.bg, $fg: color.fg, children: initials });
1382
+ }
1383
+ function OrganizationsPage({ teamsEnabled }) {
1384
+ const rbac = admin.useRBAC(index.PERMISSIONS.organization);
1385
+ const qc = reactQuery.useQueryClient();
1386
+ const { toggleNotification } = admin.useNotification();
1387
+ const [page, setPage] = react.useState(1);
1388
+ const [search, setSearch] = react.useState("");
1389
+ const [searchInput, setSearchInput] = react.useState("");
1390
+ const [selected, setSelected] = react.useState(/* @__PURE__ */ new Set());
1391
+ const [showCreate, setShowCreate] = react.useState(false);
1392
+ const [detailOrgId, setDetailOrgId] = react.useState(null);
1393
+ const [confirmDelete, setConfirmDelete] = react.useState(null);
1394
+ const [confirmDeleteMany, setConfirmDeleteMany] = react.useState(false);
1395
+ const offset = (page - 1) * PAGE_SIZE;
1396
+ const orgsQuery = reactQuery.useQuery({
1397
+ queryKey: ["dash-organizations", page, search],
1398
+ queryFn: async () => {
1399
+ const result = await index.client.dash.listOrganizations({
1400
+ query: {
1401
+ limit: PAGE_SIZE,
1402
+ offset,
1403
+ sortBy: "createdAt",
1404
+ sortOrder: "desc",
1405
+ ...search ? { search } : {}
1406
+ }
1407
+ });
1408
+ if (result.error)
1409
+ throw new Error(result.error.message ?? "Failed to load organizations");
1410
+ return result.data;
1411
+ },
1412
+ keepPreviousData: true
1413
+ });
1414
+ const deleteMutation = reactQuery.useMutation({
1415
+ mutationFn: async (organizationId) => {
1416
+ const result = await index.client.dash.organization.delete(
1417
+ { organizationId },
1418
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1419
+ );
1420
+ if (result.error)
1421
+ throw new Error(result.error.message ?? "Delete failed");
1422
+ },
1423
+ onSuccess: () => {
1424
+ setConfirmDelete(null);
1425
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
1426
+ toggleNotification({ type: "success", message: "Organization deleted" });
1427
+ },
1428
+ onError: (err) => {
1429
+ toggleNotification({
1430
+ type: "danger",
1431
+ message: err.message ?? "Failed to delete organization"
1432
+ });
1433
+ }
1434
+ });
1435
+ const deleteManyMutation = reactQuery.useMutation({
1436
+ mutationFn: async (organizationIds) => {
1437
+ const result = await index.client.dash.organization.deleteMany(
1438
+ {},
1439
+ useModelSchema.withContext({ organizationIds }, index.getAuthHeaders())
1440
+ );
1441
+ if (result.error)
1442
+ throw new Error(result.error.message ?? "Delete failed");
1443
+ return result.data;
1444
+ },
1445
+ onSuccess: (_data, organizationIds) => {
1446
+ setConfirmDeleteMany(false);
1447
+ setSelected(/* @__PURE__ */ new Set());
1448
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
1449
+ toggleNotification({
1450
+ type: "success",
1451
+ message: `${organizationIds.length} organization${organizationIds.length !== 1 ? "s" : ""} deleted`
1452
+ });
1453
+ },
1454
+ onError: (err) => {
1455
+ toggleNotification({
1456
+ type: "danger",
1457
+ message: err.message ?? "Failed to delete organizations"
1458
+ });
1459
+ }
1460
+ });
1461
+ const orgs = orgsQuery.data && "organizations" in orgsQuery.data ? orgsQuery.data.organizations : [];
1462
+ const total = orgsQuery.data && "total" in orgsQuery.data ? orgsQuery.data.total : 0;
1463
+ const pageCount = Math.ceil(total / PAGE_SIZE);
1464
+ const allSelected = orgs.length > 0 && orgs.every((o) => selected.has(o.id));
1465
+ const someSelected = selected.size > 0;
1466
+ const toggleSelect = (id) => {
1467
+ setSelected((prev) => {
1468
+ const next = new Set(prev);
1469
+ if (next.has(id)) next.delete(id);
1470
+ else next.add(id);
1471
+ return next;
1472
+ });
1473
+ };
1474
+ const handleSelectAll = () => {
1475
+ if (allSelected) setSelected(/* @__PURE__ */ new Set());
1476
+ else setSelected(new Set(orgs.map((o) => o.id)));
1477
+ };
1478
+ const handleSearch = (e) => {
1479
+ e.preventDefault();
1480
+ setSearch(searchInput);
1481
+ setPage(1);
1482
+ };
1483
+ return /* @__PURE__ */ jsxRuntime.jsxs(Wrap, { "data-testid": "organizations-page", children: [
1484
+ /* @__PURE__ */ jsxRuntime.jsxs(PageHeader, { children: [
1485
+ /* @__PURE__ */ jsxRuntime.jsxs(TitleBlock, { children: [
1486
+ /* @__PURE__ */ jsxRuntime.jsx(PageTitle, { children: "Organizations" }),
1487
+ /* @__PURE__ */ jsxRuntime.jsxs(PageSubtitle, { children: [
1488
+ total.toLocaleString(),
1489
+ " total"
1490
+ ] })
1491
+ ] }),
1492
+ rbac.allowedActions.canCreate && /* @__PURE__ */ jsxRuntime.jsx(
1493
+ designSystem.Button,
1494
+ {
1495
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1496
+ onClick: () => setShowCreate(true),
1497
+ "data-testid": "create-org-btn",
1498
+ children: "Create organization"
1499
+ }
1500
+ )
1501
+ ] }),
1502
+ /* @__PURE__ */ jsxRuntime.jsxs(Toolbar, { children: [
1503
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SearchForm, { onSubmit: handleSearch, children: /* @__PURE__ */ jsxRuntime.jsx(
1504
+ designSystem.Searchbar,
1505
+ {
1506
+ clearLabel: "Clear",
1507
+ name: "search",
1508
+ placeholder: "Search organizations…",
1509
+ value: searchInput,
1510
+ onChange: (e) => setSearchInput(e.target.value),
1511
+ onClear: () => {
1512
+ setSearchInput("");
1513
+ setSearch("");
1514
+ setPage(1);
1515
+ },
1516
+ children: "Search organizations"
1517
+ }
1518
+ ) }),
1519
+ someSelected && rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsxs(
1520
+ designSystem.Button,
1521
+ {
1522
+ variant: "danger-light",
1523
+ size: "S",
1524
+ onClick: () => setConfirmDeleteMany(true),
1525
+ "data-testid": "delete-selected-orgs-btn",
1526
+ children: [
1527
+ "Delete ",
1528
+ selected.size,
1529
+ " selected"
1530
+ ]
1531
+ }
1532
+ )
1533
+ ] }),
1534
+ orgsQuery.isError && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#d02b20", fontSize: 12, padding: "8px 0" }, children: orgsQuery.error instanceof Error ? orgsQuery.error.message : "An error occurred" }),
1535
+ /* @__PURE__ */ jsxRuntime.jsx(TableCard, { children: orgsQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: "Loading organizations…" }) }) : /* @__PURE__ */ jsxRuntime.jsxs(Table, { children: [
1536
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { children: [
1537
+ rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsx(THCheck, { children: /* @__PURE__ */ jsxRuntime.jsx(
1538
+ designSystem.Checkbox,
1539
+ {
1540
+ checked: allSelected,
1541
+ onCheckedChange: handleSelectAll,
1542
+ "aria-label": "Select all"
1543
+ }
1544
+ ) }),
1545
+ /* @__PURE__ */ jsxRuntime.jsx(TH, { children: "Name" }),
1546
+ /* @__PURE__ */ jsxRuntime.jsx(TH, { children: "Slug" }),
1547
+ /* @__PURE__ */ jsxRuntime.jsx(TH, { children: "Members" }),
1548
+ /* @__PURE__ */ jsxRuntime.jsx(TH, { children: "Created" }),
1549
+ /* @__PURE__ */ jsxRuntime.jsx(THActions, {})
1550
+ ] }) }),
1551
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: orgs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
1552
+ TD,
1553
+ {
1554
+ colSpan: 6,
1555
+ style: {
1556
+ textAlign: "center",
1557
+ padding: "40px",
1558
+ color: "#8e8ea9"
1559
+ },
1560
+ "data-testid": "orgs-empty",
1561
+ children: search ? `No organizations matching "${search}"` : "No organizations found"
1562
+ }
1563
+ ) }) : orgs.map((org, i) => /* @__PURE__ */ jsxRuntime.jsxs(
1564
+ TR,
1565
+ {
1566
+ $selected: selected.has(org.id),
1567
+ $i: i,
1568
+ "data-testid": "org-row",
1569
+ children: [
1570
+ rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsx(TDCheck, { children: /* @__PURE__ */ jsxRuntime.jsx(
1571
+ designSystem.Checkbox,
1572
+ {
1573
+ checked: selected.has(org.id),
1574
+ onCheckedChange: () => toggleSelect(org.id),
1575
+ "aria-label": `Select ${org.name}`
1576
+ }
1577
+ ) }),
1578
+ /* @__PURE__ */ jsxRuntime.jsx(TD, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
1579
+ /* @__PURE__ */ jsxRuntime.jsx(OrgAvatar, { name: org.name, logo: org.logo }),
1580
+ /* @__PURE__ */ jsxRuntime.jsx(OrgName, { children: org.name })
1581
+ ] }) }),
1582
+ /* @__PURE__ */ jsxRuntime.jsx(TD, { children: /* @__PURE__ */ jsxRuntime.jsx(SlugChip, { children: org.slug }) }),
1583
+ /* @__PURE__ */ jsxRuntime.jsx(TD, { children: /* @__PURE__ */ jsxRuntime.jsx(CountChip, { children: org.memberCount }) }),
1584
+ /* @__PURE__ */ jsxRuntime.jsx(TD, { children: /* @__PURE__ */ jsxRuntime.jsx(DateText, { children: new Date(org.createdAt).toLocaleDateString() }) }),
1585
+ /* @__PURE__ */ jsxRuntime.jsx(TDActions, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, justifyContent: "flex-end", children: [
1586
+ rbac.allowedActions.canUpdate && /* @__PURE__ */ jsxRuntime.jsx(
1587
+ designSystem.IconButton,
1588
+ {
1589
+ label: "Edit organization",
1590
+ onClick: () => setDetailOrgId(org.id),
1591
+ "data-testid": "edit-org-btn",
1592
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
1593
+ }
1594
+ ),
1595
+ rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsx(
1596
+ designSystem.IconButton,
1597
+ {
1598
+ label: "Delete organization",
1599
+ onClick: () => setConfirmDelete(org.id),
1600
+ "data-testid": "delete-org-btn",
1601
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1602
+ }
1603
+ )
1604
+ ] }) })
1605
+ ]
1606
+ },
1607
+ org.id
1608
+ )) })
1609
+ ] }) }),
1610
+ pageCount > 1 && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "flex-end", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
1611
+ /* @__PURE__ */ jsxRuntime.jsx(
1612
+ designSystem.Button,
1613
+ {
1614
+ variant: "tertiary",
1615
+ size: "S",
1616
+ disabled: page === 1,
1617
+ onClick: () => setPage((p) => p - 1),
1618
+ children: "Previous"
1619
+ }
1620
+ ),
1621
+ /* @__PURE__ */ jsxRuntime.jsx(
1622
+ designSystem.Button,
1623
+ {
1624
+ variant: "tertiary",
1625
+ size: "S",
1626
+ disabled: page >= pageCount,
1627
+ onClick: () => setPage((p) => p + 1),
1628
+ children: "Next"
1629
+ }
1630
+ )
1631
+ ] }) }),
1632
+ rbac.allowedActions.canCreate && showCreate && /* @__PURE__ */ jsxRuntime.jsx(
1633
+ CreateOrganizationDialog,
1634
+ {
1635
+ teamsEnabled,
1636
+ onClose: () => setShowCreate(false)
1637
+ }
1638
+ ),
1639
+ rbac.allowedActions.canUpdate && detailOrgId && /* @__PURE__ */ jsxRuntime.jsx(
1640
+ OrganizationDetail,
1641
+ {
1642
+ organizationId: detailOrgId,
1643
+ teamsEnabled,
1644
+ onClose: () => setDetailOrgId(null)
1645
+ }
1646
+ ),
1647
+ rbac.allowedActions.canDelete && confirmDelete && /* @__PURE__ */ jsxRuntime.jsx(
1648
+ useModelSchema.ConfirmDialog,
1649
+ {
1650
+ title: "Delete organization",
1651
+ message: "Are you sure you want to delete this organization? All members and teams will be removed. This action cannot be undone.",
1652
+ confirmLabel: "Delete",
1653
+ loading: deleteMutation.isLoading,
1654
+ onConfirm: () => deleteMutation.mutate(confirmDelete),
1655
+ onCancel: () => setConfirmDelete(null)
1656
+ }
1657
+ ),
1658
+ rbac.allowedActions.canDelete && confirmDeleteMany && /* @__PURE__ */ jsxRuntime.jsx(
1659
+ useModelSchema.ConfirmDialog,
1660
+ {
1661
+ title: `Delete ${selected.size} organization${selected.size !== 1 ? "s" : ""}`,
1662
+ message: `Are you sure you want to delete ${selected.size} organization${selected.size !== 1 ? "s" : ""}? This action cannot be undone.`,
1663
+ confirmLabel: "Delete all",
1664
+ loading: deleteManyMutation.isLoading,
1665
+ onConfirm: () => deleteManyMutation.mutate([...selected]),
1666
+ onCancel: () => setConfirmDeleteMany(false)
1667
+ }
1668
+ )
1669
+ ] });
1670
+ }
1671
+ function ProtectedOrganizationsPage() {
1672
+ const { data: organizationOptions } = index.useOrganizationOptions();
1673
+ const teamsEnabled = organizationOptions?.teamsEnabled ?? false;
1674
+ return /* @__PURE__ */ jsxRuntime.jsx(
1675
+ admin.Page.Protect,
1676
+ {
1677
+ permissions: index.PERMISSIONS.organization.filter(
1678
+ (p) => p.action === `plugin::${index.PLUGIN_ID}.organization.read`
1679
+ ),
1680
+ children: /* @__PURE__ */ jsxRuntime.jsx(OrganizationsPage, { teamsEnabled })
1681
+ }
1682
+ );
1683
+ }
1684
+ exports.OrganizationsPage = OrganizationsPage;
1685
+ exports.default = ProtectedOrganizationsPage;