@gelabs/ovr 0.4.3 → 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 (75) 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.d.ts +1 -1
  7. package/dist/chunk-4EDMZRGS.js +98 -0
  8. package/dist/chunk-4JMHQKMK.js +268 -0
  9. package/dist/chunk-6662IONX.js +69 -0
  10. package/dist/chunk-6WMPBAUH.js +18 -0
  11. package/dist/chunk-7GVZZWK3.js +74 -0
  12. package/dist/chunk-ACXED4UH.js +403 -0
  13. package/dist/chunk-BBTAG3FD.js +165 -0
  14. package/dist/chunk-HX7QT2FE.js +452 -0
  15. package/dist/chunk-LC3S47FM.js +468 -0
  16. package/dist/chunk-NPTR7GFZ.js +432 -0
  17. package/dist/chunk-SLQRZBMR.js +66 -0
  18. package/dist/chunk-UBUXPJLN.js +29 -0
  19. package/dist/chunk-UN6Z4WGF.js +31 -0
  20. package/dist/chunk-YE3D2DYY.js +83 -0
  21. package/dist/citizen.d.ts +5 -0
  22. package/dist/citizen.js +12 -0
  23. package/dist/config.d.ts +2 -2
  24. package/dist/core-i18n.d.ts +1 -1
  25. package/dist/core.d.ts +2 -2
  26. package/dist/dashboard.d.ts +10 -0
  27. package/dist/dashboard.js +3 -0
  28. package/dist/data-mock-store.d.ts +1 -1
  29. package/dist/{format-C7MSwUHK.d.ts → format-VyCUfF8R.d.ts} +1 -1
  30. package/dist/generated/client/edge.js +2 -2
  31. package/dist/generated/client/index.js +2 -2
  32. package/dist/generated/client/wasm.js +2 -2
  33. package/dist/index.d.ts +2 -2
  34. package/dist/notifications.d.ts +6 -0
  35. package/dist/notifications.js +11 -0
  36. package/dist/officers.d.ts +27 -0
  37. package/dist/officers.js +11 -0
  38. package/dist/offline.d.ts +1 -1
  39. package/dist/payments.d.ts +15 -0
  40. package/dist/payments.js +11 -0
  41. package/dist/roles.d.ts +37 -0
  42. package/dist/roles.js +12 -0
  43. package/dist/runtime.d.ts +1 -1
  44. package/dist/tickets.d.ts +6 -0
  45. package/dist/tickets.js +24 -0
  46. package/dist/ui-components-admin/accounts-manager.d.ts +3 -52
  47. package/dist/ui-components-admin/accounts-manager.js +11 -468
  48. package/dist/ui-components-admin/admin-nav.js +9 -81
  49. package/dist/ui-components-admin/issuance-form.js +15 -432
  50. package/dist/ui-components-admin/logs-viewer.d.ts +3 -13
  51. package/dist/ui-components-admin/logs-viewer.js +8 -98
  52. package/dist/ui-components-admin/notifications-list.js +6 -66
  53. package/dist/ui-components-admin/officers-manager.d.ts +3 -27
  54. package/dist/ui-components-admin/officers-manager.js +9 -268
  55. package/dist/ui-components-admin/roles-manager.d.ts +3 -37
  56. package/dist/ui-components-admin/roles-manager.js +10 -403
  57. package/dist/ui-components-admin/stat-card.d.ts +2 -10
  58. package/dist/ui-components-admin/stat-card.js +3 -29
  59. package/dist/ui-components-admin/ticket-preview.js +2 -2
  60. package/dist/ui-components-admin/tickets-table.js +7 -69
  61. package/dist/ui-components-admin/violations-manager.js +13 -452
  62. package/dist/ui-components-citizen/citizen-nav.js +3 -31
  63. package/dist/ui-components-citizen/payment-form.d.ts +3 -14
  64. package/dist/ui-components-citizen/payment-form.js +9 -165
  65. package/dist/ui-components-citizen/payment-qr-dialog.js +2 -2
  66. package/dist/ui-components-citizen/ticket-not-found.js +4 -18
  67. package/dist/ui-components-citizen/violation-history-table.js +6 -74
  68. package/dist/ui-config.d.ts +2 -2
  69. package/dist/ui-server.d.ts +2 -2
  70. package/dist/violations.d.ts +3 -0
  71. package/dist/violations.js +14 -0
  72. package/package.json +42 -2
  73. package/dist/{chunk-ZUMEOZ22.js → chunk-JTSTNZAB.js} +1 -1
  74. package/dist/{chunk-TLG4C2XI.js → chunk-QCAURREW.js} +1 -1
  75. package/dist/{schema-CdsFQxIg.d.ts → schema-BUhh_mKX.d.ts} +108 -108
