@gelabs/ovr 0.2.1 → 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.
Files changed (86) hide show
  1. package/dist/auth-auth.js +1 -1
  2. package/dist/auth.js +1 -1
  3. package/dist/{chunk-MDTRBOPQ.js → chunk-2C3VCTYJ.js} +1 -1
  4. package/dist/chunk-3YKVH4Y7.js +126 -0
  5. package/dist/chunk-6YFZLXFP.js +84 -0
  6. package/dist/{chunk-3NZ2XUBO.js → chunk-AJ2RZTVX.js} +9 -2
  7. package/dist/chunk-BI4EGLPG.js +298 -0
  8. package/dist/{chunk-3KIDW4LT.js → chunk-BVI5XDDA.js} +1 -1
  9. package/dist/{chunk-BIQ2J75Y.js → chunk-GLIK5BHP.js} +2 -2
  10. package/dist/{chunk-5YYR37CF.js → chunk-HGWPA7FU.js} +119 -0
  11. package/dist/{chunk-JEYT63LE.js → chunk-IBZVIUNI.js} +1 -1
  12. package/dist/chunk-IS3THKTE.js +89 -0
  13. package/dist/{chunk-4SZXBT56.js → chunk-NT72CQAI.js} +2 -2
  14. package/dist/{chunk-E2D7QT6N.js → chunk-TJSNVTVB.js} +1 -1
  15. package/dist/{chunk-5Z2IAD5I.js → chunk-TLG4C2XI.js} +2 -2
  16. package/dist/chunk-V7VQVDWS.js +237 -0
  17. package/dist/{chunk-IF5UAVIE.js → chunk-YC7G2IOZ.js} +1 -1
  18. package/dist/chunk-YGYA7KEG.js +423 -0
  19. package/dist/{chunk-GDOCD7LT.js → chunk-ZUMEOZ22.js} +5 -5
  20. package/dist/core-i18n.d.ts +2 -2
  21. package/dist/core-i18n.js +1 -1
  22. package/dist/core.d.ts +61 -1
  23. package/dist/core.js +1 -1
  24. package/dist/data-mock-store.js +265 -9
  25. package/dist/data-prisma-store.js +254 -1
  26. package/dist/data-seed-runner.js +18 -15
  27. package/dist/data.d.ts +54 -3
  28. package/dist/generated/client/edge.js +29 -9
  29. package/dist/generated/client/index-browser.js +26 -6
  30. package/dist/generated/client/index.d.ts +3544 -552
  31. package/dist/generated/client/index.js +29 -9
  32. package/dist/generated/client/package.json +1 -1
  33. package/dist/generated/client/schema.prisma +47 -9
  34. package/dist/generated/client/wasm.js +29 -9
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +1 -1
  37. package/dist/offline.d.ts +55 -19
  38. package/dist/offline.js +2 -375
  39. package/dist/{types-CtBC5-TW.d.ts → types-BOgdk0Jw.d.ts} +119 -0
  40. package/dist/types.d.ts +94 -1
  41. package/dist/types.js +1 -1
  42. package/dist/ui-components-admin/accounts-manager.d.ts +52 -0
  43. package/dist/ui-components-admin/accounts-manager.js +471 -0
  44. package/dist/ui-components-admin/admin-nav.d.ts +15 -1
  45. package/dist/ui-components-admin/admin-nav.js +387 -58
  46. package/dist/ui-components-admin/issuance-form.js +96 -23
  47. package/dist/ui-components-admin/logs-viewer.d.ts +13 -0
  48. package/dist/ui-components-admin/logs-viewer.js +102 -0
  49. package/dist/ui-components-admin/notifications-list.d.ts +5 -0
  50. package/dist/ui-components-admin/notifications-list.js +70 -0
  51. package/dist/ui-components-admin/officers-manager.d.ts +27 -0
  52. package/dist/ui-components-admin/officers-manager.js +271 -0
  53. package/dist/ui-components-admin/roles-manager.d.ts +37 -0
  54. package/dist/ui-components-admin/roles-manager.js +406 -0
  55. package/dist/ui-components-admin/ticket-preview.js +7 -7
  56. package/dist/ui-components-admin/tickets-table.js +56 -33
  57. package/dist/ui-components-citizen/citizen-nav.js +2 -2
  58. package/dist/ui-components-citizen/payment-form.js +5 -5
  59. package/dist/ui-components-citizen/payment-qr-dialog.js +4 -4
  60. package/dist/ui-components-citizen/ticket-not-found.js +2 -2
  61. package/dist/ui-components-citizen/violation-history-table.js +3 -3
  62. package/dist/ui-components-shared/amount-summary.js +4 -4
  63. package/dist/ui-components-shared/money.js +3 -3
  64. package/dist/ui-components-shared/municipal-seal.js +3 -3
  65. package/dist/ui-components-shared/official-header.js +4 -4
  66. package/dist/ui-components-shared/site-header.js +4 -4
  67. package/dist/ui-components-shared/sonner.js +2 -2
  68. package/dist/ui-components-shared/theme-toggle.js +3 -3
  69. package/dist/ui-components-shared/ticket-receipt.js +15 -7
  70. package/dist/ui-components-shared/violations-table.js +4 -4
  71. package/dist/ui-components-ui/badge.d.ts +1 -1
  72. package/dist/ui-components-ui/button.d.ts +1 -1
  73. package/dist/ui-components-ui/dropdown-menu.js +2 -237
  74. package/dist/ui-components-ui/sheet.js +3 -126
  75. package/dist/ui-config.d.ts +1 -1
  76. package/dist/ui-config.js +2 -2
  77. package/dist/ui-server.d.ts +1 -1
  78. package/dist/ui-server.js +2 -2
  79. package/package.json +3 -3
  80. package/prisma/migrations/20260622000000_add_issued_by/migration.sql +2 -0
  81. package/prisma/migrations/20260622010000_add_super_admin_role/migration.sql +3 -0
  82. package/prisma/migrations/20260622020000_add_apprehending_enforcer/migration.sql +4 -0
  83. package/prisma/migrations/20260622030000_custom_roles/migration.sql +30 -0
  84. package/prisma/migrations/20260622040000_add_activity_log/migration.sql +18 -0
  85. package/prisma/schema.prisma +47 -9
  86. package/dist/chunk-B634JHKZ.js +0 -181
