@datatechsolutions/ui 2.11.86 → 2.11.88

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 (75) hide show
  1. package/dist/billing-panel-DsHhhJqG.d.mts +18 -0
  2. package/dist/billing-panel-DsHhhJqG.d.ts +18 -0
  3. package/dist/chunk-4667D2ZT.mjs +61 -0
  4. package/dist/chunk-4667D2ZT.mjs.map +1 -0
  5. package/dist/chunk-5HXDJBVX.mjs +1330 -0
  6. package/dist/chunk-5HXDJBVX.mjs.map +1 -0
  7. package/dist/chunk-DJ33CSGJ.mjs +126 -0
  8. package/dist/chunk-DJ33CSGJ.mjs.map +1 -0
  9. package/dist/chunk-F4TOOARV.mjs +503 -0
  10. package/dist/chunk-F4TOOARV.mjs.map +1 -0
  11. package/dist/chunk-GEUGFYLO.mjs +237 -0
  12. package/dist/chunk-GEUGFYLO.mjs.map +1 -0
  13. package/dist/chunk-LBALE4JX.js +1342 -0
  14. package/dist/chunk-LBALE4JX.js.map +1 -0
  15. package/dist/chunk-MXFEU7A6.js +148 -0
  16. package/dist/chunk-MXFEU7A6.js.map +1 -0
  17. package/dist/chunk-NBCOVUQP.mjs +142 -0
  18. package/dist/chunk-NBCOVUQP.mjs.map +1 -0
  19. package/dist/chunk-P4RVGMZL.js +128 -0
  20. package/dist/chunk-P4RVGMZL.js.map +1 -0
  21. package/dist/chunk-Q2MG7S2E.js +239 -0
  22. package/dist/chunk-Q2MG7S2E.js.map +1 -0
  23. package/dist/chunk-RV555OEO.mjs +1009 -0
  24. package/dist/chunk-RV555OEO.mjs.map +1 -0
  25. package/dist/chunk-SAYVWIMJ.js +63 -0
  26. package/dist/chunk-SAYVWIMJ.js.map +1 -0
  27. package/dist/chunk-SUHNSUMH.mjs +1021 -0
  28. package/dist/chunk-SUHNSUMH.mjs.map +1 -0
  29. package/dist/chunk-TOEMSC4P.mjs +99 -0
  30. package/dist/chunk-TOEMSC4P.mjs.map +1 -0
  31. package/dist/chunk-UUHV5KHF.js +505 -0
  32. package/dist/chunk-UUHV5KHF.js.map +1 -0
  33. package/dist/chunk-UVEPTYZC.js +101 -0
  34. package/dist/chunk-UVEPTYZC.js.map +1 -0
  35. package/dist/chunk-X2KCCQPL.js +1049 -0
  36. package/dist/chunk-X2KCCQPL.js.map +1 -0
  37. package/dist/chunk-ZARCUQA6.js +1015 -0
  38. package/dist/chunk-ZARCUQA6.js.map +1 -0
  39. package/dist/platform/admin/index.d.mts +17 -0
  40. package/dist/platform/admin/index.d.ts +17 -0
  41. package/dist/platform/admin/index.js +39 -0
  42. package/dist/platform/admin/index.js.map +1 -0
  43. package/dist/platform/admin/index.mjs +10 -0
  44. package/dist/platform/admin/index.mjs.map +1 -0
  45. package/dist/platform/auth/index.d.mts +73 -0
  46. package/dist/platform/auth/index.d.ts +73 -0
  47. package/dist/platform/auth/index.js +107 -0
  48. package/dist/platform/auth/index.js.map +1 -0
  49. package/dist/platform/auth/index.mjs +10 -0
  50. package/dist/platform/auth/index.mjs.map +1 -0
  51. package/dist/platform/billing/index.d.mts +29 -0
  52. package/dist/platform/billing/index.d.ts +29 -0
  53. package/dist/platform/billing/index.js +22 -0
  54. package/dist/platform/billing/index.js.map +1 -0
  55. package/dist/platform/billing/index.mjs +9 -0
  56. package/dist/platform/billing/index.mjs.map +1 -0
  57. package/dist/platform/impersonation/index.d.mts +19 -0
  58. package/dist/platform/impersonation/index.d.ts +19 -0
  59. package/dist/platform/impersonation/index.js +17 -0
  60. package/dist/platform/impersonation/index.js.map +1 -0
  61. package/dist/platform/impersonation/index.mjs +8 -0
  62. package/dist/platform/impersonation/index.mjs.map +1 -0
  63. package/dist/platform/index.d.mts +45 -2
  64. package/dist/platform/index.d.ts +45 -2
  65. package/dist/platform/index.js +4850 -0
  66. package/dist/platform/index.js.map +1 -1
  67. package/dist/platform/index.mjs +4716 -3
  68. package/dist/platform/index.mjs.map +1 -1
  69. package/dist/platform/settings/index.d.mts +31 -0
  70. package/dist/platform/settings/index.d.ts +31 -0
  71. package/dist/platform/settings/index.js +21 -0
  72. package/dist/platform/settings/index.js.map +1 -0
  73. package/dist/platform/settings/index.mjs +12 -0
  74. package/dist/platform/settings/index.mjs.map +1 -0
  75. package/package.json +26 -1
