@7365admin1/layer-common 3.1.2-staging.38 → 3.1.2
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/.github/workflows/publish-staging.yml +0 -11
- package/CHANGELOG.md +6 -0
- package/components/AddEqupmentForm.vue +1 -52
- package/components/CameraMain.vue +6 -5
- package/components/DashboardMain.vue +87 -231
- package/components/EquipmentManagementMain.vue +0 -56
- package/components/InvitationMain.vue +3 -4
- package/components/ManageChecklistMain.vue +38 -44
- package/components/Nfc/NFCPatrolReportMain.vue +38 -146
- package/components/Nfc/PatrolReport/DailyReportTab.vue +6 -10
- package/components/Nfc/PatrolReport/MonthlyReportTab.vue +9 -19
- package/components/Nfc/PatrolReport/PatrolActivityTable.vue +0 -20
- package/components/Nfc/PatrolReport/PatrolReportTab.vue +3 -5
- package/components/Nfc/PatrolReport/ReportFilters.vue +20 -52
- package/components/Nfc/PatrolReport/SummaryReportTable.vue +0 -22
- package/components/PhotoUpload.vue +4 -19
- package/components/ServiceProviderMain.vue +25 -59
- package/components/VisitorForm.vue +18 -78
- package/components/VisitorManagement.vue +16 -12
- package/composables/useCleaningSchedulePermission.ts +51 -16
- package/composables/useEquipment.ts +0 -2
- package/composables/useLocalAuth.ts +1 -1
- package/composables/useNFCPatrolDailyReport.ts +4 -3
- package/composables/useNFCPatrolMonthlyReport.ts +30 -62
- package/composables/useNFCPatrolReport.ts +0 -2
- package/composables/useServiceProvider.ts +0 -21
- package/package.json +1 -1
- package/types/customer.d.ts +1 -2
- package/types/equipment.d.ts +1 -5
- package/types/nfc-patrol-report.ts +1 -19
- package/components/Nfc/PatrolReport/MonthlyReportTable.vue +0 -69
- package/components/Nfc/PatrolReport/ReportDatePicker.vue +0 -61
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
:model-value="filters"
|
|
5
5
|
:options="options"
|
|
6
6
|
@update:model-value="$emit('update:filters', $event)"
|
|
7
|
-
@export="
|
|
7
|
+
@export="$emit('export')"
|
|
8
8
|
/>
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
<v-divider :thickness="1" class="border-opacity-100" />
|
|
11
11
|
|
|
12
12
|
<template v-if="report">
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
<ReportEmptyState v-else-if="!loading" />
|
|
31
31
|
|
|
32
32
|
<RemarksDialog v-model="dialogRemarks" :remarks="selectedActivityRemarks" />
|
|
33
|
-
</div>
|
|
34
33
|
</div>
|
|
35
34
|
</template>
|
|
36
35
|
|
|
@@ -56,7 +55,7 @@ const props = defineProps<{
|
|
|
56
55
|
|
|
57
56
|
defineEmits<{
|
|
58
57
|
"update:filters": [value: NFCPatrolReportFilters];
|
|
59
|
-
export: [
|
|
58
|
+
export: [];
|
|
60
59
|
}>();
|
|
61
60
|
|
|
62
61
|
const {
|
|
@@ -78,5 +77,4 @@ watch(
|
|
|
78
77
|
},
|
|
79
78
|
{ immediate: true },
|
|
80
79
|
);
|
|
81
|
-
|
|
82
80
|
</script>
|
|
@@ -16,12 +16,7 @@
|
|
|
16
16
|
/>
|
|
17
17
|
</v-col>
|
|
18
18
|
|
|
19
|
-
<v-col
|
|
20
|
-
v-if="showTimeFilter"
|
|
21
|
-
cols="12"
|
|
22
|
-
md="2"
|
|
23
|
-
class="px-md-2 mb-2 mb-md-0"
|
|
24
|
-
>
|
|
19
|
+
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
25
20
|
<v-select
|
|
26
21
|
:model-value="modelValue.timeRange"
|
|
27
22
|
:items="options.timeRanges"
|
|
@@ -35,14 +30,10 @@
|
|
|
35
30
|
/>
|
|
36
31
|
</v-col>
|
|
37
32
|
|
|
38
|
-
<v-col
|
|
39
|
-
v-
|
|
40
|
-
cols="12"
|
|
41
|
-
md="2"
|
|
42
|
-
class="px-md-2 mb-2 mb-md-0"
|
|
43
|
-
>
|
|
44
|
-
<ReportDatePicker
|
|
33
|
+
<v-col cols="12" md="2" class="px-md-2 mb-2 mb-md-0">
|
|
34
|
+
<v-text-field
|
|
45
35
|
:model-value="modelValue.date"
|
|
36
|
+
type="date"
|
|
46
37
|
variant="outlined"
|
|
47
38
|
density="comfortable"
|
|
48
39
|
rounded="lg"
|
|
@@ -55,31 +46,17 @@
|
|
|
55
46
|
<v-spacer />
|
|
56
47
|
|
|
57
48
|
<v-col cols="12" md="2" class="pl-md-2">
|
|
58
|
-
<v-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Export
|
|
70
|
-
</v-btn>
|
|
71
|
-
</template>
|
|
72
|
-
|
|
73
|
-
<v-list density="compact">
|
|
74
|
-
<v-list-item @click="$emit('export', 'pdf')">
|
|
75
|
-
<v-list-item-title>PDF</v-list-item-title>
|
|
76
|
-
</v-list-item>
|
|
77
|
-
|
|
78
|
-
<v-list-item @click="$emit('export', 'csv')">
|
|
79
|
-
<v-list-item-title>CSV</v-list-item-title>
|
|
80
|
-
</v-list-item>
|
|
81
|
-
</v-list>
|
|
82
|
-
</v-menu>
|
|
49
|
+
<v-btn
|
|
50
|
+
variant="outlined"
|
|
51
|
+
block
|
|
52
|
+
size="large"
|
|
53
|
+
rounded="lg"
|
|
54
|
+
class="text-none"
|
|
55
|
+
prepend-icon="mdi-export"
|
|
56
|
+
@click="$emit('export')"
|
|
57
|
+
>
|
|
58
|
+
Export
|
|
59
|
+
</v-btn>
|
|
83
60
|
</v-col>
|
|
84
61
|
</v-row>
|
|
85
62
|
</template>
|
|
@@ -89,24 +66,15 @@ import type {
|
|
|
89
66
|
NFCPatrolReportFilterOptions,
|
|
90
67
|
NFCPatrolReportFilters,
|
|
91
68
|
} from "../../../types/nfc-patrol-report";
|
|
92
|
-
import ReportDatePicker from "./ReportDatePicker.vue";
|
|
93
69
|
|
|
94
|
-
const props =
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
showTimeFilter?: boolean;
|
|
99
|
-
showDateFilter?: boolean;
|
|
100
|
-
}>(),
|
|
101
|
-
{
|
|
102
|
-
showTimeFilter: true,
|
|
103
|
-
showDateFilter: true,
|
|
104
|
-
},
|
|
105
|
-
);;
|
|
70
|
+
const props = defineProps<{
|
|
71
|
+
modelValue: NFCPatrolReportFilters;
|
|
72
|
+
options: NFCPatrolReportFilterOptions;
|
|
73
|
+
}>();
|
|
106
74
|
|
|
107
75
|
const emit = defineEmits<{
|
|
108
76
|
"update:modelValue": [value: NFCPatrolReportFilters];
|
|
109
|
-
export: [
|
|
77
|
+
export: [];
|
|
110
78
|
}>();
|
|
111
79
|
|
|
112
80
|
function updateFilter(key: keyof NFCPatrolReportFilters, value: unknown) {
|
|
@@ -8,8 +8,6 @@
|
|
|
8
8
|
<tr>
|
|
9
9
|
<th>Patrol Route Name</th>
|
|
10
10
|
<th>Security Check Schedule</th>
|
|
11
|
-
<th>Security Personnel</th>
|
|
12
|
-
<th>Signature</th>
|
|
13
11
|
<th>Checked</th>
|
|
14
12
|
<th>Missed</th>
|
|
15
13
|
<th>Status</th>
|
|
@@ -22,17 +20,6 @@
|
|
|
22
20
|
<v-icon size="small" class="mr-1">mdi-clock-outline</v-icon>
|
|
23
21
|
{{ row.securityCheckSchedule }}
|
|
24
22
|
</td>
|
|
25
|
-
<td>{{ row.guardName || "-" }}</td>
|
|
26
|
-
<td>
|
|
27
|
-
<v-img
|
|
28
|
-
v-if="row.guardSignatureFileId"
|
|
29
|
-
:src="getFileUrl(row.guardSignatureFileId)"
|
|
30
|
-
:alt="`${row.guardName || 'Guard'} signature`"
|
|
31
|
-
class="guard-signature"
|
|
32
|
-
contain
|
|
33
|
-
/>
|
|
34
|
-
<span v-else>-</span>
|
|
35
|
-
</td>
|
|
36
23
|
<td>{{ row.checked }}</td>
|
|
37
24
|
<td>{{ row.missed }}</td>
|
|
38
25
|
<td>
|
|
@@ -58,16 +45,7 @@ defineProps<{
|
|
|
58
45
|
rows: NFCPatrolReportSummaryRow[];
|
|
59
46
|
}>();
|
|
60
47
|
|
|
61
|
-
const { getFileUrl } = useFile();
|
|
62
|
-
|
|
63
48
|
function statusClass(status: NFCPatrolReportSummaryStatus) {
|
|
64
49
|
return status === "Completed" ? "text-success" : "text-error";
|
|
65
50
|
}
|
|
66
51
|
</script>
|
|
67
|
-
|
|
68
|
-
<style scoped>
|
|
69
|
-
.guard-signature {
|
|
70
|
-
width: 80px;
|
|
71
|
-
height: 40px;
|
|
72
|
-
}
|
|
73
|
-
</style>
|
|
@@ -59,21 +59,6 @@
|
|
|
59
59
|
</v-col>
|
|
60
60
|
</v-row>
|
|
61
61
|
|
|
62
|
-
<v-dialog v-model="showPhotoPreview" max-width="900">
|
|
63
|
-
<v-card>
|
|
64
|
-
<v-card-actions class="pa-2 justify-end">
|
|
65
|
-
<v-btn
|
|
66
|
-
icon="mdi-close"
|
|
67
|
-
variant="text"
|
|
68
|
-
@click="showPhotoPreview = false"
|
|
69
|
-
/>
|
|
70
|
-
</v-card-actions>
|
|
71
|
-
<v-card-text class="pa-2 pt-0">
|
|
72
|
-
<v-img :src="selectedPhoto" contain max-height="80vh" />
|
|
73
|
-
</v-card-text>
|
|
74
|
-
</v-card>
|
|
75
|
-
</v-dialog>
|
|
76
|
-
|
|
77
62
|
<v-dialog v-model="photoOptionsDialog" max-width="420">
|
|
78
63
|
<v-card>
|
|
79
64
|
<v-card-title class="d-flex align-center pa-4">
|
|
@@ -211,8 +196,6 @@ const photos = ref<string[]>([...props.modelValue]);
|
|
|
211
196
|
const photosIds = ref<string[]>([...props.photoIds]);
|
|
212
197
|
const photoOptionsDialog = ref(false);
|
|
213
198
|
const showCameraDialog = ref(false);
|
|
214
|
-
const showPhotoPreview = ref(false);
|
|
215
|
-
const selectedPhoto = ref("");
|
|
216
199
|
const videoRef = ref<HTMLVideoElement | null>(null);
|
|
217
200
|
const canvasRef = ref<HTMLCanvasElement | null>(null);
|
|
218
201
|
const fileInputRef = ref<HTMLInputElement | null>(null);
|
|
@@ -222,8 +205,10 @@ const uploading = ref(false);
|
|
|
222
205
|
const { addFile } = useFile();
|
|
223
206
|
|
|
224
207
|
function viewPhoto(photoData: string) {
|
|
225
|
-
|
|
226
|
-
|
|
208
|
+
const link = document.createElement("a");
|
|
209
|
+
link.href = photoData;
|
|
210
|
+
link.target = "_blank";
|
|
211
|
+
link.click();
|
|
227
212
|
}
|
|
228
213
|
|
|
229
214
|
watch(
|
|
@@ -548,7 +548,7 @@
|
|
|
548
548
|
variant="flat"
|
|
549
549
|
class="text-none sp-invite-submit-btn"
|
|
550
550
|
height="76"
|
|
551
|
-
:disabled="!
|
|
551
|
+
:disabled="!validServiceProvider"
|
|
552
552
|
:loading="disableServiceProvider"
|
|
553
553
|
@click="submitServiceProviderInvite()"
|
|
554
554
|
>
|
|
@@ -574,7 +574,6 @@ type ListTab = "active" | "pending" | "inactive";
|
|
|
574
574
|
|
|
575
575
|
type TableRow = {
|
|
576
576
|
_id: string;
|
|
577
|
-
userId: string;
|
|
578
577
|
serviceProviderOrgId: string;
|
|
579
578
|
companyName: string;
|
|
580
579
|
email: string;
|
|
@@ -734,10 +733,11 @@ const {
|
|
|
734
733
|
updateStatus: updateServiceProviderStatus,
|
|
735
734
|
invite,
|
|
736
735
|
} = useServiceProvider();
|
|
737
|
-
const { getAll: getAllMembers
|
|
736
|
+
const { getAll: getAllMembers } = useMember();
|
|
738
737
|
|
|
739
738
|
const { getVerifications, cancelUserInvitation, getVerificationByEmail } =
|
|
740
739
|
useVerification();
|
|
740
|
+
const { getOrgsByMembership } = useMember();
|
|
741
741
|
|
|
742
742
|
const existingAccount = ref<any>(null);
|
|
743
743
|
const checkingExistingAccount = ref(false);
|
|
@@ -759,77 +759,41 @@ const createInviteDialog = ref(false);
|
|
|
759
759
|
|
|
760
760
|
const { getByUserType } = useMember();
|
|
761
761
|
const { getOrgs } = useOrg();
|
|
762
|
-
const { getByEmail } = useServiceProvider();
|
|
763
762
|
|
|
764
763
|
async function checkExistingAccount(email: string) {
|
|
765
764
|
existingAccount.value = null;
|
|
766
|
-
messageServiceProvider.value = "";
|
|
767
765
|
|
|
768
766
|
if (!email.trim() || emailRule(email) !== true) return;
|
|
769
767
|
|
|
770
768
|
checkingExistingAccount.value = true;
|
|
771
769
|
|
|
772
770
|
try {
|
|
773
|
-
// Check if user exists
|
|
774
771
|
const user = await getUserByEmail(email);
|
|
775
772
|
|
|
776
|
-
if (!user?._id) return;
|
|
777
|
-
|
|
778
|
-
// Check if this service provider already exists in current org/site
|
|
779
|
-
const serviceProviders = await getByEmail({
|
|
780
|
-
email,
|
|
781
|
-
orgId: props.orgId,
|
|
782
|
-
siteId: props.siteId,
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
console.log("serviceProviders", serviceProviders);
|
|
773
|
+
if (!user?._id) return;
|
|
786
774
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
return;
|
|
791
|
-
}
|
|
792
|
-
|
|
793
|
-
// Check existing memberships
|
|
794
|
-
const members = await getAllByUserId(user._id);
|
|
775
|
+
const orgs = await getOrgs({
|
|
776
|
+
user: user._id,
|
|
777
|
+
});
|
|
795
778
|
|
|
796
|
-
const
|
|
797
|
-
(
|
|
779
|
+
const matchedOrg = orgs.items?.find(
|
|
780
|
+
(org: any) => org.type === serviceProviderInvite.value.app
|
|
798
781
|
);
|
|
799
782
|
|
|
800
|
-
if (
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
) {
|
|
807
|
-
messageServiceProvider.value =
|
|
808
|
-
"This user is already a member of this service provider.";
|
|
809
|
-
return;
|
|
783
|
+
if (matchedOrg) {
|
|
784
|
+
existingAccount.value = {
|
|
785
|
+
_id: matchedOrg.value,
|
|
786
|
+
text: matchedOrg.text,
|
|
787
|
+
...matchedOrg,
|
|
788
|
+
};
|
|
810
789
|
}
|
|
811
|
-
|
|
812
|
-
// Existing account in another org/site
|
|
813
|
-
existingAccount.value = {
|
|
814
|
-
_id: appMember.org,
|
|
815
|
-
text: appMember.orgName,
|
|
816
|
-
org: appMember.org,
|
|
817
|
-
orgName: appMember.orgName,
|
|
818
|
-
siteId: appMember.siteId,
|
|
819
|
-
siteName: appMember.siteName,
|
|
820
|
-
};
|
|
821
790
|
} catch (error) {
|
|
822
791
|
existingAccount.value = null;
|
|
823
792
|
} finally {
|
|
824
793
|
checkingExistingAccount.value = false;
|
|
825
794
|
}
|
|
826
795
|
}
|
|
827
|
-
|
|
828
|
-
return (
|
|
829
|
-
validServiceProvider.value &&
|
|
830
|
-
!messageServiceProvider.value
|
|
831
|
-
);
|
|
832
|
-
});
|
|
796
|
+
|
|
833
797
|
watch(
|
|
834
798
|
() => serviceProviderInvite.value.email,
|
|
835
799
|
async (value) => {
|
|
@@ -975,7 +939,6 @@ function formatEmailCell(value: unknown): string {
|
|
|
975
939
|
function mapProviderRows(raw: Record<string, any>[]): TableRow[] {
|
|
976
940
|
return raw.map((row) => ({
|
|
977
941
|
_id: String(row._id ?? ""),
|
|
978
|
-
userId: String(row.user ?? ""),
|
|
979
942
|
serviceProviderOrgId: String(row.serviceProviderOrgId ?? ""),
|
|
980
943
|
companyName: row.name ?? "-",
|
|
981
944
|
email: formatEmailCell(row.email),
|
|
@@ -998,7 +961,6 @@ function inviteTypeLabel(t: string) {
|
|
|
998
961
|
function mapInviteRows(raw: Record<string, any>[]): TableRow[] {
|
|
999
962
|
return raw.map((row) => ({
|
|
1000
963
|
_id: String(row._id ?? ""),
|
|
1001
|
-
userId: String(row.user ?? ""),
|
|
1002
964
|
serviceProviderOrgId: String(row.metadata?.serviceProviderOrgId ?? ""),
|
|
1003
965
|
companyName: row.metadata?.name || "-",
|
|
1004
966
|
email: formatEmailCell(row.email),
|
|
@@ -1067,12 +1029,16 @@ async function openView(row: TableRow) {
|
|
|
1067
1029
|
|
|
1068
1030
|
loadingMembers.value = true;
|
|
1069
1031
|
try {
|
|
1070
|
-
const data = await
|
|
1071
|
-
|
|
1032
|
+
const data = await getAllMembers({
|
|
1033
|
+
page: 1,
|
|
1034
|
+
limit: 1000,
|
|
1035
|
+
org: row.serviceProviderOrgId,
|
|
1036
|
+
status: "active",
|
|
1037
|
+
});
|
|
1072
1038
|
serviceProviderMembers.value = (data.items ?? []).map((member: any) => ({
|
|
1073
|
-
_id: member._id,
|
|
1074
|
-
name: member.name || "-",
|
|
1075
|
-
role: member.role || "-",
|
|
1039
|
+
_id: String(member._id ?? ""),
|
|
1040
|
+
name: member.name || member.fullName || member.email || "-",
|
|
1041
|
+
role: member.roleName || member.role?.name || "-",
|
|
1076
1042
|
}));
|
|
1077
1043
|
} catch (error: any) {
|
|
1078
1044
|
showMessage(errorConverter(error), "error");
|
|
@@ -36,17 +36,10 @@
|
|
|
36
36
|
</template>
|
|
37
37
|
|
|
38
38
|
<v-list v-if="nricSearchResults.length" class="pa-1 phone-search-results">
|
|
39
|
-
<
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
</v-list-item>
|
|
44
|
-
</div>
|
|
45
|
-
<div class="search-results-close pa-1">
|
|
46
|
-
<v-btn block variant="flat" color="red" @click.stop="closeAutofillSearchResults('nric')">
|
|
47
|
-
Close
|
|
48
|
-
</v-btn>
|
|
49
|
-
</div>
|
|
39
|
+
<v-list-item v-for="(item, index) in nricSearchResults" :key="item._id || index"
|
|
40
|
+
@click="selectNricSearchResult(item)" class="cursor-pointer nric-search-item">
|
|
41
|
+
<VisitorSearchResultSummary :item="item" />
|
|
42
|
+
</v-list-item>
|
|
50
43
|
</v-list>
|
|
51
44
|
</v-menu>
|
|
52
45
|
</v-col>
|
|
@@ -65,17 +58,10 @@
|
|
|
65
58
|
</template>
|
|
66
59
|
|
|
67
60
|
<v-list v-if="phoneSearchResults.length" class="pa-1 phone-search-results">
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
</v-list-item>
|
|
73
|
-
</div>
|
|
74
|
-
<div class="search-results-close pa-1">
|
|
75
|
-
<v-btn block variant="flat" color="red" @click.stop="closeAutofillSearchResults('contact')">
|
|
76
|
-
Close
|
|
77
|
-
</v-btn>
|
|
78
|
-
</div>
|
|
61
|
+
<v-list-item v-for="(item, index) in phoneSearchResults" :key="item._id || index"
|
|
62
|
+
@click="selectPhoneNumberSearchResult(item)" class="cursor-pointer nric-search-item">
|
|
63
|
+
<VisitorSearchResultSummary :item="item" />
|
|
64
|
+
</v-list-item>
|
|
79
65
|
</v-list>
|
|
80
66
|
</v-menu>
|
|
81
67
|
</v-col>
|
|
@@ -91,23 +77,15 @@
|
|
|
91
77
|
<v-menu v-model="vehicleSearchMenu" location="bottom" offset-y :close-on-content-click="false">
|
|
92
78
|
<template #activator="{ props }">
|
|
93
79
|
<InputVehicleNumber v-bind="props" v-model="visitor.plateNumber" density="comfortable"
|
|
94
|
-
@update:model-value="handleUpdateVehicleNumber" @focus="
|
|
80
|
+
@update:model-value="handleUpdateVehicleNumber" @focus="isPlateNumberFieldFocused = true"
|
|
95
81
|
@blur="isPlateNumberFieldFocused = false" />
|
|
96
82
|
</template>
|
|
97
83
|
|
|
98
84
|
<v-list v-if="vehicleSearchResults.length" class="pa-1 phone-search-results">
|
|
99
|
-
<
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
</v-list-item>
|
|
104
|
-
</div>
|
|
105
|
-
<div class="search-results-close pa-1">
|
|
106
|
-
<v-btn block variant="flat" color="red"
|
|
107
|
-
@click.stop="closeAutofillSearchResults('vehicleNumber')">
|
|
108
|
-
Close
|
|
109
|
-
</v-btn>
|
|
110
|
-
</div>
|
|
85
|
+
<v-list-item v-for="(item, index) in vehicleSearchResults" :key="item._id || index"
|
|
86
|
+
@click="selectPlateNumberSearchResult(item)" class="cursor-pointer nric-search-item">
|
|
87
|
+
<VisitorSearchResultSummary :item="item" />
|
|
88
|
+
</v-list-item>
|
|
111
89
|
</v-list>
|
|
112
90
|
</v-menu>
|
|
113
91
|
</v-col>
|
|
@@ -330,10 +308,10 @@
|
|
|
330
308
|
|
|
331
309
|
<div class="nric-selected-person d-flex align-center justify-space-between ga-3 mb-5">
|
|
332
310
|
<span>{{ selectedNricSearchResult.name || "Unknown" }}</span>
|
|
333
|
-
<span
|
|
311
|
+
<span class="text-no-wrap">NRIC: {{ selectedNricSearchResult.nric || "N/A" }}</span>
|
|
334
312
|
</div>
|
|
335
313
|
|
|
336
|
-
<template v-if="
|
|
314
|
+
<template v-if="!visitor.contact && selectedNricContactOptions.length >= 1">
|
|
337
315
|
<h3 class="nric-options-section-title font-weight-bold mb-2">Contact</h3>
|
|
338
316
|
<v-radio-group v-model="selectedNricContact" hide-details color="success" class="nric-option-group">
|
|
339
317
|
<v-radio v-for="contact in selectedNricContactOptions" :key="contact" :label="contact" :value="contact"
|
|
@@ -341,7 +319,7 @@
|
|
|
341
319
|
</v-radio-group>
|
|
342
320
|
</template>
|
|
343
321
|
|
|
344
|
-
<template v-if="
|
|
322
|
+
<template v-if="!visitor.plateNumber && selectedNricPlateOptions.length >= 1">
|
|
345
323
|
<h3 class="nric-options-section-title font-weight-bold mb-2 mt-5">Vehicle</h3>
|
|
346
324
|
<v-radio-group v-model="selectedNricPlate" hide-details color="success" class="nric-option-group">
|
|
347
325
|
<v-radio v-for="plate in selectedNricPlateOptions" :key="plate" :label="plate" :value="plate"
|
|
@@ -1066,8 +1044,8 @@ function selectAutofillSearchResult(item: Partial<TPeople>, source: Exclude<Auto
|
|
|
1066
1044
|
const selectedPlate =
|
|
1067
1045
|
findMatchingPlateOption(plateOptions, visitor.plateNumber) || plateOptions[0] || "";
|
|
1068
1046
|
|
|
1069
|
-
const needsContactChoice =
|
|
1070
|
-
const needsPlateChoice =
|
|
1047
|
+
const needsContactChoice = !visitor.contact?.trim() && contactOptions.length > 1;
|
|
1048
|
+
const needsPlateChoice = !visitor.plateNumber?.trim() && plateOptions.length > 1;
|
|
1071
1049
|
|
|
1072
1050
|
if (needsContactChoice || needsPlateChoice) {
|
|
1073
1051
|
nricSearchMenu.value = false;
|
|
@@ -1088,23 +1066,6 @@ function selectAutofillSearchResult(item: Partial<TPeople>, source: Exclude<Auto
|
|
|
1088
1066
|
});
|
|
1089
1067
|
}
|
|
1090
1068
|
|
|
1091
|
-
function closeAutofillSearchResults(source: Exclude<AutofillSource, null>) {
|
|
1092
|
-
if (source === "nric") {
|
|
1093
|
-
nricSearchMenu.value = false;
|
|
1094
|
-
nricSearchResults.value = [];
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
if (source === "contact") {
|
|
1098
|
-
phoneSearchMenu.value = false;
|
|
1099
|
-
phoneSearchResults.value = [];
|
|
1100
|
-
}
|
|
1101
|
-
|
|
1102
|
-
if (source === "vehicleNumber") {
|
|
1103
|
-
vehicleSearchMenu.value = false;
|
|
1104
|
-
vehicleSearchResults.value = [];
|
|
1105
|
-
}
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
1069
|
function closeNricAutofillOptions() {
|
|
1109
1070
|
dialog.nricAutofillOptions = false;
|
|
1110
1071
|
selectedNricSearchResult.value = null;
|
|
@@ -1542,15 +1503,6 @@ function handleUpdateVehicleNumber() {
|
|
|
1542
1503
|
debounceFetchPlateNumber();
|
|
1543
1504
|
}
|
|
1544
1505
|
|
|
1545
|
-
const handleFocusPlateNumber = () => {
|
|
1546
|
-
isPlateNumberFieldFocused.value = true;
|
|
1547
|
-
if (prop.mode === 'register') {
|
|
1548
|
-
|
|
1549
|
-
handleUpdateVehicleNumber();
|
|
1550
|
-
}
|
|
1551
|
-
};
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
1506
|
function backToSelection() {
|
|
1555
1507
|
emit("back");
|
|
1556
1508
|
message.value = "";
|
|
@@ -2156,22 +2108,10 @@ onMounted(() => {
|
|
|
2156
2108
|
}
|
|
2157
2109
|
|
|
2158
2110
|
.phone-search-results {
|
|
2159
|
-
display: flex;
|
|
2160
|
-
flex-direction: column;
|
|
2161
2111
|
max-height: 400px;
|
|
2162
|
-
overflow: hidden;
|
|
2163
|
-
}
|
|
2164
|
-
|
|
2165
|
-
.search-results-scroll {
|
|
2166
|
-
flex: 1 1 auto;
|
|
2167
|
-
min-height: 0;
|
|
2168
2112
|
overflow-y: auto;
|
|
2169
2113
|
}
|
|
2170
2114
|
|
|
2171
|
-
.search-results-close {
|
|
2172
|
-
flex: 0 0 auto;
|
|
2173
|
-
}
|
|
2174
|
-
|
|
2175
2115
|
.nric-options-card {
|
|
2176
2116
|
border-radius: 16px !important;
|
|
2177
2117
|
}
|
|
@@ -825,8 +825,10 @@
|
|
|
825
825
|
item-title="label"
|
|
826
826
|
item-value="value"
|
|
827
827
|
v-model="pass.status"
|
|
828
|
-
:disabled="
|
|
829
|
-
|
|
828
|
+
:disabled="
|
|
829
|
+
selectedVisitorObject.checkOut || pass.status == 'Removed'
|
|
830
|
+
"
|
|
831
|
+
></v-select>
|
|
830
832
|
<v-textarea
|
|
831
833
|
v-if="pass.status === 'Lost' || pass.status === 'Damaged'"
|
|
832
834
|
no-resize
|
|
@@ -834,7 +836,8 @@
|
|
|
834
836
|
class="w-100"
|
|
835
837
|
density="compact"
|
|
836
838
|
v-model="pass.remarks"
|
|
837
|
-
|
|
839
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
840
|
+
></v-textarea>
|
|
838
841
|
</v-row>
|
|
839
842
|
</div>
|
|
840
843
|
<div v-if="keyReturnStatuses.length > 0" class="mb-2">
|
|
@@ -861,8 +864,10 @@
|
|
|
861
864
|
item-title="label"
|
|
862
865
|
item-value="value"
|
|
863
866
|
v-model="key.status"
|
|
864
|
-
:disabled="
|
|
865
|
-
|
|
867
|
+
:disabled="
|
|
868
|
+
selectedVisitorObject.checkOut || key.status == 'Removed'
|
|
869
|
+
"
|
|
870
|
+
></v-select>
|
|
866
871
|
<v-textarea
|
|
867
872
|
v-if="key.status === 'Lost' || key.status === 'Damaged'"
|
|
868
873
|
no-resize
|
|
@@ -870,7 +875,8 @@
|
|
|
870
875
|
class="w-100"
|
|
871
876
|
density="compact"
|
|
872
877
|
v-model="key.remarks"
|
|
873
|
-
|
|
878
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
879
|
+
></v-textarea>
|
|
874
880
|
</v-row>
|
|
875
881
|
</div>
|
|
876
882
|
|
|
@@ -880,11 +886,11 @@
|
|
|
880
886
|
color="blue"
|
|
881
887
|
density="comfortable"
|
|
882
888
|
class="text-capitalize"
|
|
889
|
+
:disabled="selectedVisitorObject.checkOut"
|
|
883
890
|
:loading="loading.updatingPassKeys"
|
|
884
891
|
@click.stop="handleUpdatePassKeys"
|
|
892
|
+
>Update Pass/Keys</v-btn
|
|
885
893
|
>
|
|
886
|
-
Update Pass/Keys
|
|
887
|
-
</v-btn>
|
|
888
894
|
</v-row>
|
|
889
895
|
</v-card-text>
|
|
890
896
|
<v-toolbar class="pa-0" density="compact">
|
|
@@ -894,9 +900,8 @@
|
|
|
894
900
|
variant="text"
|
|
895
901
|
block
|
|
896
902
|
@click="dialog.returnPassesKeys = false"
|
|
903
|
+
>Close</v-btn
|
|
897
904
|
>
|
|
898
|
-
Close
|
|
899
|
-
</v-btn>
|
|
900
905
|
</v-col>
|
|
901
906
|
<v-col cols="6">
|
|
902
907
|
<v-btn
|
|
@@ -910,9 +915,8 @@
|
|
|
910
915
|
!canConfirmCheckout || selectedVisitorObject.checkOut
|
|
911
916
|
"
|
|
912
917
|
@click="proceedCheckout"
|
|
918
|
+
>Confirm Checkout</v-btn
|
|
913
919
|
>
|
|
914
|
-
Confirm Checkout
|
|
915
|
-
</v-btn>
|
|
916
920
|
</v-col>
|
|
917
921
|
</v-row>
|
|
918
922
|
</v-toolbar>
|