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

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.
Files changed (45) hide show
  1. package/dist/admin/Avatar-CdT7eBCh.js +39 -0
  2. package/dist/admin/Avatar-zlMTAZh3.mjs +40 -0
  3. package/dist/admin/{Root-2FMy-qQg.mjs → Root-CgVBJuoM.mjs} +12 -4
  4. package/dist/admin/{Root-DDi0mtve.js → Root-CsiOnzlB.js} +12 -4
  5. package/dist/admin/cs-C_jSgeON.js +416 -0
  6. package/dist/admin/cs-wtnknvBC.mjs +416 -0
  7. package/dist/admin/de-Cg5HyoCY.js +416 -0
  8. package/dist/admin/de-DnnVdUxk.mjs +416 -0
  9. package/dist/admin/en-B3utEgMT.mjs +416 -0
  10. package/dist/admin/en-Cz1IJEvp.js +416 -0
  11. package/dist/admin/es-CGA0Tslt.js +416 -0
  12. package/dist/admin/es-RoCEriJL.mjs +416 -0
  13. package/dist/admin/fr-Ca74t21t.js +416 -0
  14. package/dist/admin/fr-CtmuSfiQ.mjs +416 -0
  15. package/dist/admin/{index-BHfYu0uA.js → index-BCBu-0nr.js} +65 -10
  16. package/dist/admin/{index-D9CiI8Yr.mjs → index-BI2WIsds.mjs} +73 -18
  17. package/dist/admin/index-CPSoAbZt.js +2321 -0
  18. package/dist/admin/index-D9ttdrT3.js +2716 -0
  19. package/dist/admin/index-DQdn5s8q.js +1184 -0
  20. package/dist/admin/index-DvfwvCj0.mjs +1182 -0
  21. package/dist/admin/index-VyHXoQ6p.mjs +2321 -0
  22. package/dist/admin/index-gFqHlMLP.mjs +2716 -0
  23. package/dist/admin/index.js +1 -1
  24. package/dist/admin/index.mjs +1 -1
  25. package/dist/admin/it-BAB9hDdp.mjs +416 -0
  26. package/dist/admin/it-dYT2_ERV.js +416 -0
  27. package/dist/admin/nl-CVqSbUU9.js +416 -0
  28. package/dist/admin/nl-CYv26PHv.mjs +416 -0
  29. package/dist/admin/pl-Bc0cS2mD.mjs +416 -0
  30. package/dist/admin/pl-HYF4BxRv.js +416 -0
  31. package/dist/admin/pt-DDUGyLtb.js +416 -0
  32. package/dist/admin/pt-DZyyK3EG.mjs +416 -0
  33. package/dist/admin/{useModelSchema-CqOTI7P1.js → useModelSchema-BogQah8f.js} +309 -287
  34. package/dist/admin/{useModelSchema-B2tmzlwQ.mjs → useModelSchema-C6bxNjW4.mjs} +320 -298
  35. package/dist/server/index.js +15 -15
  36. package/dist/server/index.mjs +15 -15
  37. package/package.json +4 -4
  38. package/dist/admin/Avatar-Cvk89aMx.js +0 -67
  39. package/dist/admin/Avatar-DNczJU0i.mjs +0 -66
  40. package/dist/admin/index-BQi_BUi6.mjs +0 -1968
  41. package/dist/admin/index-BV-4M_Fp.js +0 -1970
  42. package/dist/admin/index-CANDPUGD.mjs +0 -1215
  43. package/dist/admin/index-CqR8WpOa.js +0 -1217
  44. package/dist/admin/index-D6ktNZcj.js +0 -1685
  45. package/dist/admin/index-DszX3ibA.mjs +0 -1683
