@7365admin1/layer-common 1.11.22 → 1.11.23

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.
@@ -0,0 +1,138 @@
1
+ <template>
2
+ <v-dialog v-model="props.dialog" transition="dialog-bottom-transition" width="700" max-width="700" persistent
3
+ @after-enter="handleScanVisitorQRCode">
4
+ <v-container class="d-flex justify-center" max-height="90vh">
5
+ <!-- QR code reader container -->
6
+ <v-card elevation="2" class="d-flex flex-column align-center pa-2 w-100 h-100">
7
+ <v-toolbar>
8
+ <v-card-title class="text-h5"> Scan QR Code </v-card-title>
9
+ <v-spacer />
10
+ <v-btn color="grey-darken-1" icon="mdi-close" @click="closeDialog()" />
11
+ </v-toolbar>
12
+
13
+ <div id="reader" class="d-flex justify-center align-center w-100 h-100"
14
+ style="height: calc(100vh - 64px)">
15
+ <!-- The QR code scanner box -->
16
+ <!-- This is where the QR code scanner will be displayed -->
17
+ </div>
18
+ <!-- Show Error Messages -->
19
+ <div v-if="showNotFoundMessage" class="text-error mt-8">
20
+ <p>{{ errorMessage }}</p>
21
+ </div>
22
+ <!-- Switch camera button inside the reader box -->
23
+ <v-btn color="primary" icon class="mt-4" @click="switchCamera()">
24
+ <v-icon icon="mdi-camera-switch" large />
25
+ </v-btn>
26
+ </v-card>
27
+ </v-container>
28
+ </v-dialog>
29
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
30
+ </template>
31
+ <script setup lang="ts">
32
+ const { $Html5Qrcode }: any = useNuxtApp();
33
+
34
+ const props = defineProps({
35
+ dialog: {
36
+ type: Boolean,
37
+ default: false,
38
+ },
39
+ });
40
+
41
+ const scannedValue = defineModel<string>("scannedValue", {
42
+ default: "",
43
+ });
44
+
45
+ const emit = defineEmits(["closeDialog"]);
46
+
47
+ const message = ref("");
48
+ const messageColor = ref("");
49
+ const messageSnackbar = ref(false);
50
+
51
+ function showMessage(msg: string, color: string) {
52
+ message.value = msg;
53
+ messageColor.value = color;
54
+ messageSnackbar.value = true;
55
+ }
56
+
57
+ const html5QrCodeInstance = ref<any>(null);
58
+ const cameraFacingMode = ref<string>("environment");
59
+
60
+ const showNotFoundMessage = ref<boolean>(false);
61
+ const errorMessage = ref<string>("");
62
+
63
+ const handleScanVisitorQRCode = async () => {
64
+ if (html5QrCodeInstance.value) return;
65
+ const config = { fps: 10, qrbox: { width: 250, height: 250 } };
66
+ html5QrCodeInstance.value = new $Html5Qrcode("reader");
67
+ html5QrCodeInstance.value
68
+ .start(
69
+ { facingMode: cameraFacingMode.value },
70
+ config,
71
+ async (qrCodeMessage: string) => {
72
+ showNotFoundMessage.value = false;
73
+ scannedValue.value = qrCodeMessage;
74
+ console.log(`QR Code scanned: ${qrCodeMessage}`);
75
+ setTimeout(() => {
76
+ closeDialog();
77
+ }, 100);
78
+ },
79
+ (msg: string) => {
80
+ if (msg === "QR Code no longer in front") {
81
+ showNotFoundMessage.value = true;
82
+ errorMessage.value = msg;
83
+ showMessage(msg, "error");
84
+ }
85
+ }
86
+ )
87
+ .catch((err: any) => {
88
+ showMessage(`Unable to start scanning, error: ${err}`, "error");
89
+ });
90
+ };
91
+
92
+ const isValidUrl = (string: string) => {
93
+ try {
94
+ new URL(string);
95
+ return true;
96
+ } catch (_) {
97
+ return false;
98
+ }
99
+ };
100
+
101
+ const switchCamera = async () => {
102
+ await stopScanner();
103
+ cameraFacingMode.value =
104
+ cameraFacingMode.value === "environment" ? "user" : "environment";
105
+ showMessage(
106
+ `Switched to ${cameraFacingMode.value === "environment" ? "Back" : "Front"
107
+ } Camera`,
108
+ "info"
109
+ );
110
+ handleScanVisitorQRCode();
111
+ };
112
+
113
+ const closeDialog = () => {
114
+ emit("closeDialog");
115
+ stopScanner();
116
+ };
117
+
118
+ const stopScanner = () => {
119
+ return new Promise<void>((resolve, reject) => {
120
+ if (html5QrCodeInstance.value) {
121
+ html5QrCodeInstance.value
122
+ .stop()
123
+ .then(() => {
124
+ html5QrCodeInstance.value.clear();
125
+ html5QrCodeInstance.value = null;
126
+ showNotFoundMessage.value = false;
127
+ resolve(); // Resolve the promise when the scanner is fully stopped
128
+ })
129
+ .catch((err: any) => {
130
+ showMessage("Unable to stop scanning.", err);
131
+ reject(err); // Reject the promise if there's an error
132
+ });
133
+ } else {
134
+ resolve(); // Resolve immediately if no scanner instance exists
135
+ }
136
+ });
137
+ };
138
+ </script>
@@ -4,7 +4,7 @@ export default function () {
4
4
  value: TVisitorType;
5
5
  };
