@7365admin1/layer-common 3.1.4-staging.53 → 3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 3.2.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 5754751: 08072026 Version Release
8
+
3
9
  ## 3.1.3
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,376 @@
1
+ <template>
2
+ <v-card class="pa-2">
3
+ <!-- Header -->
4
+ <v-card-title class="d-flex align-center justify-space-between pb-2">
5
+ <span class="text-h6 font-weight-bold">
6
+ {{ isEditing ? "Edit Plan" : "Create New Plan" }}
7
+ </span>
8
+
9
+ <v-btn
10
+ icon="mdi-close"
11
+ variant="text"
12
+ density="comfortable"
13
+ aria-label="Close"
14
+ @click="$emit('cancel')"
15
+ />
16
+ </v-card-title>
17
+
18
+ <v-divider />
19
+
20
+ <!-- Form Content -->
21
+ <v-form ref="formRef" class="my-4" @submit.prevent="submit">
22
+ <v-card-text class="pa-2 max-h-70vh overflow-y-auto">
23
+ <!-- Plan Details Section -->
24
+ <div class="text-subtitle-2 font-weight-bold mb-3 text-grey-darken-3">
25
+ Plan Details
26
+ </div>
27
+
28
+ <!-- Plan Name -->
29
+ <v-text-field
30
+ v-model="form.name"
31
+ label="Plan Name"
32
+ placeholder="e.g., Business"
33
+ variant="outlined"
34
+ density="compact"
35
+ :error-messages="errors.name"
36
+ class="mb-3"
37
+ required
38
+ >
39
+ <template #label>
40
+ Plan Name <span class="text-error">*</span>
41
+ </template>
42
+ </v-text-field>
43
+
44
+ <!-- Description -->
45
+ <v-textarea
46
+ v-model="form.description"
47
+ label="Description (optional)"
48
+ placeholder="Enter description"
49
+ variant="outlined"
50
+ density="compact"
51
+ rows="2"
52
+ auto-grow
53
+ class="mb-3"
54
+ />
55
+
56
+ <!-- Max Seats -->
57
+ <v-text-field
58
+ v-model.number="form.maxSeats"
59
+ type="number"
60
+ min="0"
61
+ variant="outlined"
62
+ density="compact"
63
+ class="mb-4"
64
+ required
65
+ >
66
+ <template #label>
67
+ Max seats <span class="text-error">*</span>
68
+ </template>
69
+ </v-text-field>
70
+
71
+ <v-divider class="my-4" />
72
+
73
+ <!-- Pricing Section -->
74
+ <div class="text-subtitle-2 font-weight-bold mb-3 text-grey-darken-3">
75
+ Pricing
76
+ </div>
77
+
78
+ <!-- Plan Type -->
79
+ <v-select
80
+ v-model="form.planType"
81
+ :items="planTypeOptions"
82
+ variant="outlined"
83
+ density="compact"
84
+ class="mb-3"
85
+ >
86
+ <template #label>
87
+ Subscription plan type <span class="text-error">*</span>
88
+ </template>
89
+ </v-select>
90
+
91
+ <!-- Billing Cycle -->
92
+ <v-select
93
+ v-if="form.planType !== 'free_bundle'"
94
+ v-model="form.billingCycle"
95
+ :items="billingCycleOptions"
96
+ placeholder="Select billing cycle"
97
+ variant="outlined"
98
+ density="compact"
99
+ class="mb-3"
100
+ >
101
+ <template #label>
102
+ Billing cycle <span class="text-error">*</span>
103
+ </template>
104
+ </v-select>
105
+
106
+ <!-- Bundle Applications (Chip Toggle) -->
107
+ <div v-if="form.planType !== 'custom_apps'" class="mb-4">
108
+ <div class="text-caption font-weight-medium text-grey-darken-2 mb-2">
109
+ Included Applications
110
+ </div>
111
+
112
+ <v-chip-group
113
+ v-model="form.bundleApps"
114
+ column
115
+ multiple
116
+ selected-class="bg-grey-darken-3 text-white"
117
+ >
118
+ <v-chip
119
+ v-for="app in AVAILABLE_APPLICATIONS"
120
+ :key="app.id"
121
+ :value="app.id"
122
+ filter
123
+ variant="outlined"
124
+ size="small"
125
+ >
126
+ {{ app.name }}
127
+ </v-chip>
128
+ </v-chip-group>
129
+ </div>
130
+
131
+ <!-- Bundle Price -->
132
+ <v-text-field
133
+ v-if="form.planType === 'paid_bundle'"
134
+ v-model.number="form.bundlePrice"
135
+ type="number"
136
+ min="0"
137
+ placeholder="0"
138
+ prefix="$"
139
+ :suffix="form.billingCycle === 'annually' ? '/yr' : '/mo'"
140
+ variant="outlined"
141
+ density="compact"
142
+ class="mb-3"
143
+ >
144
+ <template #label>
145
+ Price <span class="text-error">*</span>
146
+ </template>
147
+ </v-text-field>
148
+
149
+ <!-- Custom Applications (List Checkbox) -->
150
+ <div v-if="form.planType === 'custom_apps'" class="mb-4">
151
+ <div class="text-caption font-weight-medium text-grey-darken-2 mb-1">
152
+ Included Applications <span class="text-error">*</span>
153
+ </div>
154
+
155
+ <v-card variant="outlined" class="pa-1 mb-3">
156
+ <v-list density="compact" class="py-0">
157
+ <v-list-item
158
+ v-for="app in AVAILABLE_APPLICATIONS"
159
+ :key="app.id"
160
+ class="px-2"
161
+ >
162
+ <template #prepend>
163
+ <v-checkbox-btn
164
+ :model-value="form.customApps.includes(app.id)"
165
+ density="compact"
166
+ @update:model-value="toggleApp(form.customApps, app.id)"
167
+ />
168
+ </template>
169
+
170
+ <v-list-item-title class="text-body-2">
171
+ {{ app.name }}
172
+ </v-list-item-title>
173
+
174
+ <template #append>
175
+ <span class="text-caption text-grey">
176
+ ${{ form.billingCycle === 'annually' ? app.yearlyPrice : app.monthlyPrice }}/{{ form.billingCycle === 'annually' ? 'yr' : 'mo' }}
177
+ </span>
178
+ </template>
179
+ </v-list-item>
180
+ </v-list>
181
+ </v-card>
182
+
183
+ <!-- Custom Total Price Display -->
184
+ <v-sheet
185
+ color="grey-lighten-4"
186
+ rounded
187
+ class="pa-3 d-flex align-center justify-space-between mb-1"
188
+ >
189
+ <span class="text-caption text-grey-darken-2">
190
+ Total price {{ form.billingCycle === 'annually' ? '/ year' : '/ month' }}
191
+ </span>
192
+ <span class="text-subtitle-1 font-weight-bold">
193
+ ${{ customTotalPrice.toLocaleString() }}
194
+ </span>
195
+ </v-sheet>
196
+
197
+ <p class="text-caption text-grey">
198
+ Calculated automatically from the selected applications.
199
+ </p>
200
+ </div>
201
+
202
+ <!-- Global Pricing Error Message -->
203
+ <v-alert
204
+ v-if="errors.pricing"
205
+ type="error"
206
+ variant="tonal"
207
+ density="compact"
208
+ class="mt-2 text-caption"
209
+ >
210
+ {{ errors.pricing }}
211
+ </v-alert>
212
+ </v-card-text>
213
+
214
+ <v-divider />
215
+
216
+ <!-- Footer Buttons -->
217
+ <v-card-actions class="pt-3 px-2">
218
+ <v-btn
219
+ variant="outlined"
220
+ color="grey-darken-1"
221
+ class="flex-1 text-none"
222
+ @click="$emit('cancel')"
223
+ >
224
+ Cancel
225
+ </v-btn>
226
+
227
+ <v-btn
228
+ type="submit"
229
+ color="grey-darken-4"
230
+ variant="flat"
231
+ class="flex-1 text-none"
232
+ >
233
+ {{ isEditing ? "Save Changes" : "Create Plan" }}
234
+ </v-btn>
235
+ </v-card-actions>
236
+ </v-form>
237
+ </v-card>
238
+ </template>
239
+
240
+ <script setup lang="ts">
241
+ import { reactive, computed, watch } from "vue";
242
+
243
+ import {
244
+ AVAILABLE_APPLICATIONS,
245
+ type PlanFormState,
246
+ type SubscriptionPlan,
247
+ } from "../types/subscription";
248
+
249
+ const props = defineProps<{
250
+ initialPlan?: SubscriptionPlan | null;
251
+ }>();
252
+
253
+ const emit = defineEmits<{
254
+ (e: "cancel"): void;
255
+ (e: "submit", payload: PlanFormState): void;
256
+ }>();
257
+
258
+ // Select options
259
+ const planTypeOptions = [
260
+ { title: "Free (Bundle)", value: "free_bundle" },
261
+ { title: "Paid (Bundle)", value: "paid_bundle" },
262
+ { title: "Paid (Custom Apps)", value: "custom_apps" },
263
+ ];
264
+
265
+ const billingCycleOptions = [
266
+ { title: "Monthly", value: "monthly" },
267
+ { title: "Annually", value: "annually" },
268
+ ];
269
+
270
+ const isEditing = computed(() => !!props.initialPlan);
271
+
272
+ const form = reactive<PlanFormState>({
273
+ name: props.initialPlan?.name ?? "",
274
+ description: props.initialPlan?.description ?? "",
275
+ maxSeats: props.initialPlan?.maxSeats ?? 0,
276
+ planType: props.initialPlan?.planType ?? "free_bundle",
277
+ billingCycle: props.initialPlan?.billingCycle ?? null,
278
+ bundleApps:
279
+ props.initialPlan?.planType !== "custom_apps"
280
+ ? [...(props.initialPlan?.applicationIds ?? [])]
281
+ : [],
282
+ bundlePrice: props.initialPlan?.price ?? null,
283
+ customApps:
284
+ props.initialPlan?.planType === "custom_apps"
285
+ ? [...(props.initialPlan?.applicationIds ?? [])]
286
+ : [],
287
+ });
288
+
289
+ const errors = reactive({
290
+ name: "",
291
+ pricing: "",
292
+ });
293
+
294
+ /*
295
+ * Reset pricing fields when plan type changes
296
+ */
297
+ watch(
298
+ () => form.planType,
299
+ (type) => {
300
+ if (type === "free_bundle") {
301
+ form.billingCycle = null;
302
+ form.bundlePrice = null;
303
+ }
304
+ }
305
+ );
306
+
307
+ /*
308
+ * Toggle application selection helper
309
+ */
310
+ function toggleApp(list: string[], id: string) {
311
+ const index = list.indexOf(id);
312
+ if (index === -1) {
313
+ list.push(id);
314
+ } else {
315
+ list.splice(index, 1);
316
+ }
317
+ }
318
+
319
+ /*
320
+ * Custom app total price calculation
321
+ */
322
+ const customTotalPrice = computed(() => {
323
+ return form.customApps.reduce((total, id) => {
324
+ const app = AVAILABLE_APPLICATIONS.find((item) => item.id === id);
325
+ if (!app) return total;
326
+
327
+ return (
328
+ total +
329
+ (form.billingCycle === "annually" ? app.yearlyPrice : app.monthlyPrice)
330
+ );
331
+ }, 0);
332
+ });
333
+
334
+ /*
335
+ * Validate Form
336
+ */
337
+ function validate(): boolean {
338
+ errors.name = form.name.trim() ? "" : "Plan name is required.";
339
+ errors.pricing = "";
340
+
341
+ if (form.planType !== "free_bundle" && !form.billingCycle) {
342
+ errors.pricing = "Please select a billing cycle.";
343
+ } else if (
344
+ form.planType === "paid_bundle" &&
345
+ (!form.bundlePrice || form.bundlePrice <= 0)
346
+ ) {
347
+ errors.pricing = "Please enter a price.";
348
+ } else if (
349
+ form.planType === "custom_apps" &&
350
+ form.customApps.length === 0
351
+ ) {
352
+ errors.pricing = "Select at least one application.";
353
+ }
354
+
355
+ return !errors.name && !errors.pricing;
356
+ }
357
+
358
+ /*
359
+ * Submit Form
360
+ */
361
+ function submit() {
362
+ if (!validate()) {
363
+ return;
364
+ }
365
+
366
+ emit("submit", {
367
+ ...form,
368
+ });
369
+ }
370
+ </script>
371
+
372
+ <style scoped>
373
+ .max-h-70vh {
374
+ max-height: 70vh;
375
+ }
376
+ </style>
@@ -1483,6 +1483,7 @@ const props = defineProps({
1483
1483
  },
1484
1484
  title: { type: String, default: "" },
1485
1485
  extraWidgetKeys: { type: Array as () => string[], default: () => [] },
1486
+ hiddenWidgetKeys: { type: Array as () => string[], default: () => [] },
1486
1487
  showHeaderActions: { type: Boolean, default: true },
1487
1488
  appKey: {
1488
1489
  type: String as () =>
@@ -1530,57 +1531,33 @@ const { downloadPDF } = usePDFDownload();
1530
1531
  const route = useRoute();
1531
1532
 
1532
1533
  const propertyWidgetPermissions: Record<string, string[]> = {
1533
- workOrders: [
1534
- "work_orders:see-all-work-orders",
1535
- "work-order:see-all-work-orders",
1536
- ],
1534
+ workOrders: ["work_orders:see-all-work-orders"],
1537
1535
  incidents: ["incident-reports:see-incident-reports"],
1538
- visitors: ["visitorManagement:see-all-visitor"],
1536
+ visitors: ["visitor-mgmt:see-all-visitor"],
1539
1537
  facilityBookings: ["facility-booking-mgmt:see-all-facility-booking"],
1540
- activity: [
1541
- "work_orders:see-all-work-orders",
1542
- "work-order:see-all-work-orders",
1543
- ],
1538
+ activity: ["work_orders:see-all-work-orders"],
1544
1539
  upcomingEvents: ["event-mgmt:see-all-event"],
1545
1540
  todayReminders: ["event-mgmt:see-all-event"],
1546
1541
  todayAttentions: [
1547
1542
  "incident-reports:see-incident-reports",
1548
1543
  "facility-booking-mgmt:see-all-facility-booking",
1549
1544
  ],
1550
- workOrderStatus: [
1551
- "work_orders:see-all-work-orders",
1552
- "work-order:see-all-work-orders",
1553
- ],
1545
+ workOrderStatus: ["work_orders:see-all-work-orders"],
1554
1546
  feedbacks: ["feedbacks:see-all-feedback"],
1555
1547
  activePatrol: ["virtual-patrol:see-all-virtual-patrol-logs"],
1556
1548
  };
1557
1549
 
1558
1550
  const moduleWidgetPermissions: Record<string, string[]> = {
1559
- openWorkOrder: [
1560
- "work_orders:see-all-work-orders",
1561
- "work_order:see-all-work-orders",
1562
- "work-order:see-all-work-orders",
1563
- "work-orders:see-all-work-orders",
1564
- ],
1565
- workOrders: [
1566
- "work_orders:see-all-work-orders",
1567
- "work_order:see-all-work-orders",
1568
- "work-order:see-all-work-orders",
1569
- "work-orders:see-all-work-orders",
1570
- ],
1571
- activity: [
1572
- "work_orders:see-all-work-orders",
1573
- "work_order:see-all-work-orders",
1574
- "work-order:see-all-work-orders",
1575
- "work-orders:see-all-work-orders",
1576
- "work_orders:see-all-work-orders",
1551
+ openWorkOrder: ["work_orders:see-all-work-orders"],
1552
+ workOrders: ["work_orders:see-all-work-orders"],
1553
+ activity: ["work_orders:see-all-work-orders"],
1554
+ taskSchedule: [
1555
+ "cleaning-schedule-mgmt:see-all-schedules",
1556
+ "schedule-task-mgmt:see-all-schedule-tasks",
1577
1557
  ],
1578
- recentActivity: [
1579
- "work_orders:see-all-work-orders",
1580
- "work_order:see-all-work-orders",
1581
- "work-order:see-all-work-orders",
1582
- "work-orders:see-all-work-orders",
1583
- "work_orders:see-all-work-orders",
1558
+ taskCompleted: [
1559
+ "cleaning-schedule-mgmt:see-all-schedules",
1560
+ "schedule-task-mgmt:see-all-schedule-tasks",
1584
1561
  ],
1585
1562
  staffStatus: ["attendance-mgmt:see-all-attendance"],
1586
1563
  attendance: ["attendance-mgmt:see-all-attendance"],
@@ -1594,7 +1571,7 @@ const moduleWidgetPermissions: Record<string, string[]> = {
1594
1571
  "incident-reports:see-incident-reports",
1595
1572
  "incident-reports:see-all-incident-reports",
1596
1573
  ],
1597
- visitors: ["visitor:see-all-visitor"],
1574
+ visitors: ["visitor-mgmt:see-all-visitor"],
1598
1575
  facilityBookings: ["facility-booking:see-all-facility-booking"],
1599
1576
  patrolCompliance: [
1600
1577
  "virtual-patrol:see-all-virtual-patrol-logs",
@@ -1610,6 +1587,7 @@ function hasSchedulePermission(permissions: string[], action: string) {
1610
1587
  }
1611
1588
 
1612
1589
  function canViewWidget(key: string) {
1590
+ if (props.hiddenWidgetKeys.includes(key)) return false;
1613
1591
  const permissions = userAppRole.value?.permissions;
1614
1592
  const modulePermissions = isPropertyMode.value
1615
1593
  ? propertyWidgetPermissions[key]
@@ -168,13 +168,13 @@
168
168
  <template v-if="passType === 'HID_QR'">
169
169
  <v-alert
170
170
  v-if="!hidQrPrinterConfigured"
171
- type="info"
171
+ type="warning"
172
172
  variant="tonal"
173
173
  density="compact"
174
174
  class="mt-3"
175
- icon="mdi-qrcode"
175
+ icon="mdi-printer-alert"
176
176
  >
177
- HID QR Code will be generated and shown on screen. You can download it after continuing.
177
+ No printer is configured on HID settings. QR Pass may not print.
178
178
  </v-alert>
179
179
  <v-alert
180
180
  v-else
@@ -184,7 +184,7 @@
184
184
  class="mt-3"
185
185
  icon="mdi-qrcode-scan"
186
186
  >
187
- HID QR Code will be generated and shown on screen. Printing is also available.
187
+ HID QR Code will be generated and printed when you continue.
188
188
  </v-alert>
189
189
  </template>
190
190
  </template>
@@ -418,17 +418,8 @@ const selectedHistoryRows = computed<AccessRow[]>(() => {
418
418
  if (!selectedHistoryRow.value) return [];
419
419
 
420
420
  const selectedKey = selectedHistoryRow.value.identityKey;
421
- const selectedIdentity = findIdentityByKey(selectedKey);
422
- const matchedLogs = logs.value.filter((log) =>
423
- selectedIdentity
424
- ? accessLogMatchesIdentity(log, selectedIdentity)
425
- : getLogIdentityKey(log) === selectedKey,
426
- );
427
- const matchedLiveLogs = liveAccessLogs.value.filter((log) =>
428
- selectedIdentity
429
- ? accessLogMatchesIdentity(log, selectedIdentity)
430
- : getAccessLogIdentityKey(log) === selectedKey,
431
- );
421
+ const matchedLogs = logs.value.filter((log) => getLogIdentityKey(log) === selectedKey);
422
+ const matchedLiveLogs = liveAccessLogs.value.filter((log) => getAccessLogIdentityKey(log) === selectedKey);
432
423
 
433
424
  if (!matchedLogs.length && !matchedLiveLogs.length) {
434
425
  return rows.value.filter((row) => row.identityKey === selectedKey);
@@ -844,7 +835,7 @@ function getIdentityKey(identity: Record<string, any>) {
844
835
  return [
845
836
  identity.hidUserId,
846
837
  identity.registration,
847
- identity.cardNo || identity.metadata?.qrCodeValue,
838
+ identity.cardNo,
848
839
  ].filter(Boolean).join("|");
849
840
  }
850
841
 
@@ -860,7 +851,7 @@ function getLogIdentityKey(log: Record<string, any>) {
860
851
  return [
861
852
  identity.hidUserId || lookup.hidUserId || rawLog.user_id || rawLog.userId || rawLog.hidUserId,
862
853
  identity.registration || lookup.registration,
863
- identity.cardNo || lookup.cardNo || rawLog.card_value || rawLog.cardNo || rawLog.qrcode_value,
854
+ identity.cardNo || lookup.cardNo || rawLog.card_value || rawLog.cardNo,
864
855
  ].filter(Boolean).join("|");
865
856
  }
866
857
 
@@ -1182,26 +1173,25 @@ function findIdentityForAccessLog(rawLog: Record<string, any>, event: Record<str
1182
1173
  const hidUserId = rawLog.user_id ?? rawLog.userId ?? rawLog.hidUserId ?? event.payload?.identity?.hidUserId ?? event.payload?.identityLookup?.hidUserId;
1183
1174
  const registration = rawLog.registration ?? event.payload?.identity?.registration ?? event.payload?.identityLookup?.registration;
1184
1175
  const cardNo = rawLog.card_value || rawLog.cardNo || event.payload?.identity?.cardNo || event.payload?.identityLookup?.cardNo;
1185
- const qrCodeValue = rawLog.qrcode_value || rawLog.qrCode || rawLog.qrcode;
1186
1176
 
1187
1177
  return identities.value.find((identity) => {
1188
1178
  return (
1189
1179
  (hidUserId !== undefined && hidUserId !== null && String(identity.hidUserId) === String(hidUserId)) ||
1190
1180
  (registration && identity.registration === registration) ||
1191
- (cardNo && String(identity.cardNo) === String(cardNo)) ||
1192
- (qrCodeValue && String(identity.metadata?.qrCodeValue || "") === String(qrCodeValue))
1181
+ (cardNo && String(identity.cardNo) === String(cardNo))
1193
1182
  );
1194
1183
  });
1195
1184
  }
1196
1185
 
1197
1186
  function getLatestAccessLogForIdentity(identity: Record<string, any>) {
1187
+ const identityKey = getIdentityKey(identity);
1198
1188
  const accessLogs = [
1199
1189
  ...liveAccessLogs.value,
1200
1190
  ...logs.value.flatMap((event) => getRawAccessLogs(event)),
1201
1191
  ];
1202
1192
 
1203
1193
  return accessLogs
1204
- .filter((log) => accessLogMatchesIdentity(log, identity))
1194
+ .filter((log) => getAccessLogIdentityKey(log) === identityKey)
1205
1195
  .sort((a, b) => {
1206
1196
  const aTime = parseDate(getAccessLogTime(a, {}))?.getTime() || 0;
1207
1197
  const bTime = parseDate(getAccessLogTime(b, {}))?.getTime() || 0;
@@ -1209,46 +1199,11 @@ function getLatestAccessLogForIdentity(identity: Record<string, any>) {
1209
1199
  })[0];
1210
1200
  }
1211
1201
 
1212
- function toUnknownRecord(value: unknown): Record<string, unknown> {
1213
- return typeof value === "object" && value !== null && !Array.isArray(value)
1214
- ? value as Record<string, unknown>
1215
- : {};
1216
- }
1217
-
1218
- function accessLogMatchesIdentity(log: unknown, identity: unknown) {
1219
- const logRecord = toUnknownRecord(log);
1220
- const identityRecord = toUnknownRecord(identity);
1221
- const payload = toUnknownRecord(logRecord.payload);
1222
- const rawLog = Object.keys(toUnknownRecord(payload.rawAccessLog)).length
1223
- ? toUnknownRecord(payload.rawAccessLog)
1224
- : logRecord;
1225
- const eventIdentity = Object.keys(toUnknownRecord(payload.identity)).length
1226
- ? toUnknownRecord(payload.identity)
1227
- : toUnknownRecord(payload.identityLookup);
1228
- const metadata = toUnknownRecord(identityRecord.metadata);
1229
- const hidUserId = rawLog.user_id ?? rawLog.userId ?? rawLog.hidUserId ?? eventIdentity.hidUserId;
1230
- const registration = rawLog.registration || eventIdentity.registration;
1231
- const credential =
1232
- rawLog.card_value ||
1233
- rawLog.cardNo ||
1234
- rawLog.qrcode_value ||
1235
- rawLog.qrCode ||
1236
- rawLog.qrcode ||
1237
- eventIdentity.cardNo;
1238
- const identityCredential = identityRecord.cardNo || metadata.qrCodeValue;
1239
-
1240
- return Boolean(
1241
- (hidUserId !== undefined && hidUserId !== null && String(identityRecord.hidUserId) === String(hidUserId)) ||
1242
- (registration && identityRecord.registration === registration) ||
1243
- (credential && identityCredential && String(identityCredential) === String(credential)),
1244
- );
1245
- }
1246
-
1247
1202
  function getAccessLogIdentityKey(rawLog: Record<string, any>) {
1248
1203
  return [
1249
1204
  rawLog.user_id ?? rawLog.userId ?? rawLog.hidUserId,
1250
1205
  rawLog.registration,
1251
- rawLog.card_value || rawLog.cardNo || rawLog.qrcode_value,
1206
+ rawLog.card_value || rawLog.cardNo,
1252
1207
  ].filter(Boolean).join("|");
1253
1208
  }
1254
1209