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