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