@7365admin1/layer-common 3.2.1 → 3.2.2-staging.100
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/.changeset/camera-wall-gated-endpoint.md +47 -0
- package/.changeset/camera-wall-plain-english.md +60 -0
- package/.changeset/camera-wall-selection-and-guidance.md +65 -0
- package/.changeset/dark-primary-contrast.md +39 -0
- package/.changeset/dashboard-dark-theme-contrast.md +15 -0
- package/.changeset/notification-preferences.md +17 -0
- package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
- package/.changeset/service-provider-invitation-actions.md +28 -0
- package/assets/css/primitives.css +680 -0
- package/assets/css/screens.css +541 -0
- package/assets/css/shell.css +258 -0
- package/assets/css/tokens.css +220 -0
- package/components/AccessCardQrTagging.vue +1 -1
- package/components/AccessManagement.vue +53 -72
- package/components/AppButton.vue +52 -0
- package/components/AppCard.vue +20 -0
- package/components/AppDateField.vue +35 -0
- package/components/AppField.vue +46 -0
- package/components/AppKpiTile.vue +65 -0
- package/components/AppSegmented.vue +38 -0
- package/components/AppSelect.vue +203 -0
- package/components/AreaChecklistHistoryMain.vue +6 -2
- package/components/AreaMain.vue +9 -17
- package/components/AttendanceMain.vue +4 -3
- package/components/Avatar/Main.vue +54 -2
- package/components/BillingMain.vue +26 -30
- package/components/BuildingManagement/buildings.vue +27 -38
- package/components/BuildingManagement/units.vue +26 -42
- package/components/BulletinBoardManagement.vue +23 -10
- package/components/CameraWall.vue +1034 -0
- package/components/CameraWallTile.vue +818 -0
- package/components/CardToolbar.vue +37 -0
- package/components/CleaningScheduleMain.vue +29 -46
- package/components/ClientMain.vue +90 -78
- package/components/DashboardEmptyState.vue +11 -1
- package/components/DashboardMain.vue +349 -275
- package/components/DashboardPanel.vue +1 -1
- package/components/DocumentManagement.vue +129 -93
- package/components/EntryPassInformation.vue +4 -4
- package/components/EntryPassMain.vue +49 -27
- package/components/EquipmentItemMain.vue +4 -6
- package/components/EquipmentManagementMain.vue +9 -7
- package/components/FeedbackMain.vue +42 -56
- package/components/FormDialog.vue +60 -21
- package/components/HidAccessLogDashboard.vue +119 -44
- package/components/HidCameraCaptureDialog.vue +199 -0
- package/components/HidIntercomManagement.vue +825 -202
- package/components/HidQrCodeConfiguration.vue +927 -93
- package/components/HidServiceSettingsPanel.vue +20 -1
- package/components/HidUserEnrollment.vue +454 -106
- package/components/InvitationMain.vue +46 -61
- package/components/Layout/Header.vue +289 -37
- package/components/Layout/NavigationDrawer.vue +340 -30
- package/components/ManageChecklistMain.vue +17 -17
- package/components/MemberMain.vue +79 -60
- package/components/MyAttendanceMain.vue +3 -5
- package/components/NavigationItem.vue +9 -0
- package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
- package/components/NotificationSettings.vue +361 -0
- package/components/OnlineFormConfigurationForm.vue +620 -224
- package/components/OnlineFormFill.vue +36 -8
- package/components/OnlineFormsConfiguration.vue +7 -3
- package/components/PageHeader.vue +43 -0
- package/components/PassInformation.vue +104 -45
- package/components/QrTemplate/CreditCardLandscape.vue +19 -12
- package/components/QrTemplate/PrintDialog.vue +209 -196
- package/components/RolePermissionMain.vue +184 -91
- package/components/ScanVisitorQRCode.vue +35 -7
- package/components/ScheduleAreaMain.vue +15 -49
- package/components/ScheduleTaskMain.vue +23 -26
- package/components/ScheduleTastTicketMain.vue +18 -43
- package/components/ServiceProviderInvitationPrompt.vue +150 -0
- package/components/ServiceProviderMain.vue +346 -157
- package/components/SiteSettings.vue +25 -10
- package/components/SpecificAttr.vue +10 -2
- package/components/StatusChip.vue +93 -0
- package/components/SwitchContext.vue +33 -1
- package/components/TableHygiene.vue +29 -46
- package/components/TableList.vue +1 -1
- package/components/TableListSecondary.vue +1 -1
- package/components/TableMain.vue +161 -77
- package/components/TableV3.vue +61 -66
- package/components/UnitMain.vue +9 -17
- package/components/VehicleManagement.vue +20 -9
- package/components/VisitorForm.vue +294 -78
- package/components/VisitorManagement.vue +221 -71
- package/components/VisitorSocketPopUp.vue +56 -2
- package/components/WorkOrder/Main.vue +42 -51
- package/composables/useComment.ts +3 -3
- package/composables/useHidAmico.ts +151 -0
- package/composables/useHidNavigation.ts +0 -7
- package/composables/useLocalAuth.ts +9 -36
- package/composables/useNotificationPreference.ts +87 -0
- package/composables/useOnlineFormPages.ts +2 -0
- package/composables/useOrg.ts +1 -1
- package/composables/useRole.ts +22 -0
- package/composables/useServiceProviderInvitation.ts +145 -0
- package/composables/useSipWebPhone.ts +336 -0
- package/composables/useSiteSettings.ts +50 -0
- package/composables/useThemePreference.ts +63 -0
- package/composables/useVisitor.ts +6 -1
- package/composables/useVisitorSocket.ts +26 -0
- package/composables/useWebUsb.ts +127 -37
- package/nuxt.config.ts +31 -0
- package/package.json +4 -1
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/pages/notification-settings.vue +19 -0
- package/plugins/secure-member.client.ts +21 -56
- package/plugins/vuetify.ts +68 -9
- package/test/visitor-socket.test.mjs +36 -0
- package/types/org.d.ts +2 -0
- package/types/site.d.ts +71 -0
- package/utils/camera-wall.test.ts +571 -0
- package/utils/camera-wall.ts +926 -0
- package/utils/nav-sections.test.ts +190 -0
- package/utils/nav-sections.ts +151 -0
- package/utils/status.test.ts +77 -0
- package/utils/status.ts +90 -0
- package/utils/theme.test.ts +534 -0
- package/utils/theme.ts +294 -0
- package/components/HidIdentityMapping.vue +0 -975
- package/middleware/member.ts +0 -4
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +0 -23
|
@@ -247,6 +247,40 @@
|
|
|
247
247
|
{{ resolveText(block.text) }}
|
|
248
248
|
</p>
|
|
249
249
|
|
|
250
|
+
<!-- ORDERED LIST -->
|
|
251
|
+
<div v-else-if="block.type === 'ordered-list'" class="mb-3">
|
|
252
|
+
<div
|
|
253
|
+
v-if="block.title"
|
|
254
|
+
class="text-body-2 font-weight-bold mb-1"
|
|
255
|
+
style="text-decoration: underline; text-underline-offset: 3px"
|
|
256
|
+
>{{ block.title }}</div>
|
|
257
|
+
<ol style="padding-left: 22px; margin: 0">
|
|
258
|
+
<li
|
|
259
|
+
v-for="(item, itemIdx) in block.items"
|
|
260
|
+
:key="itemIdx"
|
|
261
|
+
class="text-body-2 mb-1"
|
|
262
|
+
style="line-height: 1.55; color: #333"
|
|
263
|
+
>{{ item }}</li>
|
|
264
|
+
</ol>
|
|
265
|
+
</div>
|
|
266
|
+
|
|
267
|
+
<!-- UNORDERED LIST -->
|
|
268
|
+
<div v-else-if="block.type === 'unordered-list'" class="mb-3">
|
|
269
|
+
<div
|
|
270
|
+
v-if="block.title"
|
|
271
|
+
class="text-body-2 font-weight-bold mb-1"
|
|
272
|
+
style="text-decoration: underline; text-underline-offset: 3px"
|
|
273
|
+
>{{ block.title }}</div>
|
|
274
|
+
<ul style="padding-left: 22px; margin: 0">
|
|
275
|
+
<li
|
|
276
|
+
v-for="(item, itemIdx) in block.items"
|
|
277
|
+
:key="itemIdx"
|
|
278
|
+
class="text-body-2 mb-1"
|
|
279
|
+
style="line-height: 1.55; color: #333"
|
|
280
|
+
>{{ item }}</li>
|
|
281
|
+
</ul>
|
|
282
|
+
</div>
|
|
283
|
+
|
|
250
284
|
<!-- DIVIDER -->
|
|
251
285
|
<v-divider v-else-if="block.type === 'divider'" class="my-4" />
|
|
252
286
|
|
|
@@ -433,6 +467,7 @@ const props = defineProps({
|
|
|
433
467
|
approvalMode: { type: Boolean, default: false },
|
|
434
468
|
initialManagementValues: { type: Object as PropType<Record<string, string>>, default: () => ({}) },
|
|
435
469
|
formType: { type: String, default: "" },
|
|
470
|
+
paymentAmount: { type: Number, default: 0 },
|
|
436
471
|
/** Optional custom submit handler. Receives the collected form values.
|
|
437
472
|
* If omitted the component submits to /api/online-forms automatically. */
|
|
438
473
|
submitFn: {
|
|
@@ -596,15 +631,8 @@ const submissionStatusColor = computed(() => {
|
|
|
596
631
|
}
|
|
597
632
|
});
|
|
598
633
|
|
|
599
|
-
const PAYMENT_REQUIRED_FORM_TYPES = new Set([
|
|
600
|
-
"Application for Electronic Access Card",
|
|
601
|
-
"Application for Booking of BBQ Pit",
|
|
602
|
-
"Application for Moving In/Out & Delivery",
|
|
603
|
-
"Application for Main Entrance RFID Transponder",
|
|
604
|
-
]);
|
|
605
|
-
|
|
606
634
|
const requiresPayment = computed(() => {
|
|
607
|
-
if (
|
|
635
|
+
if (props.paymentAmount <= 0) return false;
|
|
608
636
|
if (props.formType === "Application for Main Entrance RFID Transponder") {
|
|
609
637
|
return !!(props.initialValues.reasonLost || props.initialValues.reasonDamaged);
|
|
610
638
|
}
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
rounded="lg"
|
|
23
23
|
:loading="loading"
|
|
24
24
|
>
|
|
25
|
-
<v-toolbar density="compact"
|
|
25
|
+
<v-toolbar density="compact" class="px-3 border-b-sm" color="transparent">
|
|
26
26
|
<template #prepend>
|
|
27
27
|
<v-btn
|
|
28
28
|
fab
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
>
|
|
89
89
|
|
|
90
90
|
<!-- Create Dialog -->
|
|
91
|
-
<v-dialog v-model="createDialog" width="
|
|
91
|
+
<v-dialog v-model="createDialog" width="720" persistent>
|
|
92
92
|
<OnlineFormConfigurationForm
|
|
93
93
|
mode="add"
|
|
94
94
|
@cancel="createDialog = false"
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
</v-dialog>
|
|
98
98
|
|
|
99
99
|
<!-- Edit Dialog -->
|
|
100
|
-
<v-dialog v-model="editDialog" width="
|
|
100
|
+
<v-dialog v-model="editDialog" width="720" persistent>
|
|
101
101
|
<OnlineFormConfigurationForm
|
|
102
102
|
mode="edit"
|
|
103
103
|
@cancel="editDialog = false"
|
|
@@ -113,6 +113,7 @@
|
|
|
113
113
|
:definition="selectedFormDefinition"
|
|
114
114
|
:site-id="siteId"
|
|
115
115
|
:org-id="orgId"
|
|
116
|
+
:payment-amount="selectedPaymentAmount"
|
|
116
117
|
readonly
|
|
117
118
|
@cancel="previewDialog = false"
|
|
118
119
|
>
|
|
@@ -336,6 +337,9 @@ const selectedFormDefinition = ref<any>(null);
|
|
|
336
337
|
const deleteLoading = ref(false);
|
|
337
338
|
const confirmDialog = ref(false);
|
|
338
339
|
const selectedOnlineFormId = ref<string | null>(null);
|
|
340
|
+
const selectedPaymentAmount = computed(() =>
|
|
341
|
+
Number(selectedOnlineForm.value?.fields?.paymentAmount ?? 0)
|
|
342
|
+
);
|
|
339
343
|
|
|
340
344
|
function parseFieldsToInputs(fields: Record<string, string>): any[] {
|
|
341
345
|
return Object.keys(fields)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
THE PAGE TITLE ROW.
|
|
3
|
+
|
|
4
|
+
The design opens EVERY screen with this row: the page's name at 22px/800,
|
|
5
|
+
and the screen's primary action on the right of the same line. The product
|
|
6
|
+
has never had it - the page name lived only in the browser tab and in the
|
|
7
|
+
navigation rail - which is the single most visible difference between the
|
|
8
|
+
staging site and the handoff.
|
|
9
|
+
|
|
10
|
+
It is a label and a slot. It fetches nothing, decides nothing and navigates
|
|
11
|
+
nowhere; the action passed into it is the screen's own button.
|
|
12
|
+
-->
|
|
13
|
+
<template>
|
|
14
|
+
<div class="app-page-head">
|
|
15
|
+
<!-- A caller with actions but no page name (the shared table card, when the
|
|
16
|
+
screen has not given it a title) gets just the action group, at the
|
|
17
|
+
left, rather than an empty heading holding a line of blank space. -->
|
|
18
|
+
<div v-if="title || $slots.title || subtitle || $slots.subtitle">
|
|
19
|
+
<h1 class="app-page-head__title">
|
|
20
|
+
<slot name="title">{{ title }}</slot>
|
|
21
|
+
</h1>
|
|
22
|
+
|
|
23
|
+
<!-- The design's second line: a location, a count, a date range. Only
|
|
24
|
+
drawn when a screen supplies one. -->
|
|
25
|
+
<div v-if="subtitle || $slots.subtitle" class="app-page-head__sub">
|
|
26
|
+
<v-icon v-if="subtitleIcon" :icon="subtitleIcon" size="15" />
|
|
27
|
+
<slot name="subtitle">{{ subtitle }}</slot>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
|
|
31
|
+
<div v-if="$slots.actions" class="app-page-head__actions">
|
|
32
|
+
<slot name="actions" />
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup lang="ts">
|
|
38
|
+
defineProps({
|
|
39
|
+
title: { type: String, default: "" },
|
|
40
|
+
subtitle: { type: String, default: "" },
|
|
41
|
+
subtitleIcon: { type: String, default: "" },
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
@@ -8,8 +8,8 @@
|
|
|
8
8
|
:height="40"
|
|
9
9
|
text="Scan QR Code"
|
|
10
10
|
class="text-capitalize"
|
|
11
|
-
disabled
|
|
12
11
|
prepend-icon="mdi-qrcode"
|
|
12
|
+
@click="handleScanQRCode"
|
|
13
13
|
/>
|
|
14
14
|
<v-autocomplete
|
|
15
15
|
v-model="selectedPass"
|
|
@@ -88,6 +88,11 @@
|
|
|
88
88
|
</v-card-text>
|
|
89
89
|
</v-card>
|
|
90
90
|
</v-row>
|
|
91
|
+
<visitor-pass-key-q-r-scanner
|
|
92
|
+
:dialog="dialog.scanQRCode"
|
|
93
|
+
v-model:scannedValue="scannedValue"
|
|
94
|
+
@close-dialog="dialog.scanQRCode = false"
|
|
95
|
+
/>
|
|
91
96
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
92
97
|
</template>
|
|
93
98
|
|
|
@@ -95,46 +100,36 @@
|
|
|
95
100
|
import type { PropType } from "vue";
|
|
96
101
|
import type { ValidationRule } from "vuetify/lib/types.mjs";
|
|
97
102
|
import usePassKey from "../composables/usePassKey";
|
|
98
|
-
import
|
|
99
|
-
import
|
|
103
|
+
import { errorConverter } from "../utils/data";
|
|
104
|
+
import VisitorPassKeyQRScanner from "./VisitorPassKeyQRScanner.vue";
|
|
100
105
|
|
|
101
|
-
|
|
102
|
-
passRules
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
type: Array,
|
|
124
|
-
default: [],
|
|
125
|
-
},
|
|
126
|
-
hideKeys: {
|
|
127
|
-
type: Boolean,
|
|
128
|
-
default: false,
|
|
129
|
-
},
|
|
130
|
-
clearable: {
|
|
131
|
-
type: Boolean,
|
|
132
|
-
default: false,
|
|
133
|
-
},
|
|
106
|
+
type PassInformationProps = {
|
|
107
|
+
passRules?: ValidationRule[];
|
|
108
|
+
countRules?: ValidationRule[];
|
|
109
|
+
site: string;
|
|
110
|
+
type: TVisitorType;
|
|
111
|
+
contractorType?: string;
|
|
112
|
+
passKeys?: unknown[];
|
|
113
|
+
hideKeys?: boolean;
|
|
114
|
+
clearable?: boolean;
|
|
115
|
+
pass?: TPassKeyPayload[];
|
|
116
|
+
keys?: TPassKeyPayload[];
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const props = withDefaults(defineProps<PassInformationProps>(), {
|
|
120
|
+
passRules: [],
|
|
121
|
+
countRules: [],
|
|
122
|
+
contractorType: "",
|
|
123
|
+
passKeys: [],
|
|
124
|
+
hideKeys: false,
|
|
125
|
+
clearable: false,
|
|
126
|
+
pass: [],
|
|
127
|
+
keys: [],
|
|
134
128
|
});
|
|
135
129
|
|
|
136
|
-
const
|
|
137
|
-
const
|
|
130
|
+
const emit = defineEmits(["update:pass", "update:keys"]);
|
|
131
|
+
const pass = ref<TPassKeyPayload[]>(props.pass);
|
|
132
|
+
const keys = ref<TPassKeyPayload[]>(props.keys);
|
|
138
133
|
const selectedPass = ref<string>("");
|
|
139
134
|
const selectedKeys = ref<string[]>([]);
|
|
140
135
|
const passInput = ref("");
|
|
@@ -144,6 +139,11 @@ const keyItems = ref<any[]>([]);
|
|
|
144
139
|
const selectedType = ref<"qr-pass" | "nfc-card">();
|
|
145
140
|
const count = ref(1);
|
|
146
141
|
|
|
142
|
+
const dialog = reactive({
|
|
143
|
+
scanQRCode: false,
|
|
144
|
+
});
|
|
145
|
+
const scannedValue = ref("");
|
|
146
|
+
|
|
147
147
|
const { getPassKeysByPageSearch } = usePassKey();
|
|
148
148
|
|
|
149
149
|
const typeItems = [
|
|
@@ -179,6 +179,23 @@ function showMessage(msg: string, color: string) {
|
|
|
179
179
|
messageSnackbar.value = true;
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
+
function getItemLabel(item: any) {
|
|
183
|
+
return (item?.prefixAndName ?? item?.raw?.prefixAndName ?? "")
|
|
184
|
+
.toString()
|
|
185
|
+
.trim();
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function handleScanQRCode() {
|
|
189
|
+
scannedValue.value = "";
|
|
190
|
+
dialog.scanQRCode = true;
|
|
191
|
+
if (passItems.value.length === 0) {
|
|
192
|
+
fetchPasses();
|
|
193
|
+
}
|
|
194
|
+
if (!props.hideKeys && keyItems.value.length === 0) {
|
|
195
|
+
fetchKeys();
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
182
199
|
const isPassListLoading = ref(false);
|
|
183
200
|
|
|
184
201
|
async function fetchPasses() {
|
|
@@ -207,7 +224,7 @@ const isKeyListLoading = ref(false);
|
|
|
207
224
|
async function fetchKeys() {
|
|
208
225
|
isKeyListLoading.value = true;
|
|
209
226
|
try {
|
|
210
|
-
const
|
|
227
|
+
const result: any = await getPassKeysByPageSearch({
|
|
211
228
|
page: 1,
|
|
212
229
|
sites: [props.site],
|
|
213
230
|
limit: 10000,
|
|
@@ -223,7 +240,7 @@ async function fetchKeys() {
|
|
|
223
240
|
return false;
|
|
224
241
|
});
|
|
225
242
|
|
|
226
|
-
const filteredArray =
|
|
243
|
+
const filteredArray = result.items.filter((item: any) => {
|
|
227
244
|
if (Array.isArray(props?.passKeys) && props.passKeys.length) {
|
|
228
245
|
return !props.passKeys.includes(item._id);
|
|
229
246
|
}
|
|
@@ -231,18 +248,60 @@ async function fetchKeys() {
|
|
|
231
248
|
});
|
|
232
249
|
keyItems.value = [...previouslySelectedKeys, ...filteredArray];
|
|
233
250
|
} catch (error) {
|
|
234
|
-
showMessage(errorConverter(error
|
|
251
|
+
showMessage(errorConverter(error), "error");
|
|
235
252
|
} finally {
|
|
236
253
|
isKeyListLoading.value = false;
|
|
237
254
|
}
|
|
238
255
|
}
|
|
239
256
|
|
|
240
257
|
watch(selectedPass, (newVal) => {
|
|
241
|
-
|
|
258
|
+
const updated = newVal ? [{ keyId: newVal }] : [];
|
|
259
|
+
pass.value = updated;
|
|
260
|
+
emit("update:pass", updated);
|
|
242
261
|
});
|
|
243
262
|
|
|
244
263
|
watch(selectedKeys, (newVal) => {
|
|
245
|
-
|
|
264
|
+
const updated = newVal.map((key) => ({ keyId: key }));
|
|
265
|
+
keys.value = updated;
|
|
266
|
+
emit("update:keys", updated);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
watch(scannedValue, async (newValue) => {
|
|
270
|
+
if (!newValue) return;
|
|
271
|
+
|
|
272
|
+
const scanned = String(newValue).trim();
|
|
273
|
+
if (!scanned) return;
|
|
274
|
+
|
|
275
|
+
if (passItems.value.length === 0) {
|
|
276
|
+
await fetchPasses();
|
|
277
|
+
}
|
|
278
|
+
if (!props.hideKeys && keyItems.value.length === 0) {
|
|
279
|
+
await fetchKeys();
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const matchedPass = passItems.value.find((item) => {
|
|
283
|
+
return getItemLabel(item).toLowerCase() === scanned.toLowerCase();
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
if (matchedPass && matchedPass._id) {
|
|
287
|
+
selectedPass.value = matchedPass._id;
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!props.hideKeys) {
|
|
292
|
+
const matchedKey = keyItems.value.find((item) => {
|
|
293
|
+
return getItemLabel(item).toLowerCase() === scanned.toLowerCase();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
if (matchedKey && matchedKey._id) {
|
|
297
|
+
if (!selectedKeys.value.includes(matchedKey._id)) {
|
|
298
|
+
selectedKeys.value = [...selectedKeys.value, matchedKey._id];
|
|
299
|
+
}
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
showMessage(`Scanned pass or key "${scanned}" not found`, "error");
|
|
246
305
|
});
|
|
247
306
|
|
|
248
307
|
//prevent negative value;
|
|
@@ -254,10 +313,10 @@ watch(count, (newCount) => {
|
|
|
254
313
|
|
|
255
314
|
onMounted(() => {
|
|
256
315
|
if (pass.value.length > 0) {
|
|
257
|
-
selectedPass.value = pass.value[0]
|
|
316
|
+
selectedPass.value = pass.value[0]?.keyId ?? "";
|
|
258
317
|
}
|
|
259
318
|
if (keys.value.length > 0) {
|
|
260
|
-
selectedKeys.value = keys.value.map((k) => k.keyId);
|
|
319
|
+
selectedKeys.value = keys.value.map((k) => k.keyId ?? "");
|
|
261
320
|
}
|
|
262
321
|
});
|
|
263
322
|
</script>
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
|
|
34
34
|
<qrcode-vue
|
|
35
35
|
v-if="templateQrCode.CCFrontQREnabled"
|
|
36
|
-
:value="
|
|
36
|
+
:value="getPassNumberValue(card.number)"
|
|
37
37
|
:size="templateQrCode.CCFrontQRCodeSize"
|
|
38
38
|
level="H"
|
|
39
39
|
:style="{
|
|
@@ -53,9 +53,7 @@
|
|
|
53
53
|
}"
|
|
54
54
|
class="position-absolute text-center"
|
|
55
55
|
>
|
|
56
|
-
Pass Number: {{
|
|
57
|
-
+printRangeStart + +card.number - 1
|
|
58
|
-
}}
|
|
56
|
+
Pass Number: {{ getPassNumberValue(card.number) }}
|
|
59
57
|
</div>
|
|
60
58
|
</v-card>
|
|
61
59
|
|
|
@@ -74,7 +72,7 @@
|
|
|
74
72
|
|
|
75
73
|
<qrcode-vue
|
|
76
74
|
v-if="templateQrCode.CCBackQREnabled"
|
|
77
|
-
:value="
|
|
75
|
+
:value="getPassNumberValue(card.number)"
|
|
78
76
|
:size="templateQrCode.CCBackQRCodeSize"
|
|
79
77
|
level="H"
|
|
80
78
|
:style="{
|
|
@@ -93,9 +91,7 @@
|
|
|
93
91
|
}"
|
|
94
92
|
class="position-absolute text-center"
|
|
95
93
|
>
|
|
96
|
-
Pass Number: {{
|
|
97
|
-
+printRangeStart + +card.number - 1
|
|
98
|
-
}}
|
|
94
|
+
Pass Number: {{ getPassNumberValue(card.number) }}
|
|
99
95
|
</div>
|
|
100
96
|
</v-card>
|
|
101
97
|
</v-col>
|
|
@@ -113,11 +109,22 @@ const props = defineProps({
|
|
|
113
109
|
paginatedCardData: Array,
|
|
114
110
|
templateQrCode: Object,
|
|
115
111
|
prefix: String,
|
|
116
|
-
printRangeStart: Number,
|
|
117
|
-
});
|
|
118
|
-
onMounted(() => {
|
|
119
|
-
console.log("templateQrCode 11", props.templateQrCode.CCOrientation);
|
|
112
|
+
printRangeStart: [String, Number],
|
|
120
113
|
});
|
|
114
|
+
|
|
115
|
+
const getPassNumberValue = (position) => {
|
|
116
|
+
const rawStartValue = `${props.printRangeStart ?? ""}`.trim();
|
|
117
|
+
const startMatch = rawStartValue.match(/(\d+)$/);
|
|
118
|
+
const startValue = startMatch?.[1] ?? "0";
|
|
119
|
+
const startNumber = Number(startValue);
|
|
120
|
+
const sequenceNumber = startNumber + Number(position) - 1;
|
|
121
|
+
const cleanPrefix = `${props.prefix ?? ""}`.replace(/\d+$/, "");
|
|
122
|
+
const width = startValue.length;
|
|
123
|
+
const formattedSequence =
|
|
124
|
+
width > 0 ? `${sequenceNumber}`.padStart(width, "0") : `${sequenceNumber}`;
|
|
125
|
+
|
|
126
|
+
return `${cleanPrefix}${formattedSequence}`;
|
|
127
|
+
};
|
|
121
128
|
</script>
|
|
122
129
|
|
|
123
130
|
<style scoped>
|