@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,452 @@
1
+ import { Textarea } from './chunk-QCRVT2SS.js';
2
+ import { Label } from './chunk-XQTVSNHC.js';
3
+ import { Dialog, DialogTrigger, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter, DialogClose } from './chunk-M35R6JLA.js';
4
+ import { usePagination, Pagination } from './chunk-6YFZLXFP.js';
5
+ import { Input } from './chunk-K3KIBHJF.js';
6
+ import { Money } from './chunk-BVI5XDDA.js';
7
+ import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from './chunk-OWCGEEAZ.js';
8
+ import { Badge } from './chunk-55FQP2DO.js';
9
+ import { Button } from './chunk-I4WDVYHX.js';
10
+ import { useCopy } from './chunk-TJSNVTVB.js';
11
+ import { Card, CardContent } from './chunk-SETIN6XP.js';
12
+ import { cn } from './chunk-77QBZC7J.js';
13
+ import * as React from 'react';
14
+ import { toast } from 'sonner';
15
+ import { useRouter } from 'next/navigation';
16
+ import { Plus, Pencil, Loader2, Archive, ArchiveRestore, Trash2 } from 'lucide-react';
17
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
18
+
19
+ 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";
20
+ function ViolationsManager({
21
+ violations,
22
+ createAction,
23
+ updateAction,
24
+ deleteAction,
25
+ usedCodes = [],
26
+ purgeAction
27
+ }) {
28
+ const t = useCopy().admin.violationsPage;
29
+ const router = useRouter();
30
+ const used = React.useMemo(() => new Set(usedCodes), [usedCodes]);
31
+ const { pageItems, page, setPage, totalPages, from, to, total } = usePagination(violations, 12);
32
+ const categoryLabel = (c) => c === "TRAFFIC" ? t.categoryTraffic : t.categoryOrdinance;
33
+ return /* @__PURE__ */ jsxs("div", { className: "mx-auto w-full max-w-5xl p-4 sm:p-6 lg:p-8", children: [
34
+ /* @__PURE__ */ jsxs("div", { className: "mb-6 flex items-center justify-between gap-3", children: [
35
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
36
+ /* @__PURE__ */ jsx("h1", { className: "font-heading text-2xl font-semibold tracking-tight", children: t.title }),
37
+ /* @__PURE__ */ jsx("p", { className: "max-w-2xl text-sm text-muted-foreground", children: t.subtitle })
38
+ ] }),
39
+ /* @__PURE__ */ jsx(
40
+ ViolationDialog,
41
+ {
42
+ mode: "create",
43
+ createAction,
44
+ onDone: () => router.refresh()
45
+ }
46
+ )
47
+ ] }),
48
+ /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsx(CardContent, { className: "p-0", children: violations.length === 0 ? /* @__PURE__ */ jsx("p", { className: "p-8 text-center text-sm text-muted-foreground", children: t.empty }) : /* @__PURE__ */ jsxs(Fragment, { children: [
49
+ /* @__PURE__ */ jsxs(Table, { children: [
50
+ /* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
51
+ /* @__PURE__ */ jsx(TableHead, { children: t.code }),
52
+ /* @__PURE__ */ jsx(TableHead, { children: t.titleLabel }),
53
+ /* @__PURE__ */ jsx(TableHead, { className: "hidden sm:table-cell", children: t.category }),
54
+ /* @__PURE__ */ jsx(TableHead, { className: "text-right", children: t.fine }),
55
+ /* @__PURE__ */ jsx(TableHead, { children: t.status }),
56
+ /* @__PURE__ */ jsx(TableHead, { className: "text-right", children: t.actions })
57
+ ] }) }),
58
+ /* @__PURE__ */ jsx(TableBody, { children: pageItems.map((v) => {
59
+ const archived = v.active === false;
60
+ return /* @__PURE__ */ jsxs(TableRow, { className: cn(archived && "opacity-60"), children: [
61
+ /* @__PURE__ */ jsx(TableCell, { className: "font-mono text-xs", children: v.code }),
62
+ /* @__PURE__ */ jsx(TableCell, { className: "font-medium", children: v.title }),
63
+ /* @__PURE__ */ jsx(TableCell, { className: "hidden text-muted-foreground sm:table-cell", children: categoryLabel(v.category) }),
64
+ /* @__PURE__ */ jsx(TableCell, { className: "text-right", children: /* @__PURE__ */ jsx(Money, { value: v.basicFine }) }),
65
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Badge, { variant: archived ? "outline" : "secondary", children: archived ? t.archived : t.active }) }),
66
+ /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-1.5", children: [
67
+ /* @__PURE__ */ jsx(
68
+ ViolationDialog,
69
+ {
70
+ mode: "edit",
71
+ violation: v,
72
+ updateAction,
73
+ onDone: () => router.refresh()
74
+ }
75
+ ),
76
+ archived ? /* @__PURE__ */ jsx(
77
+ RestoreButton,
78
+ {
79
+ violation: v,
80
+ updateAction,
81
+ onDone: () => router.refresh()
82
+ }
83
+ ) : /* @__PURE__ */ jsx(
84
+ ArchiveButton,
85
+ {
86
+ violation: v,
87
+ deleteAction,
88
+ onDone: () => router.refresh()
89
+ }
90
+ ),
91
+ purgeAction && !used.has(v.code) ? /* @__PURE__ */ jsx(
92
+ DeleteButton,
93
+ {
94
+ violation: v,
95
+ purgeAction,
96
+ onDone: () => router.refresh()
97
+ }
98
+ ) : null
99
+ ] }) })
100
+ ] }, v.code);
101
+ }) })
102
+ ] }),
103
+ /* @__PURE__ */ jsx(
104
+ Pagination,
105
+ {
106
+ page,
107
+ totalPages,
108
+ from,
109
+ to,
110
+ total,
111
+ onPage: setPage
112
+ }
113
+ )
114
+ ] }) }) })
115
+ ] });
116
+ }
117
+ function ViolationDialog({
118
+ mode,
119
+ violation,
120
+ createAction,
121
+ updateAction,
122
+ onDone
123
+ }) {
124
+ const t = useCopy().admin.violationsPage;
125
+ const [open, setOpen] = React.useState(false);
126
+ const [code, setCode] = React.useState(violation?.code ?? "");
127
+ const [title, setTitle] = React.useState(violation?.title ?? "");
128
+ const [category, setCategory] = React.useState(
129
+ violation?.category ?? "TRAFFIC"
130
+ );
131
+ const [fine, setFine] = React.useState(
132
+ violation ? String(violation.basicFine) : ""
133
+ );
134
+ const [legalText, setLegalText] = React.useState(violation?.legalText ?? "");
135
+ const [submitting, setSubmitting] = React.useState(false);
136
+ function resetTo(v) {
137
+ setCode(v?.code ?? "");
138
+ setTitle(v?.title ?? "");
139
+ setCategory(v?.category ?? "TRAFFIC");
140
+ setFine(v ? String(v.basicFine) : "");
141
+ setLegalText(v?.legalText ?? "");
142
+ }
143
+ async function submit(e) {
144
+ e.preventDefault();
145
+ if (mode === "create" && !code.trim())
146
+ return toast.error(`${t.code} is required.`);
147
+ if (!title.trim()) return toast.error(`${t.titleLabel} is required.`);
148
+ const fineNum = Number(fine);
149
+ if (!Number.isFinite(fineNum) || fineNum < 0)
150
+ return toast.error(`${t.fine} must be a number \u2265 0.`);
151
+ setSubmitting(true);
152
+ try {
153
+ let res;
154
+ if (mode === "create") {
155
+ const input = {
156
+ code: code.trim(),
157
+ title: title.trim(),
158
+ category,
159
+ basicFine: fineNum,
160
+ legalText: legalText.trim() || void 0
161
+ };
162
+ res = await createAction?.(input);
163
+ } else {
164
+ res = await updateAction?.(violation.code, {
165
+ title: title.trim(),
166
+ category,
167
+ basicFine: fineNum,
168
+ legalText: legalText.trim() || void 0
169
+ });
170
+ }
171
+ if (res?.error) {
172
+ toast.error(res.error);
173
+ return;
174
+ }
175
+ toast.success(
176
+ `${mode === "create" ? t.create : t.editTitle}: ${title.trim()}`
177
+ );
178
+ setOpen(false);
179
+ onDone();
180
+ } finally {
181
+ setSubmitting(false);
182
+ }
183
+ }
184
+ return /* @__PURE__ */ jsxs(
185
+ Dialog,
186
+ {
187
+ open,
188
+ onOpenChange: (o) => {
189
+ if (o) resetTo(violation);
190
+ setOpen(o);
191
+ },
192
+ children: [
193
+ /* @__PURE__ */ jsxs(
194
+ DialogTrigger,
195
+ {
196
+ render: mode === "create" ? /* @__PURE__ */ jsx(Button, { className: "gap-1.5" }) : /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "gap-1.5" }),
197
+ children: [
198
+ mode === "create" ? /* @__PURE__ */ jsx(Plus, { className: "size-4" }) : /* @__PURE__ */ jsx(Pencil, { className: "size-3.5" }),
199
+ /* @__PURE__ */ jsx("span", { className: mode === "create" ? "" : "hidden sm:inline", children: mode === "create" ? t.newViolation : t.edit })
200
+ ]
201
+ }
202
+ ),
203
+ /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md", children: [
204
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
205
+ /* @__PURE__ */ jsx(DialogTitle, { children: mode === "create" ? t.newViolation : t.editTitle }),
206
+ /* @__PURE__ */ jsx(DialogDescription, { children: t.subtitle })
207
+ ] }),
208
+ /* @__PURE__ */ jsxs("form", { onSubmit: submit, className: "space-y-4", children: [
209
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
210
+ /* @__PURE__ */ jsx(Label, { htmlFor: "vio-code", children: t.code }),
211
+ /* @__PURE__ */ jsx(
212
+ Input,
213
+ {
214
+ id: "vio-code",
215
+ value: code,
216
+ onChange: (e) => setCode(e.target.value),
217
+ placeholder: t.codePlaceholder,
218
+ autoComplete: "off",
219
+ disabled: mode === "edit",
220
+ autoFocus: mode === "create"
221
+ }
222
+ )
223
+ ] }),
224
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
225
+ /* @__PURE__ */ jsx(Label, { htmlFor: "vio-title", children: t.titleLabel }),
226
+ /* @__PURE__ */ jsx(
227
+ Input,
228
+ {
229
+ id: "vio-title",
230
+ value: title,
231
+ onChange: (e) => setTitle(e.target.value),
232
+ placeholder: t.titlePlaceholder,
233
+ autoComplete: "off",
234
+ autoFocus: mode === "edit"
235
+ }
236
+ )
237
+ ] }),
238
+ /* @__PURE__ */ jsxs("div", { className: "grid gap-4 sm:grid-cols-2", children: [
239
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
240
+ /* @__PURE__ */ jsx(Label, { htmlFor: "vio-category", children: t.category }),
241
+ /* @__PURE__ */ jsxs(
242
+ "select",
243
+ {
244
+ id: "vio-category",
245
+ value: category,
246
+ onChange: (e) => setCategory(e.target.value),
247
+ className: fieldClass,
248
+ children: [
249
+ /* @__PURE__ */ jsx("option", { value: "TRAFFIC", children: t.categoryTraffic }),
250
+ /* @__PURE__ */ jsx("option", { value: "ORDINANCE", children: t.categoryOrdinance })
251
+ ]
252
+ }
253
+ )
254
+ ] }),
255
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
256
+ /* @__PURE__ */ jsx(Label, { htmlFor: "vio-fine", children: t.fine }),
257
+ /* @__PURE__ */ jsx(
258
+ Input,
259
+ {
260
+ id: "vio-fine",
261
+ type: "number",
262
+ min: "0",
263
+ step: "1",
264
+ inputMode: "numeric",
265
+ value: fine,
266
+ onChange: (e) => setFine(e.target.value),
267
+ placeholder: t.finePlaceholder,
268
+ autoComplete: "off"
269
+ }
270
+ )
271
+ ] })
272
+ ] }),
273
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1.5", children: [
274
+ /* @__PURE__ */ jsx(Label, { htmlFor: "vio-legal", children: t.legalText }),
275
+ /* @__PURE__ */ jsx(
276
+ Textarea,
277
+ {
278
+ id: "vio-legal",
279
+ value: legalText,
280
+ onChange: (e) => setLegalText(e.target.value),
281
+ placeholder: t.legalTextPlaceholder,
282
+ rows: 2
283
+ }
284
+ )
285
+ ] }),
286
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
287
+ /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline" }), children: t.cancel }),
288
+ /* @__PURE__ */ jsxs(Button, { type: "submit", disabled: submitting, className: "gap-1.5", children: [
289
+ submitting ? /* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }) : null,
290
+ submitting ? mode === "create" ? t.creating : t.saving : mode === "create" ? t.create : t.save
291
+ ] })
292
+ ] })
293
+ ] })
294
+ ] })
295
+ ]
296
+ }
297
+ );
298
+ }
299
+ function ArchiveButton({
300
+ violation,
301
+ deleteAction,
302
+ onDone
303
+ }) {
304
+ const t = useCopy().admin.violationsPage;
305
+ const [busy, setBusy] = React.useState(false);
306
+ async function archive() {
307
+ setBusy(true);
308
+ try {
309
+ const res = await deleteAction(violation.code);
310
+ if (res?.error) {
311
+ toast.error(res.error);
312
+ return;
313
+ }
314
+ toast.success(`${t.archive}: ${violation.title}`);
315
+ onDone();
316
+ } finally {
317
+ setBusy(false);
318
+ }
319
+ }
320
+ return /* @__PURE__ */ jsxs(Dialog, { children: [
321
+ /* @__PURE__ */ jsxs(
322
+ DialogTrigger,
323
+ {
324
+ render: /* @__PURE__ */ jsx(Button, { variant: "outline", size: "sm", className: "gap-1.5" }),
325
+ children: [
326
+ /* @__PURE__ */ jsx(Archive, { className: "size-3.5" }),
327
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t.archive })
328
+ ]
329
+ }
330
+ ),
331
+ /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-sm", children: [
332
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
333
+ /* @__PURE__ */ jsx(DialogTitle, { children: t.archiveConfirmTitle }),
334
+ /* @__PURE__ */ jsxs(DialogDescription, { children: [
335
+ violation.title,
336
+ " \u2014 ",
337
+ t.archiveConfirmBody
338
+ ] })
339
+ ] }),
340
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
341
+ /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { variant: "outline" }), children: t.cancel }),
342
+ /* @__PURE__ */ jsxs(
343
+ DialogClose,
344
+ {
345
+ render: /* @__PURE__ */ jsx(Button, { className: "gap-2", disabled: busy }),
346
+ onClick: archive,
347
+ children: [
348
+ /* @__PURE__ */ jsx(Archive, { className: "size-4" }),
349
+ t.archive
350
+ ]
351
+ }
352
+ )
353
+ ] })
354
+ ] })
355
+ ] });
356
+ }
357
+ function RestoreButton({
358
+ violation,
359
+ updateAction,
360
+ onDone
361
+ }) {
362
+ const t = useCopy().admin.violationsPage;
363
+ const [busy, setBusy] = React.useState(false);
364
+ async function restore() {
365
+ setBusy(true);
366
+ try {
367
+ const res = await updateAction(violation.code, { active: true });
368
+ if (res?.error) {
369
+ toast.error(res.error);
370
+ return;
371
+ }
372
+ toast.success(`${t.restore}: ${violation.title}`);
373
+ onDone();
374
+ } finally {
375
+ setBusy(false);
376
+ }
377
+ }
378
+ return /* @__PURE__ */ jsxs(
379
+ Button,
380
+ {
381
+ variant: "outline",
382
+ size: "sm",
383
+ className: "gap-1.5",
384
+ disabled: busy,
385
+ onClick: restore,
386
+ children: [
387
+ busy ? /* @__PURE__ */ jsx(Loader2, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(ArchiveRestore, { className: "size-3.5" }),
388
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t.restore })
389
+ ]
390
+ }
391
+ );
392
+ }
393
+ function DeleteButton({
394
+ violation,
395
+ purgeAction,
396
+ onDone
397
+ }) {
398
+ const t = useCopy().admin.violationsPage;
399
+ const [busy, setBusy] = React.useState(false);
400
+ async function purge() {
401
+ setBusy(true);
402
+ try {
403
+ const res = await purgeAction(violation.code);
404
+ if (res?.error) {
405
+ toast.error(res.error);
406
+ return;
407
+ }
408
+ toast.success(`${t.delete}: ${violation.title}`);
409
+ onDone();
410
+ } finally {
411
+ setBusy(false);
412
+ }
413
+ }
414
+ return /* @__PURE__ */ jsxs(Dialog, { children: [
415
+ /* @__PURE__ */ jsxs(
416
+ DialogTrigger,
417
+ {
418
+ render: /* @__PURE__ */ jsx(Button, { variant: "destructive", size: "sm", className: "gap-1.5" }),
419
+ children: [
420
+ /* @__PURE__ */ jsx(Trash2, { className: "size-3.5" }),
421
+ /* @__PURE__ */ jsx("span", { className: "hidden sm:inline", children: t.delete })
422
+ ]
423
+ }
424
+ ),
425
+ /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-sm", children: [
426
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
427
+ /* @__PURE__ */ jsx(DialogTitle, { children: t.deleteConfirmTitle }),
428
+ /* @__PURE__ */ jsxs(DialogDescription, { children: [
429
+ violation.title,
430
+ " \u2014 ",
431
+ t.deleteConfirmBody
432
+ ] })
433
+ ] }),
434
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
435
+ /* @__PURE__ */ jsx(DialogClose, { render: /* @__PURE__ */ jsx(Button, { variant: "outline" }), children: t.cancel }),
436
+ /* @__PURE__ */ jsxs(
437
+ DialogClose,
438
+ {
439
+ render: /* @__PURE__ */ jsx(Button, { variant: "destructive", className: "gap-2", disabled: busy }),
440
+ onClick: purge,
441
+ children: [
442
+ /* @__PURE__ */ jsx(Trash2, { className: "size-4" }),
443
+ t.delete
444
+ ]
445
+ }
446
+ )
447
+ ] })
448
+ ] })
449
+ ] });
450
+ }
451
+
452
+ export { ViolationsManager };