6
6
 
7
- const visitorSelection: TVisitorSelection[] =[
7
+ const visitorSelection: TVisitorSelection[] = [
8
8
  { label: "Drive-in", value: "guest" },
9
9
  { label: "Contractor", value: "contractor" },
10
10
  { label: "Walk-In", value: "walk-in" },
@@ -13,14 +13,62 @@ export default function () {
13
13
  { label: "Drop-Off", value: "drop-off" },
14
14
  ];
15
15
 
16
- const typeFieldMap: Record<TVisitorType, (keyof TVisitorPayload | 'delivery-company')[]> = {
17
- guest: ['name', 'nric', 'contact', 'block', 'plateNumber', 'level', 'unit' , 'unitName', 'remarks'],
18
- contractor: ['contractorType', 'name', 'nric', 'company', 'contact', 'plateNumber', 'block', 'level', 'unit', 'unitName', 'remarks'],
19
- 'walk-in': ['name', 'company', 'nric', 'contact', 'block', 'level', 'unit' , 'unitName', 'remarks'],
20
- delivery: ['attachments', 'name', 'deliveryType', 'delivery-company', 'nric', 'contact', 'plateNumber', 'block', 'level', 'unit' , 'unitName', 'remarks'],
21
- 'pick-up': ['plateNumber', 'block', 'remarks'],
22
- 'drop-off': ['plateNumber', 'block', 'remarks'],
23
- }
16
+ const typeFieldMap: Record<
17
+ TVisitorType,
18
+ (keyof TVisitorPayload | "delivery-company")[]
19
+ > = {
20
+ guest: [
21
+ "name",
22
+ "nric",
23
+ "contact",
24
+ "block",
25
+ "plateNumber",
26
+ "level",
27
+ "unit",
28
+ "unitName",
29
+ "remarks",
30
+ ],
31
+ contractor: [
32
+ "contractorType",
33
+ "name",
34
+ "nric",
35
+ "company",
36
+ "contact",
37
+ "plateNumber",
38
+ "block",
39
+ "level",
40
+ "unit",
41
+ "unitName",
42
+ "remarks",
43
+ ],
44
+ "walk-in": [
45
+ "name",
46
+ "company",
47
+ "nric",
48
+ "contact",
49
+ "block",
50
+ "level",
51
+ "unit",
52
+ "unitName",
53
+ "remarks",
54
+ ],
55
+ delivery: [
56
+ "attachments",
57
+ "name",
58
+ "deliveryType",
59
+ "delivery-company",
60
+ "nric",
61
+ "contact",
62
+ "plateNumber",
63
+ "block",
64
+ "level",
65
+ "unit",
66
+ "unitName",
67
+ "remarks",
68
+ ],
69
+ "pick-up": ["plateNumber", "block", "remarks"],
70
+ "drop-off": ["plateNumber", "block", "remarks"],
71
+ };
24
72
 
25
73
  const contractorTypes = [
26
74
  { title: "Estate Contractor", value: "estate-contractor" },
@@ -29,21 +77,20 @@ export default function () {
29
77
  { title: "House Mover", value: "house-mover" },
30
78
  ];
31
79
 
32
-
33
80
  type GetVisitorsParams = {
34
- page?: number
35
- limit?: number
36
- order?: "asc" | "desc"
37
- search?: string
38
- org?: string
39
- site?: string
40
- dateTo?: string
41
- dateFrom?: string
42
- type?: string
43
- status?: string
44
- checkedOut?: boolean
45
- plateNumber?: string
46
- }
81
+ page?: number;
82
+ limit?: number;
83
+ order?: "asc" | "desc";
84
+ search?: string;
85
+ org?: string;
86
+ site?: string;
87
+ dateTo?: string;
88
+ dateFrom?: string;
89
+ type?: string;
90
+ status?: string;
91
+ checkedOut?: boolean;
92
+ plateNumber?: string;
93
+ };
47
94
 
48
95
  async function getVisitors({
49
96
  page = 1,
@@ -58,7 +105,7 @@ export default function () {
58
105
  status = "",
59
106
  // status = "registered"
60
107
  plateNumber = "",
61
- checkedOut
108
+ checkedOut,
62
109
  }: GetVisitorsParams = {}) {
63
110
  return await useNuxtApp().$api<Record<string, any>>(
64
111
  "/api/visitor-transactions",
@@ -76,7 +123,7 @@ export default function () {
76
123
  status,
77
124
  checkedOut,
78
125
  plateNumber,
79
- order
126
+ order,
80
127
  },
81
128
  }
82
129
  );
@@ -111,7 +158,13 @@ export default function () {
111
158
  );
112
159
  }
113
160
 
114
- async function updateVisitorPassKey(_id: string, payload: TPassKeyPayload[]){
161
+ function validateVisitorQrCode(site: string, id: string) {
162
+ return useNuxtApp().$api<Record<string, any>>(
163
+ `/api/visitors/visitor-qr-code/${id}?site=${site}`
164
+ );
165
+ }
166
+
167
+ async function updateVisitorPassKey(_id: string, payload: TPassKeyPayload[]) {
115
168
  return await useNuxtApp().$api<Record<string, any>>(
116
169
  `/api/visitor-transactions/replace-keys/id/${_id}`,
117
170
  {
@@ -120,7 +173,13 @@ export default function () {
120
173
  }
121
174
  );
122
175
  }
123
-
176
+
177
+ function visitorDataFromScannedQRCodeCheckInOut(payload: any) {
178
+ return useNuxtApp().$api<Record<string, any>>("/api/visitors/update-one", {
179
+ method: "PUT",
180
+ body: payload,
181
+ });
182
+ }
124
183
 
125
184
  return {
126
185
  typeFieldMap,
@@ -130,6 +189,8 @@ export default function () {
130
189
  getVisitors,
131
190
  updateVisitor,
132
191
  deleteVisitor,
133
- updateVisitorPassKey
192
+ validateVisitorQrCode,
193
+ updateVisitorPassKey,
194
+ visitorDataFromScannedQRCodeCheckInOut,
134
195
  };
135
196
  }
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.11.22",
5
+ "version": "1.11.23",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -34,6 +34,7 @@
34
34
  "@types/qrcode": "^1.5.6",
35
35
  "ckeditor5": "^47.2.0",
36
36
  "html2pdf.js": "^0.10.2",
37
+ "html5-qrcode": "^2.3.8",
37
38
  "moment-timezone": "^0.6.0",
38
39
  "nuxt-signature-pad": "^1.8.0",
39
40
  "qrcode": "^1.5.4",
@@ -16,39 +16,69 @@ declare type TVisitor = {
16
16
  nric?: string;
17
17
  contact?: string;
18
18
  remarks?: string;
19
+ purpose?: string;
19
20
  attachments: string[];
20
21
  org: string;
21
- site: string,
22
+ site: string;
22
23
  manualCheckout?: boolean;
23
24
  visitorPass?: TPassKeyPayload[];
24
- passKeys?: TPassKeyPayload[]
25
+ passKeys?: TPassKeyPayload[];
25
26
  checkInRemarks?: string;
26
27
  checkOutRemarks?: string;
27
28
  };
28
29
 
29
- declare type TVisitorType = "contractor" | "delivery" | "walk-in" | "pick-up" | "drop-off" | "guest";
30
-
31
- declare type TVisitorPayload = Pick<TVisitor, "name" | "type" | "company" | "block" | "level" | "unit" | "unitName" | "contact" | "plateNumber" | "checkOut" | "contractorType" | "deliveryType" | "nric" | "contact" | "remarks" | "attachments" | "org" | "site" | "status" | "visitorPass" | "passKeys" | "checkInRemarks" | "checkOutRemarks"> & {
32
- members?: TMemberInfo[]
33
- }
34
-
30
+ declare type TVisitorType =
31
+ | "contractor"
32
+ | "delivery"
33
+ | "walk-in"
34
+ | "pick-up"
35
+ | "drop-off"
36
+ | "guest";
35
37
 
38
+ declare type TVisitorPayload = Pick<
39
+ TVisitor,
40
+ | "name"
41
+ | "type"
42
+ | "company"
43
+ | "block"
44
+ | "level"
45
+ | "unit"
46
+ | "unitName"
47
+ | "contact"
48
+ | "plateNumber"
49
+ | "checkOut"
50
+ | "contractorType"
51
+ | "deliveryType"
52
+ | "nric"
53
+ | "contact"
54
+ | "remarks"
55
+ | "attachments"
56
+ | "org"
57
+ | "site"
58
+ | "status"
59
+ | "visitorPass"
60
+ | "passKeys"
61
+ | "checkInRemarks"
62
+ | "checkOutRemarks"
63
+ > & {
64
+ members?: TMemberInfo[];
65
+ };
36
66
 
37
67
  declare type TDefaultOptionObj = {
38
68
  title: string;
39
69
  value: any;
40
- }
70
+ };
41
71
 
42
72
  declare type TMemberInfo = {
43
- name: string;
44
- nric: string;
45
- visitorPass?: string;
46
- passKeys?: TPassKeyPayload[];
47
- contact: string
73
+ name: string;
74
+ nric: string;
75
+ visitorPass?: { keyId: string }[];
76
+ passKeys?: TPassKeyPayload[];
77
+ contact: string
48
78
  }
49
79
 
50
80
  declare type TPassKeyPayload = {
51
81
  keyId: string;
52
82
  status?: string;
53
83
  remarks?: string;
54
- }
84
+ };