@7365admin1/layer-common 1.11.21 → 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.
- package/CHANGELOG.md +12 -0
- package/components/AddPassKeyToVisitor.vue +9 -9
- package/components/AreaMain.vue +10 -8
- package/components/AttendanceMain.vue +6 -6
- package/components/CleaningScheduleMain.vue +8 -7
- package/components/ManageChecklistMain.vue +4 -3
- package/components/MemberInformation.vue +49 -13
- package/components/MyAttendanceMain.vue +6 -3
- package/components/PassInformation.vue +197 -144
- package/components/ScanVisitorQRCode.vue +47 -12
- package/components/ScheduleAreaMain.vue +6 -3
- package/components/ScheduleTaskMain.vue +11 -8
- package/components/UnitMain.vue +10 -8
- package/components/VisitorDataFromScannedQRCodeForm.vue +262 -0
- package/components/VisitorForm.vue +1 -1
- package/components/VisitorManagement.vue +80 -2
- package/components/VisitorPassKeyQRScanner.vue +138 -0
- package/composables/useVisitor.ts +98 -25
- package/package.json +2 -1
- package/types/visitor.d.ts +45 -15
package/CHANGELOG.md
CHANGED
|
@@ -123,7 +123,7 @@ const emit = defineEmits<{
|
|
|
123
123
|
}>()
|
|
124
124
|
|
|
125
125
|
const { getPassKeysByPageSearch } = usePassKey()
|
|
126
|
-
const {
|
|
126
|
+
const { updateVisitorPassKey } = useVisitor()
|
|
127
127
|
|
|
128
128
|
const processing = ref(false)
|
|
129
129
|
const errorMessage = ref('')
|
|
@@ -232,28 +232,28 @@ async function handleSubmit() {
|
|
|
232
232
|
processing.value = true
|
|
233
233
|
|
|
234
234
|
try {
|
|
235
|
-
const payload:
|
|
235
|
+
const payload: any = {}
|
|
236
236
|
|
|
237
237
|
// Build final pass list: kept existing + newly selected
|
|
238
|
-
const finalPasses: { keyId: string
|
|
238
|
+
const finalPasses: { keyId: string }[] = []
|
|
239
239
|
if (existingPass.value) {
|
|
240
|
-
finalPasses.push({ keyId: existingPass.value.keyId
|
|
240
|
+
finalPasses.push({ keyId: existingPass.value.keyId })
|
|
241
241
|
}
|
|
242
242
|
if (selectedPass.value) {
|
|
243
|
-
finalPasses.push({ keyId: selectedPass.value
|
|
243
|
+
finalPasses.push({ keyId: selectedPass.value})
|
|
244
244
|
}
|
|
245
245
|
payload.visitorPass = finalPasses
|
|
246
246
|
|
|
247
247
|
// Build final keys list: kept existing + newly selected
|
|
248
248
|
if (showKeys.value) {
|
|
249
|
-
const finalKeys: { keyId: string
|
|
250
|
-
...existingKeys.value.map(k => ({ keyId: k.keyId
|
|
251
|
-
...selectedKeys.value.map(keyId => ({ keyId
|
|
249
|
+
const finalKeys: { keyId: string}[] = [
|
|
250
|
+
...existingKeys.value.map(k => ({ keyId: k.keyId })),
|
|
251
|
+
...selectedKeys.value.map(keyId => ({ keyId })),
|
|
252
252
|
]
|
|
253
253
|
payload.passKeys = finalKeys
|
|
254
254
|
}
|
|
255
255
|
|
|
256
|
-
await
|
|
256
|
+
await updateVisitorPassKey(prop.visitor._id, payload)
|
|
257
257
|
emit('done')
|
|
258
258
|
emit('close')
|
|
259
259
|
} catch (error: any) {
|
package/components/AreaMain.vue
CHANGED
|
@@ -357,7 +357,6 @@
|
|
|
357
357
|
import QrcodeVue from "qrcode.vue";
|
|
358
358
|
import useAreas from "../composables/useAreas";
|
|
359
359
|
import useUnits from "../composables/useUnits";
|
|
360
|
-
import { useAreaPermission } from "../composables/useAreaPermission";
|
|
361
360
|
import useSiteSettings from "../composables/useSiteSettings";
|
|
362
361
|
import useUtils from "../composables/useUtils";
|
|
363
362
|
|
|
@@ -367,6 +366,11 @@ const props = defineProps({
|
|
|
367
366
|
type: { type: String, default: "" },
|
|
368
367
|
serviceType: { type: String, default: "" },
|
|
369
368
|
scheduleRoute: { type: String, default: "cleaning-schedule" },
|
|
369
|
+
canViewAreas: { type: Boolean, default: true },
|
|
370
|
+
canCreateArea: { type: Boolean, default: false },
|
|
371
|
+
canUpdateArea: { type: Boolean, default: false },
|
|
372
|
+
canDeleteArea: { type: Boolean, default: false },
|
|
373
|
+
canImportArea: { type: Boolean, default: false },
|
|
370
374
|
});
|
|
371
375
|
|
|
372
376
|
const isCleanerArea = computed(() => {
|
|
@@ -553,13 +557,11 @@ function showMessage(msg: string, color: string = "error") {
|
|
|
553
557
|
messageSnackbar.value = true;
|
|
554
558
|
}
|
|
555
559
|
|
|
556
|
-
const
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
canViewAreas,
|
|
562
|
-
} = useAreaPermission();
|
|
560
|
+
const canCreateArea = computed(() => props.canCreateArea);
|
|
561
|
+
const canUpdateArea = computed(() => props.canUpdateArea);
|
|
562
|
+
const canDeleteArea = computed(() => props.canDeleteArea);
|
|
563
|
+
const canImportArea = computed(() => props.canImportArea);
|
|
564
|
+
const canViewAreas = computed(() => props.canViewAreas);
|
|
563
565
|
|
|
564
566
|
async function handleDownloadExcel() {
|
|
565
567
|
try {
|
|
@@ -61,20 +61,20 @@
|
|
|
61
61
|
|
|
62
62
|
<script setup lang="ts">
|
|
63
63
|
import useAttendance from "../composables/useAttendance";
|
|
64
|
-
import { useAttendancePermission } from "../composables/useAttendancePermission";
|
|
65
64
|
import useUtils from "../composables/useUtils";
|
|
66
65
|
|
|
67
66
|
const props = defineProps({
|
|
68
67
|
orgId: { type: String, default: "" },
|
|
69
68
|
site: { type: String, default: "" },
|
|
70
69
|
serviceType: { type: String, default: "" },
|
|
70
|
+
canViewAllAttendance: { type: Boolean, default: true },
|
|
71
|
+
canViewAttendanceDetails: { type: Boolean, default: true },
|
|
72
|
+
canManageAttendanceSettings: { type: Boolean, default: false },
|
|
71
73
|
});
|
|
72
74
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
canManageAttendanceSettings,
|
|
77
|
-
} = useAttendancePermission();
|
|
75
|
+
const canViewAllAttendance = computed(() => props.canViewAllAttendance);
|
|
76
|
+
const canViewAttendanceDetails = computed(() => props.canViewAttendanceDetails);
|
|
77
|
+
const canManageAttendanceSettings = computed(() => props.canManageAttendanceSettings);
|
|
78
78
|
|
|
79
79
|
const submitting = ref(false);
|
|
80
80
|
const page = ref(1);
|
|
@@ -102,7 +102,6 @@
|
|
|
102
102
|
</template>
|
|
103
103
|
|
|
104
104
|
<script lang="ts" setup>
|
|
105
|
-
import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
|
|
106
105
|
import useCleaningSchedules from "../composables/useCleaningSchedules";
|
|
107
106
|
import useUtils from "../composables/useUtils";
|
|
108
107
|
|
|
@@ -112,6 +111,10 @@ const props = defineProps({
|
|
|
112
111
|
type: { type: String, required: true },
|
|
113
112
|
serviceType: { type: String, default: "" },
|
|
114
113
|
scheduleRoute: { type: String, default: "cleaning-schedule" },
|
|
114
|
+
canViewSchedules: { type: Boolean, default: true },
|
|
115
|
+
canViewScheduleDetails: { type: Boolean, default: true },
|
|
116
|
+
canDownloadSchedule: { type: Boolean, default: false },
|
|
117
|
+
canManageScheduleTasks: { type: Boolean, default: false },
|
|
115
118
|
});
|
|
116
119
|
|
|
117
120
|
const startDate = ref("");
|
|
@@ -172,12 +175,10 @@ const getCloseInColor = (id: string): string => {
|
|
|
172
175
|
return "primary";
|
|
173
176
|
};
|
|
174
177
|
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
canManageScheduleTasks,
|
|
180
|
-
} = useCleaningSchedulePermission();
|
|
178
|
+
const canDownloadSchedule = computed(() => props.canDownloadSchedule);
|
|
179
|
+
const canViewSchedules = computed(() => props.canViewSchedules);
|
|
180
|
+
const canViewScheduleDetails = computed(() => props.canViewScheduleDetails);
|
|
181
|
+
const canManageScheduleTasks = computed(() => props.canManageScheduleTasks);
|
|
181
182
|
|
|
182
183
|
const {
|
|
183
184
|
data: getCleanerChecklistReq,
|
|
@@ -300,7 +300,6 @@
|
|
|
300
300
|
</template>
|
|
301
301
|
|
|
302
302
|
<script setup lang="ts">
|
|
303
|
-
import { useCleaningSchedulePermission } from "../composables/useCleaningSchedulePermission";
|
|
304
303
|
import useCleaningSchedules from "../composables/useCleaningSchedules";
|
|
305
304
|
import useCustomerSite from "../composables/useCustomerSite";
|
|
306
305
|
import useFile from "../composables/useFile";
|
|
@@ -313,13 +312,15 @@ const props = defineProps({
|
|
|
313
312
|
scheduleAreaId: { type: String, default: "" },
|
|
314
313
|
type: { type: String, default: "cleaner" },
|
|
315
314
|
serviceType: { type: String, default: "" },
|
|
315
|
+
canAddRemarks: { type: Boolean, default: false },
|
|
316
|
+
canManageScheduleTasks: { type: Boolean, default: false },
|
|
316
317
|
});
|
|
317
318
|
|
|
318
319
|
const { getUnitCleanerChecklist, updateUnitChecklist } = useCleaningSchedules();
|
|
319
320
|
const { getBySiteAsServiceProvider } = useCustomerSite();
|
|
320
321
|
const { back } = useUtils();
|
|
321
|
-
const
|
|
322
|
-
|
|
322
|
+
const canAddRemarks = computed(() => props.canAddRemarks);
|
|
323
|
+
const canManageScheduleTasks = computed(() => props.canManageScheduleTasks);
|
|
323
324
|
|
|
324
325
|
const selectedScheduleStatus = useState<string>(
|
|
325
326
|
"selectedScheduleStatus",
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
<v-row no-gutters class="w-100">
|
|
3
3
|
<v-card class="w-100">
|
|
4
4
|
<v-card-text>
|
|
5
|
-
<v-btn block color="primary-button" :height="40" text="Scan QR Code" class="text-capitalize"
|
|
6
|
-
prepend-icon="mdi-qrcode" />
|
|
7
|
-
<v-form ref="formRef" v-model="validForm" :disabled="processing" @click="
|
|
5
|
+
<v-btn block color="primary-button" :height="40" text="Scan QR Code" class="text-capitalize"
|
|
6
|
+
prepend-icon="mdi-qrcode" @click="handleScanQRPass" />
|
|
7
|
+
<v-form ref="formRef" v-model="validForm" :disabled="processing" @click="errorScanPassMessage = ''">
|
|
8
8
|
<v-row no-gutters class="pt-5 ga-2">
|
|
9
9
|
<v-col cols="12">
|
|
10
10
|
<InputLabel class="text-capitalize" title="Full Name" required />
|
|
@@ -19,10 +19,10 @@
|
|
|
19
19
|
</v-col>
|
|
20
20
|
|
|
21
21
|
<v-col cols="12">
|
|
22
|
-
<v-autocomplete v-model="selectedPass" v-model:search="passInput"
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
22
|
+
<v-autocomplete v-model="selectedPass" v-model:search="passInput" :hide-no-data="false"
|
|
23
|
+
class="mt-3" :items="passItemsFilteredFinal" item-title="prefixAndName" item-value="_id"
|
|
24
|
+
label="Pass (optional)" variant="outlined" density="compact" :error-messages="errorScanPassMessage"
|
|
25
|
+
persistent-hint small-chips>
|
|
26
26
|
<template v-slot:no-data>
|
|
27
27
|
<v-list-item density="compact">
|
|
28
28
|
<v-list-item-title v-if="passInput">
|
|
@@ -35,8 +35,7 @@
|
|
|
35
35
|
</template>
|
|
36
36
|
|
|
37
37
|
<template v-slot:chip="{ props, item }">
|
|
38
|
-
<v-chip v-if="selectedPass" v-bind="props"
|
|
39
|
-
prepend-icon="mdi-card-bulleted-outline"
|
|
38
|
+
<v-chip v-if="selectedPass" v-bind="props" prepend-icon="mdi-card-bulleted-outline"
|
|
40
39
|
:text="item.raw?.prefixAndName"></v-chip>
|
|
41
40
|
</template>
|
|
42
41
|
</v-autocomplete>
|
|
@@ -62,12 +61,15 @@
|
|
|
62
61
|
</v-form>
|
|
63
62
|
</v-card-text>
|
|
64
63
|
</v-card>
|
|
64
|
+
<visitor-pass-key-q-r-scanner :dialog="dialog.scanQRCode" v-model:scannedValue="scannedPassValue"
|
|
65
|
+
@close-dialog="dialog.scanQRCode = false" />
|
|
65
66
|
<v-divider class="my-3" />
|
|
66
67
|
<v-row v-if="committedMembers.length > 0" no-gutters class="w-100 mt-5 ga-3">
|
|
67
68
|
<template v-for="member, index in committedMembers" :key="member.nric">
|
|
68
69
|
<CardMemberInfoSummary :member="membersDisplayed[index]" @remove="handleRemoveMember(index)" />
|
|
69
70
|
</template>
|
|
70
71
|
</v-row>
|
|
72
|
+
<!-- <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" /> -->
|
|
71
73
|
</v-row>
|
|
72
74
|
</template>
|
|
73
75
|
|
|
@@ -103,18 +105,33 @@ const { getPassKeysByPageSearch } = usePassKey()
|
|
|
103
105
|
const memberForm = reactive<TMemberInfo>({
|
|
104
106
|
name: "",
|
|
105
107
|
nric: "",
|
|
106
|
-
visitorPass:
|
|
108
|
+
visitorPass: [] as { keyId: string }[],
|
|
107
109
|
contact: ""
|
|
108
110
|
})
|
|
109
111
|
|
|
110
112
|
const validForm = ref(false)
|
|
111
113
|
const formRef = ref<HTMLFormElement>()
|
|
112
114
|
const processing = ref(false);
|
|
113
|
-
const
|
|
115
|
+
const errorScanPassMessage = ref('')
|
|
116
|
+
|
|
117
|
+
const message = ref("");
|
|
118
|
+
const messageColor = ref("");
|
|
119
|
+
const messageSnackbar = ref(false);
|
|
120
|
+
|
|
121
|
+
function showMessage(msg: string, color: string) {
|
|
122
|
+
message.value = msg;
|
|
123
|
+
messageColor.value = color;
|
|
124
|
+
messageSnackbar.value = true;
|
|
125
|
+
}
|
|
114
126
|
|
|
115
127
|
// pass
|
|
116
128
|
const passInput = ref('')
|
|
117
129
|
const passItems = ref<TPassKey[]>([])
|
|
130
|
+
const scannedPassValue = ref<string>("")
|
|
131
|
+
|
|
132
|
+
const dialog = reactive({
|
|
133
|
+
scanQRCode: false,
|
|
134
|
+
})
|
|
118
135
|
|
|
119
136
|
const selectedPass = ref<string>('')
|
|
120
137
|
|
|
@@ -160,7 +177,7 @@ watch(passesData, (data: any) => {
|
|
|
160
177
|
|
|
161
178
|
const passItemsFilteredFinal = computed(() => {
|
|
162
179
|
return passItems.value.filter((item: TPassKey) => {
|
|
163
|
-
return !props.selectedVisitorPass.some((pass: TPassKeyPayload) => pass.keyId === item._id) && !committedMembers.value.some((member: TMemberInfo) => member.visitorPass === item._id)
|
|
180
|
+
return !props.selectedVisitorPass.some((pass: TPassKeyPayload) => pass.keyId === item._id) && !committedMembers.value.some((member: TMemberInfo) => member.visitorPass?.some((pass) => pass.keyId === item._id))
|
|
164
181
|
})
|
|
165
182
|
})
|
|
166
183
|
|
|
@@ -168,15 +185,34 @@ const membersDisplayed = computed(() => {
|
|
|
168
185
|
return committedMembers.value.map((member: TMemberInfo) => {
|
|
169
186
|
return {
|
|
170
187
|
...member,
|
|
171
|
-
visitorPass: passItems.value.find((item: TPassKey) => item._id === member.visitorPass)?.prefixAndName || ""
|
|
188
|
+
visitorPass: passItems.value.find((item: TPassKey) => item._id === member.visitorPass?.[0]?.keyId)?.prefixAndName || ""
|
|
172
189
|
}
|
|
173
190
|
})
|
|
174
191
|
})
|
|
175
192
|
|
|
193
|
+
function handleScanQRPass() {
|
|
194
|
+
errorScanPassMessage.value = ""
|
|
195
|
+
dialog.scanQRCode = true
|
|
196
|
+
}
|
|
197
|
+
|
|
176
198
|
watch(selectedPass, (newVal) => {
|
|
177
199
|
memberForm.visitorPass = [{ keyId: newVal }]
|
|
178
200
|
})
|
|
179
201
|
|
|
202
|
+
watch(scannedPassValue, (newVal) => {
|
|
203
|
+
if (!newVal) return
|
|
204
|
+
|
|
205
|
+
const matchedPass = passItemsFilteredFinal.value.find((p) => p.prefixAndName === newVal)
|
|
206
|
+
|
|
207
|
+
if (matchedPass) {
|
|
208
|
+
selectedPass.value = matchedPass._id ?? ""
|
|
209
|
+
return
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
errorScanPassMessage.value = "Scanned pass not available"
|
|
213
|
+
showMessage("Scanned pass not available", "error")
|
|
214
|
+
})
|
|
215
|
+
|
|
180
216
|
|
|
181
217
|
|
|
182
218
|
function handleClearForm() {
|
|
@@ -79,16 +79,19 @@
|
|
|
79
79
|
|
|
80
80
|
<script setup lang="ts">
|
|
81
81
|
import useAttendance from "../composables/useAttendance";
|
|
82
|
-
import { useAttendancePermission } from "../composables/useAttendancePermission";
|
|
83
82
|
|
|
84
83
|
const props = defineProps({
|
|
85
84
|
orgId: { type: String, default: "" },
|
|
86
85
|
site: { type: String, default: "" },
|
|
87
86
|
serviceType: { type: String, default: "" },
|
|
87
|
+
canViewOwnAttendance: { type: Boolean, default: true },
|
|
88
|
+
canCheckInOut: { type: Boolean, default: false },
|
|
89
|
+
canViewAttendanceDetails: { type: Boolean, default: true },
|
|
88
90
|
});
|
|
89
91
|
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
+
const canViewOwnAttendance = computed(() => props.canViewOwnAttendance);
|
|
93
|
+
const canCheckInOut = computed(() => props.canCheckInOut);
|
|
94
|
+
const canViewAttendanceDetails = computed(() => props.canViewAttendanceDetails);
|
|
92
95
|
|
|
93
96
|
const searchInput = ref("");
|
|
94
97
|
const dialogShowForm = ref(false);
|