@gelabs/ovr 0.4.2 → 0.4.4

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 (76) hide show
  1. package/dist/accounts.d.ts +52 -0
  2. package/dist/accounts.js +13 -0
  3. package/dist/audit.d.ts +13 -0
  4. package/dist/audit.js +11 -0
  5. package/dist/auth-auth.d.ts +1 -1
  6. package/dist/auth-rate-limit.d.ts +11 -2
  7. package/dist/auth-rate-limit.js +1 -1
  8. package/dist/auth.d.ts +2 -2
  9. package/dist/auth.js +1 -1
  10. package/dist/chunk-4EDMZRGS.js +98 -0
  11. package/dist/chunk-4JMHQKMK.js +268 -0
  12. package/dist/chunk-6662IONX.js +69 -0
  13. package/dist/chunk-6WMPBAUH.js +18 -0
  14. package/dist/{chunk-77ULDXQX.js → chunk-6ZJSEM4Y.js} +6 -1
  15. package/dist/chunk-7GVZZWK3.js +74 -0
  16. package/dist/chunk-ACXED4UH.js +403 -0
  17. package/dist/chunk-BBTAG3FD.js +165 -0
  18. package/dist/chunk-HX7QT2FE.js +452 -0
  19. package/dist/chunk-LC3S47FM.js +468 -0
  20. package/dist/chunk-NPTR7GFZ.js +432 -0
  21. package/dist/chunk-SLQRZBMR.js +66 -0
  22. package/dist/chunk-UBUXPJLN.js +29 -0
  23. package/dist/chunk-UN6Z4WGF.js +31 -0
  24. package/dist/chunk-YE3D2DYY.js +83 -0
  25. package/dist/citizen.d.ts +5 -0
  26. package/dist/citizen.js +12 -0
  27. package/dist/config.d.ts +2 -2
  28. package/dist/core-i18n.d.ts +1 -1
  29. package/dist/core.d.ts +2 -2
  30. package/dist/dashboard.d.ts +10 -0
  31. package/dist/dashboard.js +3 -0
  32. package/dist/data-mock-store.d.ts +1 -1
  33. package/dist/{format-C7MSwUHK.d.ts → format-VyCUfF8R.d.ts} +1 -1
  34. package/dist/index.d.ts +2 -2
  35. package/dist/notifications.d.ts +6 -0
  36. package/dist/notifications.js +11 -0
  37. package/dist/officers.d.ts +27 -0
  38. package/dist/officers.js +11 -0
  39. package/dist/offline.d.ts +1 -1
  40. package/dist/payments.d.ts +15 -0
  41. package/dist/payments.js +11 -0
  42. package/dist/roles.d.ts +37 -0
  43. package/dist/roles.js +12 -0
  44. package/dist/runtime.d.ts +1 -1
  45. package/dist/tickets.d.ts +6 -0
  46. package/dist/tickets.js +24 -0
  47. package/dist/ui-components-admin/accounts-manager.d.ts +3 -52
  48. package/dist/ui-components-admin/accounts-manager.js +11 -472
  49. package/dist/ui-components-admin/admin-nav.js +11 -83
  50. package/dist/ui-components-admin/issuance-form.js +15 -432
  51. package/dist/ui-components-admin/logs-viewer.d.ts +3 -13
  52. package/dist/ui-components-admin/logs-viewer.js +8 -98
  53. package/dist/ui-components-admin/notifications-list.js +6 -66
  54. package/dist/ui-components-admin/officers-manager.d.ts +3 -27
  55. package/dist/ui-components-admin/officers-manager.js +9 -268
  56. package/dist/ui-components-admin/roles-manager.d.ts +3 -37
  57. package/dist/ui-components-admin/roles-manager.js +10 -403
  58. package/dist/ui-components-admin/stat-card.d.ts +2 -10
  59. package/dist/ui-components-admin/stat-card.js +3 -29
  60. package/dist/ui-components-admin/ticket-preview.js +2 -2
  61. package/dist/ui-components-admin/tickets-table.js +7 -69
  62. package/dist/ui-components-admin/violations-manager.js +13 -452
  63. package/dist/ui-components-citizen/citizen-nav.js +3 -31
  64. package/dist/ui-components-citizen/payment-form.d.ts +3 -14
  65. package/dist/ui-components-citizen/payment-form.js +9 -165
  66. package/dist/ui-components-citizen/payment-qr-dialog.js +2 -2
  67. package/dist/ui-components-citizen/ticket-not-found.js +4 -18
  68. package/dist/ui-components-citizen/violation-history-table.js +6 -74
  69. package/dist/ui-config.d.ts +2 -2
  70. package/dist/ui-server.d.ts +2 -2
  71. package/dist/violations.d.ts +3 -0
  72. package/dist/violations.js +14 -0
  73. package/package.json +46 -6
  74. package/dist/{chunk-ZUMEOZ22.js → chunk-JTSTNZAB.js} +1 -1
  75. package/dist/{chunk-TLG4C2XI.js → chunk-QCAURREW.js} +1 -1
  76. package/dist/{schema-CdsFQxIg.d.ts → schema-BUhh_mKX.d.ts} +108 -108
