@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.
Files changed (85) 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-IB4JVGKJ.js → chunk-YGYA7KEG.js} +47 -3
  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 +263 -9
  25. package/dist/data-prisma-store.js +251 -1
  26. package/dist/data-seed-runner.js +18 -15
  27. package/dist/data.d.ts +53 -3
  28. package/dist/generated/client/edge.js +28 -9
  29. package/dist/generated/client/index-browser.js +25 -6
  30. package/dist/generated/client/index.d.ts +3500 -552
  31. package/dist/generated/client/index.js +28 -9
  32. package/dist/generated/client/package.json +1 -1
  33. package/dist/generated/client/schema.prisma +46 -9
  34. package/dist/generated/client/wasm.js +28 -9
  35. package/dist/index.d.ts +2 -2
  36. package/dist/index.js +1 -1
  37. package/dist/offline.d.ts +34 -1
  38. package/dist/offline.js +2 -2
  39. package/dist/{types-CtBC5-TW.d.ts → types-BOgdk0Jw.d.ts} +119 -0
  40. package/dist/types.d.ts +93 -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 +386 -60
  46. package/dist/ui-components-admin/issuance-form.js +72 -13
  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 +13 -6
  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 +4 -4
  80. package/prisma/migrations/20260622010000_add_super_admin_role/migration.sql +3 -0
  81. package/prisma/migrations/20260622020000_add_apprehending_enforcer/migration.sql +4 -0
  82. package/prisma/migrations/20260622030000_custom_roles/migration.sql +30 -0
  83. package/prisma/migrations/20260622040000_add_activity_log/migration.sql +18 -0
  84. package/prisma/schema.prisma +46 -9
  85. 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."
@@ -94,6 +89,12 @@ model Ticket {
94
89
  remarks String?
95
90
  issuedBy String?
96
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?
97
+
97
98
  officerId String
98
99
  officer Officer @relation(fields: [officerId], references: [id])
99
100
 
@@ -141,12 +142,30 @@ model Payment {
141
142
  paidAt DateTime
142
143
  }
143
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
+
144
159
  model User {
145
- id String @id @default(cuid())
146
- username String @unique
160
+ id String @id @default(cuid())
161
+ username String @unique
147
162
  passwordHash String
148
- role UserRole @default(ENFORCER)
149
- 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)
150
169
 
151
170
  // Links a login to an enforcer (so issued tickets can attribute the officer).
152
171
  officerId String? @unique
@@ -154,3 +173,21 @@ model User {
154
173
 
155
174
  createdAt DateTime @default(now())
156
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 };