@gelabs/ovr 0.2.2 → 0.3.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/auth-auth.js +1 -1
- package/dist/auth.js +1 -1
- package/dist/{chunk-MDTRBOPQ.js → chunk-2C3VCTYJ.js} +1 -1
- package/dist/chunk-3YKVH4Y7.js +126 -0
- package/dist/chunk-6YFZLXFP.js +84 -0
- package/dist/{chunk-3NZ2XUBO.js → chunk-AJ2RZTVX.js} +9 -2
- package/dist/chunk-BI4EGLPG.js +298 -0
- package/dist/{chunk-3KIDW4LT.js → chunk-BVI5XDDA.js} +1 -1
- package/dist/{chunk-BIQ2J75Y.js → chunk-GLIK5BHP.js} +2 -2
- package/dist/{chunk-5YYR37CF.js → chunk-HGWPA7FU.js} +119 -0
- package/dist/{chunk-JEYT63LE.js → chunk-IBZVIUNI.js} +1 -1
- package/dist/chunk-IS3THKTE.js +89 -0
- package/dist/{chunk-4SZXBT56.js → chunk-NT72CQAI.js} +2 -2
- package/dist/{chunk-E2D7QT6N.js → chunk-TJSNVTVB.js} +1 -1
- package/dist/{chunk-5Z2IAD5I.js → chunk-TLG4C2XI.js} +2 -2
- package/dist/chunk-V7VQVDWS.js +237 -0
- package/dist/{chunk-IF5UAVIE.js → chunk-YC7G2IOZ.js} +1 -1
- package/dist/{chunk-IB4JVGKJ.js → chunk-YGYA7KEG.js} +47 -3
- package/dist/{chunk-GDOCD7LT.js → chunk-ZUMEOZ22.js} +5 -5
- package/dist/core-i18n.d.ts +2 -2
- package/dist/core-i18n.js +1 -1
- package/dist/core.d.ts +61 -1
- package/dist/core.js +1 -1
- package/dist/data-mock-store.js +263 -9
- package/dist/data-prisma-store.js +251 -1
- package/dist/data-seed-runner.js +18 -15
- package/dist/data.d.ts +53 -3
- package/dist/generated/client/edge.js +28 -9
- package/dist/generated/client/index-browser.js +25 -6
- package/dist/generated/client/index.d.ts +3500 -552
- package/dist/generated/client/index.js +28 -9
- package/dist/generated/client/package.json +1 -1
- package/dist/generated/client/schema.prisma +46 -9
- package/dist/generated/client/wasm.js +28 -9
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/offline.d.ts +34 -1
- package/dist/offline.js +2 -2
- package/dist/{types-CtBC5-TW.d.ts → types-BOgdk0Jw.d.ts} +119 -0
- package/dist/types.d.ts +93 -1
- package/dist/types.js +1 -1
- package/dist/ui-components-admin/accounts-manager.d.ts +52 -0
- package/dist/ui-components-admin/accounts-manager.js +471 -0
- package/dist/ui-components-admin/admin-nav.d.ts +15 -1
- package/dist/ui-components-admin/admin-nav.js +386 -60
- package/dist/ui-components-admin/issuance-form.js +72 -13
- package/dist/ui-components-admin/logs-viewer.d.ts +13 -0
- package/dist/ui-components-admin/logs-viewer.js +102 -0
- package/dist/ui-components-admin/notifications-list.d.ts +5 -0
- package/dist/ui-components-admin/notifications-list.js +70 -0
- package/dist/ui-components-admin/officers-manager.d.ts +27 -0
- package/dist/ui-components-admin/officers-manager.js +271 -0
- package/dist/ui-components-admin/roles-manager.d.ts +37 -0
- package/dist/ui-components-admin/roles-manager.js +406 -0
- package/dist/ui-components-admin/ticket-preview.js +7 -7
- package/dist/ui-components-admin/tickets-table.js +56 -33
- package/dist/ui-components-citizen/citizen-nav.js +2 -2
- package/dist/ui-components-citizen/payment-form.js +5 -5
- package/dist/ui-components-citizen/payment-qr-dialog.js +4 -4
- package/dist/ui-components-citizen/ticket-not-found.js +2 -2
- package/dist/ui-components-citizen/violation-history-table.js +3 -3
- package/dist/ui-components-shared/amount-summary.js +4 -4
- package/dist/ui-components-shared/money.js +3 -3
- package/dist/ui-components-shared/municipal-seal.js +3 -3
- package/dist/ui-components-shared/official-header.js +4 -4
- package/dist/ui-components-shared/site-header.js +4 -4
- package/dist/ui-components-shared/sonner.js +2 -2
- package/dist/ui-components-shared/theme-toggle.js +3 -3
- package/dist/ui-components-shared/ticket-receipt.js +13 -6
- package/dist/ui-components-shared/violations-table.js +4 -4
- package/dist/ui-components-ui/badge.d.ts +1 -1
- package/dist/ui-components-ui/button.d.ts +1 -1
- package/dist/ui-components-ui/dropdown-menu.js +2 -237
- package/dist/ui-components-ui/sheet.js +3 -126
- package/dist/ui-config.d.ts +1 -1
- package/dist/ui-config.js +2 -2
- package/dist/ui-server.d.ts +1 -1
- package/dist/ui-server.js +2 -2
- package/package.json +4 -4
- package/prisma/migrations/20260622010000_add_super_admin_role/migration.sql +3 -0
- package/prisma/migrations/20260622020000_add_apprehending_enforcer/migration.sql +4 -0
- package/prisma/migrations/20260622030000_custom_roles/migration.sql +30 -0
- package/prisma/migrations/20260622040000_add_activity_log/migration.sql +18 -0
- package/prisma/schema.prisma +46 -9
- package/dist/chunk-B634JHKZ.js +0 -181
|
@@ -95,7 +95,126 @@ var baseCopy = {
|
|
|
95
95
|
dashboard: "Dashboard",
|
|
96
96
|
tickets: "Tickets",
|
|
97
97
|
issueTicket: "Issue ticket",
|
|
98
|
+
accounts: "Accounts",
|
|
99
|
+
roles: "Roles",
|
|
100
|
+
officers: "Officers",
|
|
101
|
+
logs: "Activity log",
|
|
102
|
+
more: "More",
|
|
103
|
+
signOut: "Sign out",
|
|
104
|
+
signOutConfirmTitle: "Sign out?",
|
|
105
|
+
signOutConfirmBody: "You'll need to sign in again to issue or manage tickets.",
|
|
106
|
+
cancel: "Cancel",
|
|
98
107
|
newTicketTitle: "Issue Violation Ticket",
|
|
108
|
+
accountsPage: {
|
|
109
|
+
title: "Accounts",
|
|
110
|
+
subtitle: "Create and manage who can sign in to the enforcer portal. Each account's role decides what it can see and do.",
|
|
111
|
+
newAccount: "New account",
|
|
112
|
+
edit: "Edit",
|
|
113
|
+
editTitle: "Edit account",
|
|
114
|
+
saveChanges: "Save changes",
|
|
115
|
+
username: "Username",
|
|
116
|
+
password: "Password",
|
|
117
|
+
role: "Role",
|
|
118
|
+
officer: "Linked officer",
|
|
119
|
+
officerNone: "\u2014 None \u2014",
|
|
120
|
+
officerHint: "Link an enforcer to an officer record so issued tickets are attributed.",
|
|
121
|
+
status: "Status",
|
|
122
|
+
active: "Active",
|
|
123
|
+
inactive: "Inactive",
|
|
124
|
+
created: "Created",
|
|
125
|
+
actions: "Actions",
|
|
126
|
+
create: "Create account",
|
|
127
|
+
creating: "Creating\u2026",
|
|
128
|
+
deactivate: "Deactivate",
|
|
129
|
+
activate: "Activate",
|
|
130
|
+
resetPassword: "Reset password",
|
|
131
|
+
newPassword: "New password",
|
|
132
|
+
save: "Save",
|
|
133
|
+
saving: "Saving\u2026",
|
|
134
|
+
cancel: "Cancel",
|
|
135
|
+
empty: "No accounts yet \u2014 create the first one.",
|
|
136
|
+
you: "You"
|
|
137
|
+
},
|
|
138
|
+
rolesPage: {
|
|
139
|
+
title: "Roles & permissions",
|
|
140
|
+
subtitle: "Define what each role can do. Create custom roles, set their permissions, then assign them to accounts.",
|
|
141
|
+
newRole: "New role",
|
|
142
|
+
roleName: "Role name",
|
|
143
|
+
roleNamePlaceholder: "e.g. Cashier, Supervisor",
|
|
144
|
+
permissions: "Permissions",
|
|
145
|
+
editPermissions: "Edit permissions",
|
|
146
|
+
noPermissions: "No permissions",
|
|
147
|
+
permissionsCount: "permissions",
|
|
148
|
+
system: "System",
|
|
149
|
+
custom: "Custom",
|
|
150
|
+
create: "Create role",
|
|
151
|
+
creating: "Creating\u2026",
|
|
152
|
+
save: "Save",
|
|
153
|
+
saving: "Saving\u2026",
|
|
154
|
+
saved: "Saved",
|
|
155
|
+
cancel: "Cancel",
|
|
156
|
+
delete: "Delete",
|
|
157
|
+
deleteConfirmTitle: "Delete this role?",
|
|
158
|
+
deleteConfirmBody: "Move any accounts off this role first. This can't be undone.",
|
|
159
|
+
lockedHint: "Super Admin always keeps account & role management.",
|
|
160
|
+
empty: "No roles yet \u2014 create the first one.",
|
|
161
|
+
inUseSuffix: "in use"
|
|
162
|
+
},
|
|
163
|
+
logsPage: {
|
|
164
|
+
title: "Activity log",
|
|
165
|
+
subtitle: "Audit trail of what accounts did \u2014 sign-ins, ticket issuance, and account & role changes.",
|
|
166
|
+
allActions: "All actions",
|
|
167
|
+
search: "Search actor or detail\u2026",
|
|
168
|
+
actor: "Actor",
|
|
169
|
+
action: "Action",
|
|
170
|
+
detail: "Detail",
|
|
171
|
+
when: "When",
|
|
172
|
+
empty: "No activity recorded yet.",
|
|
173
|
+
system: "system"
|
|
174
|
+
},
|
|
175
|
+
officersPage: {
|
|
176
|
+
title: "Apprehending officers",
|
|
177
|
+
subtitle: "Officers who apprehend violations \u2014 shown in the Issue-Ticket form and linked to enforcer accounts.",
|
|
178
|
+
newOfficer: "New officer",
|
|
179
|
+
name: "Full name",
|
|
180
|
+
namePlaceholder: "e.g. DELA CRUZ, JUAN P.",
|
|
181
|
+
badge: "Badge no.",
|
|
182
|
+
badgePlaceholder: "e.g. A176",
|
|
183
|
+
office: "Office",
|
|
184
|
+
officePlaceholder: "e.g. POSO",
|
|
185
|
+
edit: "Edit",
|
|
186
|
+
editTitle: "Edit officer",
|
|
187
|
+
create: "Add officer",
|
|
188
|
+
creating: "Adding\u2026",
|
|
189
|
+
save: "Save",
|
|
190
|
+
saving: "Saving\u2026",
|
|
191
|
+
cancel: "Cancel",
|
|
192
|
+
delete: "Remove",
|
|
193
|
+
deleteConfirmTitle: "Remove this officer?",
|
|
194
|
+
deleteConfirmBody: "This can't be undone. Officers with issued tickets or a linked account can't be removed.",
|
|
195
|
+
empty: "No officers yet \u2014 add the first one.",
|
|
196
|
+
actions: "Actions"
|
|
197
|
+
},
|
|
198
|
+
notifications: {
|
|
199
|
+
title: "Notifications",
|
|
200
|
+
subtitle: "Tickets that need attention \u2014 overdue and outstanding.",
|
|
201
|
+
empty: "You're all caught up.",
|
|
202
|
+
overdue: "Overdue",
|
|
203
|
+
outstanding: "Outstanding",
|
|
204
|
+
viewAll: "View all"
|
|
205
|
+
},
|
|
206
|
+
changePassword: {
|
|
207
|
+
title: "Change password",
|
|
208
|
+
subtitle: "Enter your current password, then your new one.",
|
|
209
|
+
current: "Current password",
|
|
210
|
+
new: "New password",
|
|
211
|
+
confirm: "Confirm new password",
|
|
212
|
+
submit: "Update password",
|
|
213
|
+
saving: "Updating\u2026",
|
|
214
|
+
cancel: "Cancel",
|
|
215
|
+
mismatch: "New passwords don't match.",
|
|
216
|
+
success: "Password updated."
|
|
217
|
+
},
|
|
99
218
|
sync: {
|
|
100
219
|
offline: "Offline",
|
|
101
220
|
syncing: "Syncing\u2026",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { Money } from './chunk-BVI5XDDA.js';
|
|
1
2
|
import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from './chunk-OWCGEEAZ.js';
|
|
2
|
-
import { Money } from './chunk-3KIDW4LT.js';
|
|
3
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
function ViolationsTable({
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// ../ovr-types/src/index.ts
|
|
2
|
+
var PERMISSION_CATALOG = [
|
|
3
|
+
{ key: "dashboard:view", label: "View dashboard", description: "See the admin dashboard and statistics." },
|
|
4
|
+
{ key: "tickets:view", label: "View tickets", description: "Browse and search issued tickets." },
|
|
5
|
+
{ key: "tickets:create", label: "Issue tickets", description: "Create new violation tickets." },
|
|
6
|
+
{ key: "accounts:manage", label: "Manage accounts", description: "Create and manage user accounts." },
|
|
7
|
+
{ key: "roles:manage", label: "Manage roles", description: "Create roles and set their permissions." },
|
|
8
|
+
{ key: "officers:manage", label: "Manage officers", description: "Add, edit, and remove apprehending officers." },
|
|
9
|
+
{ key: "logs:view", label: "View activity logs", description: "Browse the audit trail of account actions." },
|
|
10
|
+
{ key: "notifications:view", label: "View notifications", description: "See the alert bell for overdue / pending tickets." }
|
|
11
|
+
];
|
|
12
|
+
var ALL_PERMISSIONS = PERMISSION_CATALOG.map((p) => p.key);
|
|
13
|
+
var SYSTEM_ROLES = [
|
|
14
|
+
{
|
|
15
|
+
name: "SUPER_ADMIN",
|
|
16
|
+
label: "Super Admin",
|
|
17
|
+
isSystem: true,
|
|
18
|
+
permissions: [
|
|
19
|
+
"dashboard:view",
|
|
20
|
+
"tickets:view",
|
|
21
|
+
"tickets:create",
|
|
22
|
+
"accounts:manage",
|
|
23
|
+
"roles:manage",
|
|
24
|
+
"officers:manage",
|
|
25
|
+
"logs:view",
|
|
26
|
+
"notifications:view"
|
|
27
|
+
]
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
name: "ADMIN",
|
|
31
|
+
label: "Administrator",
|
|
32
|
+
isSystem: true,
|
|
33
|
+
permissions: [
|
|
34
|
+
"dashboard:view",
|
|
35
|
+
"tickets:view",
|
|
36
|
+
"tickets:create",
|
|
37
|
+
"officers:manage",
|
|
38
|
+
"logs:view",
|
|
39
|
+
"notifications:view"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
name: "ENFORCER",
|
|
44
|
+
label: "Enforcer",
|
|
45
|
+
isSystem: true,
|
|
46
|
+
permissions: ["tickets:view", "tickets:create", "notifications:view"]
|
|
47
|
+
}
|
|
48
|
+
];
|
|
49
|
+
var DEFAULT_ROLE_NAME = "ENFORCER";
|
|
50
|
+
var SUPER_ADMIN_LOCKED_PERMISSIONS = ["accounts:manage", "roles:manage"];
|
|
51
|
+
function hasPermission(permissions, permission) {
|
|
52
|
+
return !!permissions && permissions.includes(permission);
|
|
53
|
+
}
|
|
54
|
+
var ACTIVITY_ACTIONS = [
|
|
55
|
+
"auth.login",
|
|
56
|
+
"auth.logout",
|
|
57
|
+
"ticket.issued",
|
|
58
|
+
"account.create",
|
|
59
|
+
"account.update",
|
|
60
|
+
"account.activate",
|
|
61
|
+
"account.deactivate",
|
|
62
|
+
"account.password_reset",
|
|
63
|
+
"account.password_change",
|
|
64
|
+
"role.create",
|
|
65
|
+
"role.update",
|
|
66
|
+
"role.delete",
|
|
67
|
+
"officer.create",
|
|
68
|
+
"officer.update",
|
|
69
|
+
"officer.delete"
|
|
70
|
+
];
|
|
71
|
+
var ACTIVITY_ACTION_LABELS = {
|
|
72
|
+
"auth.login": "Signed in",
|
|
73
|
+
"auth.logout": "Signed out",
|
|
74
|
+
"ticket.issued": "Issued ticket",
|
|
75
|
+
"account.create": "Created account",
|
|
76
|
+
"account.update": "Updated account",
|
|
77
|
+
"account.activate": "Activated account",
|
|
78
|
+
"account.deactivate": "Deactivated account",
|
|
79
|
+
"account.password_reset": "Reset password",
|
|
80
|
+
"account.password_change": "Changed own password",
|
|
81
|
+
"role.create": "Created role",
|
|
82
|
+
"role.update": "Updated role",
|
|
83
|
+
"role.delete": "Deleted role",
|
|
84
|
+
"officer.create": "Added officer",
|
|
85
|
+
"officer.update": "Updated officer",
|
|
86
|
+
"officer.delete": "Removed officer"
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export { ACTIVITY_ACTIONS, ACTIVITY_ACTION_LABELS, ALL_PERMISSIONS, DEFAULT_ROLE_NAME, PERMISSION_CATALOG, SUPER_ADMIN_LOCKED_PERMISSIONS, SYSTEM_ROLES, hasPermission };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { MunicipalSeal } from './chunk-
|
|
2
|
-
import { useOvrConfig } from './chunk-
|
|
1
|
+
import { MunicipalSeal } from './chunk-YC7G2IOZ.js';
|
|
2
|
+
import { useOvrConfig } from './chunk-TJSNVTVB.js';
|
|
3
3
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
5
5
|
function OfficialHeader() {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Alert, AlertDescription } from './chunk-EYFZWQ4J.js';
|
|
2
|
-
import { Money } from './chunk-
|
|
2
|
+
import { Money } from './chunk-BVI5XDDA.js';
|
|
3
3
|
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from './chunk-M35R6JLA.js';
|
|
4
4
|
import { Button } from './chunk-I4WDVYHX.js';
|
|
5
|
-
import { useCopy } from './chunk-
|
|
5
|
+
import { useCopy } from './chunk-TJSNVTVB.js';
|
|
6
6
|
import * as React from 'react';
|
|
7
7
|
import { toast } from 'sonner';
|
|
8
8
|
import { QrCode, Loader2, Lock } from 'lucide-react';
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { cn } from './chunk-77QBZC7J.js';
|
|
2
|
+
import { Menu } from '@base-ui/react/menu';
|
|
3
|
+
import { ChevronRightIcon, CheckIcon } from 'lucide-react';
|
|
4
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
function DropdownMenu({ ...props }) {
|
|
7
|
+
return /* @__PURE__ */ jsx(Menu.Root, { "data-slot": "dropdown-menu", ...props });
|
|
8
|
+
}
|
|
9
|
+
function DropdownMenuPortal({ ...props }) {
|
|
10
|
+
return /* @__PURE__ */ jsx(Menu.Portal, { "data-slot": "dropdown-menu-portal", ...props });
|
|
11
|
+
}
|
|
12
|
+
function DropdownMenuTrigger({ ...props }) {
|
|
13
|
+
return /* @__PURE__ */ jsx(Menu.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
|
|
14
|
+
}
|
|
15
|
+
function DropdownMenuContent({
|
|
16
|
+
align = "start",
|
|
17
|
+
alignOffset = 0,
|
|
18
|
+
side = "bottom",
|
|
19
|
+
sideOffset = 4,
|
|
20
|
+
className,
|
|
21
|
+
...props
|
|
22
|
+
}) {
|
|
23
|
+
return /* @__PURE__ */ jsx(Menu.Portal, { children: /* @__PURE__ */ jsx(
|
|
24
|
+
Menu.Positioner,
|
|
25
|
+
{
|
|
26
|
+
className: "isolate z-50 outline-none",
|
|
27
|
+
align,
|
|
28
|
+
alignOffset,
|
|
29
|
+
side,
|
|
30
|
+
sideOffset,
|
|
31
|
+
children: /* @__PURE__ */ jsx(
|
|
32
|
+
Menu.Popup,
|
|
33
|
+
{
|
|
34
|
+
"data-slot": "dropdown-menu-content",
|
|
35
|
+
className: cn("z-50 max-h-(--available-height) w-(--anchor-width) min-w-32 origin-(--transform-origin) overflow-x-hidden overflow-y-auto rounded-lg bg-popover p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 outline-none data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:overflow-hidden data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
36
|
+
...props
|
|
37
|
+
}
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
) });
|
|
41
|
+
}
|
|
42
|
+
function DropdownMenuGroup({ ...props }) {
|
|
43
|
+
return /* @__PURE__ */ jsx(Menu.Group, { "data-slot": "dropdown-menu-group", ...props });
|
|
44
|
+
}
|
|
45
|
+
function DropdownMenuLabel({
|
|
46
|
+
className,
|
|
47
|
+
inset,
|
|
48
|
+
...props
|
|
49
|
+
}) {
|
|
50
|
+
return /* @__PURE__ */ jsx(
|
|
51
|
+
Menu.GroupLabel,
|
|
52
|
+
{
|
|
53
|
+
"data-slot": "dropdown-menu-label",
|
|
54
|
+
"data-inset": inset,
|
|
55
|
+
className: cn(
|
|
56
|
+
"px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
|
|
57
|
+
className
|
|
58
|
+
),
|
|
59
|
+
...props
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
function DropdownMenuItem({
|
|
64
|
+
className,
|
|
65
|
+
inset,
|
|
66
|
+
variant = "default",
|
|
67
|
+
...props
|
|
68
|
+
}) {
|
|
69
|
+
return /* @__PURE__ */ jsx(
|
|
70
|
+
Menu.Item,
|
|
71
|
+
{
|
|
72
|
+
"data-slot": "dropdown-menu-item",
|
|
73
|
+
"data-inset": inset,
|
|
74
|
+
"data-variant": variant,
|
|
75
|
+
className: cn(
|
|
76
|
+
"group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
|
|
77
|
+
className
|
|
78
|
+
),
|
|
79
|
+
...props
|
|
80
|
+
}
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
function DropdownMenuSub({ ...props }) {
|
|
84
|
+
return /* @__PURE__ */ jsx(Menu.SubmenuRoot, { "data-slot": "dropdown-menu-sub", ...props });
|
|
85
|
+
}
|
|
86
|
+
function DropdownMenuSubTrigger({
|
|
87
|
+
className,
|
|
88
|
+
inset,
|
|
89
|
+
children,
|
|
90
|
+
...props
|
|
91
|
+
}) {
|
|
92
|
+
return /* @__PURE__ */ jsxs(
|
|
93
|
+
Menu.SubmenuTrigger,
|
|
94
|
+
{
|
|
95
|
+
"data-slot": "dropdown-menu-sub-trigger",
|
|
96
|
+
"data-inset": inset,
|
|
97
|
+
className: cn(
|
|
98
|
+
"flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-popup-open:bg-accent data-popup-open:text-accent-foreground data-open:bg-accent data-open:text-accent-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
99
|
+
className
|
|
100
|
+
),
|
|
101
|
+
...props,
|
|
102
|
+
children: [
|
|
103
|
+
children,
|
|
104
|
+
/* @__PURE__ */ jsx(ChevronRightIcon, { className: "ml-auto" })
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
function DropdownMenuSubContent({
|
|
110
|
+
align = "start",
|
|
111
|
+
alignOffset = -3,
|
|
112
|
+
side = "right",
|
|
113
|
+
sideOffset = 0,
|
|
114
|
+
className,
|
|
115
|
+
...props
|
|
116
|
+
}) {
|
|
117
|
+
return /* @__PURE__ */ jsx(
|
|
118
|
+
DropdownMenuContent,
|
|
119
|
+
{
|
|
120
|
+
"data-slot": "dropdown-menu-sub-content",
|
|
121
|
+
className: cn("w-auto min-w-[96px] rounded-lg bg-popover p-1 text-popover-foreground shadow-lg ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className),
|
|
122
|
+
align,
|
|
123
|
+
alignOffset,
|
|
124
|
+
side,
|
|
125
|
+
sideOffset,
|
|
126
|
+
...props
|
|
127
|
+
}
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
function DropdownMenuCheckboxItem({
|
|
131
|
+
className,
|
|
132
|
+
children,
|
|
133
|
+
checked,
|
|
134
|
+
inset,
|
|
135
|
+
...props
|
|
136
|
+
}) {
|
|
137
|
+
return /* @__PURE__ */ jsxs(
|
|
138
|
+
Menu.CheckboxItem,
|
|
139
|
+
{
|
|
140
|
+
"data-slot": "dropdown-menu-checkbox-item",
|
|
141
|
+
"data-inset": inset,
|
|
142
|
+
className: cn(
|
|
143
|
+
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
144
|
+
className
|
|
145
|
+
),
|
|
146
|
+
checked,
|
|
147
|
+
...props,
|
|
148
|
+
children: [
|
|
149
|
+
/* @__PURE__ */ jsx(
|
|
150
|
+
"span",
|
|
151
|
+
{
|
|
152
|
+
className: "pointer-events-none absolute right-2 flex items-center justify-center",
|
|
153
|
+
"data-slot": "dropdown-menu-checkbox-item-indicator",
|
|
154
|
+
children: /* @__PURE__ */ jsx(Menu.CheckboxItemIndicator, { children: /* @__PURE__ */ jsx(
|
|
155
|
+
CheckIcon,
|
|
156
|
+
{}
|
|
157
|
+
) })
|
|
158
|
+
}
|
|
159
|
+
),
|
|
160
|
+
children
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
function DropdownMenuRadioGroup({ ...props }) {
|
|
166
|
+
return /* @__PURE__ */ jsx(
|
|
167
|
+
Menu.RadioGroup,
|
|
168
|
+
{
|
|
169
|
+
"data-slot": "dropdown-menu-radio-group",
|
|
170
|
+
...props
|
|
171
|
+
}
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
function DropdownMenuRadioItem({
|
|
175
|
+
className,
|
|
176
|
+
children,
|
|
177
|
+
inset,
|
|
178
|
+
...props
|
|
179
|
+
}) {
|
|
180
|
+
return /* @__PURE__ */ jsxs(
|
|
181
|
+
Menu.RadioItem,
|
|
182
|
+
{
|
|
183
|
+
"data-slot": "dropdown-menu-radio-item",
|
|
184
|
+
"data-inset": inset,
|
|
185
|
+
className: cn(
|
|
186
|
+
"relative flex cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground focus:**:text-accent-foreground data-inset:pl-7 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
187
|
+
className
|
|
188
|
+
),
|
|
189
|
+
...props,
|
|
190
|
+
children: [
|
|
191
|
+
/* @__PURE__ */ jsx(
|
|
192
|
+
"span",
|
|
193
|
+
{
|
|
194
|
+
className: "pointer-events-none absolute right-2 flex items-center justify-center",
|
|
195
|
+
"data-slot": "dropdown-menu-radio-item-indicator",
|
|
196
|
+
children: /* @__PURE__ */ jsx(Menu.RadioItemIndicator, { children: /* @__PURE__ */ jsx(
|
|
197
|
+
CheckIcon,
|
|
198
|
+
{}
|
|
199
|
+
) })
|
|
200
|
+
}
|
|
201
|
+
),
|
|
202
|
+
children
|
|
203
|
+
]
|
|
204
|
+
}
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
function DropdownMenuSeparator({
|
|
208
|
+
className,
|
|
209
|
+
...props
|
|
210
|
+
}) {
|
|
211
|
+
return /* @__PURE__ */ jsx(
|
|
212
|
+
Menu.Separator,
|
|
213
|
+
{
|
|
214
|
+
"data-slot": "dropdown-menu-separator",
|
|
215
|
+
className: cn("-mx-1 my-1 h-px bg-border", className),
|
|
216
|
+
...props
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
function DropdownMenuShortcut({
|
|
221
|
+
className,
|
|
222
|
+
...props
|
|
223
|
+
}) {
|
|
224
|
+
return /* @__PURE__ */ jsx(
|
|
225
|
+
"span",
|
|
226
|
+
{
|
|
227
|
+
"data-slot": "dropdown-menu-shortcut",
|
|
228
|
+
className: cn(
|
|
229
|
+
"ml-auto text-xs tracking-widest text-muted-foreground group-focus/dropdown-menu-item:text-accent-foreground",
|
|
230
|
+
className
|
|
231
|
+
),
|
|
232
|
+
...props
|
|
233
|
+
}
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Seal } from './chunk-WOPU6DI7.js';
|
|
2
|
-
import { useOvrConfig } from './chunk-
|
|
2
|
+
import { useOvrConfig } from './chunk-TJSNVTVB.js';
|
|
3
3
|
import { cn } from './chunk-77QBZC7J.js';
|
|
4
4
|
import * as React from 'react';
|
|
5
5
|
import { jsx } from 'react/jsx-runtime';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { makeOvrTicketNo, addDays, makeBillNo, makeOrderOfPaymentNo, enrich } from './chunk-
|
|
1
|
+
import { makeOvrTicketNo, addDays, makeBillNo, makeOrderOfPaymentNo, enrich, fullName } from './chunk-BI4EGLPG.js';
|
|
2
2
|
import Dexie from 'dexie';
|
|
3
3
|
import { useState, useEffect } from 'react';
|
|
4
4
|
import { useLiveQuery } from 'dexie-react-hooks';
|
|
@@ -186,6 +186,10 @@ async function issueTicketOffline(input, rules) {
|
|
|
186
186
|
record.placeOfViolation = input.placeOfViolation.trim();
|
|
187
187
|
if (input.remarks?.trim()) record.remarks = input.remarks.trim();
|
|
188
188
|
if (input.issuedBy?.trim()) record.issuedBy = input.issuedBy.trim();
|
|
189
|
+
if (input.apprehendingEnforcerId)
|
|
190
|
+
record.apprehendingEnforcerId = input.apprehendingEnforcerId;
|
|
191
|
+
if (input.apprehendingEnforcerName?.trim())
|
|
192
|
+
record.apprehendingEnforcerName = input.apprehendingEnforcerName.trim();
|
|
189
193
|
const ticket = enrich(record, now, rules.surchargeRatePerMonth);
|
|
190
194
|
await db.transaction("rw", db.tickets, db.outbox, async () => {
|
|
191
195
|
await db.tickets.put(ticket);
|
|
@@ -201,7 +205,9 @@ async function issueTicketOffline(input, rules) {
|
|
|
201
205
|
...record.placeOfViolation ? { placeOfViolation: record.placeOfViolation } : {},
|
|
202
206
|
officerId: input.officerId,
|
|
203
207
|
violations: input.violations,
|
|
204
|
-
...record.remarks ? { remarks: record.remarks } : {}
|
|
208
|
+
...record.remarks ? { remarks: record.remarks } : {},
|
|
209
|
+
...record.apprehendingEnforcerId ? { apprehendingEnforcerId: record.apprehendingEnforcerId } : {},
|
|
210
|
+
...record.apprehendingEnforcerName ? { apprehendingEnforcerName: record.apprehendingEnforcerName } : {}
|
|
205
211
|
}
|
|
206
212
|
});
|
|
207
213
|
});
|
|
@@ -320,6 +326,44 @@ function useIdentity() {
|
|
|
320
326
|
async () => (await db.meta.get("identity"))?.value
|
|
321
327
|
);
|
|
322
328
|
}
|
|
329
|
+
function useNotifications() {
|
|
330
|
+
const tickets = useTickets();
|
|
331
|
+
if (!tickets) return [];
|
|
332
|
+
const items = [];
|
|
333
|
+
for (const t of tickets) {
|
|
334
|
+
const type = t.status === "OVERDUE" ? "overdue" : t.status === "OUTSTANDING" ? "outstanding" : null;
|
|
335
|
+
if (!type) continue;
|
|
336
|
+
items.push({
|
|
337
|
+
id: `${t.ovrTicketNo}:${type}`,
|
|
338
|
+
type,
|
|
339
|
+
ovrTicketNo: t.ovrTicketNo,
|
|
340
|
+
name: fullName(t.violator),
|
|
341
|
+
at: type === "overdue" ? t.dueDate : t.createdAt,
|
|
342
|
+
href: `/admin/tickets/${encodeURIComponent(t.ovrTicketNo)}`
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
const rank = (n) => n.type === "overdue" ? 0 : 1;
|
|
346
|
+
items.sort((a, b) => rank(a) - rank(b) || (a.at < b.at ? 1 : a.at > b.at ? -1 : 0));
|
|
347
|
+
return items;
|
|
348
|
+
}
|
|
349
|
+
function notifSignature(items) {
|
|
350
|
+
return items.map((n) => n.id).join("|");
|
|
351
|
+
}
|
|
352
|
+
function useNotificationsState() {
|
|
353
|
+
const items = useNotifications();
|
|
354
|
+
const seen = useLiveQuery(
|
|
355
|
+
async () => (await db.meta.get("notif_seen"))?.value
|
|
356
|
+
);
|
|
357
|
+
const signature = notifSignature(items);
|
|
358
|
+
return {
|
|
359
|
+
items,
|
|
360
|
+
total: items.length,
|
|
361
|
+
unseen: items.length > 0 && signature !== seen,
|
|
362
|
+
markSeen: () => {
|
|
363
|
+
void setMeta("notif_seen", signature);
|
|
364
|
+
}
|
|
365
|
+
};
|
|
366
|
+
}
|
|
323
367
|
function useStats() {
|
|
324
368
|
return useLiveQuery(
|
|
325
369
|
async () => (await db.meta.get("stats"))?.value
|
|
@@ -376,4 +420,4 @@ function useSync() {
|
|
|
376
420
|
return { syncing, online, error };
|
|
377
421
|
}
|
|
378
422
|
|
|
379
|
-
export { SessionExpired, cacheCredential, clearIdentity, db, ensureLease, getIdentity, getMeta, isOnline, issueTicketOffline, offlineApiBase, pullAll, pushOutbox, setMeta, setOfflineApiBase, sync, useAdminAuth, useCatalog, useIdentity, useOfficers, usePendingSync, useStats, useSync, useTicket, useTickets, verifyOffline };
|
|
423
|
+
export { SessionExpired, cacheCredential, clearIdentity, db, ensureLease, getIdentity, getMeta, isOnline, issueTicketOffline, offlineApiBase, pullAll, pushOutbox, setMeta, setOfflineApiBase, sync, useAdminAuth, useCatalog, useIdentity, useNotifications, useNotificationsState, useOfficers, usePendingSync, useStats, useSync, useTicket, useTickets, verifyOffline };
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { ViolationsTable } from './chunk-
|
|
2
|
-
import { AmountSummary } from './chunk-
|
|
3
|
-
import { Card, CardHeader, CardTitle, CardContent } from './chunk-SETIN6XP.js';
|
|
1
|
+
import { ViolationsTable } from './chunk-IBZVIUNI.js';
|
|
2
|
+
import { AmountSummary } from './chunk-GLIK5BHP.js';
|
|
4
3
|
import { StatusBadge } from './chunk-OE525ZER.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
4
|
+
import { Card, CardHeader, CardTitle, CardContent } from './chunk-SETIN6XP.js';
|
|
5
|
+
import { useFormatters } from './chunk-TJSNVTVB.js';
|
|
6
|
+
import { formalName, formatAddress } from './chunk-BI4EGLPG.js';
|
|
7
7
|
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
8
8
|
|
|
9
9
|
function Field({ label, value }) {
|
package/dist/core-i18n.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as CopyOverrides, D as Dictionary } from './types-
|
|
2
|
-
export { b as baseCopy } from './types-
|
|
1
|
+
import { C as CopyOverrides, D as Dictionary } from './types-BOgdk0Jw.js';
|
|
2
|
+
export { b as baseCopy } from './types-BOgdk0Jw.js';
|
|
3
3
|
import { M as MunicipalityConfig } from './schema-CdsFQxIg.js';
|
|
4
4
|
import 'zod';
|
|
5
5
|
|
package/dist/core-i18n.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { baseCopy, mergeCopy } from './chunk-
|
|
1
|
+
export { baseCopy, mergeCopy } from './chunk-HGWPA7FU.js';
|
package/dist/core.d.ts
CHANGED
|
@@ -73,4 +73,64 @@ interface IssuanceDraft {
|
|
|
73
73
|
type PreviewRules = Pick<RulesConfig, "dueWindowDays" | "surchargeRatePerMonth">;
|
|
74
74
|
declare function toPreviewTicket(draft: IssuanceDraft, now: Date, rules: PreviewRules): Ticket;
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Dashboard time-range presets (GE-010): 1D / 7D / MTD / prev-MTD (+ All).
|
|
78
|
+
*
|
|
79
|
+
* Ranges are computed in the LGU's timezone (config `timeZone`) so "today" / "this
|
|
80
|
+
* month" mean the local calendar, not the server's. Stats are derived from the
|
|
81
|
+
* on-device ticket list (offline-first) — no server round-trip — by ISSUE date
|
|
82
|
+
* (`createdAt`); each card reflects the CURRENT status of tickets issued in range.
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
type DashboardRangePreset = "1d" | "7d" | "mtd" | "prev-mtd" | "all";
|
|
86
|
+
interface DashboardPreset {
|
|
87
|
+
key: DashboardRangePreset;
|
|
88
|
+
label: string;
|
|
89
|
+
}
|
|
90
|
+
/** The presets, in display order. */
|
|
91
|
+
declare const DASHBOARD_PRESETS: DashboardPreset[];
|
|
92
|
+
interface DashboardRange {
|
|
93
|
+
/** Inclusive lower bound (ISO), or null for "all time" (no lower bound). */
|
|
94
|
+
fromISO: string | null;
|
|
95
|
+
/** Inclusive upper bound (ISO). */
|
|
96
|
+
toISO: string;
|
|
97
|
+
}
|
|
98
|
+
interface DashboardStats {
|
|
99
|
+
issued: number;
|
|
100
|
+
outstanding: number;
|
|
101
|
+
overdue: number;
|
|
102
|
+
paid: number;
|
|
103
|
+
collected: number;
|
|
104
|
+
}
|
|
105
|
+
/** Resolve a preset to a concrete [from, to] range, anchored to `now` in `timeZone`. */
|
|
106
|
+
declare function dashboardDateRange(preset: DashboardRangePreset, now: Date, timeZone: string): DashboardRange;
|
|
107
|
+
/** A custom date range from two `YYYY-MM-DD` local dates (either may be empty:
|
|
108
|
+
* empty `from` → no lower bound; empty `to` → up to `now`). Interpreted in
|
|
109
|
+
* `timeZone` as [from 00:00:00, to 23:59:59]. If from > to, they're swapped. */
|
|
110
|
+
declare function customDateRange(fromYMD: string | null | undefined, toYMD: string | null | undefined, now: Date, timeZone: string): DashboardRange;
|
|
111
|
+
/** Compute the dashboard cards from the local ticket list, scoped to `range`
|
|
112
|
+
* by issue date. Each card reflects each ticket's CURRENT status. */
|
|
113
|
+
declare function computeDashboardStats(tickets: Ticket[], range: DashboardRange): DashboardStats;
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Philippine locations (PSGC) for the Issue-Ticket address dropdowns (GE-015).
|
|
117
|
+
*
|
|
118
|
+
* Provinces + their cities/municipalities ONLY (barangays omitted to keep the
|
|
119
|
+
* bundle small — barangay stays free-text). Bundled (no runtime API) so it works
|
|
120
|
+
* offline. NCR districts are flattened into a single "Metro Manila" province.
|
|
121
|
+
*
|
|
122
|
+
* Source (open data, CC0/public): flores-jacob/philippine-regions-provinces-
|
|
123
|
+
* cities-municipalities-barangays (2019v2, derived from PSA PSGC). Names are
|
|
124
|
+
* title-cased from the source's all-caps.
|
|
125
|
+
*/
|
|
126
|
+
interface PhProvince {
|
|
127
|
+
province: string;
|
|
128
|
+
cities: string[];
|
|
129
|
+
}
|
|
130
|
+
declare const PH_PROVINCES: PhProvince[];
|
|
131
|
+
/** All province names (display order = alphabetical). */
|
|
132
|
+
declare function listProvinces(): string[];
|
|
133
|
+
/** Cities/municipalities of a province (case-insensitive match; [] if unknown). */
|
|
134
|
+
declare function citiesOfProvince(province: string | null | undefined): string[];
|
|
135
|
+
|
|
136
|
+
export { type ChargeInput, type ChargeResult, DASHBOARD_PRESETS, type DashboardPreset, type DashboardRange, type DashboardRangePreset, type DashboardStats, type IssuanceDraft, PH_PROVINCES, PREVIEW_PLACEHOLDER, type PhProvince, type PreviewRules, citiesOfProvince, computeCharges, computeDashboardStats, customDateRange, dashboardDateRange, enrich, listProvinces, makeBillNo, makeOrderOfPaymentNo, makeOvrTicketNo, makePaymentRef, round2, startedMonthsSince, toPreviewTicket };
|
package/dist/core.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { PREVIEW_PLACEHOLDER, addDays, computeCharges, createFormatters, enrich, formalName, formatAddress, fullName, localToManilaISO, makeBillNo, makeOrderOfPaymentNo, makeOvrTicketNo, makePaymentRef, round2, startedMonthsSince, toPreviewTicket } from './chunk-
|
|
1
|
+
export { DASHBOARD_PRESETS, PH_PROVINCES, PREVIEW_PLACEHOLDER, addDays, citiesOfProvince, computeCharges, computeDashboardStats, createFormatters, customDateRange, dashboardDateRange, enrich, formalName, formatAddress, fullName, listProvinces, localToManilaISO, makeBillNo, makeOrderOfPaymentNo, makeOvrTicketNo, makePaymentRef, round2, startedMonthsSince, toPreviewTicket } from './chunk-BI4EGLPG.js';
|