@@ -0,0 +1,468 @@
1
+ import { Label } from './chunk-XQTVSNHC.js';
2
+ import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose } from './chunk-M35R6JLA.js';
3
+ import { usePagination, Pagination } from './chunk-6YFZLXFP.js';
4
+ import { Input } from './chunk-K3KIBHJF.js';
5
+ import { hasPermission } from './chunk-QZRRFE6E.js';
6
+ import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from './chunk-OWCGEEAZ.js';
7
+ import { Badge } from './chunk-55FQP2DO.js';
8
+ import { Button } from './chunk-I4WDVYHX.js';
9
+ import { useCopy, useOvrConfig } from './chunk-TJSNVTVB.js';
10
+ import { Card, CardContent } from './chunk-SETIN6XP.js';
11
+ import * as React from 'react';
12
+ import { toast } from 'sonner';
13
+ import { useRouter } from 'next/navigation';
14
+ import { Loader2, UserPlus, KeyRound, Pencil } from 'lucide-react';
15
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
16
+
17
+ var fieldClass = "h-9 w-full rounded-lg border border-input bg-transparent px-2.5 text-sm shadow-xs outline-none transition-colors focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:opacity-50 dark:bg-input/30";
18
+ function AccountsManager({
19
+ users,
20
+ officers,
21
+ roles,
22
+ currentUserId,
23
+ createAction,
24
+ editAction,
25
+ setActiveAction,
26
+ resetPasswordAction
27
+ }) {
28
+ const t = useCopy().admin.accountsPage;
29
+ const { rules } = useOvrConfig();
30
+ const router = useRouter();
31
+ const [busyId, setBusyId] = React.useState(null);
32
+ const { pageItems, page, setPage, totalPages, from, to, total } = usePagination(users, 12);
33
+ const fmtDate = (iso) => new Date(iso).toLocaleDateString(rules.locale, {
34
+ timeZone: rules.timeZone,
35
+ year: "numeric",
36
+ month: "short",
37
+ day: "numeric"
38
+ });
39
+ async function toggleActive(u) {
40
+ setBusyId(u.id);
41
+ try {
42
+ const res = await setActiveAction(u.id, !u.active);
43
+ if (res?.error) {
44
+ toast.error(res.error);
45
+ return;
46
+ }
47
+ router.refresh();
48
+ } finally {
49
+ setBusyId(null);
50
+ }
51
+ }
52
+ return /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-5xl p-4 sm:p-6 lg:p-8", children: [
53
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between gap-3", children: [
54
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
55
+ /* @__PURE__ */ jsx("h1", { className: "font-heading text-2xl font-semibold tracking-tight", children: t.title }),
56
+ /* @__PURE__ */ jsx("p", { className: "max-w-2xl text-sm text-muted-foreground", children: t.subtitle })
57
+ ] }),
58
+ /* @__PURE__ */ jsx(
59
+ NewAccountDialog,
60
+ {
61
+ officers,
62
+ roles,
63
+ createAction,
64
+ onCreated: () => router.refresh()
65
+ }
66
+ )
67
+ ] }),
68
+ /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "p-0", children: users.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-8 text-center text-sm text-muted-foreground", children: t.empty }) : /* @__PURE__ */ jsxs(Fragment, { children: [
69
+ /* @__PURE__ */ jsxs(Table, { children: [
70
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
71
+ /* @__PURE__ */ jsx(TableHead, { children: t.username }),
72
+ /* @__PURE__ */ jsx(TableHead, { children: t.role }),
73
+ /* @__PURE__ */ jsx(TableHead, { children: t.officer }),
74
+ /* @__PURE__ */ jsx(TableHead, { children: t.status }),
75
+ /* @__PURE__ */ jsx(TableHead, { children: t.created }),
76
+ /* @__PURE__ */ jsx(TableHead, { className: "text-right", children: t.actions })
77
+ ] }) }),
78
+ /* @__PURE__ */ jsx(TableBody, { children: pageItems.map((u) => {
79
+ const isSelf = u.id === currentUserId;
80
+ return /* @__PURE__ */ jsxs(TableRow, { children: [
81
+ /* @__PURE__ */ jsxs(TableCell, { className: "font-medium", children: [
82
+ u.username,
83
+ isSelf ? /* @__PURE__ */ jsxs("span", { className: "ml-1.5 text-xs text-muted-foreground", children: [
84
+ "(",
85
+ t.you,
86
+ ")"
87
+ ] }) : null
88
+ ] }),
89
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, { variant: "secondary", children: u.roleLabel ?? u.role }) }),
90
+ /* @__PURE__ */ jsx(TableCell, { className: "text-muted-foreground", children: u.officerName ?? "\u2014" }),
91
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, { variant: u.active ? "default" : "outline", children: u.active ? t.active : t.inactive }) }),
92
+ /* @__PURE__ */ jsx(TableCell, { className: "text-muted-foreground", children: fmtDate(u.createdAt) }),
93
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1.5", children: [
94
+ /* @__PURE__ */ jsx(
95
+ EditAccountDialog,
96
+ {
97
+ user: u,
98
+ officers,
99
+ roles,
100
+ editAction,
101
+ onSaved: () => router.refresh()
102
+ }
103
+ ),
104
+ /* @__PURE__ */ jsx(
105
+ ResetPasswordDialog,
106
+ {
107
+ user: u,
108
+ resetPasswordAction
109
+ }
110
+ ),
111
+ /* @__PURE__ */ jsxs(
112
+ Button,
113
+ {
114
+ variant: u.active ? "destructive" : "outline",
115
+ size: "sm",
116
+ disabled: isSelf || busyId === u.id,
117
+ onClick: () => toggleActive(u),
118
+ title: isSelf ? "You can't deactivate yourself." : void 0,
119
+ children: [
120
+ busyId === u.id ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : null,
121
+ u.active ? t.deactivate : t.activate
122
+ ]
123
+ }
124
+ )
125
+ ] }) })
126
+ ] }, u.id);
127
+ }) })
128
+ ] }),
129
+ /* @__PURE__ */ jsx(
130
+ Pagination,
131
+ {
132
+ page,
133
+ totalPages,
134
+ from,
135
+ to,
136
+ total,
137
+ onPage: setPage
138
+ }
139
+ )
140
+ ] }) }) })
141
+ ] });
142
+ }
143
+ function NewAccountDialog({
144
+ officers,
145
+ roles,
146
+ createAction,
147
+ onCreated
148
+ }) {
149
+ const t = useCopy().admin.accountsPage;
150
+ const defaultRole = roles.find((r) => r.name === "ENFORCER")?.name ?? roles[0]?.name ?? "";
151
+ const [open, setOpen] = React.useState(false);
152
+ const [username, setUsername] = React.useState("");
153
+ const [password, setPassword] = React.useState("");
154
+ const [role, setRole] = React.useState(defaultRole);
155
+ const [officerId, setOfficerId] = React.useState("");
156
+ const [submitting, setSubmitting] = React.useState(false);
157
+ const selectedRole = roles.find((r) => r.name === role);
158
+ const showOfficer = hasPermission(selectedRole?.permissions, "tickets:create");
159
+ function reset() {
160
+ setUsername("");
161
+ setPassword("");
162
+ setRole(defaultRole);
163
+ setOfficerId("");
164
+ }
165
+ async function submit(e) {
166
+ e.preventDefault();
167
+ if (!username.trim()) return toast.error(`${t.username} is required.`);
168
+ if (password.length < 6)
169
+ return toast.error("Password must be at least 6 characters.");
170
+ if (!role) return toast.error(`${t.role} is required.`);
171
+ setSubmitting(true);
172
+ try {
173
+ const res = await createAction({
174
+ username: username.trim(),
175
+ password,
176
+ role,
177
+ officerId: showOfficer && officerId ? officerId : null
178
+ });
179
+ if (res?.error) {
180
+ toast.error(res.error);
181
+ return;
182
+ }
183
+ toast.success(`Account "${username.trim()}" created.`);
184
+ reset();
185
+ setOpen(false);
186
+ onCreated();
187
+ } finally {
188
+ setSubmitting(false);
189
+ }
190
+ }
191
+ return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
192
+ /* @__PURE__ */ jsxs(DialogTrigger, { render: /* @__PURE__ */ jsx(Button, { className: "gap-1.5" }), children: [
193
+ /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
194
+ t.newAccount
195
+ ] }),
196
+ /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
197
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
198
+ /* @__PURE__ */ jsx(DialogTitle, { children: t.newAccount }),
199
+ /* @__PURE__ */ jsx(DialogDescription, { children: t.subtitle })
200
+ ] }),
201
+ /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
202
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
203
+ /* @__PURE__ */ jsx(Label, { htmlFor: "acc-username", children: t.username }),
204
+ /* @__PURE__ */ jsx(
205
+ Input,
206
+ {
207
+ id: "acc-username",
208
+ value: username,
209
+ onChange: (e) => setUsername(e.target.value),
210
+ autoComplete: "off",
211
+ autoFocus: true
212
+ }
213
+ )
214
+ ] }),
215
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
216
+ /* @__PURE__ */ jsx(Label, { htmlFor: "acc-password", children: t.password }),
217
+ /* @__PURE__ */ jsx(
218
+ Input,
219
+ {
220
+ id: "acc-password",
221
+ type: "password",
222
+ value: password,
223
+ onChange: (e) => setPassword(e.target.value),
224
+ autoComplete: "new-password"
225
+ }
226
+ )
227
+ ] }),
228
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
229
+ /* @__PURE__ */ jsx(Label, { htmlFor: "acc-role", children: t.role }),
230
+ /* @__PURE__ */ jsx(
231
+ "select",
232
+ {
233
+ id: "acc-role",
234
+ value: role,
235
+ onChange: (e) => setRole(e.target.value),
236
+ className: fieldClass,
237
+ children: roles.map((r) => /* @__PURE__ */ jsx("option", { value: r.name, children: r.label }, r.name))
238
+ }
239
+ )
240
+ ] }),
241
+ showOfficer ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
242
+ /* @__PURE__ */ jsx(Label, { htmlFor: "acc-officer", children: t.officer }),
243
+ /* @__PURE__ */ jsxs(
244
+ "select",
245
+ {
246
+ id: "acc-officer",
247
+ value: officerId,
248
+ onChange: (e) => setOfficerId(e.target.value),
249
+ className: fieldClass,
250
+ children: [
251
+ /* @__PURE__ */ jsx("option", { value: "", children: t.officerNone }),
252
+ officers.map((o) => /* @__PURE__ */ jsxs("option", { value: o.id, children: [
253
+ o.name,
254
+ o.badgeNo ? ` (${o.badgeNo})` : ""
255
+ ] }, o.id))
256
+ ]
257
+ }
258
+ ),
259
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t.officerHint })
260
+ ] }) : null,
261
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
262
+ /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline" }), children: t.cancel }),
263
+ /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: submitting, className: "gap-1.5", children: [
264
+ submitting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
265
+ submitting ? t.creating : t.create
266
+ ] })
267
+ ] })
268
+ ] })
269
+ ] })
270
+ ] });
271
+ }
272
+ function ResetPasswordDialog({
273
+ user,
274
+ resetPasswordAction
275
+ }) {
276
+ const t = useCopy().admin.accountsPage;
277
+ const [open, setOpen] = React.useState(false);
278
+ const [password, setPassword] = React.useState("");
279
+ const [submitting, setSubmitting] = React.useState(false);
280
+ async function submit(e) {
281
+ e.preventDefault();
282
+ if (password.length < 6)
283
+ return toast.error("Password must be at least 6 characters.");
284
+ setSubmitting(true);
285
+ try {
286
+ const res = await resetPasswordAction(user.id, password);
287
+ if (res?.error) {
288
+ toast.error(res.error);
289
+ return;
290
+ }
291
+ toast.success(`Password reset for "${user.username}".`);
292
+ setPassword("");
293
+ setOpen(false);
294
+ } finally {
295
+ setSubmitting(false);
296
+ }
297
+ }
298
+ return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
299
+ /* @__PURE__ */ jsxs(
300
+ DialogTrigger,
301
+ {
302
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "gap-1.5" }),
303
+ children: [
304
+ /* @__PURE__ */ jsx(KeyRound, { className: "size-3.5" }),
305
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t.resetPassword })
306
+ ]
307
+ }
308
+ ),
309
+ /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-sm", children: [
310
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
311
+ /* @__PURE__ */ jsx(DialogTitle, { children: t.resetPassword }),
312
+ /* @__PURE__ */ jsxs(DialogDescription, { children: [
313
+ t.resetPassword,
314
+ " \u2014 ",
315
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: user.username })
316
+ ] })
317
+ ] }),
318
+ /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
319
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
320
+ /* @__PURE__ */ jsx(Label, { htmlFor: `reset-${user.id}`, children: t.newPassword }),
321
+ /* @__PURE__ */ jsx(
322
+ Input,
323
+ {
324
+ id: `reset-${user.id}`,
325
+ type: "password",
326
+ value: password,
327
+ onChange: (e) => setPassword(e.target.value),
328
+ autoComplete: "new-password",
329
+ autoFocus: true
330
+ }
331
+ )
332
+ ] }),
333
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
334
+ /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline" }), children: t.cancel }),
335
+ /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: submitting, className: "gap-1.5", children: [
336
+ submitting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : null,
337
+ submitting ? t.saving : t.save
338
+ ] })
339
+ ] })
340
+ ] })
341
+ ] })
342
+ ] });
343
+ }
344
+ function EditAccountDialog({
345
+ user,
346
+ officers,
347
+ roles,
348
+ editAction,
349
+ onSaved
350
+ }) {
351
+ const t = useCopy().admin.accountsPage;
352
+ const [open, setOpen] = React.useState(false);
353
+ const [username, setUsername] = React.useState(user.username);
354
+ const [role, setRole] = React.useState(user.role);
355
+ const [officerId, setOfficerId] = React.useState(user.officerId ?? "");
356
+ const [submitting, setSubmitting] = React.useState(false);
357
+ const selectedRole = roles.find((r) => r.name === role);
358
+ const showOfficer = hasPermission(selectedRole?.permissions, "tickets:create");
359
+ async function submit(e) {
360
+ e.preventDefault();
361
+ if (!username.trim()) return toast.error(`${t.username} is required.`);
362
+ setSubmitting(true);
363
+ try {
364
+ const res = await editAction(user.id, {
365
+ username: username.trim(),
366
+ role,
367
+ officerId: showOfficer && officerId ? officerId : null
368
+ });
369
+ if (res?.error) {
370
+ toast.error(res.error);
371
+ return;
372
+ }
373
+ toast.success(`${t.editTitle}: ${username.trim()}`);
374
+ setOpen(false);
375
+ onSaved();
376
+ } finally {
377
+ setSubmitting(false);
378
+ }
379
+ }
380
+ return /* @__PURE__ */ jsxs(
381
+ Dialog,
382
+ {
383
+ open,
384
+ onOpenChange: (o) => {
385
+ if (o) {
386
+ setUsername(user.username);
387
+ setRole(user.role);
388
+ setOfficerId(user.officerId ?? "");
389
+ }
390
+ setOpen(o);
391
+ },
392
+ children: [
393
+ /* @__PURE__ */ jsxs(
394
+ DialogTrigger,
395
+ {
396
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "gap-1.5" }),
397
+ children: [
398
+ /* @__PURE__ */ jsx(Pencil, { className: "size-3.5" }),
399
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t.edit })
400
+ ]
401
+ }
402
+ ),
403
+ /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
404
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
405
+ /* @__PURE__ */ jsx(DialogTitle, { children: t.editTitle }),
406
+ /* @__PURE__ */ jsx(DialogDescription, { children: user.username })
407
+ ] }),
408
+ /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
409
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
410
+ /* @__PURE__ */ jsx(Label, { htmlFor: `edit-username-${user.id}`, children: t.username }),
411
+ /* @__PURE__ */ jsx(
412
+ Input,
413
+ {
414
+ id: `edit-username-${user.id}`,
415
+ value: username,
416
+ onChange: (e) => setUsername(e.target.value),
417
+ autoComplete: "off"
418
+ }
419
+ )
420
+ ] }),
421
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
422
+ /* @__PURE__ */ jsx(Label, { htmlFor: `edit-role-${user.id}`, children: t.role }),
423
+ /* @__PURE__ */ jsx(
424
+ "select",
425
+ {
426
+ id: `edit-role-${user.id}`,
427
+ value: role,
428
+ onChange: (e) => setRole(e.target.value),
429
+ className: fieldClass,
430
+ children: roles.map((r) => /* @__PURE__ */ jsx("option", { value: r.name, children: r.label }, r.name))
431
+ }
432
+ )
433
+ ] }),
434
+ showOfficer ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
435
+ /* @__PURE__ */ jsx(Label, { htmlFor: `edit-officer-${user.id}`, children: t.officer }),
436
+ /* @__PURE__ */ jsxs(
437
+ "select",
438
+ {
439
+ id: `edit-officer-${user.id}`,
440
+ value: officerId,
441
+ onChange: (e) => setOfficerId(e.target.value),
442
+ className: fieldClass,
443
+ children: [
444
+ /* @__PURE__ */ jsx("option", { value: "", children: t.officerNone }),
445
+ officers.map((o) => /* @__PURE__ */ jsxs("option", { value: o.id, children: [
446
+ o.name,
447
+ o.badgeNo ? ` (${o.badgeNo})` : ""
448
+ ] }, o.id))
449
+ ]
450
+ }
451
+ ),
452
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t.officerHint })
453
+ ] }) : null,
454
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
455
+ /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline" }), children: t.cancel }),
456
+ /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: submitting, className: "gap-1.5", children: [
457
+ submitting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Pencil, { className: "size-4" }),
458
+ submitting ? t.saving : t.saveChanges
459
+ ] })
460
+ ] })
461
+ ] })
462
+ ] })
463
+ ]
464
+ }
465
+ );
466
+ }
467
+
468
+ export { AccountsManager };