@@ -1,471 +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 { Label } from '../chunk-XQTVSNHC.js';
7
- import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose } from '../chunk-M35R6JLA.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 defaultRole = roles.find((r) => r.name === "ENFORCER")?.name ?? roles[0]?.name ?? "";
154
- const [open, setOpen] = React.useState(false);
155
- const [username, setUsername] = React.useState("");
156
- const [password, setPassword] = React.useState("");
157
- const [role, setRole] = React.useState(defaultRole);
158
- const [officerId, setOfficerId] = React.useState("");
159
- const [submitting, setSubmitting] = React.useState(false);
160
- const selectedRole = roles.find((r) => r.name === role);
161
- const showOfficer = hasPermission(selectedRole?.permissions, "tickets:create");
162
- function reset() {
163
- setUsername("");
164
- setPassword("");
165
- setRole(defaultRole);
166
- setOfficerId("");
167
- }
168
- async function submit(e) {
169
- e.preventDefault();
170
- if (!username.trim()) return toast.error(`${t.username} is required.`);
171
- if (password.length < 6)
172
- return toast.error("Password must be at least 6 characters.");
173
- if (!role) return toast.error(`${t.role} is required.`);
174
- setSubmitting(true);
175
- try {
176
- const res = await createAction({
177
- username: username.trim(),
178
- password,
179
- role,
180
- officerId: showOfficer && officerId ? officerId : null
181
- });
182
- if (res?.error) {
183
- toast.error(res.error);
184
- return;
185
- }
186
- toast.success(`Account "${username.trim()}" created.`);
187
- reset();
188
- setOpen(false);
189
- onCreated();
190
- } finally {
191
- setSubmitting(false);
192
- }
193
- }
194
- return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
195
- /* @__PURE__ */ jsxs(DialogTrigger, { render: /* @__PURE__ */ jsx(Button, { className: "gap-1.5" }), children: [
196
- /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
197
- t.newAccount
198
- ] }),
199
- /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
200
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
201
- /* @__PURE__ */ jsx(DialogTitle, { children: t.newAccount }),
202
- /* @__PURE__ */ jsx(DialogDescription, { children: t.subtitle })
203
- ] }),
204
- /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
205
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
206
- /* @__PURE__ */ jsx(Label, { htmlFor: "acc-username", children: t.username }),
207
- /* @__PURE__ */ jsx(
208
- Input,
209
- {
210
- id: "acc-username",
211
- value: username,
212
- onChange: (e) => setUsername(e.target.value),
213
- autoComplete: "off",
214
- autoFocus: true
215
- }
216
- )
217
- ] }),
218
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
219
- /* @__PURE__ */ jsx(Label, { htmlFor: "acc-password", children: t.password }),
220
- /* @__PURE__ */ jsx(
221
- Input,
222
- {
223
- id: "acc-password",
224
- type: "password",
225
- value: password,
226
- onChange: (e) => setPassword(e.target.value),
227
- autoComplete: "new-password"
228
- }
229
- )
230
- ] }),
231
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
232
- /* @__PURE__ */ jsx(Label, { htmlFor: "acc-role", children: t.role }),
233
- /* @__PURE__ */ jsx(
234
- "select",
235
- {
236
- id: "acc-role",
237
- value: role,
238
- onChange: (e) => setRole(e.target.value),
239
- className: fieldClass,
240
- children: roles.map((r) => /* @__PURE__ */ jsx("option", { value: r.name, children: r.label }, r.name))
241
- }
242
- )
243
- ] }),
244
- showOfficer ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
245
- /* @__PURE__ */ jsx(Label, { htmlFor: "acc-officer", children: t.officer }),
246
- /* @__PURE__ */ jsxs(
247
- "select",
248
- {
249
- id: "acc-officer",
250
- value: officerId,
251
- onChange: (e) => setOfficerId(e.target.value),
252
- className: fieldClass,
253
- children: [
254
- /* @__PURE__ */ jsx("option", { value: "", children: t.officerNone }),
255
- officers.map((o) => /* @__PURE__ */ jsxs("option", { value: o.id, children: [
256
- o.name,
257
- o.badgeNo ? ` (${o.badgeNo})` : ""
258
- ] }, o.id))
259
- ]
260
- }
261
- ),
262
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t.officerHint })
263
- ] }) : null,
264
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
265
- /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline" }), children: t.cancel }),
266
- /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: submitting, className: "gap-1.5", children: [
267
- submitting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(UserPlus, { className: "size-4" }),
268
- submitting ? t.creating : t.create
269
- ] })
270
- ] })
271
- ] })
272
- ] })
273
- ] });
274
- }
275
- function ResetPasswordDialog({
276
- user,
277
- resetPasswordAction
278
- }) {
279
- const t = useCopy().admin.accountsPage;
280
- const [open, setOpen] = React.useState(false);
281
- const [password, setPassword] = React.useState("");
282
- const [submitting, setSubmitting] = React.useState(false);
283
- async function submit(e) {
284
- e.preventDefault();
285
- if (password.length < 6)
286
- return toast.error("Password must be at least 6 characters.");
287
- setSubmitting(true);
288
- try {
289
- const res = await resetPasswordAction(user.id, password);
290
- if (res?.error) {
291
- toast.error(res.error);
292
- return;
293
- }
294
- toast.success(`Password reset for "${user.username}".`);
295
- setPassword("");
296
- setOpen(false);
297
- } finally {
298
- setSubmitting(false);
299
- }
300
- }
301
- return /* @__PURE__ */ jsxs(Dialog, { open, onOpenChange: setOpen, children: [
302
- /* @__PURE__ */ jsxs(
303
- DialogTrigger,
304
- {
305
- render: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "gap-1.5" }),
306
- children: [
307
- /* @__PURE__ */ jsx(KeyRound, { className: "size-3.5" }),
308
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t.resetPassword })
309
- ]
310
- }
311
- ),
312
- /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-sm", children: [
313
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
314
- /* @__PURE__ */ jsx(DialogTitle, { children: t.resetPassword }),
315
- /* @__PURE__ */ jsxs(DialogDescription, { children: [
316
- t.resetPassword,
317
- " \u2014 ",
318
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: user.username })
319
- ] })
320
- ] }),
321
- /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
322
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
323
- /* @__PURE__ */ jsx(Label, { htmlFor: `reset-${user.id}`, children: t.newPassword }),
324
- /* @__PURE__ */ jsx(
325
- Input,
326
- {
327
- id: `reset-${user.id}`,
328
- type: "password",
329
- value: password,
330
- onChange: (e) => setPassword(e.target.value),
331
- autoComplete: "new-password",
332
- autoFocus: true
333
- }
334
- )
335
- ] }),
336
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
337
- /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline" }), children: t.cancel }),
338
- /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: submitting, className: "gap-1.5", children: [
339
- submitting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : null,
340
- submitting ? t.saving : t.save
341
- ] })
342
- ] })
343
- ] })
344
- ] })
345
- ] });
346
- }
347
- function EditAccountDialog({
348
- user,
349
- officers,
350
- roles,
351
- editAction,
352
- onSaved
353
- }) {
354
- const t = useCopy().admin.accountsPage;
355
- const [open, setOpen] = React.useState(false);
356
- const [username, setUsername] = React.useState(user.username);
357
- const [role, setRole] = React.useState(user.role);
358
- const [officerId, setOfficerId] = React.useState(user.officerId ?? "");
359
- const [submitting, setSubmitting] = React.useState(false);
360
- const selectedRole = roles.find((r) => r.name === role);
361
- const showOfficer = hasPermission(selectedRole?.permissions, "tickets:create");
362
- async function submit(e) {
363
- e.preventDefault();
364
- if (!username.trim()) return toast.error(`${t.username} is required.`);
365
- setSubmitting(true);
366
- try {
367
- const res = await editAction(user.id, {
368
- username: username.trim(),
369
- role,
370
- officerId: showOfficer && officerId ? officerId : null
371
- });
372
- if (res?.error) {
373
- toast.error(res.error);
374
- return;
375
- }
376
- toast.success(`${t.editTitle}: ${username.trim()}`);
377
- setOpen(false);
378
- onSaved();
379
- } finally {
380
- setSubmitting(false);
381
- }
382
- }
383
- return /* @__PURE__ */ jsxs(
384
- Dialog,
385
- {
386
- open,
387
- onOpenChange: (o) => {
388
- if (o) {
389
- setUsername(user.username);
390
- setRole(user.role);
391
- setOfficerId(user.officerId ?? "");
392
- }
393
- setOpen(o);
394
- },
395
- children: [
396
- /* @__PURE__ */ jsxs(
397
- DialogTrigger,
398
- {
399
- render: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "gap-1.5" }),
400
- children: [
401
- /* @__PURE__ */ jsx(Pencil, { className: "size-3.5" }),
402
- /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t.edit })
403
- ]
404
- }
405
- ),
406
- /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
407
- /* @__PURE__ */ jsxs(DialogHeader, { children: [
408
- /* @__PURE__ */ jsx(DialogTitle, { children: t.editTitle }),
409
- /* @__PURE__ */ jsx(DialogDescription, { children: user.username })
410
- ] }),
411
- /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
412
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
413
- /* @__PURE__ */ jsx(Label, { htmlFor: `edit-username-${user.id}`, children: t.username }),
414
- /* @__PURE__ */ jsx(
415
- Input,
416
- {
417
- id: `edit-username-${user.id}`,
418
- value: username,
419
- onChange: (e) => setUsername(e.target.value),
420
- autoComplete: "off"
421
- }
422
- )
423
- ] }),
424
- /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
425
- /* @__PURE__ */ jsx(Label, { htmlFor: `edit-role-${user.id}`, children: t.role }),
426
- /* @__PURE__ */ jsx(
427
- "select",
428
- {
429
- id: `edit-role-${user.id}`,
430
- value: role,
431
- onChange: (e) => setRole(e.target.value),
432
- className: fieldClass,
433
- children: roles.map((r) => /* @__PURE__ */ jsx("option", { value: r.name, children: r.label }, r.name))
434
- }
435
- )
436
- ] }),
437
- showOfficer ? /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
438
- /* @__PURE__ */ jsx(Label, { htmlFor: `edit-officer-${user.id}`, children: t.officer }),
439
- /* @__PURE__ */ jsxs(
440
- "select",
441
- {
442
- id: `edit-officer-${user.id}`,
443
- value: officerId,
444
- onChange: (e) => setOfficerId(e.target.value),
445
- className: fieldClass,
446
- children: [
447
- /* @__PURE__ */ jsx("option", { value: "", children: t.officerNone }),
448
- officers.map((o) => /* @__PURE__ */ jsxs("option", { value: o.id, children: [
449
- o.name,
450
- o.badgeNo ? ` (${o.badgeNo})` : ""
451
- ] }, o.id))
452
- ]
453
- }
454
- ),
455
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: t.officerHint })
456
- ] }) : null,
457
- /* @__PURE__ */ jsxs(DialogFooter, { children: [
458
- /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline" }), children: t.cancel }),
459
- /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: submitting, className: "gap-1.5", children: [
460
- submitting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : /* @__PURE__ */ jsx(Pencil, { className: "size-4" }),
461
- submitting ? t.saving : t.saveChanges
462
- ] })
463
- ] })
464
- ] })
465
- ] })
466
- ]
467
- }
468
- );
469
- }
470
-
471
- export { AccountsManager };
@@ -1,95 +1,23 @@
1
1
  "use client";
2
2
  import { Sheet, SheetTrigger, SheetContent, SheetHeader, SheetTitle, SheetDescription, SheetFooter } from '../chunk-3YKVH4Y7.js';
3
+ import { NotificationBell } from '../chunk-YE3D2DYY.js';
3
4
  import { DropdownMenu, DropdownMenuTrigger, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator } from '../chunk-V7VQVDWS.js';
5
+ import { useIdentity, cacheCredential } from '../chunk-YGYA7KEG.js';
4
6
  import { Label } from '../chunk-XQTVSNHC.js';
5
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),