@@ -0,0 +1,2321 @@
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 reactIntl = require("react-intl");
9
+ const reactQuery = require("react-query");
10
+ const index = require("./index-BCBu-0nr.js");
11
+ const Avatar = require("./Avatar-CdT7eBCh.js");
12
+ const useModelSchema = require("./useModelSchema-BogQah8f.js");
13
+ function UserCombobox({
14
+ label,
15
+ hint,
16
+ placeholder,
17
+ value,
18
+ onChange,
19
+ error,
20
+ required
21
+ }) {
22
+ const { formatMessage } = reactIntl.useIntl();
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 ?? formatMessage({
57
+ id: index.getTranslation("components.userCombobox.label"),
58
+ defaultMessage: "User"
59
+ }) }),
60
+ /* @__PURE__ */ jsxRuntime.jsx(
61
+ designSystem.Combobox,
62
+ {
63
+ placeholder: placeholder ?? formatMessage({
64
+ id: index.getTranslation("components.userCombobox.placeholder"),
65
+ defaultMessage: "Search users by email or name…"
66
+ }),
67
+ value,
68
+ onChange: (val) => onChange(val),
69
+ onInputChange: (e) => setSearch(e.target.value),
70
+ loading: isLoading,
71
+ required,
72
+ children: users.map((user) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.ComboboxOption, { value: user.id, children: [
73
+ user.name,
74
+ " — ",
75
+ user.email
76
+ ] }, user.id))
77
+ }
78
+ ),
79
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {}),
80
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Error, {})
81
+ ] });
82
+ }
83
+ function CreateOrganizationDialog({ teamsEnabled, onClose }) {
84
+ const { formatMessage } = reactIntl.useIntl();
85
+ const qc = reactQuery.useQueryClient();
86
+ const { toggleNotification } = admin.useNotification();
87
+ const [name, setName] = react.useState("");
88
+ const [slug, setSlug] = react.useState("");
89
+ const [logo, setLogo] = react.useState("");
90
+ const [ownerId, setOwnerId] = react.useState("");
91
+ const [skipDefaultTeam, setSkipDefaultTeam] = react.useState(false);
92
+ const [defaultTeamName, setDefaultTeamName] = react.useState("");
93
+ const createMutation = reactQuery.useMutation({
94
+ mutationFn: async () => {
95
+ const result = await index.client.dash.organization.create(
96
+ {
97
+ name,
98
+ slug,
99
+ ...logo ? { logo } : {},
100
+ ...teamsEnabled && !skipDefaultTeam && defaultTeamName ? { defaultTeamName } : {}
101
+ },
102
+ useModelSchema.withContext({ userId: ownerId, skipDefaultTeam }, index.getAuthHeaders())
103
+ );
104
+ if (result.error)
105
+ throw new Error(
106
+ result.error.message ?? formatMessage({
107
+ id: index.getTranslation("organizations.create.error.createFailed"),
108
+ defaultMessage: "Create failed"
109
+ })
110
+ );
111
+ return result.data;
112
+ },
113
+ onSuccess: () => {
114
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
115
+ toggleNotification({
116
+ type: "success",
117
+ message: formatMessage({
118
+ id: index.getTranslation("organizations.create.notification.success"),
119
+ defaultMessage: "Organization created successfully"
120
+ })
121
+ });
122
+ onClose();
123
+ },
124
+ onError: (err) => {
125
+ toggleNotification({
126
+ type: "danger",
127
+ message: err.message ?? formatMessage({
128
+ id: index.getTranslation("organizations.create.notification.error"),
129
+ defaultMessage: "Failed to create organization"
130
+ })
131
+ });
132
+ }
133
+ });
134
+ const handleNameChange = (e) => {
135
+ const val = e.target.value;
136
+ setName(val);
137
+ if (!slug || slug === slugify(name)) {
138
+ setSlug(slugify(val));
139
+ }
140
+ };
141
+ const footer = /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
142
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Button, { variant: "tertiary", onClick: onClose, children: formatMessage({
143
+ id: index.getTranslation("organizations.common.cancel"),
144
+ defaultMessage: "Cancel"
145
+ }) }),
146
+ /* @__PURE__ */ jsxRuntime.jsx(
147
+ designSystem.Button,
148
+ {
149
+ loading: createMutation.isLoading,
150
+ disabled: !name || !slug || !ownerId,
151
+ onClick: () => createMutation.mutate(),
152
+ "data-testid": "create-org-submit",
153
+ children: formatMessage({
154
+ id: index.getTranslation("organizations.common.createOrganization"),
155
+ defaultMessage: "Create organization"
156
+ })
157
+ }
158
+ )
159
+ ] });
160
+ return /* @__PURE__ */ jsxRuntime.jsx(
161
+ useModelSchema.SheetDialog,
162
+ {
163
+ title: formatMessage({
164
+ id: index.getTranslation("organizations.common.createOrganization"),
165
+ defaultMessage: "Create organization"
166
+ }),
167
+ actionButtons: footer,
168
+ onClose,
169
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, children: [
170
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
171
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
172
+ id: index.getTranslation("organizations.common.details"),
173
+ defaultMessage: "Details"
174
+ }) }),
175
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { width: "100%" }, required: true, children: [
176
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
177
+ id: index.getTranslation("organizations.common.name"),
178
+ defaultMessage: "Name"
179
+ }) }),
180
+ /* @__PURE__ */ jsxRuntime.jsx(
181
+ designSystem.TextInput,
182
+ {
183
+ name: "name",
184
+ value: name,
185
+ onChange: handleNameChange,
186
+ placeholder: formatMessage({
187
+ id: index.getTranslation("organizations.create.name.placeholder"),
188
+ defaultMessage: "Acme Corp"
189
+ })
190
+ }
191
+ )
192
+ ] }),
193
+ /* @__PURE__ */ jsxRuntime.jsxs(
194
+ designSystem.Field.Root,
195
+ {
196
+ style: { width: "100%" },
197
+ hint: formatMessage({
198
+ id: index.getTranslation("organizations.common.slugHint"),
199
+ defaultMessage: "Used in URLs"
200
+ }),
201
+ required: true,
202
+ children: [
203
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
204
+ id: index.getTranslation("organizations.common.slug"),
205
+ defaultMessage: "Slug"
206
+ }) }),
207
+ /* @__PURE__ */ jsxRuntime.jsx(
208
+ designSystem.TextInput,
209
+ {
210
+ name: "slug",
211
+ value: slug,
212
+ onChange: (e) => setSlug(e.target.value),
213
+ placeholder: formatMessage({
214
+ id: index.getTranslation("organizations.create.slug.placeholder"),
215
+ defaultMessage: "acme-corp"
216
+ })
217
+ }
218
+ ),
219
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
220
+ ]
221
+ }
222
+ ),
223
+ /* @__PURE__ */ jsxRuntime.jsx(
224
+ useModelSchema.MediaPickerField,
225
+ {
226
+ label: formatMessage({
227
+ id: index.getTranslation("organizations.common.logo.label"),
228
+ defaultMessage: "Logo URL"
229
+ }),
230
+ name: "logo",
231
+ value: logo,
232
+ onChange: setLogo,
233
+ placeholder: formatMessage({
234
+ id: index.getTranslation("organizations.common.logo.placeholder"),
235
+ defaultMessage: "https://example.com/logo.png"
236
+ }),
237
+ hint: formatMessage({
238
+ id: index.getTranslation("organizations.common.logo.hint"),
239
+ defaultMessage: "Optional — publicly accessible image URL"
240
+ })
241
+ }
242
+ )
243
+ ] }),
244
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
245
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
246
+ id: index.getTranslation("organizations.create.owner.label"),
247
+ defaultMessage: "Owner"
248
+ }) }),
249
+ /* @__PURE__ */ jsxRuntime.jsx(
250
+ UserCombobox,
251
+ {
252
+ label: formatMessage({
253
+ id: index.getTranslation("organizations.create.owner.label"),
254
+ defaultMessage: "Owner"
255
+ }),
256
+ hint: formatMessage({
257
+ id: index.getTranslation("organizations.create.owner.hint"),
258
+ defaultMessage: "The user who will own and manage this organization"
259
+ }),
260
+ value: ownerId,
261
+ onChange: setOwnerId,
262
+ required: true
263
+ }
264
+ )
265
+ ] }),
266
+ teamsEnabled && /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
267
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
268
+ id: index.getTranslation("organizations.create.defaultTeam.title"),
269
+ defaultMessage: "Default team"
270
+ }) }),
271
+ /* @__PURE__ */ jsxRuntime.jsx(
272
+ designSystem.Checkbox,
273
+ {
274
+ name: "skipDefaultTeam",
275
+ checked: skipDefaultTeam,
276
+ onCheckedChange: (checked) => setSkipDefaultTeam(checked),
277
+ children: formatMessage({
278
+ id: index.getTranslation("organizations.create.defaultTeam.skip"),
279
+ defaultMessage: "Don't create a default team"
280
+ })
281
+ }
282
+ ),
283
+ !skipDefaultTeam && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { width: "100%" }, children: [
284
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
285
+ id: index.getTranslation(
286
+ "organizations.create.defaultTeam.name.label"
287
+ ),
288
+ defaultMessage: "Default team name"
289
+ }) }),
290
+ /* @__PURE__ */ jsxRuntime.jsx(
291
+ designSystem.TextInput,
292
+ {
293
+ name: "defaultTeamName",
294
+ value: defaultTeamName,
295
+ onChange: (e) => setDefaultTeamName(e.target.value),
296
+ placeholder: formatMessage({
297
+ id: index.getTranslation(
298
+ "organizations.create.defaultTeam.name.placeholder"
299
+ ),
300
+ defaultMessage: "Leave blank to use the system default"
301
+ })
302
+ }
303
+ )
304
+ ] }),
305
+ skipDefaultTeam && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage({
306
+ id: index.getTranslation(
307
+ "organizations.create.defaultTeam.skipHint"
308
+ ),
309
+ defaultMessage: "Members can be added to teams manually after the organization is created."
310
+ }) }) })
311
+ ] })
312
+ ] })
313
+ }
314
+ );
315
+ }
316
+ function slugify(str) {
317
+ return str.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
318
+ }
319
+ const INVITATION_STATUS_VARIANT = {
320
+ pending: "warning",
321
+ accepted: "success",
322
+ rejected: "danger",
323
+ canceled: "neutral"
324
+ };
325
+ const INVITATION_STATUS_LABEL = {
326
+ pending: {
327
+ id: index.getTranslation("organizations.detail.invitations.status.pending"),
328
+ defaultMessage: "pending"
329
+ },
330
+ accepted: {
331
+ id: index.getTranslation("organizations.detail.invitations.status.accepted"),
332
+ defaultMessage: "accepted"
333
+ },
334
+ rejected: {
335
+ id: index.getTranslation("organizations.detail.invitations.status.rejected"),
336
+ defaultMessage: "rejected"
337
+ },
338
+ canceled: {
339
+ id: index.getTranslation("organizations.detail.invitations.status.canceled"),
340
+ defaultMessage: "canceled"
341
+ }
342
+ };
343
+ const ROLE_OPTIONS = [
344
+ {
345
+ value: "member",
346
+ label: {
347
+ id: index.getTranslation("organizations.common.roles.member"),
348
+ defaultMessage: "Member"
349
+ }
350
+ },
351
+ {
352
+ value: "admin",
353
+ label: {
354
+ id: index.getTranslation("organizations.common.roles.admin"),
355
+ defaultMessage: "Admin"
356
+ }
357
+ },
358
+ {
359
+ value: "owner",
360
+ label: {
361
+ id: index.getTranslation("organizations.common.roles.owner"),
362
+ defaultMessage: "Owner"
363
+ }
364
+ }
365
+ ];
366
+ function InlineLoader() {
367
+ const { formatMessage } = reactIntl.useIntl();
368
+ return /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 4, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { small: true, children: formatMessage({
369
+ id: index.getTranslation("organizations.common.loading"),
370
+ defaultMessage: "Loading…"
371
+ }) }) });
372
+ }
373
+ const STANDARD_ORG_FIELDS = /* @__PURE__ */ new Set([
374
+ "id",
375
+ "name",
376
+ "slug",
377
+ "logo",
378
+ "metadata",
379
+ "memberCount",
380
+ "createdAt",
381
+ "updatedAt"
382
+ ]);
383
+ function OrganizationDetail({
384
+ organizationId,
385
+ teamsEnabled,
386
+ onClose
387
+ }) {
388
+ const { formatMessage } = reactIntl.useIntl();
389
+ const schemaQuery = useModelSchema.useModelSchema("organization");
390
+ const qc = reactQuery.useQueryClient();
391
+ const { toggleNotification } = admin.useNotification();
392
+ const { get, put } = admin.useFetchClient();
393
+ const orgQuery = reactQuery.useQuery({
394
+ queryKey: ["dash-org", organizationId],
395
+ queryFn: async () => {
396
+ const result = await index.client.dash.organization[organizationId](
397
+ {},
398
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
399
+ );
400
+ if (result.error)
401
+ throw new Error(
402
+ result.error.message ?? formatMessage({
403
+ id: index.getTranslation("organizations.detail.error.loadOrg"),
404
+ defaultMessage: "Failed to load org"
405
+ })
406
+ );
407
+ return result.data;
408
+ }
409
+ });
410
+ const membersQuery = reactQuery.useQuery({
411
+ queryKey: ["dash-org-members", organizationId],
412
+ queryFn: async () => {
413
+ const result = await index.client.dash.organization[organizationId].members({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
414
+ if (result.error)
415
+ throw new Error(
416
+ result.error.message ?? formatMessage({
417
+ id: index.getTranslation("organizations.common.failed"),
418
+ defaultMessage: "Failed"
419
+ })
420
+ );
421
+ return result.data ?? [];
422
+ }
423
+ });
424
+ const teamsQuery = reactQuery.useQuery({
425
+ queryKey: ["dash-org-teams", organizationId],
426
+ queryFn: async () => {
427
+ if (!teamsEnabled) return [];
428
+ const result = await index.client.dash.organization[organizationId].teams({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
429
+ if (result.error)
430
+ throw new Error(
431
+ result.error.message ?? formatMessage({
432
+ id: index.getTranslation("organizations.common.failed"),
433
+ defaultMessage: "Failed"
434
+ })
435
+ );
436
+ return result.data ?? [];
437
+ },
438
+ enabled: teamsEnabled
439
+ });
440
+ const ssoQuery = reactQuery.useQuery({
441
+ queryKey: ["dash-org-sso", organizationId],
442
+ queryFn: async () => {
443
+ const result = await index.client.dash.organization[organizationId].ssoProviders({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
444
+ if (result.error)
445
+ throw new Error(
446
+ result.error.message ?? formatMessage({
447
+ id: index.getTranslation("organizations.common.failed"),
448
+ defaultMessage: "Failed"
449
+ })
450
+ );
451
+ return result.data ?? [];
452
+ }
453
+ });
454
+ const invitationsQuery = reactQuery.useQuery({
455
+ queryKey: ["dash-org-invitations", organizationId],
456
+ queryFn: async () => {
457
+ const result = await index.client.dash.organization[organizationId].invitations({}, useModelSchema.withContext({ organizationId }, index.getAuthHeaders()));
458
+ if (result.error)
459
+ throw new Error(
460
+ result.error.message ?? formatMessage({
461
+ id: index.getTranslation("organizations.common.failed"),
462
+ defaultMessage: "Failed"
463
+ })
464
+ );
465
+ return result.data ?? [];
466
+ }
467
+ });
468
+ const strapiOrgQuery = reactQuery.useQuery({
469
+ queryKey: ["dash-strapi-org", organizationId],
470
+ enabled: !!orgQuery.data,
471
+ queryFn: async () => {
472
+ const { data } = await get(
473
+ `/better-auth-dashboard/db?uid=plugin::better-auth.organization&filters[id][$eq]=${organizationId}&pagination[pageSize]=1`
474
+ );
475
+ return data.results?.[0] ?? null;
476
+ }
477
+ });
478
+ const [activeTab, setActiveTab] = react.useState("details");
479
+ const [editName, setEditName] = react.useState(void 0);
480
+ const [editSlug, setEditSlug] = react.useState(void 0);
481
+ const [editLogo, setEditLogo] = react.useState(void 0);
482
+ const [editExtra, setEditExtra] = react.useState({});
483
+ const [confirmRemoveMemberId, setConfirmRemoveMemberId] = react.useState(null);
484
+ const [confirmDeleteTeamId, setConfirmDeleteTeamId] = react.useState(
485
+ null
486
+ );
487
+ const [confirmDeleteSsoId, setConfirmDeleteSsoId] = react.useState(
488
+ null
489
+ );
490
+ const [inviteEmail, setInviteEmail] = react.useState("");
491
+ const [inviteRole, setInviteRole] = react.useState("member");
492
+ const [confirmCancelInvitationId, setConfirmCancelInvitationId] = react.useState(null);
493
+ const handleExtraChange = (name, value) => {
494
+ setEditExtra((prev) => ({ ...prev, [name]: value }));
495
+ };
496
+ const org = orgQuery.data;
497
+ const extraData = {
498
+ ...strapiOrgQuery.data ?? {},
499
+ ...editExtra
500
+ };
501
+ const updateOrgMutation = reactQuery.useMutation({
502
+ mutationFn: async () => {
503
+ const body = { ...editExtra };
504
+ if (editName !== void 0) body.name = editName;
505
+ if (editSlug !== void 0) body.slug = editSlug;
506
+ if (editLogo !== void 0) body.logo = editLogo;
507
+ const documentId = strapiOrgQuery.data?.documentId;
508
+ if (!documentId)
509
+ throw new Error(
510
+ formatMessage({
511
+ id: index.getTranslation("organizations.detail.error.documentId"),
512
+ defaultMessage: "Could not resolve documentId for organization"
513
+ })
514
+ );
515
+ await put(
516
+ `/better-auth-dashboard/db/${documentId}?uid=plugin::better-auth.organization`,
517
+ body
518
+ );
519
+ },
520
+ onSuccess: async () => {
521
+ await qc.invalidateQueries({
522
+ queryKey: ["dash-strapi-org", organizationId]
523
+ });
524
+ qc.invalidateQueries({ queryKey: ["dash-org", organizationId] });
525
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
526
+ qc.invalidateQueries({ queryKey: ["dash-strapi-org", organizationId] });
527
+ setEditName(void 0);
528
+ setEditSlug(void 0);
529
+ setEditLogo(void 0);
530
+ setEditExtra({});
531
+ toggleNotification({
532
+ type: "success",
533
+ message: formatMessage({
534
+ id: index.getTranslation("organizations.detail.notification.updated"),
535
+ defaultMessage: "Organization updated successfully"
536
+ })
537
+ });
538
+ onClose();
539
+ },
540
+ onError: (err) => {
541
+ toggleNotification({
542
+ type: "danger",
543
+ message: err.message ?? formatMessage({
544
+ id: index.getTranslation("organizations.detail.notification.updateError"),
545
+ defaultMessage: "Update failed"
546
+ })
547
+ });
548
+ }
549
+ });
550
+ const [addUserId, setAddUserId] = react.useState("");
551
+ const [addRole, setAddRole] = react.useState("member");
552
+ const addMemberMutation = reactQuery.useMutation({
553
+ mutationFn: async () => {
554
+ const result = await index.client.dash.organization.addMember(
555
+ { userId: addUserId, role: addRole },
556
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
557
+ );
558
+ if (result.error)
559
+ throw new Error(
560
+ result.error.message ?? formatMessage({
561
+ id: index.getTranslation(
562
+ "organizations.detail.members.error.addFailed"
563
+ ),
564
+ defaultMessage: "Add member failed"
565
+ })
566
+ );
567
+ return result.data;
568
+ },
569
+ onSuccess: () => {
570
+ qc.invalidateQueries({ queryKey: ["dash-org-members", organizationId] });
571
+ setAddUserId("");
572
+ setAddRole("member");
573
+ toggleNotification({
574
+ type: "success",
575
+ message: formatMessage({
576
+ id: index.getTranslation("organizations.detail.members.notification.added"),
577
+ defaultMessage: "Member added"
578
+ })
579
+ });
580
+ },
581
+ onError: (err) => {
582
+ toggleNotification({
583
+ type: "danger",
584
+ message: err.message ?? formatMessage({
585
+ id: index.getTranslation(
586
+ "organizations.detail.members.notification.addError"
587
+ ),
588
+ defaultMessage: "Failed to add member"
589
+ })
590
+ });
591
+ }
592
+ });
593
+ const removeMemberMutation = reactQuery.useMutation({
594
+ mutationFn: async (memberId) => {
595
+ const result = await index.client.dash.organization.removeMember(
596
+ { memberId },
597
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
598
+ );
599
+ if (result.error)
600
+ throw new Error(
601
+ result.error.message ?? formatMessage({
602
+ id: index.getTranslation("organizations.common.failed"),
603
+ defaultMessage: "Failed"
604
+ })
605
+ );
606
+ },
607
+ onSuccess: () => {
608
+ setConfirmRemoveMemberId(null);
609
+ qc.invalidateQueries({ queryKey: ["dash-org-members", organizationId] });
610
+ toggleNotification({
611
+ type: "success",
612
+ message: formatMessage({
613
+ id: index.getTranslation(
614
+ "organizations.detail.members.notification.removed"
615
+ ),
616
+ defaultMessage: "Member removed"
617
+ })
618
+ });
619
+ },
620
+ onError: (err) => {
621
+ toggleNotification({
622
+ type: "danger",
623
+ message: err.message ?? formatMessage({
624
+ id: index.getTranslation(
625
+ "organizations.detail.members.notification.removeError"
626
+ ),
627
+ defaultMessage: "Failed to remove member"
628
+ })
629
+ });
630
+ }
631
+ });
632
+ const updateRoleMutation = reactQuery.useMutation({
633
+ mutationFn: async ({
634
+ memberId,
635
+ role
636
+ }) => {
637
+ const result = await index.client.dash.organization.updateMemberRole(
638
+ { memberId, role },
639
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
640
+ );
641
+ if (result.error)
642
+ throw new Error(
643
+ result.error.message ?? formatMessage({
644
+ id: index.getTranslation("organizations.common.failed"),
645
+ defaultMessage: "Failed"
646
+ })
647
+ );
648
+ return result.data;
649
+ },
650
+ onSuccess: () => {
651
+ qc.invalidateQueries({ queryKey: ["dash-org-members", organizationId] });
652
+ toggleNotification({
653
+ type: "success",
654
+ message: formatMessage({
655
+ id: index.getTranslation(
656
+ "organizations.detail.members.notification.roleUpdated"
657
+ ),
658
+ defaultMessage: "Role updated"
659
+ })
660
+ });
661
+ },
662
+ onError: (err) => {
663
+ toggleNotification({
664
+ type: "danger",
665
+ message: err.message ?? formatMessage({
666
+ id: index.getTranslation(
667
+ "organizations.detail.members.notification.roleUpdateError"
668
+ ),
669
+ defaultMessage: "Failed to update role"
670
+ })
671
+ });
672
+ }
673
+ });
674
+ const [newTeamName, setNewTeamName] = react.useState("");
675
+ const createTeamMutation = reactQuery.useMutation({
676
+ mutationFn: async () => {
677
+ const result = await index.client.dash.organization.createTeam(
678
+ { name: newTeamName },
679
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
680
+ );
681
+ if (result.error)
682
+ throw new Error(
683
+ result.error.message ?? formatMessage({
684
+ id: index.getTranslation(
685
+ "organizations.detail.teams.error.createFailed"
686
+ ),
687
+ defaultMessage: "Create team failed"
688
+ })
689
+ );
690
+ return result.data;
691
+ },
692
+ onSuccess: () => {
693
+ qc.invalidateQueries({ queryKey: ["dash-org-teams", organizationId] });
694
+ setNewTeamName("");
695
+ toggleNotification({
696
+ type: "success",
697
+ message: formatMessage({
698
+ id: index.getTranslation("organizations.detail.teams.notification.created"),
699
+ defaultMessage: "Team created"
700
+ })
701
+ });
702
+ },
703
+ onError: (err) => {
704
+ toggleNotification({
705
+ type: "danger",
706
+ message: err.message ?? formatMessage({
707
+ id: index.getTranslation(
708
+ "organizations.detail.teams.notification.createError"
709
+ ),
710
+ defaultMessage: "Failed to create team"
711
+ })
712
+ });
713
+ }
714
+ });
715
+ const deleteTeamMutation = reactQuery.useMutation({
716
+ mutationFn: async (teamId) => {
717
+ const result = await index.client.dash.organization.deleteTeam(
718
+ { teamId },
719
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
720
+ );
721
+ if (result.error)
722
+ throw new Error(
723
+ result.error.message ?? formatMessage({
724
+ id: index.getTranslation("organizations.common.failed"),
725
+ defaultMessage: "Failed"
726
+ })
727
+ );
728
+ },
729
+ onSuccess: () => {
730
+ setConfirmDeleteTeamId(null);
731
+ qc.invalidateQueries({ queryKey: ["dash-org-teams", organizationId] });
732
+ toggleNotification({
733
+ type: "success",
734
+ message: formatMessage({
735
+ id: index.getTranslation("organizations.detail.teams.notification.deleted"),
736
+ defaultMessage: "Team deleted"
737
+ })
738
+ });
739
+ },
740
+ onError: (err) => {
741
+ toggleNotification({
742
+ type: "danger",
743
+ message: err.message ?? formatMessage({
744
+ id: index.getTranslation(
745
+ "organizations.detail.teams.notification.deleteError"
746
+ ),
747
+ defaultMessage: "Failed to delete team"
748
+ })
749
+ });
750
+ }
751
+ });
752
+ const deleteSsoMutation = reactQuery.useMutation({
753
+ mutationFn: async (providerId) => {
754
+ const result = await index.client.dash.organization[organizationId].ssoProvider.delete(
755
+ { providerId },
756
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
757
+ );
758
+ if (result.error)
759
+ throw new Error(
760
+ result.error.message ?? formatMessage({
761
+ id: index.getTranslation("organizations.common.failed"),
762
+ defaultMessage: "Failed"
763
+ })
764
+ );
765
+ },
766
+ onSuccess: () => {
767
+ setConfirmDeleteSsoId(null);
768
+ qc.invalidateQueries({ queryKey: ["dash-org-sso", organizationId] });
769
+ toggleNotification({
770
+ type: "success",
771
+ message: formatMessage({
772
+ id: index.getTranslation("organizations.detail.sso.notification.deleted"),
773
+ defaultMessage: "SSO provider deleted"
774
+ })
775
+ });
776
+ },
777
+ onError: (err) => {
778
+ toggleNotification({
779
+ type: "danger",
780
+ message: err.message ?? formatMessage({
781
+ id: index.getTranslation(
782
+ "organizations.detail.sso.notification.deleteError"
783
+ ),
784
+ defaultMessage: "Failed to delete SSO provider"
785
+ })
786
+ });
787
+ }
788
+ });
789
+ const inviteMemberMutation = reactQuery.useMutation({
790
+ mutationFn: async () => {
791
+ const result = await index.client.dash.organization.inviteMember(
792
+ { email: inviteEmail, role: inviteRole, invitedBy: "" },
793
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
794
+ );
795
+ if (result.error)
796
+ throw new Error(
797
+ result.error.message ?? formatMessage({
798
+ id: index.getTranslation("organizations.common.failed"),
799
+ defaultMessage: "Failed"
800
+ })
801
+ );
802
+ return result.data;
803
+ },
804
+ onSuccess: () => {
805
+ qc.invalidateQueries({
806
+ queryKey: ["dash-org-invitations", organizationId]
807
+ });
808
+ setInviteEmail("");
809
+ setInviteRole("member");
810
+ toggleNotification({
811
+ type: "success",
812
+ message: formatMessage({
813
+ id: index.getTranslation(
814
+ "organizations.detail.invitations.notification.sent"
815
+ ),
816
+ defaultMessage: "Invitation sent"
817
+ })
818
+ });
819
+ },
820
+ onError: (err) => {
821
+ toggleNotification({
822
+ type: "danger",
823
+ message: err.message ?? formatMessage({
824
+ id: index.getTranslation(
825
+ "organizations.detail.invitations.notification.inviteError"
826
+ ),
827
+ defaultMessage: "Failed to invite"
828
+ })
829
+ });
830
+ }
831
+ });
832
+ const cancelInvitationMutation = reactQuery.useMutation({
833
+ mutationFn: async (invitationId) => {
834
+ const result = await index.client.dash.organization.cancelInvitation(
835
+ { invitationId },
836
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
837
+ );
838
+ if (result.error)
839
+ throw new Error(
840
+ result.error.message ?? formatMessage({
841
+ id: index.getTranslation("organizations.common.failed"),
842
+ defaultMessage: "Failed"
843
+ })
844
+ );
845
+ },
846
+ onSuccess: () => {
847
+ setConfirmCancelInvitationId(null);
848
+ qc.invalidateQueries({
849
+ queryKey: ["dash-org-invitations", organizationId]
850
+ });
851
+ toggleNotification({
852
+ type: "success",
853
+ message: formatMessage({
854
+ id: index.getTranslation(
855
+ "organizations.detail.invitations.notification.cancelled"
856
+ ),
857
+ defaultMessage: "Invitation cancelled"
858
+ })
859
+ });
860
+ },
861
+ onError: (err) => {
862
+ toggleNotification({
863
+ type: "danger",
864
+ message: err.message ?? formatMessage({
865
+ id: index.getTranslation(
866
+ "organizations.detail.invitations.notification.cancelError"
867
+ ),
868
+ defaultMessage: "Failed to cancel"
869
+ })
870
+ });
871
+ }
872
+ });
873
+ const resendInvitationMutation = reactQuery.useMutation({
874
+ mutationFn: async (invitationId) => {
875
+ const result = await index.client.dash.organization.resendInvitation(
876
+ { invitationId },
877
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
878
+ );
879
+ if (result.error)
880
+ throw new Error(
881
+ result.error.message ?? formatMessage({
882
+ id: index.getTranslation("organizations.common.failed"),
883
+ defaultMessage: "Failed"
884
+ })
885
+ );
886
+ },
887
+ onSuccess: () => {
888
+ toggleNotification({
889
+ type: "success",
890
+ message: formatMessage({
891
+ id: index.getTranslation(
892
+ "organizations.detail.invitations.notification.resent"
893
+ ),
894
+ defaultMessage: "Invitation resent"
895
+ })
896
+ });
897
+ },
898
+ onError: (err) => {
899
+ toggleNotification({
900
+ type: "danger",
901
+ message: err.message ?? formatMessage({
902
+ id: index.getTranslation(
903
+ "organizations.detail.invitations.notification.resendError"
904
+ ),
905
+ defaultMessage: "Failed to resend"
906
+ })
907
+ });
908
+ }
909
+ });
910
+ const members = membersQuery.data ?? [];
911
+ const teams = teamsQuery.data ?? [];
912
+ const ssoProviders = ssoQuery.data ?? [];
913
+ const invitations = invitationsQuery.data ?? [];
914
+ const hasOrgEdits = editName !== void 0 || editSlug !== void 0 || editLogo !== void 0 || Object.keys(editExtra).length > 0;
915
+ const customFields = Object.entries(schemaQuery.data ?? {}).filter(([name]) => !STANDARD_ORG_FIELDS.has(name)).map(([name, attribute]) => ({ name, attribute }));
916
+ const detailsFooter = activeTab === "details" ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
917
+ /* @__PURE__ */ jsxRuntime.jsx(
918
+ designSystem.Button,
919
+ {
920
+ variant: "tertiary",
921
+ onClick: () => {
922
+ setEditName(void 0);
923
+ setEditSlug(void 0);
924
+ setEditLogo(void 0);
925
+ setEditExtra({});
926
+ onClose();
927
+ },
928
+ children: formatMessage({
929
+ id: index.getTranslation("organizations.detail.discard"),
930
+ defaultMessage: "Discard"
931
+ })
932
+ }
933
+ ),
934
+ /* @__PURE__ */ jsxRuntime.jsx(
935
+ designSystem.Button,
936
+ {
937
+ disabled: !hasOrgEdits,
938
+ loading: updateOrgMutation.isLoading,
939
+ onClick: () => updateOrgMutation.mutate(),
940
+ "data-testid": "save-org-btn",
941
+ children: formatMessage({
942
+ id: index.getTranslation("organizations.detail.save"),
943
+ defaultMessage: "Save changes"
944
+ })
945
+ }
946
+ )
947
+ ] }) : void 0;
948
+ return /* @__PURE__ */ jsxRuntime.jsxs(
949
+ useModelSchema.SheetDialog,
950
+ {
951
+ title: org?.name ?? formatMessage({
952
+ id: index.getTranslation("organizations.detail.fallbackTitle"),
953
+ defaultMessage: "Organization"
954
+ }),
955
+ actionButtons: detailsFooter,
956
+ onClose,
957
+ "data-testid": "org-detail-drawer",
958
+ children: [
959
+ orgQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(InlineLoader, {}) : orgQuery.isError ? /* @__PURE__ */ jsxRuntime.jsx(
960
+ designSystem.Alert,
961
+ {
962
+ closeLabel: formatMessage({
963
+ id: index.getTranslation("organizations.common.close"),
964
+ defaultMessage: "Close"
965
+ }),
966
+ title: formatMessage({
967
+ id: index.getTranslation("organizations.detail.error.loadTitle"),
968
+ defaultMessage: "Failed to load organization"
969
+ }),
970
+ variant: "danger",
971
+ children: orgQuery.error instanceof Error ? orgQuery.error.message : formatMessage({
972
+ id: index.getTranslation("organizations.common.errorOccurred"),
973
+ defaultMessage: "An error occurred"
974
+ })
975
+ }
976
+ ) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.Root, { defaultValue: "details", onValueChange: setActiveTab, children: [
977
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tabs.List, { children: [
978
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "details", children: formatMessage({
979
+ id: index.getTranslation("organizations.common.details"),
980
+ defaultMessage: "Details"
981
+ }) }),
982
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "members", "data-testid": "org-members-tab", children: formatMessage(
983
+ {
984
+ id: index.getTranslation("organizations.detail.tabs.members"),
985
+ defaultMessage: "Members ({count})"
986
+ },
987
+ { count: members.length }
988
+ ) }),
989
+ teamsEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "teams", children: formatMessage(
990
+ {
991
+ id: index.getTranslation("organizations.detail.tabs.teams"),
992
+ defaultMessage: "Teams ({count})"
993
+ },
994
+ { count: teams.length }
995
+ ) }),
996
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "invitations", children: formatMessage(
997
+ {
998
+ id: index.getTranslation("organizations.detail.tabs.invitations"),
999
+ defaultMessage: "Invitations ({count})"
1000
+ },
1001
+ { count: invitations.length }
1002
+ ) }),
1003
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Trigger, { value: "sso", children: formatMessage(
1004
+ {
1005
+ id: index.getTranslation("organizations.detail.tabs.sso"),
1006
+ defaultMessage: "SSO ({count})"
1007
+ },
1008
+ { count: ssoProviders.length }
1009
+ ) })
1010
+ ] }),
1011
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "details", children: /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.EditLayout, { children: [
1012
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, children: [
1013
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1014
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
1015
+ id: index.getTranslation("organizations.common.details"),
1016
+ defaultMessage: "Details"
1017
+ }) }),
1018
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { style: { width: "100%" }, children: [
1019
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1020
+ id: index.getTranslation("organizations.common.name"),
1021
+ defaultMessage: "Name"
1022
+ }) }),
1023
+ /* @__PURE__ */ jsxRuntime.jsx(
1024
+ designSystem.TextInput,
1025
+ {
1026
+ value: editName ?? org?.name ?? "",
1027
+ onChange: (e) => setEditName(e.target.value),
1028
+ "data-testid": "org-name-input"
1029
+ }
1030
+ )
1031
+ ] }),
1032
+ /* @__PURE__ */ jsxRuntime.jsxs(
1033
+ designSystem.Field.Root,
1034
+ {
1035
+ hint: formatMessage({
1036
+ id: index.getTranslation("organizations.common.slugHint"),
1037
+ defaultMessage: "Used in URLs"
1038
+ }),
1039
+ style: { width: "100%" },
1040
+ children: [
1041
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1042
+ id: index.getTranslation("organizations.common.slug"),
1043
+ defaultMessage: "Slug"
1044
+ }) }),
1045
+ /* @__PURE__ */ jsxRuntime.jsx(
1046
+ designSystem.TextInput,
1047
+ {
1048
+ value: editSlug ?? org?.slug ?? "",
1049
+ onChange: (e) => setEditSlug(e.target.value),
1050
+ "data-testid": "org-slug-input"
1051
+ }
1052
+ ),
1053
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, {})
1054
+ ]
1055
+ }
1056
+ ),
1057
+ /* @__PURE__ */ jsxRuntime.jsx(
1058
+ useModelSchema.MediaPickerField,
1059
+ {
1060
+ label: formatMessage({
1061
+ id: index.getTranslation("organizations.common.logo.label"),
1062
+ defaultMessage: "Logo URL"
1063
+ }),
1064
+ name: "logo",
1065
+ value: editLogo ?? org?.logo ?? "",
1066
+ onChange: setEditLogo,
1067
+ placeholder: formatMessage({
1068
+ id: index.getTranslation(
1069
+ "organizations.common.logo.placeholder"
1070
+ ),
1071
+ defaultMessage: "https://example.com/logo.png"
1072
+ }),
1073
+ hint: formatMessage({
1074
+ id: index.getTranslation("organizations.common.logo.hint"),
1075
+ defaultMessage: "Optional — publicly accessible image URL"
1076
+ })
1077
+ }
1078
+ )
1079
+ ] }),
1080
+ /* @__PURE__ */ jsxRuntime.jsx(
1081
+ useModelSchema.CustomFieldsSection,
1082
+ {
1083
+ fields: customFields,
1084
+ data: extraData,
1085
+ onChange: handleExtraChange
1086
+ }
1087
+ )
1088
+ ] }),
1089
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.EditSidebar, { children: [
1090
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1091
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
1092
+ id: index.getTranslation("organizations.common.details"),
1093
+ defaultMessage: "Details"
1094
+ }) }),
1095
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 3, gridCols: 2, children: [
1096
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 2, children: /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.MetaItem, { children: [
1097
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaKey, { children: formatMessage({
1098
+ id: index.getTranslation("organizations.detail.meta.id"),
1099
+ defaultMessage: "Organization ID"
1100
+ }) }),
1101
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MonoChip, { children: org?.id })
1102
+ ] }) }),
1103
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.MetaItem, { children: [
1104
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaKey, { children: formatMessage({
1105
+ id: index.getTranslation("organizations.common.members"),
1106
+ defaultMessage: "Members"
1107
+ }) }),
1108
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaVal, { children: org?.memberCount ?? 0 })
1109
+ ] }) }),
1110
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.MetaItem, { children: [
1111
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaKey, { children: formatMessage({
1112
+ id: index.getTranslation("organizations.common.created"),
1113
+ defaultMessage: "Created"
1114
+ }) }),
1115
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.MetaVal, { children: org?.createdAt ? new Date(org.createdAt).toLocaleDateString() : "—" })
1116
+ ] }) })
1117
+ ] })
1118
+ ] }),
1119
+ /* @__PURE__ */ jsxRuntime.jsx(
1120
+ useModelSchema.EditViewSidePanels,
1121
+ {
1122
+ model: "plugin::better-auth.organization",
1123
+ documentId: strapiOrgQuery.data?.documentId,
1124
+ document: strapiOrgQuery.data ?? void 0
1125
+ }
1126
+ )
1127
+ ] })
1128
+ ] }) }),
1129
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "members", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, paddingTop: 6, children: [
1130
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1131
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
1132
+ id: index.getTranslation("organizations.detail.members.add"),
1133
+ defaultMessage: "Add member"
1134
+ }) }),
1135
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 3, children: [
1136
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 8, children: /* @__PURE__ */ jsxRuntime.jsx(
1137
+ UserCombobox,
1138
+ {
1139
+ label: formatMessage({
1140
+ id: index.getTranslation("organizations.detail.members.user"),
1141
+ defaultMessage: "User"
1142
+ }),
1143
+ value: addUserId,
1144
+ onChange: setAddUserId
1145
+ }
1146
+ ) }),
1147
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1148
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1149
+ id: index.getTranslation("organizations.common.role"),
1150
+ defaultMessage: "Role"
1151
+ }) }),
1152
+ /* @__PURE__ */ jsxRuntime.jsx(
1153
+ designSystem.SingleSelect,
1154
+ {
1155
+ value: addRole,
1156
+ onChange: (role) => setAddRole(String(role)),
1157
+ "aria-label": formatMessage({
1158
+ id: index.getTranslation(
1159
+ "organizations.detail.members.roleLabel"
1160
+ ),
1161
+ defaultMessage: "Member role"
1162
+ }),
1163
+ children: ROLE_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
1164
+ designSystem.SingleSelectOption,
1165
+ {
1166
+ value: option.value,
1167
+ children: formatMessage(option.label)
1168
+ },
1169
+ option.value
1170
+ ))
1171
+ }
1172
+ )
1173
+ ] }) })
1174
+ ] }),
1175
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
1176
+ designSystem.Button,
1177
+ {
1178
+ size: "S",
1179
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1180
+ disabled: !addUserId,
1181
+ loading: addMemberMutation.isLoading,
1182
+ onClick: () => addMemberMutation.mutate(),
1183
+ "data-testid": "add-member-btn",
1184
+ children: formatMessage({
1185
+ id: index.getTranslation("organizations.detail.members.add"),
1186
+ defaultMessage: "Add member"
1187
+ })
1188
+ }
1189
+ ) })
1190
+ ] }),
1191
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1192
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage(
1193
+ {
1194
+ id: index.getTranslation("organizations.detail.tabs.members"),
1195
+ defaultMessage: "Members ({count})"
1196
+ },
1197
+ { count: members.length }
1198
+ ) }),
1199
+ membersQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(InlineLoader, {}) : members.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage({
1200
+ id: index.getTranslation("organizations.detail.members.empty"),
1201
+ defaultMessage: "No members yet."
1202
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: members.map((member) => /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.AccountRow, { "data-testid": "member-row", children: [
1203
+ /* @__PURE__ */ jsxRuntime.jsxs(
1204
+ designSystem.Flex,
1205
+ {
1206
+ direction: "column",
1207
+ gap: 1,
1208
+ alignItems: "flex-start",
1209
+ flex: 1,
1210
+ children: [
1211
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: member.user?.name ?? formatMessage({
1212
+ id: index.getTranslation(
1213
+ "organizations.detail.members.unknown"
1214
+ ),
1215
+ defaultMessage: "Unknown"
1216
+ }) }),
1217
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: member.user?.email })
1218
+ ]
1219
+ }
1220
+ ),
1221
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1222
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { style: { width: 110 }, children: /* @__PURE__ */ jsxRuntime.jsx(
1223
+ designSystem.SingleSelect,
1224
+ {
1225
+ value: member.role,
1226
+ onChange: (role) => updateRoleMutation.mutate({
1227
+ memberId: member.id,
1228
+ role: String(role)
1229
+ }),
1230
+ size: "S",
1231
+ "aria-label": formatMessage({
1232
+ id: index.getTranslation(
1233
+ "organizations.detail.members.roleLabel"
1234
+ ),
1235
+ defaultMessage: "Member role"
1236
+ }),
1237
+ children: ROLE_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
1238
+ designSystem.SingleSelectOption,
1239
+ {
1240
+ value: option.value,
1241
+ children: formatMessage(option.label)
1242
+ },
1243
+ option.value
1244
+ ))
1245
+ }
1246
+ ) }),
1247
+ /* @__PURE__ */ jsxRuntime.jsx(
1248
+ designSystem.IconButton,
1249
+ {
1250
+ label: formatMessage({
1251
+ id: index.getTranslation(
1252
+ "organizations.detail.members.remove"
1253
+ ),
1254
+ defaultMessage: "Remove member"
1255
+ }),
1256
+ onClick: () => setConfirmRemoveMemberId(member.id),
1257
+ "data-testid": "remove-member-btn",
1258
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1259
+ }
1260
+ )
1261
+ ] })
1262
+ ] }, member.id)) })
1263
+ ] })
1264
+ ] }) }),
1265
+ teamsEnabled && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "teams", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, paddingTop: 6, children: [
1266
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
1268
+ id: index.getTranslation("organizations.detail.teams.create"),
1269
+ defaultMessage: "Create team"
1270
+ }) }),
1271
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", children: [
1272
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: 1, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1273
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1274
+ id: index.getTranslation(
1275
+ "organizations.detail.teams.name.label"
1276
+ ),
1277
+ defaultMessage: "Team name"
1278
+ }) }),
1279
+ /* @__PURE__ */ jsxRuntime.jsx(
1280
+ designSystem.TextInput,
1281
+ {
1282
+ value: newTeamName,
1283
+ onChange: (e) => setNewTeamName(e.target.value),
1284
+ placeholder: formatMessage({
1285
+ id: index.getTranslation(
1286
+ "organizations.detail.teams.name.placeholder"
1287
+ ),
1288
+ defaultMessage: "e.g. Engineering"
1289
+ })
1290
+ }
1291
+ )
1292
+ ] }) }),
1293
+ /* @__PURE__ */ jsxRuntime.jsx(
1294
+ designSystem.Button,
1295
+ {
1296
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1297
+ disabled: !newTeamName,
1298
+ loading: createTeamMutation.isLoading,
1299
+ onClick: () => createTeamMutation.mutate(),
1300
+ children: formatMessage({
1301
+ id: index.getTranslation(
1302
+ "organizations.detail.teams.createSubmit"
1303
+ ),
1304
+ defaultMessage: "Create"
1305
+ })
1306
+ }
1307
+ )
1308
+ ] })
1309
+ ] }),
1310
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1311
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage(
1312
+ {
1313
+ id: index.getTranslation("organizations.detail.tabs.teams"),
1314
+ defaultMessage: "Teams ({count})"
1315
+ },
1316
+ { count: teams.length }
1317
+ ) }),
1318
+ teamsQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(InlineLoader, {}) : teams.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage({
1319
+ id: index.getTranslation("organizations.detail.teams.empty"),
1320
+ defaultMessage: "No teams yet."
1321
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: teams.map((team) => /* @__PURE__ */ jsxRuntime.jsx(
1322
+ TeamRow,
1323
+ {
1324
+ team,
1325
+ organizationId,
1326
+ onDelete: () => setConfirmDeleteTeamId(team.id)
1327
+ },
1328
+ team.id
1329
+ )) })
1330
+ ] })
1331
+ ] }) }),
1332
+ /* @__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: [
1333
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
1334
+ id: index.getTranslation("organizations.detail.sso.title"),
1335
+ defaultMessage: "SSO providers"
1336
+ }) }),
1337
+ ssoQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(InlineLoader, {}) : ssoProviders.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage({
1338
+ id: index.getTranslation("organizations.detail.sso.empty"),
1339
+ defaultMessage: "No SSO providers configured."
1340
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: ssoProviders.map((provider) => /* @__PURE__ */ jsxRuntime.jsxs(
1341
+ useModelSchema.AccountRow,
1342
+ {
1343
+ "data-testid": "sso-provider-row",
1344
+ children: [
1345
+ /* @__PURE__ */ jsxRuntime.jsxs(
1346
+ designSystem.Flex,
1347
+ {
1348
+ direction: "column",
1349
+ gap: 1,
1350
+ alignItems: "flex-start",
1351
+ flex: 1,
1352
+ children: [
1353
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.ProviderBadge, { children: provider.providerId }),
1354
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: provider.domain }),
1355
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage(
1356
+ {
1357
+ id: index.getTranslation(
1358
+ "organizations.detail.sso.issuer"
1359
+ ),
1360
+ defaultMessage: "Issuer: {issuer}"
1361
+ },
1362
+ { issuer: provider.issuer }
1363
+ ) })
1364
+ ]
1365
+ }
1366
+ ),
1367
+ /* @__PURE__ */ jsxRuntime.jsx(
1368
+ designSystem.IconButton,
1369
+ {
1370
+ label: formatMessage({
1371
+ id: index.getTranslation(
1372
+ "organizations.detail.sso.delete"
1373
+ ),
1374
+ defaultMessage: "Delete SSO provider"
1375
+ }),
1376
+ onClick: () => setConfirmDeleteSsoId(provider.providerId),
1377
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1378
+ }
1379
+ )
1380
+ ]
1381
+ },
1382
+ provider.id
1383
+ )) })
1384
+ ] }) }) }),
1385
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tabs.Content, { value: "invitations", children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", gap: 5, paddingTop: 6, children: [
1386
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1387
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage({
1388
+ id: index.getTranslation(
1389
+ "organizations.detail.invitations.invite"
1390
+ ),
1391
+ defaultMessage: "Invite member by email"
1392
+ }) }),
1393
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Grid.Root, { gap: 3, children: [
1394
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 8, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1395
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1396
+ id: index.getTranslation(
1397
+ "organizations.detail.invitations.email.label"
1398
+ ),
1399
+ defaultMessage: "Email address"
1400
+ }) }),
1401
+ /* @__PURE__ */ jsxRuntime.jsx(
1402
+ designSystem.TextInput,
1403
+ {
1404
+ type: "email",
1405
+ value: inviteEmail,
1406
+ onChange: (e) => setInviteEmail(e.target.value),
1407
+ placeholder: formatMessage({
1408
+ id: index.getTranslation(
1409
+ "organizations.detail.invitations.email.placeholder"
1410
+ ),
1411
+ defaultMessage: "user@example.com"
1412
+ })
1413
+ }
1414
+ )
1415
+ ] }) }),
1416
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Grid.Item, { col: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
1417
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: formatMessage({
1418
+ id: index.getTranslation("organizations.common.role"),
1419
+ defaultMessage: "Role"
1420
+ }) }),
1421
+ /* @__PURE__ */ jsxRuntime.jsx(
1422
+ designSystem.SingleSelect,
1423
+ {
1424
+ value: inviteRole,
1425
+ onChange: (v) => setInviteRole(String(v)),
1426
+ "aria-label": formatMessage({
1427
+ id: index.getTranslation(
1428
+ "organizations.detail.invitations.roleLabel"
1429
+ ),
1430
+ defaultMessage: "Invite role"
1431
+ }),
1432
+ children: ROLE_OPTIONS.map((option) => /* @__PURE__ */ jsxRuntime.jsx(
1433
+ designSystem.SingleSelectOption,
1434
+ {
1435
+ value: option.value,
1436
+ children: formatMessage(option.label)
1437
+ },
1438
+ option.value
1439
+ ))
1440
+ }
1441
+ )
1442
+ ] }) })
1443
+ ] }),
1444
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { children: /* @__PURE__ */ jsxRuntime.jsx(
1445
+ designSystem.Button,
1446
+ {
1447
+ size: "S",
1448
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1449
+ disabled: !inviteEmail,
1450
+ loading: inviteMemberMutation.isLoading,
1451
+ onClick: () => inviteMemberMutation.mutate(),
1452
+ children: formatMessage({
1453
+ id: index.getTranslation(
1454
+ "organizations.detail.invitations.send"
1455
+ ),
1456
+ defaultMessage: "Send invitation"
1457
+ })
1458
+ }
1459
+ ) })
1460
+ ] }),
1461
+ /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.FormSection, { children: [
1462
+ /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.SectionLabel, { children: formatMessage(
1463
+ {
1464
+ id: index.getTranslation(
1465
+ "organizations.detail.tabs.invitations"
1466
+ ),
1467
+ defaultMessage: "Invitations ({count})"
1468
+ },
1469
+ { count: invitations.length }
1470
+ ) }),
1471
+ invitationsQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(InlineLoader, {}) : invitations.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage({
1472
+ id: index.getTranslation(
1473
+ "organizations.detail.invitations.empty"
1474
+ ),
1475
+ defaultMessage: "No invitations yet."
1476
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", gap: 2, children: invitations.map((inv) => {
1477
+ const isPending = inv.status === "pending";
1478
+ return /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.AccountRow, { children: [
1479
+ /* @__PURE__ */ jsxRuntime.jsxs(
1480
+ designSystem.Flex,
1481
+ {
1482
+ direction: "column",
1483
+ gap: 1,
1484
+ alignItems: "flex-start",
1485
+ flex: 1,
1486
+ children: [
1487
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: inv.user?.name ?? inv.email }),
1488
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage(
1489
+ {
1490
+ id: index.getTranslation(
1491
+ "organizations.detail.invitations.emailRole"
1492
+ ),
1493
+ defaultMessage: "{email} · role: {role}"
1494
+ },
1495
+ { email: inv.email, role: inv.role }
1496
+ ) }),
1497
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage(
1498
+ {
1499
+ id: index.getTranslation(
1500
+ "organizations.detail.invitations.expires"
1501
+ ),
1502
+ defaultMessage: "Expires {date}"
1503
+ },
1504
+ {
1505
+ date: new Date(
1506
+ inv.expiresAt
1507
+ ).toLocaleDateString()
1508
+ }
1509
+ ) })
1510
+ ]
1511
+ }
1512
+ ),
1513
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "center", children: [
1514
+ /* @__PURE__ */ jsxRuntime.jsx(
1515
+ designSystem.Badge,
1516
+ {
1517
+ size: "S",
1518
+ variant: INVITATION_STATUS_VARIANT[inv.status] ?? "neutral",
1519
+ children: INVITATION_STATUS_LABEL[inv.status] ? formatMessage(
1520
+ INVITATION_STATUS_LABEL[inv.status]
1521
+ ) : inv.status
1522
+ }
1523
+ ),
1524
+ isPending && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1525
+ /* @__PURE__ */ jsxRuntime.jsx(
1526
+ designSystem.Button,
1527
+ {
1528
+ size: "S",
1529
+ variant: "secondary",
1530
+ loading: resendInvitationMutation.isLoading,
1531
+ onClick: () => resendInvitationMutation.mutate(inv.id),
1532
+ children: formatMessage({
1533
+ id: index.getTranslation(
1534
+ "organizations.detail.invitations.resend"
1535
+ ),
1536
+ defaultMessage: "Resend"
1537
+ })
1538
+ }
1539
+ ),
1540
+ /* @__PURE__ */ jsxRuntime.jsx(
1541
+ designSystem.IconButton,
1542
+ {
1543
+ label: formatMessage({
1544
+ id: index.getTranslation(
1545
+ "organizations.detail.invitations.cancel"
1546
+ ),
1547
+ defaultMessage: "Cancel invitation"
1548
+ }),
1549
+ onClick: () => setConfirmCancelInvitationId(inv.id),
1550
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1551
+ }
1552
+ )
1553
+ ] })
1554
+ ] })
1555
+ ] }, inv.id);
1556
+ }) })
1557
+ ] })
1558
+ ] }) })
1559
+ ] }),
1560
+ confirmRemoveMemberId && /* @__PURE__ */ jsxRuntime.jsx(
1561
+ useModelSchema.ConfirmDialog,
1562
+ {
1563
+ title: formatMessage({
1564
+ id: index.getTranslation("organizations.detail.members.remove"),
1565
+ defaultMessage: "Remove member"
1566
+ }),
1567
+ message: formatMessage({
1568
+ id: index.getTranslation(
1569
+ "organizations.detail.members.confirmRemove.message"
1570
+ ),
1571
+ defaultMessage: "Are you sure you want to remove this member from the organization?"
1572
+ }),
1573
+ confirmLabel: formatMessage({
1574
+ id: index.getTranslation("organizations.common.remove"),
1575
+ defaultMessage: "Remove"
1576
+ }),
1577
+ loading: removeMemberMutation.isLoading,
1578
+ onConfirm: () => removeMemberMutation.mutate(confirmRemoveMemberId),
1579
+ onCancel: () => setConfirmRemoveMemberId(null)
1580
+ }
1581
+ ),
1582
+ confirmDeleteTeamId && /* @__PURE__ */ jsxRuntime.jsx(
1583
+ useModelSchema.ConfirmDialog,
1584
+ {
1585
+ title: formatMessage({
1586
+ id: index.getTranslation("organizations.detail.teams.delete"),
1587
+ defaultMessage: "Delete team"
1588
+ }),
1589
+ message: formatMessage({
1590
+ id: index.getTranslation(
1591
+ "organizations.detail.teams.confirmDelete.message"
1592
+ ),
1593
+ defaultMessage: "Are you sure you want to delete this team? All team members will be removed. This action cannot be undone."
1594
+ }),
1595
+ confirmLabel: formatMessage({
1596
+ id: index.getTranslation("organizations.common.delete"),
1597
+ defaultMessage: "Delete"
1598
+ }),
1599
+ loading: deleteTeamMutation.isLoading,
1600
+ onConfirm: () => deleteTeamMutation.mutate(confirmDeleteTeamId),
1601
+ onCancel: () => setConfirmDeleteTeamId(null)
1602
+ }
1603
+ ),
1604
+ confirmDeleteSsoId && /* @__PURE__ */ jsxRuntime.jsx(
1605
+ useModelSchema.ConfirmDialog,
1606
+ {
1607
+ title: formatMessage({
1608
+ id: index.getTranslation("organizations.detail.sso.delete"),
1609
+ defaultMessage: "Delete SSO provider"
1610
+ }),
1611
+ message: formatMessage({
1612
+ id: index.getTranslation(
1613
+ "organizations.detail.sso.confirmDelete.message"
1614
+ ),
1615
+ defaultMessage: "Are you sure you want to delete this SSO provider? Members using this provider will need to authenticate differently."
1616
+ }),
1617
+ confirmLabel: formatMessage({
1618
+ id: index.getTranslation("organizations.common.delete"),
1619
+ defaultMessage: "Delete"
1620
+ }),
1621
+ loading: deleteSsoMutation.isLoading,
1622
+ onConfirm: () => deleteSsoMutation.mutate(confirmDeleteSsoId),
1623
+ onCancel: () => setConfirmDeleteSsoId(null)
1624
+ }
1625
+ ),
1626
+ confirmCancelInvitationId && /* @__PURE__ */ jsxRuntime.jsx(
1627
+ useModelSchema.ConfirmDialog,
1628
+ {
1629
+ title: formatMessage({
1630
+ id: index.getTranslation("organizations.detail.invitations.cancel"),
1631
+ defaultMessage: "Cancel invitation"
1632
+ }),
1633
+ message: formatMessage({
1634
+ id: index.getTranslation(
1635
+ "organizations.detail.invitations.confirmCancel.message"
1636
+ ),
1637
+ defaultMessage: "Are you sure you want to cancel this invitation? The invite link will no longer work."
1638
+ }),
1639
+ confirmLabel: formatMessage({
1640
+ id: index.getTranslation("organizations.detail.invitations.cancel"),
1641
+ defaultMessage: "Cancel invitation"
1642
+ }),
1643
+ loading: cancelInvitationMutation.isLoading,
1644
+ onConfirm: () => cancelInvitationMutation.mutate(confirmCancelInvitationId),
1645
+ onCancel: () => setConfirmCancelInvitationId(null)
1646
+ }
1647
+ )
1648
+ ]
1649
+ }
1650
+ );
1651
+ }
1652
+ function TeamRow({
1653
+ team,
1654
+ organizationId,
1655
+ onDelete
1656
+ }) {
1657
+ const { formatMessage } = reactIntl.useIntl();
1658
+ const qc = reactQuery.useQueryClient();
1659
+ const [expanded, setExpanded] = react.useState(false);
1660
+ const [confirmRemoveTeamMemberId, setConfirmRemoveTeamMemberId] = react.useState(null);
1661
+ const teamMembersQuery = reactQuery.useQuery({
1662
+ queryKey: ["dash-team-members", organizationId, team.id],
1663
+ queryFn: async () => {
1664
+ const result = await index.client.dash.organization[
1665
+ // @ts-expect-error
1666
+ organizationId
1667
+ ].teams[team.id].members(
1668
+ { params: { orgId: organizationId, teamId: team.id } },
1669
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1670
+ );
1671
+ if (result.error)
1672
+ throw new Error(
1673
+ result.error.message ?? formatMessage({
1674
+ id: index.getTranslation("organizations.common.failed"),
1675
+ defaultMessage: "Failed"
1676
+ })
1677
+ );
1678
+ return result.data ?? [];
1679
+ },
1680
+ enabled: expanded
1681
+ });
1682
+ const [addUserId, setAddUserId] = react.useState("");
1683
+ const addTeamMemberMutation = reactQuery.useMutation({
1684
+ mutationFn: async () => {
1685
+ const result = await index.client.dash.organization.addTeamMember(
1686
+ { teamId: team.id, userId: addUserId },
1687
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1688
+ );
1689
+ if (result.error)
1690
+ throw new Error(
1691
+ result.error.message ?? formatMessage({
1692
+ id: index.getTranslation("organizations.common.failed"),
1693
+ defaultMessage: "Failed"
1694
+ })
1695
+ );
1696
+ return result.data;
1697
+ },
1698
+ onSuccess: () => {
1699
+ qc.invalidateQueries({
1700
+ queryKey: ["dash-team-members", organizationId, team.id]
1701
+ });
1702
+ setAddUserId("");
1703
+ }
1704
+ });
1705
+ const removeTeamMemberMutation = reactQuery.useMutation({
1706
+ mutationFn: async (userId) => {
1707
+ const result = await index.client.dash.organization.removeTeamMember(
1708
+ { teamId: team.id, userId },
1709
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1710
+ );
1711
+ if (result.error)
1712
+ throw new Error(
1713
+ result.error.message ?? formatMessage({
1714
+ id: index.getTranslation("organizations.common.failed"),
1715
+ defaultMessage: "Failed"
1716
+ })
1717
+ );
1718
+ },
1719
+ onSuccess: () => {
1720
+ setConfirmRemoveTeamMemberId(null);
1721
+ qc.invalidateQueries({
1722
+ queryKey: ["dash-team-members", organizationId, team.id]
1723
+ });
1724
+ }
1725
+ });
1726
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { width: "100%", children: [
1727
+ /* @__PURE__ */ jsxRuntime.jsx(
1728
+ designSystem.Accordion.Root,
1729
+ {
1730
+ value: expanded ? team.id : "",
1731
+ onValueChange: (v) => setExpanded(v === team.id),
1732
+ collapsible: true,
1733
+ children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Accordion.Item, { value: team.id, "data-testid": "team-row", children: [
1734
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Accordion.Header, { children: [
1735
+ /* @__PURE__ */ jsxRuntime.jsx(
1736
+ designSystem.Accordion.Trigger,
1737
+ {
1738
+ description: formatMessage(
1739
+ {
1740
+ id: index.getTranslation("organizations.detail.teams.memberCount"),
1741
+ defaultMessage: "{count, plural, =-1 {— members} one {# member} other {# members}}"
1742
+ },
1743
+ { count: teamMembersQuery.data?.length ?? -1 }
1744
+ ),
1745
+ children: team.name
1746
+ }
1747
+ ),
1748
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Actions, { children: /* @__PURE__ */ jsxRuntime.jsx(
1749
+ designSystem.IconButton,
1750
+ {
1751
+ label: formatMessage({
1752
+ id: index.getTranslation("organizations.detail.teams.delete"),
1753
+ defaultMessage: "Delete team"
1754
+ }),
1755
+ variant: "ghost",
1756
+ onClick: onDelete,
1757
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1758
+ }
1759
+ ) })
1760
+ ] }),
1761
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Accordion.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 3, padding: 4, children: [
1762
+ /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, alignItems: "flex-end", children: [
1763
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { flex: 1, children: /* @__PURE__ */ jsxRuntime.jsx(
1764
+ UserCombobox,
1765
+ {
1766
+ label: formatMessage({
1767
+ id: index.getTranslation(
1768
+ "organizations.detail.teams.addMember"
1769
+ ),
1770
+ defaultMessage: "Add member to team"
1771
+ }),
1772
+ value: addUserId,
1773
+ onChange: setAddUserId
1774
+ }
1775
+ ) }),
1776
+ /* @__PURE__ */ jsxRuntime.jsx(
1777
+ designSystem.Button,
1778
+ {
1779
+ size: "S",
1780
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
1781
+ disabled: !addUserId,
1782
+ loading: addTeamMemberMutation.isLoading,
1783
+ onClick: () => addTeamMemberMutation.mutate(),
1784
+ children: formatMessage({
1785
+ id: index.getTranslation(
1786
+ "organizations.detail.teams.addMemberSubmit"
1787
+ ),
1788
+ defaultMessage: "Add"
1789
+ })
1790
+ }
1791
+ )
1792
+ ] }),
1793
+ teamMembersQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(InlineLoader, {}) : (teamMembersQuery.data ?? []).length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: formatMessage({
1794
+ id: index.getTranslation(
1795
+ "organizations.detail.teams.membersEmpty"
1796
+ ),
1797
+ defaultMessage: "No members in this team."
1798
+ }) }) : /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 2, children: (teamMembersQuery.data ?? []).map((tm) => /* @__PURE__ */ jsxRuntime.jsxs(useModelSchema.AccountRow, { children: [
1799
+ /* @__PURE__ */ jsxRuntime.jsxs(
1800
+ designSystem.Flex,
1801
+ {
1802
+ direction: "column",
1803
+ gap: 1,
1804
+ alignItems: "flex-start",
1805
+ flex: 1,
1806
+ children: [
1807
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "omega", fontWeight: "semiBold", children: tm.user?.name ?? tm.userId }),
1808
+ tm.user?.email && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "pi", textColor: "neutral500", children: tm.user.email })
1809
+ ]
1810
+ }
1811
+ ),
1812
+ /* @__PURE__ */ jsxRuntime.jsx(
1813
+ designSystem.IconButton,
1814
+ {
1815
+ label: formatMessage({
1816
+ id: index.getTranslation(
1817
+ "organizations.detail.teams.removeMember"
1818
+ ),
1819
+ defaultMessage: "Remove from team"
1820
+ }),
1821
+ onClick: () => setConfirmRemoveTeamMemberId(tm.userId),
1822
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
1823
+ }
1824
+ )
1825
+ ] }, tm.id)) })
1826
+ ] }) })
1827
+ ] })
1828
+ }
1829
+ ),
1830
+ confirmRemoveTeamMemberId && /* @__PURE__ */ jsxRuntime.jsx(
1831
+ useModelSchema.ConfirmDialog,
1832
+ {
1833
+ title: formatMessage({
1834
+ id: index.getTranslation("organizations.detail.teams.removeMember"),
1835
+ defaultMessage: "Remove from team"
1836
+ }),
1837
+ message: formatMessage({
1838
+ id: index.getTranslation(
1839
+ "organizations.detail.teams.confirmRemoveMember.message"
1840
+ ),
1841
+ defaultMessage: "Are you sure you want to remove this member from the team?"
1842
+ }),
1843
+ confirmLabel: formatMessage({
1844
+ id: index.getTranslation("organizations.common.remove"),
1845
+ defaultMessage: "Remove"
1846
+ }),
1847
+ loading: removeTeamMemberMutation.isLoading,
1848
+ onConfirm: () => removeTeamMemberMutation.mutate(confirmRemoveTeamMemberId),
1849
+ onCancel: () => setConfirmRemoveTeamMemberId(null)
1850
+ }
1851
+ )
1852
+ ] });
1853
+ }
1854
+ const PAGE_SIZE = 25;
1855
+ function OrganizationsPage({ teamsEnabled }) {
1856
+ const { formatMessage } = reactIntl.useIntl();
1857
+ const rbac = admin.useRBAC(index.PERMISSIONS.organization);
1858
+ const qc = reactQuery.useQueryClient();
1859
+ const { toggleNotification } = admin.useNotification();
1860
+ const [page, setPage] = react.useState(1);
1861
+ const [search, setSearch] = react.useState("");
1862
+ const [searchInput, setSearchInput] = react.useState("");
1863
+ const [selected, setSelected] = react.useState(/* @__PURE__ */ new Set());
1864
+ const [showCreate, setShowCreate] = react.useState(false);
1865
+ const [detailOrgId, setDetailOrgId] = react.useState(null);
1866
+ const [confirmDelete, setConfirmDelete] = react.useState(null);
1867
+ const [confirmDeleteMany, setConfirmDeleteMany] = react.useState(false);
1868
+ const offset = (page - 1) * PAGE_SIZE;
1869
+ const orgsQuery = reactQuery.useQuery({
1870
+ queryKey: ["dash-organizations", page, search],
1871
+ queryFn: async () => {
1872
+ const result = await index.client.dash.listOrganizations({
1873
+ query: {
1874
+ limit: PAGE_SIZE,
1875
+ offset,
1876
+ sortBy: "createdAt",
1877
+ sortOrder: "desc",
1878
+ ...search ? { search } : {}
1879
+ }
1880
+ });
1881
+ if (result.error)
1882
+ throw new Error(
1883
+ result.error.message ?? formatMessage({
1884
+ id: index.getTranslation("organizations.list.error.load"),
1885
+ defaultMessage: "Failed to load organizations"
1886
+ })
1887
+ );
1888
+ return result.data;
1889
+ },
1890
+ keepPreviousData: true
1891
+ });
1892
+ const deleteMutation = reactQuery.useMutation({
1893
+ mutationFn: async (organizationId) => {
1894
+ const result = await index.client.dash.organization.delete(
1895
+ { organizationId },
1896
+ useModelSchema.withContext({ organizationId }, index.getAuthHeaders())
1897
+ );
1898
+ if (result.error)
1899
+ throw new Error(
1900
+ result.error.message ?? formatMessage({
1901
+ id: index.getTranslation("organizations.common.deleteFailed"),
1902
+ defaultMessage: "Delete failed"
1903
+ })
1904
+ );
1905
+ },
1906
+ onSuccess: () => {
1907
+ setConfirmDelete(null);
1908
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
1909
+ toggleNotification({
1910
+ type: "success",
1911
+ message: formatMessage({
1912
+ id: index.getTranslation("organizations.list.notification.deleted"),
1913
+ defaultMessage: "Organization deleted"
1914
+ })
1915
+ });
1916
+ },
1917
+ onError: (err) => {
1918
+ toggleNotification({
1919
+ type: "danger",
1920
+ message: err.message ?? formatMessage({
1921
+ id: index.getTranslation("organizations.list.notification.deleteError"),
1922
+ defaultMessage: "Failed to delete organization"
1923
+ })
1924
+ });
1925
+ }
1926
+ });
1927
+ const deleteManyMutation = reactQuery.useMutation({
1928
+ mutationFn: async (organizationIds) => {
1929
+ const result = await index.client.dash.organization.deleteMany(
1930
+ {},
1931
+ useModelSchema.withContext({ organizationIds }, index.getAuthHeaders())
1932
+ );
1933
+ if (result.error)
1934
+ throw new Error(
1935
+ result.error.message ?? formatMessage({
1936
+ id: index.getTranslation("organizations.common.deleteFailed"),
1937
+ defaultMessage: "Delete failed"
1938
+ })
1939
+ );
1940
+ return result.data;
1941
+ },
1942
+ onSuccess: (_data, organizationIds) => {
1943
+ setConfirmDeleteMany(false);
1944
+ setSelected(/* @__PURE__ */ new Set());
1945
+ qc.invalidateQueries({ queryKey: ["dash-organizations"] });
1946
+ toggleNotification({
1947
+ type: "success",
1948
+ message: formatMessage(
1949
+ {
1950
+ id: index.getTranslation("organizations.list.notification.deletedMany"),
1951
+ defaultMessage: "{count, plural, one {# organization} other {# organizations}} deleted"
1952
+ },
1953
+ { count: organizationIds.length }
1954
+ )
1955
+ });
1956
+ },
1957
+ onError: (err) => {
1958
+ toggleNotification({
1959
+ type: "danger",
1960
+ message: err.message ?? formatMessage({
1961
+ id: index.getTranslation(
1962
+ "organizations.list.notification.deleteManyError"
1963
+ ),
1964
+ defaultMessage: "Failed to delete organizations"
1965
+ })
1966
+ });
1967
+ }
1968
+ });
1969
+ const orgs = orgsQuery.data && "organizations" in orgsQuery.data ? orgsQuery.data.organizations : [];
1970
+ const total = orgsQuery.data && "total" in orgsQuery.data ? orgsQuery.data.total : 0;
1971
+ const pageCount = Math.ceil(total / PAGE_SIZE);
1972
+ const allSelected = orgs.length > 0 && orgs.every((o) => selected.has(o.id));
1973
+ const someSelected = selected.size > 0;
1974
+ const toggleSelect = (id) => {
1975
+ setSelected((prev) => {
1976
+ const next = new Set(prev);
1977
+ if (next.has(id)) next.delete(id);
1978
+ else next.add(id);
1979
+ return next;
1980
+ });
1981
+ };
1982
+ const handleSelectAll = () => {
1983
+ if (allSelected) setSelected(/* @__PURE__ */ new Set());
1984
+ else setSelected(new Set(orgs.map((o) => o.id)));
1985
+ };
1986
+ const handleSearch = (e) => {
1987
+ e.preventDefault();
1988
+ setSearch(searchInput);
1989
+ setPage(1);
1990
+ };
1991
+ const colCount = rbac.allowedActions.canDelete ? 6 : 5;
1992
+ return /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Main, { "data-testid": "organizations-page", children: [
1993
+ /* @__PURE__ */ jsxRuntime.jsx(
1994
+ admin.Layouts.Header,
1995
+ {
1996
+ title: formatMessage({
1997
+ id: index.getTranslation("organizations.list.header.title"),
1998
+ defaultMessage: "Organizations"
1999
+ }),
2000
+ subtitle: formatMessage(
2001
+ {
2002
+ id: index.getTranslation("organizations.list.header.subtitle"),
2003
+ defaultMessage: "{total} total"
2004
+ },
2005
+ { total: total.toLocaleString() }
2006
+ ),
2007
+ primaryAction: rbac.allowedActions.canCreate && /* @__PURE__ */ jsxRuntime.jsx(
2008
+ designSystem.Button,
2009
+ {
2010
+ startIcon: /* @__PURE__ */ jsxRuntime.jsx(icons.Plus, {}),
2011
+ onClick: () => setShowCreate(true),
2012
+ "data-testid": "create-org-btn",
2013
+ children: formatMessage({
2014
+ id: index.getTranslation("organizations.common.createOrganization"),
2015
+ defaultMessage: "Create organization"
2016
+ })
2017
+ }
2018
+ )
2019
+ }
2020
+ ),
2021
+ /* @__PURE__ */ jsxRuntime.jsx(
2022
+ admin.Layouts.Action,
2023
+ {
2024
+ startActions: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 2, children: [
2025
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.SearchForm, { onSubmit: handleSearch, children: /* @__PURE__ */ jsxRuntime.jsx(
2026
+ designSystem.Searchbar,
2027
+ {
2028
+ clearLabel: formatMessage({
2029
+ id: index.getTranslation("organizations.list.search.clear"),
2030
+ defaultMessage: "Clear"
2031
+ }),
2032
+ name: "search",
2033
+ placeholder: formatMessage({
2034
+ id: index.getTranslation("organizations.list.search.placeholder"),
2035
+ defaultMessage: "Search organizations…"
2036
+ }),
2037
+ value: searchInput,
2038
+ onChange: (e) => setSearchInput(e.target.value),
2039
+ onClear: () => {
2040
+ setSearchInput("");
2041
+ setSearch("");
2042
+ setPage(1);
2043
+ },
2044
+ children: formatMessage({
2045
+ id: index.getTranslation("organizations.list.search.label"),
2046
+ defaultMessage: "Search organizations"
2047
+ })
2048
+ }
2049
+ ) }),
2050
+ someSelected && rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsx(
2051
+ designSystem.Button,
2052
+ {
2053
+ variant: "danger-light",
2054
+ size: "S",
2055
+ onClick: () => setConfirmDeleteMany(true),
2056
+ "data-testid": "delete-selected-orgs-btn",
2057
+ children: formatMessage(
2058
+ {
2059
+ id: index.getTranslation("organizations.list.deleteSelected"),
2060
+ defaultMessage: "Delete {count} selected"
2061
+ },
2062
+ { count: selected.size }
2063
+ )
2064
+ }
2065
+ )
2066
+ ] })
2067
+ }
2068
+ ),
2069
+ /* @__PURE__ */ jsxRuntime.jsx(admin.Layouts.Content, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
2070
+ orgsQuery.isError && /* @__PURE__ */ jsxRuntime.jsx(
2071
+ designSystem.Alert,
2072
+ {
2073
+ closeLabel: formatMessage({
2074
+ id: index.getTranslation("organizations.common.close"),
2075
+ defaultMessage: "Close"
2076
+ }),
2077
+ title: formatMessage({
2078
+ id: index.getTranslation("organizations.list.error.title"),
2079
+ defaultMessage: "Error"
2080
+ }),
2081
+ variant: "danger",
2082
+ children: orgsQuery.error instanceof Error ? orgsQuery.error.message : formatMessage({
2083
+ id: index.getTranslation("organizations.common.errorOccurred"),
2084
+ defaultMessage: "An error occurred"
2085
+ })
2086
+ }
2087
+ ),
2088
+ orgsQuery.isLoading ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Loader, { children: formatMessage({
2089
+ id: index.getTranslation("organizations.list.loading"),
2090
+ defaultMessage: "Loading organizations…"
2091
+ }) }) }) : /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Table, { colCount, rowCount: orgs.length + 1, children: [
2092
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Thead, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { children: [
2093
+ rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(
2094
+ designSystem.Checkbox,
2095
+ {
2096
+ checked: allSelected ? true : someSelected ? "indeterminate" : false,
2097
+ onCheckedChange: handleSelectAll,
2098
+ "aria-label": formatMessage({
2099
+ id: index.getTranslation(
2100
+ "organizations.list.table.selectAll"
2101
+ ),
2102
+ defaultMessage: "Select all"
2103
+ })
2104
+ }
2105
+ ) }),
2106
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: formatMessage({
2107
+ id: index.getTranslation("organizations.common.name"),
2108
+ defaultMessage: "Name"
2109
+ }) }) }),
2110
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: formatMessage({
2111
+ id: index.getTranslation("organizations.common.slug"),
2112
+ defaultMessage: "Slug"
2113
+ }) }) }),
2114
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: formatMessage({
2115
+ id: index.getTranslation("organizations.common.members"),
2116
+ defaultMessage: "Members"
2117
+ }) }) }),
2118
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { variant: "sigma", children: formatMessage({
2119
+ id: index.getTranslation("organizations.common.created"),
2120
+ defaultMessage: "Created"
2121
+ }) }) }),
2122
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Th, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.VisuallyHidden, { children: formatMessage({
2123
+ id: index.getTranslation("organizations.list.table.actions"),
2124
+ defaultMessage: "Actions"
2125
+ }) }) })
2126
+ ] }) }),
2127
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tbody, { children: orgs.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(designSystem.Tr, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { colSpan: colCount, children: /* @__PURE__ */ jsxRuntime.jsx(
2128
+ designSystem.Flex,
2129
+ {
2130
+ justifyContent: "center",
2131
+ padding: 8,
2132
+ "data-testid": "orgs-empty",
2133
+ children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral600", children: search ? formatMessage(
2134
+ {
2135
+ id: index.getTranslation(
2136
+ "organizations.list.empty.search"
2137
+ ),
2138
+ defaultMessage: 'No organizations matching "{search}"'
2139
+ },
2140
+ { search }
2141
+ ) : formatMessage({
2142
+ id: index.getTranslation(
2143
+ "organizations.list.empty.default"
2144
+ ),
2145
+ defaultMessage: "No organizations found"
2146
+ }) })
2147
+ }
2148
+ ) }) }) : orgs.map((org) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Tr, { "data-testid": "org-row", children: [
2149
+ rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(
2150
+ designSystem.Checkbox,
2151
+ {
2152
+ checked: selected.has(org.id),
2153
+ onCheckedChange: () => toggleSelect(org.id),
2154
+ "aria-label": formatMessage(
2155
+ {
2156
+ id: index.getTranslation(
2157
+ "organizations.list.table.selectRow"
2158
+ ),
2159
+ defaultMessage: "Select {name}"
2160
+ },
2161
+ { name: org.name }
2162
+ )
2163
+ }
2164
+ ) }),
2165
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { alignItems: "center", gap: 2, children: [
2166
+ /* @__PURE__ */ jsxRuntime.jsx(Avatar.Avatar, { name: org.name, src: org.logo, size: 28 }),
2167
+ /* @__PURE__ */ jsxRuntime.jsx(
2168
+ designSystem.Typography,
2169
+ {
2170
+ fontWeight: "semiBold",
2171
+ textColor: "neutral800",
2172
+ children: org.name
2173
+ }
2174
+ )
2175
+ ] }) }),
2176
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(useModelSchema.Code, { variant: "pi", textColor: "neutral600", children: org.slug }) }),
2177
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Badge, { size: "S", children: org.memberCount }) }),
2178
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Typography, { textColor: "neutral800", children: new Date(org.createdAt).toLocaleDateString() }) }),
2179
+ /* @__PURE__ */ jsxRuntime.jsx(designSystem.Td, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { gap: 1, justifyContent: "flex-end", children: [
2180
+ rbac.allowedActions.canUpdate && /* @__PURE__ */ jsxRuntime.jsx(
2181
+ designSystem.IconButton,
2182
+ {
2183
+ label: formatMessage({
2184
+ id: index.getTranslation(
2185
+ "organizations.list.table.edit"
2186
+ ),
2187
+ defaultMessage: "Edit organization"
2188
+ }),
2189
+ variant: "ghost",
2190
+ onClick: () => setDetailOrgId(org.id),
2191
+ "data-testid": "edit-org-btn",
2192
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Pencil, {})
2193
+ }
2194
+ ),
2195
+ rbac.allowedActions.canDelete && /* @__PURE__ */ jsxRuntime.jsx(
2196
+ designSystem.IconButton,
2197
+ {
2198
+ label: formatMessage({
2199
+ id: index.getTranslation(
2200
+ "organizations.common.deleteOrganization"
2201
+ ),
2202
+ defaultMessage: "Delete organization"
2203
+ }),
2204
+ variant: "ghost",
2205
+ onClick: () => setConfirmDelete(org.id),
2206
+ "data-testid": "delete-org-btn",
2207
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, {})
2208
+ }
2209
+ )
2210
+ ] }) })
2211
+ ] }, org.id)) })
2212
+ ] }),
2213
+ pageCount > 1 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "flex-end", gap: 2, children: [
2214
+ /* @__PURE__ */ jsxRuntime.jsx(
2215
+ designSystem.Button,
2216
+ {
2217
+ variant: "tertiary",
2218
+ size: "S",
2219
+ disabled: page === 1,
2220
+ onClick: () => setPage((p) => p - 1),
2221
+ children: formatMessage({
2222
+ id: index.getTranslation("organizations.list.pagination.previous"),
2223
+ defaultMessage: "Previous"
2224
+ })
2225
+ }
2226
+ ),
2227
+ /* @__PURE__ */ jsxRuntime.jsx(
2228
+ designSystem.Button,
2229
+ {
2230
+ variant: "tertiary",
2231
+ size: "S",
2232
+ disabled: page >= pageCount,
2233
+ onClick: () => setPage((p) => p + 1),
2234
+ children: formatMessage({
2235
+ id: index.getTranslation("organizations.list.pagination.next"),
2236
+ defaultMessage: "Next"
2237
+ })
2238
+ }
2239
+ )
2240
+ ] })
2241
+ ] }) }),
2242
+ rbac.allowedActions.canCreate && showCreate && /* @__PURE__ */ jsxRuntime.jsx(
2243
+ CreateOrganizationDialog,
2244
+ {
2245
+ teamsEnabled,
2246
+ onClose: () => setShowCreate(false)
2247
+ }
2248
+ ),
2249
+ rbac.allowedActions.canUpdate && detailOrgId && /* @__PURE__ */ jsxRuntime.jsx(
2250
+ OrganizationDetail,
2251
+ {
2252
+ organizationId: detailOrgId,
2253
+ teamsEnabled,
2254
+ onClose: () => setDetailOrgId(null)
2255
+ }
2256
+ ),
2257
+ rbac.allowedActions.canDelete && confirmDelete && /* @__PURE__ */ jsxRuntime.jsx(
2258
+ useModelSchema.ConfirmDialog,
2259
+ {
2260
+ title: formatMessage({
2261
+ id: index.getTranslation("organizations.common.deleteOrganization"),
2262
+ defaultMessage: "Delete organization"
2263
+ }),
2264
+ message: formatMessage({
2265
+ id: index.getTranslation("organizations.list.confirmDelete.message"),
2266
+ defaultMessage: "Are you sure you want to delete this organization? All members and teams will be removed. This action cannot be undone."
2267
+ }),
2268
+ confirmLabel: formatMessage({
2269
+ id: index.getTranslation("organizations.common.delete"),
2270
+ defaultMessage: "Delete"
2271
+ }),
2272
+ loading: deleteMutation.isLoading,
2273
+ onConfirm: () => deleteMutation.mutate(confirmDelete),
2274
+ onCancel: () => setConfirmDelete(null)
2275
+ }
2276
+ ),
2277
+ rbac.allowedActions.canDelete && confirmDeleteMany && /* @__PURE__ */ jsxRuntime.jsx(
2278
+ useModelSchema.ConfirmDialog,
2279
+ {
2280
+ title: formatMessage(
2281
+ {
2282
+ id: index.getTranslation("organizations.list.confirmDeleteMany.title"),
2283
+ defaultMessage: "Delete {count, plural, one {# organization} other {# organizations}}"
2284
+ },
2285
+ { count: selected.size }
2286
+ ),
2287
+ message: formatMessage(
2288
+ {
2289
+ id: index.getTranslation(
2290
+ "organizations.list.confirmDeleteMany.message"
2291
+ ),
2292
+ defaultMessage: "Are you sure you want to delete {count, plural, one {# organization} other {# organizations}}? This action cannot be undone."
2293
+ },
2294
+ { count: selected.size }
2295
+ ),
2296
+ confirmLabel: formatMessage({
2297
+ id: index.getTranslation("organizations.list.confirmDeleteMany.confirm"),
2298
+ defaultMessage: "Delete all"
2299
+ }),
2300
+ loading: deleteManyMutation.isLoading,
2301
+ onConfirm: () => deleteManyMutation.mutate([...selected]),
2302
+ onCancel: () => setConfirmDeleteMany(false)
2303
+ }
2304
+ )
2305
+ ] });
2306
+ }
2307
+ function ProtectedOrganizationsPage() {
2308
+ const { data: organizationOptions } = index.useOrganizationOptions();
2309
+ const teamsEnabled = organizationOptions?.teamsEnabled ?? false;
2310
+ return /* @__PURE__ */ jsxRuntime.jsx(
2311
+ admin.Page.Protect,
2312
+ {
2313
+ permissions: index.PERMISSIONS.organization.filter(
2314
+ (p) => p.action === `plugin::${index.PLUGIN_ID}.organization.read`
2315
+ ),
2316
+ children: /* @__PURE__ */ jsxRuntime.jsx(OrganizationsPage, { teamsEnabled })
2317
+ }
2318
+ );
2319
+ }
2320
+ exports.OrganizationsPage = OrganizationsPage;
2321
+ exports.default = ProtectedOrganizationsPage;