@@ -0,0 +1,1009 @@
1
+ "use client";
2
+ import { PasswordStrengthMeter } from './chunk-NBCOVUQP.mjs';
3
+ import { SectionCard, SearchInput, InlineSpinner, EmptyState, Avatar, Badge, StatusBadge, Button, DynamicIslandConfirm, GlassModal, Input, PasswordInput, Sheet } from './chunk-ZJQ5RLGK.mjs';
4
+ import { triggerHaptic } from './chunk-D2JF6C3E.mjs';
5
+ import { useTranslations, useFormatter } from './chunk-7VJ7CMMT.mjs';
6
+ import { useState, useCallback, useEffect } from 'react';
7
+ import { UsersIcon, ShieldCheckIcon, KeyIcon, TrashIcon, UserPlusIcon, BuildingOffice2Icon, PencilSquareIcon, PlusIcon } from '@heroicons/react/24/outline';
8
+ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
9
+
10
+ var PAGE_SIZE = 20;
11
+ function AdminUserList({ client, onUserSelect }) {
12
+ const t = useTranslations("windsock");
13
+ const format = useFormatter();
14
+ const [users, setUsers] = useState([]);
15
+ const [total, setTotal] = useState(0);
16
+ const [isLoading, setIsLoading] = useState(true);
17
+ const [search, setSearch] = useState("");
18
+ const [offset, setOffset] = useState(0);
19
+ const [deleteTarget, setDeleteTarget] = useState(null);
20
+ const [isDeletingUser, setIsDeletingUser] = useState(false);
21
+ const fetchUsers = useCallback(async (searchQuery, pageOffset) => {
22
+ setIsLoading(true);
23
+ try {
24
+ const result = await client.listUsers({
25
+ search: searchQuery || void 0,
26
+ limit: PAGE_SIZE,
27
+ offset: pageOffset
28
+ });
29
+ setUsers(result.items);
30
+ setTotal(result.total);
31
+ } catch {
32
+ } finally {
33
+ setIsLoading(false);
34
+ }
35
+ }, [client]);
36
+ useEffect(() => {
37
+ fetchUsers(search, offset);
38
+ }, [fetchUsers, search, offset]);
39
+ const handleSearch = useCallback((value) => {
40
+ setSearch(value);
41
+ setOffset(0);
42
+ }, []);
43
+ const handleNextPage = useCallback(() => {
44
+ triggerHaptic("light");
45
+ setOffset((previous) => previous + PAGE_SIZE);
46
+ }, []);
47
+ const handlePreviousPage = useCallback(() => {
48
+ triggerHaptic("light");
49
+ setOffset((previous) => Math.max(0, previous - PAGE_SIZE));
50
+ }, []);
51
+ const handleDeleteRequest = useCallback((user) => {
52
+ triggerHaptic("warning");
53
+ setDeleteTarget(user);
54
+ }, []);
55
+ const handleDeleteConfirm = useCallback(async () => {
56
+ if (!deleteTarget) return;
57
+ setIsDeletingUser(true);
58
+ triggerHaptic("medium");
59
+ try {
60
+ await client.deleteUser(deleteTarget.id);
61
+ setDeleteTarget(null);
62
+ triggerHaptic("success");
63
+ fetchUsers(search, offset);
64
+ } catch {
65
+ triggerHaptic("error");
66
+ } finally {
67
+ setIsDeletingUser(false);
68
+ }
69
+ }, [deleteTarget, client, fetchUsers, search, offset]);
70
+ const handleDeleteCancel = useCallback(() => {
71
+ triggerHaptic("light");
72
+ setDeleteTarget(null);
73
+ }, []);
74
+ const handleUserClick = useCallback((user) => {
75
+ triggerHaptic("light");
76
+ onUserSelect?.(user);
77
+ }, [onUserSelect]);
78
+ const totalPages = Math.ceil(total / PAGE_SIZE);
79
+ const currentPage = Math.floor(offset / PAGE_SIZE) + 1;
80
+ const roleBadgeColor = (role) => {
81
+ if (role === "owner" || role === "super_admin") return "purple";
82
+ if (role === "admin") return "blue";
83
+ if (role === "manager") return "amber";
84
+ return "zinc";
85
+ };
86
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
87
+ /* @__PURE__ */ jsxs(
88
+ SectionCard,
89
+ {
90
+ header: {
91
+ icon: /* @__PURE__ */ jsx(UsersIcon, { className: "h-5 w-5 text-white" }),
92
+ title: t("admin.users.title"),
93
+ subtitle: t("admin.users.subtitle"),
94
+ gradient: "from-blue-500 via-indigo-500 to-violet-500"
95
+ },
96
+ padded: false,
97
+ children: [
98
+ /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200/60 p-4 dark:border-white/10", children: /* @__PURE__ */ jsx(
99
+ SearchInput,
100
+ {
101
+ value: search,
102
+ onChange: (event) => handleSearch(event.target.value),
103
+ placeholder: t("admin.users.searchPlaceholder")
104
+ }
105
+ ) }),
106
+ isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 p-8 text-sm text-gray-500 dark:text-gray-400", children: [
107
+ /* @__PURE__ */ jsx(InlineSpinner, {}),
108
+ /* @__PURE__ */ jsx("span", { children: t("admin.users.loading") })
109
+ ] }) : users.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 sm:p-6", children: /* @__PURE__ */ jsx(
110
+ EmptyState,
111
+ {
112
+ message: t("admin.users.emptyMessage"),
113
+ description: t("admin.users.emptyDescription"),
114
+ icon: UsersIcon,
115
+ variant: "card"
116
+ }
117
+ ) }) : /* @__PURE__ */ jsxs(Fragment, { children: [
118
+ /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
119
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-gray-200/60 dark:border-white/10", children: [
120
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.users.columnUser") }),
121
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.users.columnRole") }),
122
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.users.columnStatus") }),
123
+ /* @__PURE__ */ jsx("th", { className: "hidden px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 sm:table-cell", children: t("admin.users.columnSecurity") }),
124
+ /* @__PURE__ */ jsx("th", { className: "hidden px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 md:table-cell", children: t("admin.users.columnLastLogin") }),
125
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.users.columnActions") })
126
+ ] }) }),
127
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-gray-200/60 dark:divide-white/10", children: users.map((user) => /* @__PURE__ */ jsxs(
128
+ "tr",
129
+ {
130
+ className: "cursor-pointer transition-colors hover:bg-white/40 dark:hover:bg-white/5",
131
+ onClick: () => handleUserClick(user),
132
+ children: [
133
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
134
+ /* @__PURE__ */ jsx(
135
+ Avatar,
136
+ {
137
+ src: user.image ?? void 0,
138
+ initials: (user.name ?? user.email).slice(0, 2).toUpperCase(),
139
+ alt: user.name ?? user.email
140
+ }
141
+ ),
142
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
143
+ /* @__PURE__ */ jsx("p", { className: "truncate text-sm font-medium text-gray-900 dark:text-white", children: user.name ?? user.email }),
144
+ /* @__PURE__ */ jsx("p", { className: "truncate text-xs text-gray-500 dark:text-gray-400", children: user.email })
145
+ ] })
146
+ ] }) }),
147
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(Badge, { color: roleBadgeColor(user.role), children: user.role }) }),
148
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(
149
+ StatusBadge,
150
+ {
151
+ status: user.status === "active" ? "active" : "inactive",
152
+ label: user.status,
153
+ size: "sm"
154
+ }
155
+ ) }),
156
+ /* @__PURE__ */ jsx("td", { className: "hidden px-4 py-3 sm:table-cell", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
157
+ user.mfaEnabled && /* @__PURE__ */ jsx("span", { title: t("admin.users.mfaEnabled"), children: /* @__PURE__ */ jsx(ShieldCheckIcon, { className: "h-4 w-4 text-emerald-500" }) }),
158
+ user.emailVerified && /* @__PURE__ */ jsx("span", { title: t("admin.users.emailVerified"), children: /* @__PURE__ */ jsx(KeyIcon, { className: "h-4 w-4 text-blue-500" }) })
159
+ ] }) }),
160
+ /* @__PURE__ */ jsx("td", { className: "hidden px-4 py-3 text-gray-500 dark:text-gray-400 md:table-cell", children: user.lastLoginAt ? format.relativeTime(new Date(user.lastLoginAt)) : t("admin.users.neverLoggedIn") }),
161
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsx(
162
+ Button,
163
+ {
164
+ size: "sm",
165
+ color: "ios-glass-red",
166
+ onClick: (event) => {
167
+ event.stopPropagation();
168
+ handleDeleteRequest(user);
169
+ },
170
+ children: /* @__PURE__ */ jsx(TrashIcon, { className: "h-3.5 w-3.5" })
171
+ }
172
+ ) })
173
+ ]
174
+ },
175
+ user.id
176
+ )) })
177
+ ] }) }),
178
+ totalPages > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between border-t border-gray-200/60 px-4 py-3 dark:border-white/10", children: [
179
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: t("admin.users.pagination", { current: currentPage, total: totalPages, count: total }) }),
180
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
181
+ /* @__PURE__ */ jsx(
182
+ Button,
183
+ {
184
+ size: "sm",
185
+ outline: true,
186
+ onClick: handlePreviousPage,
187
+ disabled: offset === 0,
188
+ children: t("admin.users.previousPage")
189
+ }
190
+ ),
191
+ /* @__PURE__ */ jsx(
192
+ Button,
193
+ {
194
+ size: "sm",
195
+ outline: true,
196
+ onClick: handleNextPage,
197
+ disabled: offset + PAGE_SIZE >= total,
198
+ children: t("admin.users.nextPage")
199
+ }
200
+ )
201
+ ] })
202
+ ] })
203
+ ] })
204
+ ]
205
+ }
206
+ ),
207
+ /* @__PURE__ */ jsx(
208
+ DynamicIslandConfirm,
209
+ {
210
+ open: deleteTarget !== null,
211
+ onClose: handleDeleteCancel,
212
+ onConfirm: handleDeleteConfirm,
213
+ title: t("admin.users.deleteConfirmTitle", { name: deleteTarget?.name ?? deleteTarget?.email ?? "" }),
214
+ icon: /* @__PURE__ */ jsx(TrashIcon, { className: "h-5 w-5 text-white" }),
215
+ iconBackground: "bg-ios-red",
216
+ confirmLabel: t("admin.users.deleteConfirmButton"),
217
+ cancelLabel: t("admin.users.deleteCancelButton")
218
+ }
219
+ )
220
+ ] });
221
+ }
222
+ var DEFAULT_ROLES = ["viewer", "member", "operator", "analyst", "manager", "admin", "owner"];
223
+ function AdminUserForm({
224
+ open,
225
+ onClose,
226
+ onSubmit,
227
+ roles = DEFAULT_ROLES
228
+ }) {
229
+ const t = useTranslations("windsock");
230
+ const [email, setEmail] = useState("");
231
+ const [name, setName] = useState("");
232
+ const [password, setPassword] = useState("");
233
+ const [selectedRole, setSelectedRole] = useState("viewer");
234
+ const [isSubmitting, setIsSubmitting] = useState(false);
235
+ const [error, setError] = useState(null);
236
+ const resetForm = useCallback(() => {
237
+ setEmail("");
238
+ setName("");
239
+ setPassword("");
240
+ setSelectedRole("viewer");
241
+ setError(null);
242
+ }, []);
243
+ const handleClose = useCallback(() => {
244
+ resetForm();
245
+ onClose();
246
+ }, [resetForm, onClose]);
247
+ const handleSubmit = useCallback(async () => {
248
+ if (!email.trim()) {
249
+ setError(t("admin.userForm.errorEmailRequired"));
250
+ triggerHaptic("error");
251
+ return;
252
+ }
253
+ setError(null);
254
+ setIsSubmitting(true);
255
+ triggerHaptic("light");
256
+ try {
257
+ const data = {
258
+ email: email.trim().toLowerCase(),
259
+ name: name.trim() || void 0,
260
+ password: password || void 0,
261
+ role: selectedRole
262
+ };
263
+ await onSubmit(data);
264
+ triggerHaptic("success");
265
+ resetForm();
266
+ onClose();
267
+ } catch {
268
+ setError(t("admin.userForm.errorCreateFailed"));
269
+ triggerHaptic("error");
270
+ } finally {
271
+ setIsSubmitting(false);
272
+ }
273
+ }, [email, name, password, selectedRole, onSubmit, onClose, resetForm, t]);
274
+ return /* @__PURE__ */ jsx(
275
+ GlassModal,
276
+ {
277
+ open,
278
+ onClose: handleClose,
279
+ title: t("admin.userForm.title"),
280
+ subtitle: t("admin.userForm.subtitle"),
281
+ icon: /* @__PURE__ */ jsx(UserPlusIcon, { className: "h-5 w-5 text-white" }),
282
+ maxWidth: "md",
283
+ children: /* @__PURE__ */ jsxs(
284
+ "form",
285
+ {
286
+ onSubmit: (event) => {
287
+ event.preventDefault();
288
+ handleSubmit();
289
+ },
290
+ className: "space-y-4 p-4 sm:p-6",
291
+ children: [
292
+ /* @__PURE__ */ jsx(
293
+ Input,
294
+ {
295
+ label: t("admin.userForm.emailLabel"),
296
+ type: "email",
297
+ value: email,
298
+ onChange: (event) => setEmail(event.target.value),
299
+ placeholder: t("admin.userForm.emailPlaceholder"),
300
+ disabled: isSubmitting,
301
+ autoComplete: "email",
302
+ autoFocus: true
303
+ }
304
+ ),
305
+ /* @__PURE__ */ jsx(
306
+ Input,
307
+ {
308
+ label: t("admin.userForm.nameLabel"),
309
+ value: name,
310
+ onChange: (event) => setName(event.target.value),
311
+ placeholder: t("admin.userForm.namePlaceholder"),
312
+ disabled: isSubmitting,
313
+ autoComplete: "name"
314
+ }
315
+ ),
316
+ /* @__PURE__ */ jsx(
317
+ PasswordInput,
318
+ {
319
+ label: t("admin.userForm.passwordLabel"),
320
+ value: password,
321
+ onChange: (event) => setPassword(event.target.value),
322
+ placeholder: t("admin.userForm.passwordPlaceholder"),
323
+ disabled: isSubmitting,
324
+ autoComplete: "new-password"
325
+ }
326
+ ),
327
+ password && /* @__PURE__ */ jsx(PasswordStrengthMeter, { password }),
328
+ /* @__PURE__ */ jsxs("div", { children: [
329
+ /* @__PURE__ */ jsx("label", { className: "mb-1.5 block text-sm font-medium text-gray-900 dark:text-white", children: t("admin.userForm.roleLabel") }),
330
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-3 gap-2 sm:grid-cols-4", children: roles.map((role) => /* @__PURE__ */ jsx(
331
+ "button",
332
+ {
333
+ type: "button",
334
+ onClick: () => {
335
+ setSelectedRole(role);
336
+ triggerHaptic("light");
337
+ },
338
+ className: `rounded-xl border px-2 py-2 text-xs font-semibold backdrop-blur-xl transition ${selectedRole === role ? "border-indigo-400/80 bg-gradient-to-br from-indigo-100/85 via-white/80 to-sky-100/75 text-slate-900 dark:border-indigo-300/70 dark:bg-[linear-gradient(140deg,rgba(99,102,241,0.32)_0%,rgba(30,41,59,0.72)_100%)] dark:text-indigo-100" : "border-white/55 bg-gradient-to-br from-white/82 via-white/66 to-slate-100/62 text-slate-700 hover:from-white/92 hover:to-sky-100/72 dark:border-white/15 dark:bg-[linear-gradient(140deg,rgba(30,41,59,0.72)_0%,rgba(15,23,42,0.62)_100%)] dark:text-slate-100 dark:hover:bg-[linear-gradient(140deg,rgba(51,65,85,0.76)_0%,rgba(30,41,59,0.68)_100%)]"}`,
339
+ children: role
340
+ },
341
+ role
342
+ )) })
343
+ ] }),
344
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-red-600 dark:text-red-400", role: "alert", children: error }),
345
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 pt-2", children: [
346
+ /* @__PURE__ */ jsx(
347
+ Button,
348
+ {
349
+ type: "button",
350
+ outline: true,
351
+ onClick: handleClose,
352
+ disabled: isSubmitting,
353
+ className: "flex-1",
354
+ children: t("admin.userForm.cancelButton")
355
+ }
356
+ ),
357
+ /* @__PURE__ */ jsx(
358
+ Button,
359
+ {
360
+ type: "submit",
361
+ color: "ios-glass-blue",
362
+ loading: isSubmitting,
363
+ disabled: isSubmitting || !email.trim(),
364
+ className: "flex-1",
365
+ children: t("admin.userForm.createButton")
366
+ }
367
+ )
368
+ ] })
369
+ ]
370
+ }
371
+ )
372
+ }
373
+ );
374
+ }
375
+ function AdminOrganizationList({ client, onOrganizationSelect }) {
376
+ const t = useTranslations("windsock");
377
+ const format = useFormatter();
378
+ const [organizations, setOrganizations] = useState([]);
379
+ const [filteredOrganizations, setFilteredOrganizations] = useState([]);
380
+ const [isLoading, setIsLoading] = useState(true);
381
+ const [search, setSearch] = useState("");
382
+ const [deleteTarget, setDeleteTarget] = useState(null);
383
+ const [isDeleting, setIsDeleting] = useState(false);
384
+ const fetchOrganizations = useCallback(async () => {
385
+ setIsLoading(true);
386
+ try {
387
+ const result = await client.listOrganizations();
388
+ setOrganizations(result);
389
+ } catch {
390
+ } finally {
391
+ setIsLoading(false);
392
+ }
393
+ }, [client]);
394
+ useEffect(() => {
395
+ fetchOrganizations();
396
+ }, [fetchOrganizations]);
397
+ useEffect(() => {
398
+ if (!search.trim()) {
399
+ setFilteredOrganizations(organizations);
400
+ return;
401
+ }
402
+ const query = search.toLowerCase();
403
+ setFilteredOrganizations(
404
+ organizations.filter(
405
+ (organization) => organization.name.toLowerCase().includes(query) || organization.displayName?.toLowerCase().includes(query) || organization.description?.toLowerCase().includes(query)
406
+ )
407
+ );
408
+ }, [organizations, search]);
409
+ const handleOrganizationClick = useCallback((organization) => {
410
+ triggerHaptic("light");
411
+ onOrganizationSelect?.(organization);
412
+ }, [onOrganizationSelect]);
413
+ const handleDeleteRequest = useCallback((organization) => {
414
+ triggerHaptic("warning");
415
+ setDeleteTarget(organization);
416
+ }, []);
417
+ const handleDeleteConfirm = useCallback(async () => {
418
+ if (!deleteTarget) return;
419
+ setIsDeleting(true);
420
+ triggerHaptic("medium");
421
+ try {
422
+ await client.deleteOrganization(deleteTarget.id);
423
+ setDeleteTarget(null);
424
+ triggerHaptic("success");
425
+ fetchOrganizations();
426
+ } catch {
427
+ triggerHaptic("error");
428
+ } finally {
429
+ setIsDeleting(false);
430
+ }
431
+ }, [deleteTarget, client, fetchOrganizations]);
432
+ const handleDeleteCancel = useCallback(() => {
433
+ triggerHaptic("light");
434
+ setDeleteTarget(null);
435
+ }, []);
436
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
437
+ /* @__PURE__ */ jsxs(
438
+ SectionCard,
439
+ {
440
+ header: {
441
+ icon: /* @__PURE__ */ jsx(BuildingOffice2Icon, { className: "h-5 w-5 text-white" }),
442
+ title: t("admin.organizations.title"),
443
+ subtitle: t("admin.organizations.subtitle"),
444
+ gradient: "from-emerald-500 via-teal-500 to-cyan-500"
445
+ },
446
+ padded: false,
447
+ children: [
448
+ /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200/60 p-4 dark:border-white/10", children: /* @__PURE__ */ jsx(
449
+ SearchInput,
450
+ {
451
+ value: search,
452
+ onChange: (event) => setSearch(event.target.value),
453
+ placeholder: t("admin.organizations.searchPlaceholder")
454
+ }
455
+ ) }),
456
+ isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 p-8 text-sm text-gray-500 dark:text-gray-400", children: [
457
+ /* @__PURE__ */ jsx(InlineSpinner, {}),
458
+ /* @__PURE__ */ jsx("span", { children: t("admin.organizations.loading") })
459
+ ] }) : filteredOrganizations.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 sm:p-6", children: /* @__PURE__ */ jsx(
460
+ EmptyState,
461
+ {
462
+ message: t("admin.organizations.emptyMessage"),
463
+ description: t("admin.organizations.emptyDescription"),
464
+ icon: BuildingOffice2Icon,
465
+ variant: "card"
466
+ }
467
+ ) }) : /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
468
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-gray-200/60 dark:border-white/10", children: [
469
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.organizations.columnName") }),
470
+ /* @__PURE__ */ jsx("th", { className: "hidden px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 sm:table-cell", children: t("admin.organizations.columnDisplayName") }),
471
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.organizations.columnStatus") }),
472
+ /* @__PURE__ */ jsx("th", { className: "hidden px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 md:table-cell", children: t("admin.organizations.columnCreated") }),
473
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.organizations.columnActions") })
474
+ ] }) }),
475
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-gray-200/60 dark:divide-white/10", children: filteredOrganizations.map((organization) => /* @__PURE__ */ jsxs(
476
+ "tr",
477
+ {
478
+ className: "cursor-pointer transition-colors hover:bg-white/40 dark:hover:bg-white/5",
479
+ onClick: () => handleOrganizationClick(organization),
480
+ children: [
481
+ /* @__PURE__ */ jsxs("td", { className: "px-4 py-3", children: [
482
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-white", children: organization.name }),
483
+ organization.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 truncate text-xs text-gray-500 dark:text-gray-400", children: organization.description })
484
+ ] }),
485
+ /* @__PURE__ */ jsx("td", { className: "hidden px-4 py-3 text-gray-500 dark:text-gray-400 sm:table-cell", children: organization.displayName ?? "\u2014" }),
486
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsx(
487
+ StatusBadge,
488
+ {
489
+ status: organization.enabled ? "active" : "inactive",
490
+ label: organization.enabled ? t("admin.organizations.statusEnabled") : t("admin.organizations.statusDisabled"),
491
+ size: "sm"
492
+ }
493
+ ) }),
494
+ /* @__PURE__ */ jsx("td", { className: "hidden px-4 py-3 text-gray-500 dark:text-gray-400 md:table-cell", children: format.dateTime(new Date(organization.createdAt), {
495
+ year: "numeric",
496
+ month: "short",
497
+ day: "numeric"
498
+ }) }),
499
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsx(
500
+ Button,
501
+ {
502
+ size: "sm",
503
+ color: "ios-glass-red",
504
+ onClick: (event) => {
505
+ event.stopPropagation();
506
+ handleDeleteRequest(organization);
507
+ },
508
+ children: /* @__PURE__ */ jsx(TrashIcon, { className: "h-3.5 w-3.5" })
509
+ }
510
+ ) })
511
+ ]
512
+ },
513
+ organization.id
514
+ )) })
515
+ ] }) })
516
+ ]
517
+ }
518
+ ),
519
+ /* @__PURE__ */ jsx(
520
+ DynamicIslandConfirm,
521
+ {
522
+ open: deleteTarget !== null,
523
+ onClose: handleDeleteCancel,
524
+ onConfirm: handleDeleteConfirm,
525
+ title: t("admin.organizations.deleteConfirmTitle", { name: deleteTarget?.displayName ?? deleteTarget?.name ?? "" }),
526
+ icon: /* @__PURE__ */ jsx(TrashIcon, { className: "h-5 w-5 text-white" }),
527
+ iconBackground: "bg-ios-red",
528
+ confirmLabel: t("admin.organizations.deleteConfirmButton"),
529
+ cancelLabel: t("admin.organizations.deleteCancelButton")
530
+ }
531
+ )
532
+ ] });
533
+ }
534
+ function AdminOrganizationForm({
535
+ open,
536
+ onClose,
537
+ organization,
538
+ onSubmit
539
+ }) {
540
+ const t = useTranslations("windsock");
541
+ const isEditing = organization !== null && organization !== void 0;
542
+ const [name, setName] = useState("");
543
+ const [displayName, setDisplayName] = useState("");
544
+ const [description, setDescription] = useState("");
545
+ const [isSubmitting, setIsSubmitting] = useState(false);
546
+ const [error, setError] = useState(null);
547
+ useEffect(() => {
548
+ if (organization) {
549
+ setName(organization.name);
550
+ setDisplayName(organization.displayName ?? "");
551
+ setDescription(organization.description ?? "");
552
+ } else {
553
+ setName("");
554
+ setDisplayName("");
555
+ setDescription("");
556
+ }
557
+ setError(null);
558
+ }, [organization, open]);
559
+ const handleClose = useCallback(() => {
560
+ setError(null);
561
+ onClose();
562
+ }, [onClose]);
563
+ const handleSubmit = useCallback(async () => {
564
+ if (!name.trim()) {
565
+ setError(t("admin.organizationForm.errorNameRequired"));
566
+ triggerHaptic("error");
567
+ return;
568
+ }
569
+ setError(null);
570
+ setIsSubmitting(true);
571
+ triggerHaptic("light");
572
+ try {
573
+ if (isEditing) {
574
+ const data = {
575
+ name: name.trim(),
576
+ displayName: displayName.trim() || null,
577
+ description: description.trim() || null
578
+ };
579
+ await onSubmit(data);
580
+ } else {
581
+ const data = {
582
+ name: name.trim(),
583
+ displayName: displayName.trim() || void 0,
584
+ description: description.trim() || void 0
585
+ };
586
+ await onSubmit(data);
587
+ }
588
+ triggerHaptic("success");
589
+ onClose();
590
+ } catch {
591
+ setError(
592
+ isEditing ? t("admin.organizationForm.errorUpdateFailed") : t("admin.organizationForm.errorCreateFailed")
593
+ );
594
+ triggerHaptic("error");
595
+ } finally {
596
+ setIsSubmitting(false);
597
+ }
598
+ }, [name, displayName, description, isEditing, onSubmit, onClose, t]);
599
+ return /* @__PURE__ */ jsx(
600
+ GlassModal,
601
+ {
602
+ open,
603
+ onClose: handleClose,
604
+ title: isEditing ? t("admin.organizationForm.editTitle") : t("admin.organizationForm.createTitle"),
605
+ subtitle: isEditing ? t("admin.organizationForm.editSubtitle") : t("admin.organizationForm.createSubtitle"),
606
+ icon: /* @__PURE__ */ jsx(BuildingOffice2Icon, { className: "h-5 w-5 text-white" }),
607
+ maxWidth: "md",
608
+ children: /* @__PURE__ */ jsxs(
609
+ "form",
610
+ {
611
+ onSubmit: (event) => {
612
+ event.preventDefault();
613
+ handleSubmit();
614
+ },
615
+ className: "space-y-4 p-4 sm:p-6",
616
+ children: [
617
+ /* @__PURE__ */ jsx(
618
+ Input,
619
+ {
620
+ label: t("admin.organizationForm.nameLabel"),
621
+ value: name,
622
+ onChange: (event) => setName(event.target.value),
623
+ placeholder: t("admin.organizationForm.namePlaceholder"),
624
+ disabled: isSubmitting,
625
+ autoFocus: true
626
+ }
627
+ ),
628
+ /* @__PURE__ */ jsx(
629
+ Input,
630
+ {
631
+ label: t("admin.organizationForm.displayNameLabel"),
632
+ value: displayName,
633
+ onChange: (event) => setDisplayName(event.target.value),
634
+ placeholder: t("admin.organizationForm.displayNamePlaceholder"),
635
+ disabled: isSubmitting
636
+ }
637
+ ),
638
+ /* @__PURE__ */ jsx(
639
+ Input,
640
+ {
641
+ label: t("admin.organizationForm.descriptionLabel"),
642
+ value: description,
643
+ onChange: (event) => setDescription(event.target.value),
644
+ placeholder: t("admin.organizationForm.descriptionPlaceholder"),
645
+ disabled: isSubmitting
646
+ }
647
+ ),
648
+ error && /* @__PURE__ */ jsx("p", { className: "text-sm text-red-600 dark:text-red-400", role: "alert", children: error }),
649
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3 pt-2", children: [
650
+ /* @__PURE__ */ jsx(
651
+ Button,
652
+ {
653
+ type: "button",
654
+ outline: true,
655
+ onClick: handleClose,
656
+ disabled: isSubmitting,
657
+ className: "flex-1",
658
+ children: t("admin.organizationForm.cancelButton")
659
+ }
660
+ ),
661
+ /* @__PURE__ */ jsx(
662
+ Button,
663
+ {
664
+ type: "submit",
665
+ color: "ios-glass-blue",
666
+ loading: isSubmitting,
667
+ disabled: isSubmitting || !name.trim(),
668
+ className: "flex-1",
669
+ children: isEditing ? t("admin.organizationForm.saveButton") : t("admin.organizationForm.createButton")
670
+ }
671
+ )
672
+ ] })
673
+ ]
674
+ }
675
+ )
676
+ }
677
+ );
678
+ }
679
+ function AdminPermissionList({ client }) {
680
+ const t = useTranslations("windsock");
681
+ const format = useFormatter();
682
+ const [permissions, setPermissions] = useState([]);
683
+ const [filteredPermissions, setFilteredPermissions] = useState([]);
684
+ const [isLoading, setIsLoading] = useState(true);
685
+ const [search, setSearch] = useState("");
686
+ const [isFormOpen, setIsFormOpen] = useState(false);
687
+ const [editingPermission, setEditingPermission] = useState(null);
688
+ const [formId, setFormId] = useState("");
689
+ const [formName, setFormName] = useState("");
690
+ const [formDescription, setFormDescription] = useState("");
691
+ const [formResource, setFormResource] = useState("");
692
+ const [formAction, setFormAction] = useState("");
693
+ const [isSubmitting, setIsSubmitting] = useState(false);
694
+ const [formError, setFormError] = useState(null);
695
+ const [deleteTarget, setDeleteTarget] = useState(null);
696
+ const [isDeleting, setIsDeleting] = useState(false);
697
+ const fetchPermissions = useCallback(async () => {
698
+ setIsLoading(true);
699
+ try {
700
+ const result = await client.listPermissions();
701
+ setPermissions(result);
702
+ } catch {
703
+ } finally {
704
+ setIsLoading(false);
705
+ }
706
+ }, [client]);
707
+ useEffect(() => {
708
+ fetchPermissions();
709
+ }, [fetchPermissions]);
710
+ useEffect(() => {
711
+ if (!search.trim()) {
712
+ setFilteredPermissions(permissions);
713
+ return;
714
+ }
715
+ const query = search.toLowerCase();
716
+ setFilteredPermissions(
717
+ permissions.filter(
718
+ (permission) => permission.name.toLowerCase().includes(query) || permission.description?.toLowerCase().includes(query) || permission.resource?.toLowerCase().includes(query) || permission.action?.toLowerCase().includes(query)
719
+ )
720
+ );
721
+ }, [permissions, search]);
722
+ const handleOpenCreate = useCallback(() => {
723
+ triggerHaptic("light");
724
+ setEditingPermission(null);
725
+ setFormId("");
726
+ setFormName("");
727
+ setFormDescription("");
728
+ setFormResource("");
729
+ setFormAction("");
730
+ setFormError(null);
731
+ setIsFormOpen(true);
732
+ }, []);
733
+ const handleOpenEdit = useCallback((permission) => {
734
+ triggerHaptic("light");
735
+ setEditingPermission(permission);
736
+ setFormId(permission.id);
737
+ setFormName(permission.name);
738
+ setFormDescription(permission.description ?? "");
739
+ setFormResource(permission.resource ?? "");
740
+ setFormAction(permission.action ?? "");
741
+ setFormError(null);
742
+ setIsFormOpen(true);
743
+ }, []);
744
+ const handleCloseForm = useCallback(() => {
745
+ triggerHaptic("light");
746
+ setIsFormOpen(false);
747
+ }, []);
748
+ const handleSubmitForm = useCallback(async () => {
749
+ const isEditing = editingPermission !== null;
750
+ if (!isEditing && !formId.trim()) {
751
+ setFormError(t("admin.permissions.errorIdRequired"));
752
+ triggerHaptic("error");
753
+ return;
754
+ }
755
+ if (!formName.trim()) {
756
+ setFormError(t("admin.permissions.errorNameRequired"));
757
+ triggerHaptic("error");
758
+ return;
759
+ }
760
+ setFormError(null);
761
+ setIsSubmitting(true);
762
+ triggerHaptic("light");
763
+ try {
764
+ if (isEditing) {
765
+ const data = {
766
+ name: formName.trim(),
767
+ description: formDescription.trim() || void 0,
768
+ resource: formResource.trim() || void 0,
769
+ action: formAction.trim() || void 0
770
+ };
771
+ await client.updatePermission(editingPermission.id, data);
772
+ } else {
773
+ const data = {
774
+ id: formId.trim(),
775
+ name: formName.trim(),
776
+ description: formDescription.trim() || void 0,
777
+ resource: formResource.trim() || void 0,
778
+ action: formAction.trim() || void 0
779
+ };
780
+ await client.createPermission(data);
781
+ }
782
+ triggerHaptic("success");
783
+ setIsFormOpen(false);
784
+ fetchPermissions();
785
+ } catch {
786
+ setFormError(
787
+ isEditing ? t("admin.permissions.errorUpdateFailed") : t("admin.permissions.errorCreateFailed")
788
+ );
789
+ triggerHaptic("error");
790
+ } finally {
791
+ setIsSubmitting(false);
792
+ }
793
+ }, [editingPermission, formId, formName, formDescription, formResource, formAction, client, fetchPermissions, t]);
794
+ const handleDeleteRequest = useCallback((permission) => {
795
+ triggerHaptic("warning");
796
+ setDeleteTarget(permission);
797
+ }, []);
798
+ const handleDeleteConfirm = useCallback(async () => {
799
+ if (!deleteTarget) return;
800
+ setIsDeleting(true);
801
+ triggerHaptic("medium");
802
+ try {
803
+ await client.deletePermission(deleteTarget.id);
804
+ setDeleteTarget(null);
805
+ triggerHaptic("success");
806
+ fetchPermissions();
807
+ } catch {
808
+ triggerHaptic("error");
809
+ } finally {
810
+ setIsDeleting(false);
811
+ }
812
+ }, [deleteTarget, client, fetchPermissions]);
813
+ const handleDeleteCancel = useCallback(() => {
814
+ triggerHaptic("light");
815
+ setDeleteTarget(null);
816
+ }, []);
817
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
818
+ /* @__PURE__ */ jsxs(
819
+ SectionCard,
820
+ {
821
+ header: {
822
+ icon: /* @__PURE__ */ jsx(KeyIcon, { className: "h-5 w-5 text-white" }),
823
+ title: t("admin.permissions.title"),
824
+ subtitle: t("admin.permissions.subtitle"),
825
+ gradient: "from-violet-500 via-purple-500 to-pink-500",
826
+ rightContent: /* @__PURE__ */ jsxs(Button, { size: "sm", color: "ios-glass-blue", onClick: handleOpenCreate, children: [
827
+ /* @__PURE__ */ jsx(PlusIcon, { className: "mr-1 h-3.5 w-3.5" }),
828
+ t("admin.permissions.createButton")
829
+ ] })
830
+ },
831
+ padded: false,
832
+ children: [
833
+ /* @__PURE__ */ jsx("div", { className: "border-b border-gray-200/60 p-4 dark:border-white/10", children: /* @__PURE__ */ jsx(
834
+ SearchInput,
835
+ {
836
+ value: search,
837
+ onChange: (event) => setSearch(event.target.value),
838
+ placeholder: t("admin.permissions.searchPlaceholder")
839
+ }
840
+ ) }),
841
+ isLoading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-center gap-2 p-8 text-sm text-gray-500 dark:text-gray-400", children: [
842
+ /* @__PURE__ */ jsx(InlineSpinner, {}),
843
+ /* @__PURE__ */ jsx("span", { children: t("admin.permissions.loading") })
844
+ ] }) : filteredPermissions.length === 0 ? /* @__PURE__ */ jsx("div", { className: "p-4 sm:p-6", children: /* @__PURE__ */ jsx(
845
+ EmptyState,
846
+ {
847
+ message: t("admin.permissions.emptyMessage"),
848
+ description: t("admin.permissions.emptyDescription"),
849
+ icon: KeyIcon,
850
+ action: {
851
+ label: t("admin.permissions.createButton"),
852
+ onClick: handleOpenCreate
853
+ },
854
+ variant: "card"
855
+ }
856
+ ) }) : /* @__PURE__ */ jsx("div", { className: "overflow-x-auto", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-sm", children: [
857
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b border-gray-200/60 dark:border-white/10", children: [
858
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.permissions.columnName") }),
859
+ /* @__PURE__ */ jsx("th", { className: "hidden px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 sm:table-cell", children: t("admin.permissions.columnResource") }),
860
+ /* @__PURE__ */ jsx("th", { className: "hidden px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 sm:table-cell", children: t("admin.permissions.columnAction") }),
861
+ /* @__PURE__ */ jsx("th", { className: "hidden px-4 py-3 text-left text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400 md:table-cell", children: t("admin.permissions.columnCreated") }),
862
+ /* @__PURE__ */ jsx("th", { className: "px-4 py-3 text-right text-xs font-medium uppercase tracking-wide text-gray-500 dark:text-gray-400", children: t("admin.permissions.columnActions") })
863
+ ] }) }),
864
+ /* @__PURE__ */ jsx("tbody", { className: "divide-y divide-gray-200/60 dark:divide-white/10", children: filteredPermissions.map((permission) => /* @__PURE__ */ jsxs("tr", { className: "transition-colors hover:bg-white/40 dark:hover:bg-white/5", children: [
865
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { children: [
866
+ /* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-gray-900 dark:text-white", children: permission.name }),
867
+ permission.description && /* @__PURE__ */ jsx("p", { className: "mt-0.5 truncate text-xs text-gray-500 dark:text-gray-400", children: permission.description })
868
+ ] }) }),
869
+ /* @__PURE__ */ jsx("td", { className: "hidden px-4 py-3 sm:table-cell", children: permission.resource ? /* @__PURE__ */ jsx(Badge, { color: "blue", children: permission.resource }) : /* @__PURE__ */ jsx("span", { className: "text-gray-400 dark:text-gray-500", children: "\u2014" }) }),
870
+ /* @__PURE__ */ jsx("td", { className: "hidden px-4 py-3 sm:table-cell", children: permission.action ? /* @__PURE__ */ jsx(Badge, { color: "amber", children: permission.action }) : /* @__PURE__ */ jsx("span", { className: "text-gray-400 dark:text-gray-500", children: "\u2014" }) }),
871
+ /* @__PURE__ */ jsx("td", { className: "hidden px-4 py-3 text-gray-500 dark:text-gray-400 md:table-cell", children: format.dateTime(new Date(permission.createdAt), {
872
+ year: "numeric",
873
+ month: "short",
874
+ day: "numeric"
875
+ }) }),
876
+ /* @__PURE__ */ jsx("td", { className: "px-4 py-3 text-right", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-1", children: [
877
+ /* @__PURE__ */ jsx(
878
+ Button,
879
+ {
880
+ size: "sm",
881
+ outline: true,
882
+ onClick: () => handleOpenEdit(permission),
883
+ children: /* @__PURE__ */ jsx(PencilSquareIcon, { className: "h-3.5 w-3.5" })
884
+ }
885
+ ),
886
+ /* @__PURE__ */ jsx(
887
+ Button,
888
+ {
889
+ size: "sm",
890
+ color: "ios-glass-red",
891
+ onClick: () => handleDeleteRequest(permission),
892
+ children: /* @__PURE__ */ jsx(TrashIcon, { className: "h-3.5 w-3.5" })
893
+ }
894
+ )
895
+ ] }) })
896
+ ] }, permission.id)) })
897
+ ] }) })
898
+ ]
899
+ }
900
+ ),
901
+ /* @__PURE__ */ jsx(
902
+ Sheet,
903
+ {
904
+ open: isFormOpen,
905
+ onClose: handleCloseForm,
906
+ title: editingPermission ? t("admin.permissions.editSheetTitle") : t("admin.permissions.createSheetTitle"),
907
+ side: "bottom",
908
+ size: "md",
909
+ children: /* @__PURE__ */ jsxs("div", { className: "space-y-4 p-4 sm:p-6", children: [
910
+ !editingPermission && /* @__PURE__ */ jsx(
911
+ Input,
912
+ {
913
+ label: t("admin.permissions.formIdLabel"),
914
+ value: formId,
915
+ onChange: (event) => setFormId(event.target.value),
916
+ placeholder: t("admin.permissions.formIdPlaceholder"),
917
+ disabled: isSubmitting,
918
+ autoFocus: true
919
+ }
920
+ ),
921
+ /* @__PURE__ */ jsx(
922
+ Input,
923
+ {
924
+ label: t("admin.permissions.formNameLabel"),
925
+ value: formName,
926
+ onChange: (event) => setFormName(event.target.value),
927
+ placeholder: t("admin.permissions.formNamePlaceholder"),
928
+ disabled: isSubmitting,
929
+ autoFocus: !!editingPermission
930
+ }
931
+ ),
932
+ /* @__PURE__ */ jsx(
933
+ Input,
934
+ {
935
+ label: t("admin.permissions.formDescriptionLabel"),
936
+ value: formDescription,
937
+ onChange: (event) => setFormDescription(event.target.value),
938
+ placeholder: t("admin.permissions.formDescriptionPlaceholder"),
939
+ disabled: isSubmitting
940
+ }
941
+ ),
942
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-3", children: [
943
+ /* @__PURE__ */ jsx(
944
+ Input,
945
+ {
946
+ label: t("admin.permissions.formResourceLabel"),
947
+ value: formResource,
948
+ onChange: (event) => setFormResource(event.target.value),
949
+ placeholder: t("admin.permissions.formResourcePlaceholder"),
950
+ disabled: isSubmitting
951
+ }
952
+ ),
953
+ /* @__PURE__ */ jsx(
954
+ Input,
955
+ {
956
+ label: t("admin.permissions.formActionLabel"),
957
+ value: formAction,
958
+ onChange: (event) => setFormAction(event.target.value),
959
+ placeholder: t("admin.permissions.formActionPlaceholder"),
960
+ disabled: isSubmitting
961
+ }
962
+ )
963
+ ] }),
964
+ formError && /* @__PURE__ */ jsx("p", { className: "text-sm text-red-600 dark:text-red-400", role: "alert", children: formError }),
965
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
966
+ /* @__PURE__ */ jsx(
967
+ Button,
968
+ {
969
+ outline: true,
970
+ onClick: handleCloseForm,
971
+ disabled: isSubmitting,
972
+ className: "flex-1",
973
+ children: t("admin.permissions.formCancelButton")
974
+ }
975
+ ),
976
+ /* @__PURE__ */ jsx(
977
+ Button,
978
+ {
979
+ color: "ios-glass-blue",
980
+ onClick: handleSubmitForm,
981
+ loading: isSubmitting,
982
+ disabled: isSubmitting || !formName.trim() || !editingPermission && !formId.trim(),
983
+ className: "flex-1",
984
+ children: editingPermission ? t("admin.permissions.formSaveButton") : t("admin.permissions.formCreateButton")
985
+ }
986
+ )
987
+ ] })
988
+ ] })
989
+ }
990
+ ),
991
+ /* @__PURE__ */ jsx(
992
+ DynamicIslandConfirm,
993
+ {
994
+ open: deleteTarget !== null,
995
+ onClose: handleDeleteCancel,
996
+ onConfirm: handleDeleteConfirm,
997
+ title: t("admin.permissions.deleteConfirmTitle", { name: deleteTarget?.name ?? "" }),
998
+ icon: /* @__PURE__ */ jsx(TrashIcon, { className: "h-5 w-5 text-white" }),
999
+ iconBackground: "bg-ios-red",
1000
+ confirmLabel: t("admin.permissions.deleteConfirmButton"),
1001
+ cancelLabel: t("admin.permissions.deleteCancelButton")
1002
+ }
1003
+ )
1004
+ ] });
1005
+ }
1006
+
1007
+ export { AdminOrganizationForm, AdminOrganizationList, AdminPermissionList, AdminUserForm, AdminUserList };
1008
+ //# sourceMappingURL=chunk-RV555OEO.mjs.map
1009
+ //# sourceMappingURL=chunk-RV555OEO.mjs.map