@7365admin1/layer-common 1.11.19 → 1.11.21
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 +12 -0
- package/components/AddPassKeyToVisitor.vue +114 -53
- package/components/AreaChecklistHistoryMain.vue +29 -1
- package/components/BulletinBoardView.vue +158 -16
- package/components/CleaningScheduleMain.vue +2 -2
- package/components/Input/DateTimePicker.vue +45 -9
- package/components/OvernightParkingAvailability.vue +291 -155
- package/components/ScanVisitorQRCode.vue +157 -0
- package/components/ScheduleAreaMain.vue +6 -6
- package/components/SiteSettings.vue +303 -243
- package/components/TableMain.vue +72 -21
- package/components/VisitorManagement.vue +656 -234
- package/composables/useFeedback.ts +1 -1
- package/composables/useOrg.ts +16 -0
- package/composables/useSiteSettings.ts +30 -1
- package/package.json +1 -1
- package/plugins/html5-qrcode.client.js +8 -0
- package/types/overnight-parking.d.ts +3 -2
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-dialog
|
|
3
|
+
v-model="props.dialog"
|
|
4
|
+
transition="dialog-bottom-transition"
|
|
5
|
+
width="700"
|
|
6
|
+
max-width="700"
|
|
7
|
+
persistent
|
|
8
|
+
@after-enter="handleScanVisitorQRCode"
|
|
9
|
+
>
|
|
10
|
+
<v-container class="d-flex justify-center" max-height="90vh">
|
|
11
|
+
<!-- QR code reader container -->
|
|
12
|
+
<v-card
|
|
13
|
+
elevation="2"
|
|
14
|
+
class="d-flex flex-column align-center pa-2 w-100 h-100"
|
|
15
|
+
>
|
|
16
|
+
<v-toolbar>
|
|
17
|
+
<v-card-title class="text-h5"> Scan QR Code </v-card-title>
|
|
18
|
+
<v-spacer />
|
|
19
|
+
<v-btn
|
|
20
|
+
color="grey-darken-1"
|
|
21
|
+
icon="mdi-close"
|
|
22
|
+
@click="closeDialog()"
|
|
23
|
+
/>
|
|
24
|
+
</v-toolbar>
|
|
25
|
+
|
|
26
|
+
<div
|
|
27
|
+
id="reader"
|
|
28
|
+
class="d-flex justify-center align-center w-100 h-100"
|
|
29
|
+
style="height: calc(100vh - 64px)"
|
|
30
|
+
>
|
|
31
|
+
<!-- The QR code scanner box -->
|
|
32
|
+
<!-- This is where the QR code scanner will be displayed -->
|
|
33
|
+
</div>
|
|
34
|
+
<!-- Show Error Messages -->
|
|
35
|
+
<div v-if="showNotFoundMessage" class="text-error mt-8">
|
|
36
|
+
<p>{{ errorMessage }}</p>
|
|
37
|
+
</div>
|
|
38
|
+
<!-- Switch camera button inside the reader box -->
|
|
39
|
+
<v-btn color="primary" icon class="mt-4" @click="switchCamera()">
|
|
40
|
+
<v-icon icon="mdi-camera-switch" large />
|
|
41
|
+
</v-btn>
|
|
42
|
+
</v-card>
|
|
43
|
+
</v-container>
|
|
44
|
+
</v-dialog>
|
|
45
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
46
|
+
</template>
|
|
47
|
+
<script setup lang="ts">
|
|
48
|
+
const { $Html5Qrcode }: any = useNuxtApp();
|
|
49
|
+
|
|
50
|
+
const props = defineProps({
|
|
51
|
+
dialog: {
|
|
52
|
+
type: Boolean,
|
|
53
|
+
default: false,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const emit = defineEmits(["closeDialog"]);
|
|
58
|
+
|
|
59
|
+
const message = ref("");
|
|
60
|
+
const messageColor = ref("");
|
|
61
|
+
const messageSnackbar = ref(false);
|
|
62
|
+
|
|
63
|
+
function showMessage(msg: string, color: string) {
|
|
64
|
+
message.value = msg;
|
|
65
|
+
messageColor.value = color;
|
|
66
|
+
messageSnackbar.value = true;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const html5QrCodeInstance = ref<any>(null);
|
|
70
|
+
const cameraFacingMode = ref<string>("environment");
|
|
71
|
+
|
|
72
|
+
const showNotFoundMessage = ref<boolean>(false);
|
|
73
|
+
const errorMessage = ref<string>("");
|
|
74
|
+
|
|
75
|
+
const handleScanVisitorQRCode = async () => {
|
|
76
|
+
console.log("startScanner cameraFacingMode", cameraFacingMode.value);
|
|
77
|
+
console.log("STARTING QR Code Scanner...");
|
|
78
|
+
if (html5QrCodeInstance.value) return;
|
|
79
|
+
const config = { fps: 10, qrbox: { width: 250, height: 250 } };
|
|
80
|
+
html5QrCodeInstance.value = new $Html5Qrcode("reader");
|
|
81
|
+
html5QrCodeInstance.value
|
|
82
|
+
.start(
|
|
83
|
+
{ facingMode: cameraFacingMode.value },
|
|
84
|
+
config,
|
|
85
|
+
async (qrCodeMessage: string) => {
|
|
86
|
+
showNotFoundMessage.value = false;
|
|
87
|
+
// Automatically navigate to the scanned URL if it's a valid link
|
|
88
|
+
if (isValidUrl(qrCodeMessage)) {
|
|
89
|
+
console.log(`QR Code value: ${qrCodeMessage}`);
|
|
90
|
+
window.location.href = qrCodeMessage;
|
|
91
|
+
} else {
|
|
92
|
+
showNotFoundMessage.value = true;
|
|
93
|
+
errorMessage.value = "Invalid url";
|
|
94
|
+
showMessage("Invalid url", "error");
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
(msg: string) => {
|
|
98
|
+
if (msg === "QR Code no longer in front") {
|
|
99
|
+
showNotFoundMessage.value = true;
|
|
100
|
+
errorMessage.value = msg;
|
|
101
|
+
showMessage(msg, "error");
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
)
|
|
105
|
+
.catch((err: any) => {
|
|
106
|
+
showMessage(`Unable to start scanning, error: ${err}`, "error");
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const isValidUrl = (string: string) => {
|
|
111
|
+
try {
|
|
112
|
+
new URL(string);
|
|
113
|
+
return true;
|
|
114
|
+
} catch (_) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const switchCamera = async () => {
|
|
120
|
+
await stopScanner();
|
|
121
|
+
cameraFacingMode.value =
|
|
122
|
+
cameraFacingMode.value === "environment" ? "user" : "environment";
|
|
123
|
+
showMessage(
|
|
124
|
+
`Switched to ${
|
|
125
|
+
cameraFacingMode.value === "environment" ? "Back" : "Front"
|
|
126
|
+
} Camera`,
|
|
127
|
+
"info"
|
|
128
|
+
);
|
|
129
|
+
handleScanVisitorQRCode();
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
const closeDialog = () => {
|
|
133
|
+
emit("closeDialog");
|
|
134
|
+
stopScanner();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const stopScanner = () => {
|
|
138
|
+
return new Promise<void>((resolve, reject) => {
|
|
139
|
+
if (html5QrCodeInstance.value) {
|
|
140
|
+
html5QrCodeInstance.value
|
|
141
|
+
.stop()
|
|
142
|
+
.then(() => {
|
|
143
|
+
html5QrCodeInstance.value.clear();
|
|
144
|
+
html5QrCodeInstance.value = null;
|
|
145
|
+
showNotFoundMessage.value = false;
|
|
146
|
+
resolve(); // Resolve the promise when the scanner is fully stopped
|
|
147
|
+
})
|
|
148
|
+
.catch((err: any) => {
|
|
149
|
+
showMessage("Unable to stop scanning.", err);
|
|
150
|
+
reject(err); // Reject the promise if there's an error
|
|
151
|
+
});
|
|
152
|
+
} else {
|
|
153
|
+
resolve(); // Resolve immediately if no scanner instance exists
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
</script>
|
|
@@ -208,12 +208,12 @@ const isScheduleClosed = computed(
|
|
|
208
208
|
|
|
209
209
|
const items = ref<Array<Record<string, any>>>([]);
|
|
210
210
|
|
|
211
|
-
const statusFilter = ref<TScheduleAreaStatus>("
|
|
211
|
+
const statusFilter = ref<TScheduleAreaStatus>("all");
|
|
212
212
|
const statusOptions = [
|
|
213
|
-
{ title: "All", value: "
|
|
214
|
-
{ title: "Open", value: "
|
|
215
|
-
{ title: "Ongoing", value: "
|
|
216
|
-
{ title: "Completed", value: "
|
|
213
|
+
{ title: "All", value: "all" },
|
|
214
|
+
{ title: "Open", value: "open" },
|
|
215
|
+
{ title: "Ongoing", value: "ongoing" },
|
|
216
|
+
{ title: "Completed", value: "completed" },
|
|
217
217
|
];
|
|
218
218
|
const areaTypeFilter = ref<TAreaType>("all");
|
|
219
219
|
const typeOptions = [
|
|
@@ -235,7 +235,7 @@ const {
|
|
|
235
235
|
getScheduleAreas({
|
|
236
236
|
page: page.value,
|
|
237
237
|
scheduleAreaId: props.scheduleAreaId,
|
|
238
|
-
status: statusFilter.value === "
|
|
238
|
+
status: statusFilter.value === "all" ? undefined : statusFilter.value,
|
|
239
239
|
type: areaTypeFilter.value === "all" ? undefined : areaTypeFilter.value,
|
|
240
240
|
serviceType: props.serviceType,
|
|
241
241
|
}),
|