@@ -42,11 +42,6 @@ enum PaymentStatus {
42
42
  CONTESTED
43
43
  }
44
44
 
45
- enum UserRole {
46
- ENFORCER
47
- ADMIN
48
- }
49
-
50
45
  model Officer {
51
46
  id String @id // slug, e.g. "off-novelo"
52
47
  name String // "NOVELO, RAYMUNDO V."
@@ -92,6 +87,13 @@ model Ticket {
92
87
  apprehendedAt DateTime
93
88
  placeOfViolation String?
94
89
  remarks String?
90
+ issuedBy String?
91
+
92
+ // GE-011: the enforcer ACCOUNT who apprehended (+ snapshot name for display).
93
+ // Soft reference to User.id — intentionally NOT a hard FK so the attribution
94
+ // survives if the account is later renamed or removed.
95
+ apprehendingEnforcerId String?
96
+ apprehendingEnforcerName String?
95
97
 
96
98
  officerId String
97
99
  officer Officer @relation(fields: [officerId], references: [id])
@@ -140,12 +142,30 @@ model Payment {
140
142
  paidAt DateTime
141
143
  }
142
144
 
145
+ // A role + its permission set (GE-013, custom roles). Permission keys come from
146
+ // the SDK catalog (@gelabs/ovr-types PERMISSION_CATALOG); the mapping is data.
147
+ model Role {
148
+ name String @id // "SUPER_ADMIN" / "ENFORCER" / custom slug e.g. "cashier"
149
+ label String // display name shown in the UI
150
+ isSystem Boolean @default(false) // system roles can't be renamed or deleted
151
+ permissions String[] // granted Permission keys
152
+
153
+ users User[]
154
+
155
+ createdAt DateTime @default(now())
156
+ updatedAt DateTime @updatedAt
157
+ }
158
+
143
159
  model User {
144
- id String @id @default(cuid())
145
- username String @unique
160
+ id String @id @default(cuid())
161
+ username String @unique
146
162
  passwordHash String
147
- role UserRole @default(ENFORCER)
148
- active Boolean @default(true)
163
+
164
+ // Role is DATA now (see model Role): a string FK so custom roles are allowed.
165
+ role String @default("ENFORCER")
166
+ roleRef Role @relation(fields: [role], references: [name])
167
+
168
+ active Boolean @default(true)
149
169
 
150
170
  // Links a login to an enforcer (so issued tickets can attribute the officer).
151
171
  officerId String? @unique
@@ -153,3 +173,21 @@ model User {
153
173
 
154
174
  createdAt DateTime @default(now())
155
175
  }
176
+
177
+ // Audit trail (GE-022): who did what, when. Append-only. Actor is a SOFT
178
+ // reference (snapshot username) so entries survive account deletion.
179
+ model ActivityLog {
180
+ id String @id @default(cuid())
181
+ actorId String?
182
+ actorUsername String?
183
+ action String // e.g. "auth.login", "account.create", "role.update"
184
+ summary String // human-readable one-liner
185
+ targetType String? // "account" | "role" | "ticket" | …
186
+ targetId String?
187
+
188
+ createdAt DateTime @default(now())
189
+
190
+ @@index([action])
191
+ @@index([actorId])
192
+ @@index([createdAt])
193
+ }
@@ -1,181 +0,0 @@
1
- // ../ovr-core/src/penalty.ts
2
- var MS_PER_DAY = 864e5;
3
- function startedMonthsSince(from, to) {
4
- const days = Math.max(0, (to.getTime() - from.getTime()) / MS_PER_DAY);
5
- return Math.ceil(days / 30);
6
- }
7
- function computeCharges({
8
- basicFines,
9
- apprehendedAt,
10
- asOf,
11
- dueDate,
12
- ratePerMonth
13
- }) {
14
- if (asOf.getTime() <= dueDate.getTime()) {
15
- return { basicFines, penalty: 0, total: basicFines, monthsOverdue: 0 };
16
- }
17
- const months = Math.max(1, startedMonthsSince(apprehendedAt, asOf));
18
- const penalty = Math.round(basicFines * ratePerMonth * months * 100) / 100;
19
- return {
20
- basicFines,
21
- penalty,
22
- total: Math.round((basicFines + penalty) * 100) / 100,
23
- monthsOverdue: months
24
- };
25
- }
26
-
27
- // ../ovr-core/src/ids.ts
28
- var pad = (n, len) => String(n).padStart(len, "0");
29
- var two = (n) => pad(n, 2);
30
- function makeOvrTicketNo(idPrefix, year, seq) {
31
- return `${idPrefix}-${year}-${pad(seq, 6)}`;
32
- }
33
- function makeOrderOfPaymentNo(ovrTicketNo) {
34
- return `${ovrTicketNo}-01`;
35
- }
36
- function makeBillNo(date, officeAbbr, officerCode, seq) {
37
- const ymd = `${date.getFullYear()}-${two(date.getMonth() + 1)}-${two(date.getDate())}`;
38
- return `M-${ymd}-${officeAbbr}-${officerCode}-${pad(seq, 6)}`;
39
- }
40
- function makePaymentRef(method, at) {
41
- const prefix = method.slice(0, 3);
42
- const ymd = `${at.getFullYear()}${two(at.getMonth() + 1)}${two(at.getDate())}`;
43
- const hms = `${two(at.getHours())}${two(at.getMinutes())}${two(at.getSeconds())}`;
44
- return `${prefix}-${ymd}-${hms}`;
45
- }
46
-
47
- // ../ovr-core/src/format.ts
48
- function createFormatters(rules) {
49
- const pesoFmt = new Intl.NumberFormat(rules.locale, {
50
- style: "currency",
51
- currency: rules.currency
52
- });
53
- const dateFmt = new Intl.DateTimeFormat(rules.locale, {
54
- year: "numeric",
55
- month: "short",
56
- day: "2-digit",
57
- timeZone: rules.timeZone
58
- });
59
- const dateTimeFmt = new Intl.DateTimeFormat(rules.locale, {
60
- year: "numeric",
61
- month: "short",
62
- day: "2-digit",
63
- hour: "numeric",
64
- minute: "2-digit",
65
- timeZone: rules.timeZone
66
- });
67
- return {
68
- formatPeso: (amount) => pesoFmt.format(amount),
69
- formatDate: (iso) => dateFmt.format(new Date(iso)),
70
- formatDateTime: (iso) => dateTimeFmt.format(new Date(iso)),
71
- nowManilaLocalInput: () => {
72
- const parts = new Intl.DateTimeFormat("en-CA", {
73
- timeZone: rules.timeZone,
74
- year: "numeric",
75
- month: "2-digit",
76
- day: "2-digit",
77
- hour: "2-digit",
78
- minute: "2-digit",
79
- hour12: false
80
- }).formatToParts(/* @__PURE__ */ new Date());
81
- const get = (t) => parts.find((p) => p.type === t)?.value ?? "00";
82
- return `${get("year")}-${get("month")}-${get("day")}T${get("hour")}:${get("minute")}`;
83
- }
84
- };
85
- }
86
- function fullName(v) {
87
- return [v.firstName, v.middleName, v.lastName].filter(Boolean).join(" ");
88
- }
89
- function formalName(v) {
90
- const given = [v.firstName, v.middleName].filter(Boolean).join(" ");
91
- return `${v.lastName}, ${given}`;
92
- }
93
- function formatAddress(v) {
94
- return [v.street, v.barangay, v.cityMunicipality, v.province].map((p) => p?.trim()).filter(Boolean).join(", ");
95
- }
96
- function addDays(date, days) {
97
- const d = new Date(date);
98
- d.setDate(d.getDate() + days);
99
- return d;
100
- }
101
- function localToManilaISO(local) {
102
- if (!local) return (/* @__PURE__ */ new Date()).toISOString();
103
- const withSeconds = local.length === 16 ? `${local}:00` : local;
104
- return (/* @__PURE__ */ new Date(`${withSeconds}+08:00`)).toISOString();
105
- }
106
-
107
- // ../ovr-core/src/enrich.ts
108
- var round2 = (n) => Math.round(n * 100) / 100;
109
- function enrich(rec, asOf, ratePerMonth) {
110
- if (rec.paymentStatus === "PAID") {
111
- const paid = rec.payment?.amount ?? rec.basicFinesTotal;
112
- return {
113
- ...rec,
114
- status: "PAID",
115
- penaltyAmount: Math.max(0, round2(paid - rec.basicFinesTotal)),
116
- totalAmountDue: 0
117
- };
118
- }
119
- const { penalty, total } = computeCharges({
120
- basicFines: rec.basicFinesTotal,
121
- apprehendedAt: new Date(rec.apprehendedAt),
122
- asOf,
123
- dueDate: new Date(rec.dueDate),
124
- ratePerMonth
125
- });
126
- if (rec.paymentStatus === "CONTESTED") {
127
- return { ...rec, status: "CONTESTED", penaltyAmount: penalty, totalAmountDue: total };
128
- }
129
- const overdue = asOf.getTime() > new Date(rec.dueDate).getTime();
130
- return {
131
- ...rec,
132
- status: overdue ? "OVERDUE" : "OUTSTANDING",
133
- penaltyAmount: penalty,
134
- totalAmountDue: total
135
- };
136
- }
137
-
138
- // ../ovr-core/src/preview.ts
139
- var PREVIEW_PLACEHOLDER = "\u2014 assigned on confirm \u2014";
140
- function toPreviewTicket(draft, now, rules) {
141
- const basicFinesTotal = draft.violations.reduce(
142
- (s, v) => s + v.item.basicFine,
143
- 0
144
- );
145
- const apprehendedAt = draft.apprehendedAtISO || now.toISOString();
146
- const dueDate = addDays(now, rules.dueWindowDays);
147
- const { penalty, total } = computeCharges({
148
- basicFines: basicFinesTotal,
149
- apprehendedAt: new Date(apprehendedAt),
150
- asOf: now,
151
- dueDate,
152
- ratePerMonth: rules.surchargeRatePerMonth
153
- });
154
- const officer = draft.officer ?? { id: "", name: PREVIEW_PLACEHOLDER, office: "" };
155
- return {
156
- ovrTicketNo: PREVIEW_PLACEHOLDER,
157
- orderOfPaymentNo: PREVIEW_PLACEHOLDER,
158
- billNo: PREVIEW_PLACEHOLDER,
159
- violator: draft.violator,
160
- apprehendedAt,
161
- placeOfViolation: draft.placeOfViolation,
162
- officer,
163
- violations: draft.violations.map((v) => ({
164
- catalogCode: v.item.code,
165
- title: v.item.title,
166
- basicFine: v.item.basicFine,
167
- details: v.details?.trim() || void 0
168
- })),
169
- remarks: draft.remarks,
170
- assessedAt: now.toISOString(),
171
- dueDate: dueDate.toISOString(),
172
- basicFinesTotal,
173
- paymentStatus: "UNPAID",
174
- status: "OUTSTANDING",
175
- penaltyAmount: penalty,
176
- totalAmountDue: total,
177
- createdAt: now.toISOString()
178
- };
179
- }
180
-
181
- export { PREVIEW_PLACEHOLDER, addDays, computeCharges, createFormatters, enrich, formalName, formatAddress, fullName, localToManilaISO, makeBillNo, makeOrderOfPaymentNo, makeOvrTicketNo, makePaymentRef, round2, startedMonthsSince, toPreviewTicket };