@gelabs/ovr 0.1.1 → 0.2.0
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.
- package/dist/chunk-3KIDW4LT.js +13 -0
- package/dist/chunk-4SZXBT56.js +26 -0
- package/dist/chunk-55FQP2DO.js +46 -0
- package/dist/{chunk-JV7QQ22Q.js → chunk-5YYR37CF.js} +10 -1
- package/dist/chunk-5Z2IAD5I.js +72 -0
- package/dist/chunk-BBQBKQA4.js +31 -0
- package/dist/chunk-BIQ2J75Y.js +54 -0
- package/dist/chunk-E2D7QT6N.js +92 -0
- package/dist/chunk-EGKFELO3.js +15 -0
- package/dist/chunk-EYFZWQ4J.js +74 -0
- package/dist/chunk-GDOCD7LT.js +46 -0
- package/dist/chunk-IF5UAVIE.js +25 -0
- package/dist/chunk-JEYT63LE.js +25 -0
- package/dist/chunk-K3KIBHJF.js +20 -0
- package/dist/chunk-M35R6JLA.js +142 -0
- package/dist/chunk-MDTRBOPQ.js +22 -0
- package/dist/chunk-NSCIBSCW.js +24 -0
- package/dist/chunk-OE525ZER.js +31 -0
- package/dist/chunk-OWCGEEAZ.js +107 -0
- package/dist/chunk-QCRVT2SS.js +18 -0
- package/dist/chunk-SETIN6XP.js +95 -0
- package/dist/chunk-WOPU6DI7.js +77 -0
- package/dist/chunk-XQTVSNHC.js +18 -0
- package/dist/core-i18n.d.ts +2 -2
- package/dist/core-i18n.js +1 -1
- package/dist/data-db.d.ts +1 -4
- package/dist/data-mock-store.js +53 -1
- package/dist/data-prisma-store.d.ts +3 -1
- package/dist/data-prisma-store.js +84 -1
- package/dist/data-seed-runner.d.ts +2 -1
- package/dist/data.d.ts +13 -1
- package/dist/offline.d.ts +126 -0
- package/dist/offline.js +376 -0
- package/dist/{types-DrrNO_Ak.d.ts → types-CtBC5-TW.d.ts} +8 -0
- package/dist/ui-components-admin/admin-nav.d.ts +7 -0
- package/dist/ui-components-admin/admin-nav.js +83 -0
- package/dist/ui-components-admin/issuance-form.d.ts +15 -0
- package/dist/ui-components-admin/issuance-form.js +367 -0
- package/dist/ui-components-admin/stat-card.d.ts +10 -0
- package/dist/ui-components-admin/stat-card.js +29 -0
- package/dist/ui-components-admin/ticket-preview.d.ts +9 -0
- package/dist/ui-components-admin/ticket-preview.js +13 -0
- package/dist/ui-components-admin/tickets-table.d.ts +8 -0
- package/dist/ui-components-admin/tickets-table.js +50 -0
- package/dist/ui-components-citizen/citizen-nav.d.ts +5 -0
- package/dist/ui-components-citizen/citizen-nav.js +33 -0
- package/dist/ui-components-citizen/payment-form.d.ts +14 -0
- package/dist/ui-components-citizen/payment-form.js +168 -0
- package/dist/ui-components-citizen/payment-qr-dialog.d.ts +30 -0
- package/dist/ui-components-citizen/payment-qr-dialog.js +9 -0
- package/dist/ui-components-citizen/ticket-not-found.d.ts +5 -0
- package/dist/ui-components-citizen/ticket-not-found.js +21 -0
- package/dist/ui-components-citizen/violation-history-table.d.ts +16 -0
- package/dist/ui-components-citizen/violation-history-table.js +77 -0
- package/dist/ui-components-shared/amount-summary.d.ts +8 -0
- package/dist/ui-components-shared/amount-summary.js +7 -0
- package/dist/ui-components-shared/money.d.ts +9 -0
- package/dist/ui-components-shared/money.js +5 -0
- package/dist/ui-components-shared/municipal-seal.d.ts +11 -0
- package/dist/ui-components-shared/municipal-seal.js +6 -0
- package/dist/ui-components-shared/official-header.d.ts +5 -0
- package/dist/ui-components-shared/official-header.js +7 -0
- package/dist/ui-components-shared/print-button.d.ts +8 -0
- package/dist/ui-components-shared/print-button.js +17 -0
- package/dist/ui-components-shared/seal.d.ts +12 -0
- package/dist/ui-components-shared/seal.js +2 -0
- package/dist/ui-components-shared/site-header.d.ts +13 -0
- package/dist/ui-components-shared/site-header.js +40 -0
- package/dist/ui-components-shared/sonner.d.ts +6 -0
- package/dist/ui-components-shared/sonner.js +38 -0
- package/dist/ui-components-shared/status-badge.d.ts +9 -0
- package/dist/ui-components-shared/status-badge.js +3 -0
- package/dist/ui-components-shared/theme-toggle.d.ts +7 -0
- package/dist/ui-components-shared/theme-toggle.js +6 -0
- package/dist/ui-components-shared/ticket-receipt.d.ts +12 -0
- package/dist/ui-components-shared/ticket-receipt.js +158 -0
- package/dist/ui-components-shared/violations-table.d.ts +8 -0
- package/dist/ui-components-shared/violations-table.js +7 -0
- package/dist/ui-components-ui/alert.js +2 -74
- package/dist/ui-components-ui/badge.d.ts +1 -1
- package/dist/ui-components-ui/badge.js +2 -46
- package/dist/ui-components-ui/button.d.ts +2 -2
- package/dist/ui-components-ui/card.js +2 -95
- package/dist/ui-components-ui/checkbox.js +2 -31
- package/dist/ui-components-ui/dialog.js +3 -142
- package/dist/ui-components-ui/input.js +2 -20
- package/dist/ui-components-ui/label.js +2 -18
- package/dist/ui-components-ui/separator.js +2 -24
- package/dist/ui-components-ui/skeleton.js +2 -15
- package/dist/ui-components-ui/table.js +2 -107
- package/dist/ui-components-ui/textarea.js +2 -18
- package/dist/ui-config.d.ts +19 -2
- package/dist/ui-config.js +2 -36
- package/dist/ui-server.d.ts +1 -1
- package/dist/ui-server.js +1 -1
- package/package.json +24 -4
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { PaymentQrDialog } from '../chunk-5Z2IAD5I.js';
|
|
3
|
+
import { Separator } from '../chunk-NSCIBSCW.js';
|
|
4
|
+
import { Alert, AlertDescription } from '../chunk-EYFZWQ4J.js';
|
|
5
|
+
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from '../chunk-SETIN6XP.js';
|
|
6
|
+
import { Money } from '../chunk-3KIDW4LT.js';
|
|
7
|
+
import '../chunk-M35R6JLA.js';
|
|
8
|
+
import { Button } from '../chunk-I4WDVYHX.js';
|
|
9
|
+
import { useOvrConfig, useCopy } from '../chunk-E2D7QT6N.js';
|
|
10
|
+
import { cn } from '../chunk-77QBZC7J.js';
|
|
11
|
+
import '../chunk-B634JHKZ.js';
|
|
12
|
+
import * as React from 'react';
|
|
13
|
+
import { toast } from 'sonner';
|
|
14
|
+
import { Check, TriangleAlert, Loader2, Lock, Building2, Landmark, Smartphone, Wallet } from 'lucide-react';
|
|
15
|
+
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
16
|
+
|
|
17
|
+
var ICONS = {
|
|
18
|
+
GCASH: /* @__PURE__ */ jsx(Wallet, { className: "size-5" }),
|
|
19
|
+
MAYA: /* @__PURE__ */ jsx(Smartphone, { className: "size-5" }),
|
|
20
|
+
LANDBANK: /* @__PURE__ */ jsx(Landmark, { className: "size-5" }),
|
|
21
|
+
OVER_THE_COUNTER: /* @__PURE__ */ jsx(Building2, { className: "size-5" })
|
|
22
|
+
};
|
|
23
|
+
function PaymentForm({
|
|
24
|
+
ovrTicketNo,
|
|
25
|
+
lastName,
|
|
26
|
+
violatorName,
|
|
27
|
+
basicFines,
|
|
28
|
+
penalty,
|
|
29
|
+
amount,
|
|
30
|
+
payAction
|
|
31
|
+
}) {
|
|
32
|
+
const { paymentMethods } = useOvrConfig();
|
|
33
|
+
const copy = useCopy();
|
|
34
|
+
const payMethods = paymentMethods.filter((m) => m.id !== "OVER_THE_COUNTER");
|
|
35
|
+
const [method, setMethod] = React.useState(
|
|
36
|
+
() => payMethods[0]?.id ?? "GCASH"
|
|
37
|
+
);
|
|
38
|
+
const [error, setError] = React.useState(null);
|
|
39
|
+
const [isPending, startTransition] = React.useTransition();
|
|
40
|
+
const [qrOpen, setQrOpen] = React.useState(false);
|
|
41
|
+
function pay() {
|
|
42
|
+
setError(null);
|
|
43
|
+
startTransition(async () => {
|
|
44
|
+
const res = await payAction({ ovrTicketNo, lastName, method });
|
|
45
|
+
if (res?.error) {
|
|
46
|
+
setError(res.error);
|
|
47
|
+
toast.error(res.error);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
const methodLabel = paymentMethods.find((m) => m.id === method)?.label ?? method;
|
|
52
|
+
function handlePay() {
|
|
53
|
+
if (method === "OVER_THE_COUNTER") {
|
|
54
|
+
pay();
|
|
55
|
+
} else {
|
|
56
|
+
setError(null);
|
|
57
|
+
setQrOpen(true);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
61
|
+
/* @__PURE__ */ jsxs(Card, { className: "overflow-visible", children: [
|
|
62
|
+
/* @__PURE__ */ jsxs(CardHeader, { children: [
|
|
63
|
+
/* @__PURE__ */ jsx(CardTitle, { className: "text-xl", children: copy.citizen.pay.title }),
|
|
64
|
+
/* @__PURE__ */ jsxs(CardDescription, { children: [
|
|
65
|
+
violatorName,
|
|
66
|
+
" \xB7 ",
|
|
67
|
+
/* @__PURE__ */ jsx("span", { className: "font-mono", children: ovrTicketNo })
|
|
68
|
+
] })
|
|
69
|
+
] }),
|
|
70
|
+
/* @__PURE__ */ jsxs(CardContent, { className: "space-y-5", children: [
|
|
71
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2 rounded-lg border bg-muted/20 p-3 text-sm", children: [
|
|
72
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
73
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Basic fines" }),
|
|
74
|
+
/* @__PURE__ */ jsx(Money, { value: basicFines })
|
|
75
|
+
] }),
|
|
76
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-between", children: [
|
|
77
|
+
/* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "Penalty / surcharge" }),
|
|
78
|
+
/* @__PURE__ */ jsx(
|
|
79
|
+
Money,
|
|
80
|
+
{
|
|
81
|
+
value: penalty,
|
|
82
|
+
className: cn(penalty === 0 && "text-muted-foreground")
|
|
83
|
+
}
|
|
84
|
+
)
|
|
85
|
+
] }),
|
|
86
|
+
/* @__PURE__ */ jsx(Separator, {}),
|
|
87
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between", children: [
|
|
88
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", children: "Total to pay" }),
|
|
89
|
+
/* @__PURE__ */ jsx(Money, { value: amount, className: "text-xl font-bold" })
|
|
90
|
+
] })
|
|
91
|
+
] }),
|
|
92
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
93
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium", children: copy.citizen.pay.choose }),
|
|
94
|
+
/* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 gap-2", children: payMethods.map((m) => {
|
|
95
|
+
const active = method === m.id;
|
|
96
|
+
return /* @__PURE__ */ jsxs(
|
|
97
|
+
"button",
|
|
98
|
+
{
|
|
99
|
+
type: "button",
|
|
100
|
+
onClick: () => setMethod(m.id),
|
|
101
|
+
"aria-pressed": active,
|
|
102
|
+
className: cn(
|
|
103
|
+
"flex items-center gap-2.5 rounded-lg border p-3 text-left transition-colors outline-none focus-visible:ring-3 focus-visible:ring-ring/50",
|
|
104
|
+
active ? "border-primary bg-primary/5 ring-1 ring-primary/30" : "hover:bg-muted/40"
|
|
105
|
+
),
|
|
106
|
+
children: [
|
|
107
|
+
/* @__PURE__ */ jsx(
|
|
108
|
+
"span",
|
|
109
|
+
{
|
|
110
|
+
className: cn(
|
|
111
|
+
"flex size-9 shrink-0 items-center justify-center rounded-lg",
|
|
112
|
+
active ? "bg-primary text-primary-foreground" : "bg-muted text-muted-foreground"
|
|
113
|
+
),
|
|
114
|
+
children: ICONS[m.id]
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
/* @__PURE__ */ jsx("span", { className: "min-w-0", children: /* @__PURE__ */ jsx("span", { className: "block truncate text-sm font-medium", children: m.label }) }),
|
|
118
|
+
active ? /* @__PURE__ */ jsx(Check, { className: "ml-auto size-4 shrink-0 text-primary" }) : null
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
m.id
|
|
122
|
+
);
|
|
123
|
+
}) })
|
|
124
|
+
] }),
|
|
125
|
+
error ? /* @__PURE__ */ jsxs(Alert, { variant: "destructive", children: [
|
|
126
|
+
/* @__PURE__ */ jsx(TriangleAlert, {}),
|
|
127
|
+
/* @__PURE__ */ jsx(AlertDescription, { children: error })
|
|
128
|
+
] }) : null,
|
|
129
|
+
/* @__PURE__ */ jsx(
|
|
130
|
+
Button,
|
|
131
|
+
{
|
|
132
|
+
onClick: handlePay,
|
|
133
|
+
disabled: isPending,
|
|
134
|
+
className: "h-11 w-full gap-2 text-base",
|
|
135
|
+
children: isPending ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
136
|
+
/* @__PURE__ */ jsx(Loader2, { className: "size-4 animate-spin" }),
|
|
137
|
+
copy.citizen.pay.processing
|
|
138
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
139
|
+
/* @__PURE__ */ jsx(Lock, { className: "size-4" }),
|
|
140
|
+
copy.citizen.pay.payNow,
|
|
141
|
+
" ",
|
|
142
|
+
/* @__PURE__ */ jsx(Money, { value: amount, className: "font-semibold" })
|
|
143
|
+
] })
|
|
144
|
+
}
|
|
145
|
+
),
|
|
146
|
+
/* @__PURE__ */ jsxs("p", { className: "flex items-center justify-center gap-1.5 text-center text-xs text-muted-foreground", children: [
|
|
147
|
+
/* @__PURE__ */ jsx(Lock, { className: "size-3" }),
|
|
148
|
+
copy.citizen.pay.secure
|
|
149
|
+
] })
|
|
150
|
+
] })
|
|
151
|
+
] }),
|
|
152
|
+
/* @__PURE__ */ jsx(
|
|
153
|
+
PaymentQrDialog,
|
|
154
|
+
{
|
|
155
|
+
open: qrOpen,
|
|
156
|
+
onOpenChange: setQrOpen,
|
|
157
|
+
method,
|
|
158
|
+
methodLabel,
|
|
159
|
+
amount,
|
|
160
|
+
ovrTicketNo,
|
|
161
|
+
lastName,
|
|
162
|
+
payAction
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
] });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export { PaymentForm };
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/** The Server Action that settles the payment, injected by the consuming page. */
|
|
4
|
+
type PayAction = (args: {
|
|
5
|
+
ovrTicketNo: string;
|
|
6
|
+
lastName: string;
|
|
7
|
+
method: string;
|
|
8
|
+
}) => Promise<{
|
|
9
|
+
error?: string;
|
|
10
|
+
} | void>;
|
|
11
|
+
/**
|
|
12
|
+
* "Scan to pay" modal for the online payment methods (GCash / Maya / Landbank).
|
|
13
|
+
*
|
|
14
|
+
* The QR is a DEMO PLACEHOLDER — there is no real payment gateway yet. Once a
|
|
15
|
+
* gateway is integrated, the placeholder block is replaced by the gateway's
|
|
16
|
+
* per-transaction QR, and "I've completed payment" is replaced by a webhook/
|
|
17
|
+
* callback confirmation BEFORE the ticket is marked PAID.
|
|
18
|
+
*/
|
|
19
|
+
declare function PaymentQrDialog({ open, onOpenChange, method, methodLabel, amount, ovrTicketNo, lastName, payAction, }: {
|
|
20
|
+
open: boolean;
|
|
21
|
+
onOpenChange: (open: boolean) => void;
|
|
22
|
+
method: string;
|
|
23
|
+
methodLabel: string;
|
|
24
|
+
amount: number;
|
|
25
|
+
ovrTicketNo: string;
|
|
26
|
+
lastName: string;
|
|
27
|
+
payAction: PayAction;
|
|
28
|
+
}): React.JSX.Element;
|
|
29
|
+
|
|
30
|
+
export { type PayAction, PaymentQrDialog };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
export { PaymentQrDialog } from '../chunk-5Z2IAD5I.js';
|
|
3
|
+
import '../chunk-EYFZWQ4J.js';
|
|
4
|
+
import '../chunk-3KIDW4LT.js';
|
|
5
|
+
import '../chunk-M35R6JLA.js';
|
|
6
|
+
import '../chunk-I4WDVYHX.js';
|
|
7
|
+
import '../chunk-E2D7QT6N.js';
|
|
8
|
+
import '../chunk-77QBZC7J.js';
|
|
9
|
+
import '../chunk-B634JHKZ.js';
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Card, CardContent } from '../chunk-SETIN6XP.js';
|
|
3
|
+
import { buttonVariants } from '../chunk-I4WDVYHX.js';
|
|
4
|
+
import { useCopy } from '../chunk-E2D7QT6N.js';
|
|
5
|
+
import '../chunk-77QBZC7J.js';
|
|
6
|
+
import '../chunk-B634JHKZ.js';
|
|
7
|
+
import Link from 'next/link';
|
|
8
|
+
import { FileQuestion } from 'lucide-react';
|
|
9
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
10
|
+
|
|
11
|
+
function TicketNotFound() {
|
|
12
|
+
const copy = useCopy();
|
|
13
|
+
return /* @__PURE__ */ jsx("div", { className: "mx-auto w-full max-w-lg px-4 py-16 text-center", children: /* @__PURE__ */ jsx(Card, { children: /* @__PURE__ */ jsxs(CardContent, { className: "flex flex-col items-center gap-3 py-8", children: [
|
|
14
|
+
/* @__PURE__ */ jsx("div", { className: "flex size-12 items-center justify-center rounded-full bg-muted text-muted-foreground", children: /* @__PURE__ */ jsx(FileQuestion, { className: "size-6" }) }),
|
|
15
|
+
/* @__PURE__ */ jsx("h1", { className: "font-heading text-lg font-semibold", children: copy.citizen.notFoundTitle }),
|
|
16
|
+
/* @__PURE__ */ jsx("p", { className: "max-w-sm text-sm text-muted-foreground", children: copy.citizen.notFoundBody }),
|
|
17
|
+
/* @__PURE__ */ jsx(Link, { href: "/citizen/search", className: buttonVariants({}), children: copy.citizen.ticket.backToSearch })
|
|
18
|
+
] }) }) });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { TicketNotFound };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Ticket } from '../types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Read-only "violation history" table for the citizen ticket page: every OVR
|
|
6
|
+
* ticket on record for the same violator (matched by name). Mirrors the admin
|
|
7
|
+
* `TicketsTable` layout but is citizen-scoped — its rows link back into the
|
|
8
|
+
* citizen flow (`/citizen/ticket/<no>?ln=...`), never the admin pages.
|
|
9
|
+
*/
|
|
10
|
+
declare function ViolationHistoryTable({ tickets, currentTicketNo, lastName, }: {
|
|
11
|
+
tickets: Ticket[];
|
|
12
|
+
currentTicketNo: string;
|
|
13
|
+
lastName: string;
|
|
14
|
+
}): React.JSX.Element;
|
|
15
|
+
|
|
16
|
+
export { ViolationHistoryTable };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { StatusBadge } from '../chunk-OE525ZER.js';
|
|
3
|
+
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from '../chunk-OWCGEEAZ.js';
|
|
4
|
+
import '../chunk-55FQP2DO.js';
|
|
5
|
+
import { Money } from '../chunk-3KIDW4LT.js';
|
|
6
|
+
import { useFormatters, useCopy } from '../chunk-E2D7QT6N.js';
|
|
7
|
+
import { cn } from '../chunk-77QBZC7J.js';
|
|
8
|
+
import '../chunk-B634JHKZ.js';
|
|
9
|
+
import Link from 'next/link';
|
|
10
|
+
import { ChevronRight } from 'lucide-react';
|
|
11
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
12
|
+
|
|
13
|
+
function ViolationHistoryTable({
|
|
14
|
+
tickets,
|
|
15
|
+
currentTicketNo,
|
|
16
|
+
lastName
|
|
17
|
+
}) {
|
|
18
|
+
const { formatDate } = useFormatters();
|
|
19
|
+
const copy = useCopy();
|
|
20
|
+
const t = copy.citizen.ticket.history;
|
|
21
|
+
if (tickets.length === 0) {
|
|
22
|
+
return /* @__PURE__ */ jsx("div", { className: "px-4 py-10 text-center text-sm text-muted-foreground", children: t.empty });
|
|
23
|
+
}
|
|
24
|
+
return /* @__PURE__ */ jsxs(Table, { children: [
|
|
25
|
+
/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsxs(TableRow, { children: [
|
|
26
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.colTicketNo }),
|
|
27
|
+
/* @__PURE__ */ jsx(TableHead, { className: "hidden sm:table-cell", children: t.colDate }),
|
|
28
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.colViolations }),
|
|
29
|
+
/* @__PURE__ */ jsx(TableHead, { children: t.colStatus }),
|
|
30
|
+
/* @__PURE__ */ jsx(TableHead, { className: "text-right", children: t.colAmount }),
|
|
31
|
+
/* @__PURE__ */ jsx(TableHead, { className: "w-8" })
|
|
32
|
+
] }) }),
|
|
33
|
+
/* @__PURE__ */ jsx(TableBody, { children: tickets.map((ticket) => {
|
|
34
|
+
const isCurrent = ticket.ovrTicketNo === currentTicketNo;
|
|
35
|
+
const href = `/citizen/ticket/${encodeURIComponent(
|
|
36
|
+
ticket.ovrTicketNo
|
|
37
|
+
)}?ln=${encodeURIComponent(lastName)}`;
|
|
38
|
+
const amount = ticket.status === "PAID" ? ticket.payment?.amount ?? 0 : ticket.totalAmountDue;
|
|
39
|
+
const first = ticket.violations[0];
|
|
40
|
+
const extra = ticket.violations.length - 1;
|
|
41
|
+
return /* @__PURE__ */ jsxs(
|
|
42
|
+
TableRow,
|
|
43
|
+
{
|
|
44
|
+
className: cn(isCurrent && "bg-muted/40"),
|
|
45
|
+
children: [
|
|
46
|
+
/* @__PURE__ */ jsx(TableCell, { className: "font-mono text-xs", children: /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2", children: [
|
|
47
|
+
/* @__PURE__ */ jsx(Link, { href, className: "hover:underline", children: ticket.ovrTicketNo }),
|
|
48
|
+
isCurrent ? /* @__PURE__ */ jsx("span", { className: "rounded bg-brand/12 px-1.5 py-0.5 text-[0.65rem] font-medium uppercase tracking-wide text-brand", children: t.current }) : null
|
|
49
|
+
] }) }),
|
|
50
|
+
/* @__PURE__ */ jsx(TableCell, { className: "hidden text-muted-foreground sm:table-cell", children: formatDate(ticket.apprehendedAt) }),
|
|
51
|
+
/* @__PURE__ */ jsxs(TableCell, { className: "max-w-[16rem] truncate text-muted-foreground", children: [
|
|
52
|
+
first?.title ?? "\u2014",
|
|
53
|
+
extra > 0 ? /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
|
|
54
|
+
" +",
|
|
55
|
+
extra
|
|
56
|
+
] }) : null
|
|
57
|
+
] }),
|
|
58
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(StatusBadge, { status: ticket.status }) }),
|
|
59
|
+
/* @__PURE__ */ jsx(TableCell, { className: "text-right", children: /* @__PURE__ */ jsx(Money, { value: amount }) }),
|
|
60
|
+
/* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(
|
|
61
|
+
Link,
|
|
62
|
+
{
|
|
63
|
+
href,
|
|
64
|
+
className: "text-muted-foreground hover:text-foreground",
|
|
65
|
+
"aria-label": `Open ${ticket.ovrTicketNo}`,
|
|
66
|
+
children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
|
|
67
|
+
}
|
|
68
|
+
) })
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
ticket.ovrTicketNo
|
|
72
|
+
);
|
|
73
|
+
}) })
|
|
74
|
+
] });
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export { ViolationHistoryTable };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Renders the official municipal seal from the config `sealSrc` when present,
|
|
5
|
+
* falling back to the placeholder SVG emblem if the image is missing.
|
|
6
|
+
*/
|
|
7
|
+
declare function MunicipalSeal({ className }: {
|
|
8
|
+
className?: string;
|
|
9
|
+
}): React.JSX.Element;
|
|
10
|
+
|
|
11
|
+
export { MunicipalSeal };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Button } from '../chunk-I4WDVYHX.js';
|
|
3
|
+
import { cn } from '../chunk-77QBZC7J.js';
|
|
4
|
+
import { Printer } from 'lucide-react';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
function PrintButton({
|
|
8
|
+
className,
|
|
9
|
+
label = "Print"
|
|
10
|
+
}) {
|
|
11
|
+
return /* @__PURE__ */ jsxs(Button, { onClick: () => window.print(), className: cn("gap-2", className), children: [
|
|
12
|
+
/* @__PURE__ */ jsx(Printer, { className: "size-4" }),
|
|
13
|
+
label
|
|
14
|
+
] });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { PrintButton };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Generic placeholder municipal emblem — a "sun over waves" mark in the brand
|
|
5
|
+
* palette, used as the fallback when an LGU has not supplied an official seal.
|
|
6
|
+
* The real seal is config-driven (see municipal-seal.tsx and the config sealSrc).
|
|
7
|
+
*/
|
|
8
|
+
declare function Seal({ className }: {
|
|
9
|
+
className?: string;
|
|
10
|
+
}): React.JSX.Element;
|
|
11
|
+
|
|
12
|
+
export { Seal };
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import * as React$1 from 'react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The single, official header used across the app: municipal seal + Municipality
|
|
5
|
+
* identity + e-OVR name/meaning on the left; a context nav slot (`children`) + a
|
|
6
|
+
* theme toggle on the right. One bar — no stacked headers.
|
|
7
|
+
*/
|
|
8
|
+
declare function SiteHeader({ homeHref, children, }: {
|
|
9
|
+
homeHref?: string;
|
|
10
|
+
children?: React.ReactNode;
|
|
11
|
+
}): React$1.JSX.Element;
|
|
12
|
+
|
|
13
|
+
export { SiteHeader };
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ThemeToggle } from '../chunk-MDTRBOPQ.js';
|
|
3
|
+
import { MunicipalSeal } from '../chunk-IF5UAVIE.js';
|
|
4
|
+
import '../chunk-WOPU6DI7.js';
|
|
5
|
+
import '../chunk-I4WDVYHX.js';
|
|
6
|
+
import { useOvrConfig, useCopy } from '../chunk-E2D7QT6N.js';
|
|
7
|
+
import '../chunk-77QBZC7J.js';
|
|
8
|
+
import '../chunk-B634JHKZ.js';
|
|
9
|
+
import Link from 'next/link';
|
|
10
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
11
|
+
|
|
12
|
+
function SiteHeader({
|
|
13
|
+
homeHref = "/",
|
|
14
|
+
children
|
|
15
|
+
}) {
|
|
16
|
+
const { municipality } = useOvrConfig();
|
|
17
|
+
const copy = useCopy();
|
|
18
|
+
return /* @__PURE__ */ jsx("header", { className: "no-print sticky top-0 z-40 border-b border-white/10 bg-gov text-gov-foreground", children: /* @__PURE__ */ jsxs("div", { className: "mx-auto flex w-full max-w-6xl items-center justify-between gap-3 px-4 py-2.5 sm:px-6", children: [
|
|
19
|
+
/* @__PURE__ */ jsxs(Link, { href: homeHref, className: "flex items-center gap-3", children: [
|
|
20
|
+
/* @__PURE__ */ jsx(MunicipalSeal, { className: "size-10 shrink-0 sm:size-11" }),
|
|
21
|
+
/* @__PURE__ */ jsxs("span", { className: "leading-tight", children: [
|
|
22
|
+
/* @__PURE__ */ jsx("span", { className: "block text-[0.7rem] opacity-80", children: municipality.name }),
|
|
23
|
+
/* @__PURE__ */ jsxs("span", { className: "block text-sm font-bold tracking-tight sm:text-base", children: [
|
|
24
|
+
copy.app.name,
|
|
25
|
+
/* @__PURE__ */ jsxs("span", { className: "hidden font-normal opacity-80 sm:inline", children: [
|
|
26
|
+
" ",
|
|
27
|
+
"\u2014 ",
|
|
28
|
+
copy.app.tagline
|
|
29
|
+
] })
|
|
30
|
+
] })
|
|
31
|
+
] })
|
|
32
|
+
] }),
|
|
33
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
|
|
34
|
+
children,
|
|
35
|
+
/* @__PURE__ */ jsx(ThemeToggle, { className: "text-gov-foreground hover:bg-white/10 hover:text-gov-foreground" })
|
|
36
|
+
] })
|
|
37
|
+
] }) });
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export { SiteHeader };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useTheme } from '../chunk-E2D7QT6N.js';
|
|
3
|
+
import '../chunk-B634JHKZ.js';
|
|
4
|
+
import { Toaster as Toaster$1 } from 'sonner';
|
|
5
|
+
import { Loader2Icon, OctagonXIcon, TriangleAlertIcon, InfoIcon, CircleCheckIcon } from 'lucide-react';
|
|
6
|
+
import { jsx } from 'react/jsx-runtime';
|
|
7
|
+
|
|
8
|
+
var Toaster = ({ ...props }) => {
|
|
9
|
+
const { resolvedTheme } = useTheme();
|
|
10
|
+
return /* @__PURE__ */ jsx(
|
|
11
|
+
Toaster$1,
|
|
12
|
+
{
|
|
13
|
+
theme: resolvedTheme,
|
|
14
|
+
className: "toaster group",
|
|
15
|
+
icons: {
|
|
16
|
+
success: /* @__PURE__ */ jsx(CircleCheckIcon, { className: "size-4" }),
|
|
17
|
+
info: /* @__PURE__ */ jsx(InfoIcon, { className: "size-4" }),
|
|
18
|
+
warning: /* @__PURE__ */ jsx(TriangleAlertIcon, { className: "size-4" }),
|
|
19
|
+
error: /* @__PURE__ */ jsx(OctagonXIcon, { className: "size-4" }),
|
|
20
|
+
loading: /* @__PURE__ */ jsx(Loader2Icon, { className: "size-4 animate-spin" })
|
|
21
|
+
},
|
|
22
|
+
style: {
|
|
23
|
+
"--normal-bg": "var(--popover)",
|
|
24
|
+
"--normal-text": "var(--popover-foreground)",
|
|
25
|
+
"--normal-border": "var(--border)",
|
|
26
|
+
"--border-radius": "var(--radius)"
|
|
27
|
+
},
|
|
28
|
+
toastOptions: {
|
|
29
|
+
classNames: {
|
|
30
|
+
toast: "cn-toast"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
...props
|
|
34
|
+
}
|
|
35
|
+
);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export { Toaster };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as React from 'react';
|
|
2
|
+
import { Ticket } from '../types.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The canonical OVR "Order of Payment" document — rendered identically for the
|
|
6
|
+
* citizen receipt and the admin print view. Print-friendly (white paper look).
|
|
7
|
+
*/
|
|
8
|
+
declare function TicketReceipt({ ticket }: {
|
|
9
|
+
ticket: Ticket;
|
|
10
|
+
}): React.JSX.Element;
|
|
11
|
+
|
|
12
|
+
export { TicketReceipt };
|