@7365admin1/layer-common 3.2.2-staging.79 → 3.2.2-staging.80
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.
|
@@ -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>
|
|
@@ -60,7 +60,9 @@ const props = defineProps({
|
|
|
60
60
|
|
|
61
61
|
const { standardFormatDate } = useUtils();
|
|
62
62
|
|
|
63
|
-
const { validateVisitorQrCode } = useVisitor();
|
|
63
|
+
const { validateVisitorQrCode, getVisitors } = useVisitor();
|
|
64
|
+
|
|
65
|
+
const { getPassKeysByPageSearch } = usePassKey();
|
|
64
66
|
|
|
65
67
|
const emits = defineEmits([
|
|
66
68
|
"closeDialog",
|
|
@@ -97,8 +99,6 @@ const handleScanVisitorQRCode = async () => {
|
|
|
97
99
|
showNotFoundMessage.value = false;
|
|
98
100
|
// Automatically navigate to the scanned URL if it's a valid link
|
|
99
101
|
if (isValidUrl(qrCodeMessage)) {
|
|
100
|
-
console.log("QR Code value", qrCodeMessage);
|
|
101
|
-
console.log("Last _id", qrCodeMessage.split("/").pop());
|
|
102
102
|
// get the last _id in the url
|
|
103
103
|
const id = qrCodeMessage.split("/").pop();
|
|
104
104
|
// check if it's a valid mongodb _id
|
|
@@ -108,7 +108,6 @@ const handleScanVisitorQRCode = async () => {
|
|
|
108
108
|
props.site as string,
|
|
109
109
|
id as string
|
|
110
110
|
);
|
|
111
|
-
console.log("validateVisitorQrCode", response);
|
|
112
111
|
if (response.errorMessage) {
|
|
113
112
|
showNotFoundMessage.value = true;
|
|
114
113
|
errorMessage.value =
|
|
@@ -122,9 +121,38 @@ const handleScanVisitorQRCode = async () => {
|
|
|
122
121
|
emits("openVisitorDataFromScannedQrCodeDialog", response);
|
|
123
122
|
}
|
|
124
123
|
} else {
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
124
|
+
const result: any = await getPassKeysByPageSearch({
|
|
125
|
+
sites: [props.site as string],
|
|
126
|
+
search: qrCodeMessage,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
if (result.items?.length < 1) {
|
|
130
|
+
showMessage(`Pass or Key ${qrCodeMessage} not found.`, "error");
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const data: any = await getVisitors({
|
|
135
|
+
site: props.site as string,
|
|
136
|
+
passOrKey: {
|
|
137
|
+
keyId: result.items[0]._id,
|
|
138
|
+
type: result.items[0].passType == "pass-key" ? "key" : "pass",
|
|
139
|
+
},
|
|
140
|
+
checkedOut: false,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
if (data.items?.length < 1) {
|
|
144
|
+
showMessage(
|
|
145
|
+
`There are no unchecked-out visitor transactions for the scanned ${
|
|
146
|
+
result.items[0].passType == "pass-key" ? "Key" : "Pass"
|
|
147
|
+
} ${qrCodeMessage}.`,
|
|
148
|
+
"error"
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
closeDialog();
|
|
155
|
+
emits("openVisitorDataFromScannedQrCodeDialog", data.items[0]);
|
|
128
156
|
}
|
|
129
157
|
|
|
130
158
|
// window.location.href = qrCodeMessage;
|
|
@@ -92,6 +92,7 @@ export default function () {
|
|
|
92
92
|
checkedOut?: boolean;
|
|
93
93
|
plateNumber?: string;
|
|
94
94
|
tab?: string;
|
|
95
|
+
passOrKey?: string;
|
|
95
96
|
};
|
|
96
97
|
|
|
97
98
|
type SearchUncheckedOutUnregisteredVisitorParams = {
|
|
@@ -130,6 +131,7 @@ export default function () {
|
|
|
130
131
|
plateNumber = "",
|
|
131
132
|
checkedOut,
|
|
132
133
|
tab,
|
|
134
|
+
passOrKey,
|
|
133
135
|
}: GetVisitorsParams = {}) {
|
|
134
136
|
const query = Object.fromEntries(
|
|
135
137
|
Object.entries({
|
|
@@ -146,7 +148,10 @@ export default function () {
|
|
|
146
148
|
plateNumber,
|
|
147
149
|
order,
|
|
148
150
|
tab,
|
|
149
|
-
|
|
151
|
+
passOrKey,
|
|
152
|
+
}).filter(
|
|
153
|
+
([, value]) => value !== undefined && value !== null && value !== ""
|
|
154
|
+
)
|
|
150
155
|
);
|
|
151
156
|
|
|
152
157
|
return await useNuxtApp().$api<Record<string, any>>(
|