@7365admin1/layer-common 1.11.47 → 1.11.49
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/CameraForm.vue +6 -4
- package/components/DocumentForm.vue +2 -0
- package/components/DocumentManagement.vue +137 -19
- package/components/Input/DatePicker.vue +11 -2
- package/components/SiteSettings.vue +105 -0
- package/components/VehicleManagement.vue +10 -9
- package/components/VisitorManagement.vue +7 -1
- package/composables/useANPRSettings.ts +27 -0
- package/composables/useDocument.ts +17 -9
- package/composables/useLocal.ts +18 -0
- package/composables/useMember.ts +16 -1
- package/composables/useUtils.ts +14 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
/>
|
|
31
31
|
</v-col>
|
|
32
32
|
|
|
33
|
-
<v-col v-if="isANPR" cols="12">
|
|
33
|
+
<!-- <v-col v-if="isANPR" cols="12">
|
|
34
34
|
<InputLabel class="text-capitalize" title="Category" required />
|
|
35
35
|
<v-select
|
|
36
36
|
v-model="camera.category"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
density="comfortable"
|
|
39
39
|
:rules="[requiredRule]"
|
|
40
40
|
/>
|
|
41
|
-
</v-col>
|
|
41
|
+
</v-col> -->
|
|
42
42
|
|
|
43
43
|
<v-col v-if="isANPR" cols="12">
|
|
44
44
|
<InputLabel class="text-capitalize" title="Type" required />
|
|
@@ -50,14 +50,14 @@
|
|
|
50
50
|
/>
|
|
51
51
|
</v-col>
|
|
52
52
|
|
|
53
|
-
<v-col v-if="isANPR" cols="12">
|
|
53
|
+
<!-- <v-col v-if="isANPR" cols="12">
|
|
54
54
|
<InputLabel class="text-capitalize" title="Guard House" />
|
|
55
55
|
<v-select
|
|
56
56
|
v-model="camera.guardPost"
|
|
57
57
|
:items="guardPostOptions"
|
|
58
58
|
density="comfortable"
|
|
59
59
|
/>
|
|
60
|
-
</v-col>
|
|
60
|
+
</v-col> -->
|
|
61
61
|
|
|
62
62
|
<v-col v-if="isANPR" cols="12">
|
|
63
63
|
<InputLabel class="text-capitalize" title="User" required />
|
|
@@ -231,6 +231,8 @@ const anprDirectionOptions = computed(() => [
|
|
|
231
231
|
{ title: "Entry", value: "entry" },
|
|
232
232
|
{ title: "Exit", value: "exit" },
|
|
233
233
|
{ title: "Both Entry and Exit", value: "both" },
|
|
234
|
+
{ title: "Visitors", value: "visitors" },
|
|
235
|
+
{ title: "Residents", value: "residents" },
|
|
234
236
|
]);
|
|
235
237
|
|
|
236
238
|
function back() {
|
|
@@ -178,6 +178,7 @@ async function submit() {
|
|
|
178
178
|
name: document.value.name,
|
|
179
179
|
attachment: document.value.attachment,
|
|
180
180
|
type: derivedType,
|
|
181
|
+
size: fileData?.data?.size ?? fileData?.data?.file_size ?? 0,
|
|
181
182
|
status: "active",
|
|
182
183
|
org: orgId,
|
|
183
184
|
site: siteId,
|
|
@@ -202,6 +203,7 @@ async function submit() {
|
|
|
202
203
|
const fileName = fileData?.data?.name || String(fileId || "");
|
|
203
204
|
const derivedType = deriveTypeFromFilename(fileName);
|
|
204
205
|
if (derivedType) payload.type = derivedType;
|
|
206
|
+
payload.size = fileData?.data?.size ?? fileData?.data?.file_size ?? 0;
|
|
205
207
|
}
|
|
206
208
|
|
|
207
209
|
await updateById(prop.document._id ?? "", payload);
|
|
@@ -265,21 +265,72 @@
|
|
|
265
265
|
<v-card width="100%">
|
|
266
266
|
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
|
|
267
267
|
<v-row no-gutters class="mb-4">
|
|
268
|
-
<v-col cols="12">
|
|
269
|
-
<
|
|
268
|
+
<v-col cols="12" class="mb-3">
|
|
269
|
+
<div class="text-h6 font-weight-bold">
|
|
270
|
+
{{ selectedDocument?.name || "N/A" }}
|
|
271
|
+
</div>
|
|
270
272
|
</v-col>
|
|
271
|
-
|
|
272
|
-
|
|
273
|
+
|
|
274
|
+
<v-col cols="12" class="mb-2">
|
|
275
|
+
<v-row no-gutters>
|
|
276
|
+
<v-col cols="6" class="text-body-1">Document</v-col>
|
|
277
|
+
<v-col cols="6" class="text-right text-body-1">
|
|
278
|
+
<NuxtLink
|
|
279
|
+
v-if="selectedDocument?.attachment"
|
|
280
|
+
:to="getFileUrl(selectedDocument.attachment)"
|
|
281
|
+
external
|
|
282
|
+
target="_blank"
|
|
283
|
+
>
|
|
284
|
+
View Document
|
|
285
|
+
</NuxtLink>
|
|
286
|
+
<span v-else>N/A</span>
|
|
287
|
+
</v-col>
|
|
288
|
+
</v-row>
|
|
289
|
+
</v-col>
|
|
290
|
+
|
|
291
|
+
<v-col cols="12" class="mb-2">
|
|
292
|
+
<v-row no-gutters>
|
|
293
|
+
<v-col cols="6" class="text-body-1">File Type</v-col>
|
|
294
|
+
<v-col cols="6" class="text-right text-body-1">{{
|
|
295
|
+
previewDetails.fileType
|
|
296
|
+
}}</v-col>
|
|
297
|
+
</v-row>
|
|
298
|
+
</v-col>
|
|
299
|
+
|
|
300
|
+
<v-col cols="12" class="mb-2">
|
|
301
|
+
<v-row no-gutters>
|
|
302
|
+
<v-col cols="6" class="text-body-1">File Size</v-col>
|
|
303
|
+
<v-col cols="6" class="text-right text-body-1">{{
|
|
304
|
+
previewDetails.fileSize
|
|
305
|
+
}}</v-col>
|
|
306
|
+
</v-row>
|
|
273
307
|
</v-col>
|
|
308
|
+
|
|
309
|
+
<v-col cols="12" class="mb-2">
|
|
310
|
+
<v-row no-gutters>
|
|
311
|
+
<v-col cols="6" class="text-body-1">Date Created</v-col>
|
|
312
|
+
<v-col cols="6" class="text-right text-body-1">{{
|
|
313
|
+
previewDetails.dateCreated
|
|
314
|
+
}}</v-col>
|
|
315
|
+
</v-row>
|
|
316
|
+
</v-col>
|
|
317
|
+
|
|
318
|
+
<v-col cols="12" class="mb-2">
|
|
319
|
+
<v-row no-gutters>
|
|
320
|
+
<v-col cols="6" class="text-body-1">Date Modified</v-col>
|
|
321
|
+
<v-col cols="6" class="text-right text-body-1">{{
|
|
322
|
+
previewDetails.dateModified
|
|
323
|
+
}}</v-col>
|
|
324
|
+
</v-row>
|
|
325
|
+
</v-col>
|
|
326
|
+
|
|
274
327
|
<v-col cols="12">
|
|
275
|
-
<
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
>
|
|
281
|
-
View Document
|
|
282
|
-
</NuxtLink>
|
|
328
|
+
<v-row no-gutters>
|
|
329
|
+
<v-col cols="6" class="text-body-1">Remarks</v-col>
|
|
330
|
+
<v-col cols="6" class="text-right text-body-1">{{
|
|
331
|
+
previewDetails.remarks
|
|
332
|
+
}}</v-col>
|
|
333
|
+
</v-row>
|
|
283
334
|
</v-col>
|
|
284
335
|
</v-row>
|
|
285
336
|
</v-card-text>
|
|
@@ -511,6 +562,9 @@ const currentFolderPath = computed(() => {
|
|
|
511
562
|
return folderStack.value.map((folder) => folder.name).join(" / ");
|
|
512
563
|
});
|
|
513
564
|
|
|
565
|
+
const fileTypes = ref(["all", "pdf", "csv", "doc"]);
|
|
566
|
+
const selectedFileType = ref("all");
|
|
567
|
+
|
|
514
568
|
const {
|
|
515
569
|
data: getDocumentReq,
|
|
516
570
|
refresh: getDocuments,
|
|
@@ -523,9 +577,11 @@ const {
|
|
|
523
577
|
search: headerSearch.value,
|
|
524
578
|
site: siteId,
|
|
525
579
|
parentId: activeParentId.value,
|
|
580
|
+
type:
|
|
581
|
+
selectedFileType.value === "all" ? undefined : selectedFileType.value,
|
|
526
582
|
}),
|
|
527
583
|
{
|
|
528
|
-
watch: [page, headerSearch, activeParentId],
|
|
584
|
+
watch: [page, headerSearch, activeParentId, selectedFileType],
|
|
529
585
|
}
|
|
530
586
|
);
|
|
531
587
|
|
|
@@ -559,12 +615,24 @@ function formatDate(value?: string | null) {
|
|
|
559
615
|
});
|
|
560
616
|
}
|
|
561
617
|
|
|
618
|
+
function formatBytes(bytes = 0) {
|
|
619
|
+
const b = Number(bytes) || 0;
|
|
620
|
+
if (b === 0) return "0 B";
|
|
621
|
+
const k = 1024;
|
|
622
|
+
const sizes = ["B", "KB", "MB", "GB", "TB"];
|
|
623
|
+
const i = Math.floor(Math.log(b) / Math.log(k));
|
|
624
|
+
return `${parseFloat((b / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
625
|
+
}
|
|
626
|
+
|
|
562
627
|
const tableItems = computed(() => {
|
|
563
628
|
return items.value.map((doc: any) => ({
|
|
564
629
|
...doc,
|
|
565
630
|
_raw: doc,
|
|
566
631
|
file_type: doc.type === "folder" ? "Folder" : doc.type || "N/A",
|
|
567
|
-
file_size:
|
|
632
|
+
file_size:
|
|
633
|
+
doc.type === "folder"
|
|
634
|
+
? "—"
|
|
635
|
+
: formatBytes(Number(doc.file_size ?? doc.size ?? 0)),
|
|
568
636
|
date_created: formatDate(doc.created_at || doc.createdAt),
|
|
569
637
|
date_modified: formatDate(doc.updated_at || doc.updatedAt),
|
|
570
638
|
remarks: doc.remarks || "N/A",
|
|
@@ -582,8 +650,8 @@ const folderRemarks = ref("");
|
|
|
582
650
|
const folderLoading = ref(false);
|
|
583
651
|
const validFolderForm = ref(false);
|
|
584
652
|
|
|
585
|
-
|
|
586
|
-
|
|
653
|
+
// fileTypes and selectedFileType are declared above to allow
|
|
654
|
+
// the fetch to include the selected filter.
|
|
587
655
|
const filterMenu = ref(false);
|
|
588
656
|
const selectedDocument = ref<TDocument>({
|
|
589
657
|
_id: "",
|
|
@@ -591,6 +659,26 @@ const selectedDocument = ref<TDocument>({
|
|
|
591
659
|
attachment: "",
|
|
592
660
|
type: "",
|
|
593
661
|
});
|
|
662
|
+
|
|
663
|
+
const previewDetails = computed(() => {
|
|
664
|
+
const document = selectedDocument.value as Record<string, any>;
|
|
665
|
+
const fileType =
|
|
666
|
+
document?.type === "folder"
|
|
667
|
+
? "Folder"
|
|
668
|
+
: document?.type || document?.file_type || "N/A";
|
|
669
|
+
const fileSize =
|
|
670
|
+
document?.type === "folder"
|
|
671
|
+
? "N/A"
|
|
672
|
+
: formatBytes(Number(document?.file_size ?? document?.size ?? 0));
|
|
673
|
+
|
|
674
|
+
return {
|
|
675
|
+
fileType,
|
|
676
|
+
fileSize,
|
|
677
|
+
dateCreated: formatDate(document?.created_at || document?.createdAt),
|
|
678
|
+
dateModified: formatDate(document?.updated_at || document?.updatedAt),
|
|
679
|
+
remarks: document?.remarks || "N/A",
|
|
680
|
+
};
|
|
681
|
+
});
|
|
594
682
|
const deleteLoading = ref(false);
|
|
595
683
|
const confirmDialog = ref(false);
|
|
596
684
|
const selectedDocumentId = ref<string | null>(null);
|
|
@@ -603,8 +691,35 @@ function tableRowClickHandler(data: any) {
|
|
|
603
691
|
return;
|
|
604
692
|
}
|
|
605
693
|
|
|
606
|
-
|
|
694
|
+
openPreviewDialog(rowData);
|
|
695
|
+
}
|
|
696
|
+
|
|
697
|
+
async function openPreviewDialog(document: TDocument) {
|
|
698
|
+
selectedDocument.value = document;
|
|
607
699
|
previewDialog.value = true;
|
|
700
|
+
|
|
701
|
+
const documentId = String(document?._id || "");
|
|
702
|
+
if (!documentId) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
try {
|
|
707
|
+
const response = await documentService.getById(documentId);
|
|
708
|
+
const fetchedDocument =
|
|
709
|
+
(response as any)?.item ||
|
|
710
|
+
(response as any)?.data ||
|
|
711
|
+
(response as any)?.document ||
|
|
712
|
+
response;
|
|
713
|
+
|
|
714
|
+
if (fetchedDocument && typeof fetchedDocument === "object") {
|
|
715
|
+
selectedDocument.value = {
|
|
716
|
+
...document,
|
|
717
|
+
...(fetchedDocument as Record<string, any>),
|
|
718
|
+
} as TDocument;
|
|
719
|
+
}
|
|
720
|
+
} catch (_error) {
|
|
721
|
+
// Keep table data in modal if detail API request fails.
|
|
722
|
+
}
|
|
608
723
|
}
|
|
609
724
|
|
|
610
725
|
function isFolderItem(document?: Partial<TDocument> & Record<string, any>) {
|
|
@@ -663,8 +778,11 @@ function setDocument({
|
|
|
663
778
|
editDialog.value = dialog;
|
|
664
779
|
selectedDocument.value = data;
|
|
665
780
|
} else if (mode === "preview") {
|
|
666
|
-
|
|
667
|
-
|
|
781
|
+
if (!dialog) {
|
|
782
|
+
previewDialog.value = false;
|
|
783
|
+
return;
|
|
784
|
+
}
|
|
785
|
+
openPreviewDialog(data);
|
|
668
786
|
}
|
|
669
787
|
}
|
|
670
788
|
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="d-flex flex-column">
|
|
3
3
|
<v-text-field v-bind="$attrs" ref="datePickerRef" :model-value="dateFormattedReadOnly" autocomplete="off"
|
|
4
|
-
:placeholder="placeholder" :rules="rules" style="z-index: 10" @click="openDatePicker">
|
|
4
|
+
:placeholder="placeholder" :rules="rules" style="z-index: 10" @click="openDatePicker" @click:clear.stop="clearDate">
|
|
5
5
|
<template #append-inner>
|
|
6
6
|
<v-icon icon="mdi-calendar" @click.stop="openDatePicker" />
|
|
7
7
|
</template>
|
|
8
8
|
</v-text-field>
|
|
9
9
|
<div class="w-100 d-flex align-end ga-3 hidden-input">
|
|
10
|
-
<input ref="dateInput" type="date" v-model="date" />
|
|
10
|
+
<input ref="dateInput" type="date" v-model="date" :min="min" />
|
|
11
11
|
</div>
|
|
12
12
|
</div>
|
|
13
13
|
</template>
|
|
@@ -22,6 +22,10 @@ const prop = defineProps({
|
|
|
22
22
|
placeholder: {
|
|
23
23
|
type: String,
|
|
24
24
|
default: 'DD/MM/YYYY'
|
|
25
|
+
},
|
|
26
|
+
min: {
|
|
27
|
+
type: String,
|
|
28
|
+
default: undefined
|
|
25
29
|
}
|
|
26
30
|
})
|
|
27
31
|
|
|
@@ -34,6 +38,11 @@ const datePickerRef = ref<HTMLInputElement | null>(null)
|
|
|
34
38
|
|
|
35
39
|
const isInitialLoad = ref(true)
|
|
36
40
|
|
|
41
|
+
function clearDate() {
|
|
42
|
+
date.value = null
|
|
43
|
+
dateFormattedReadOnly.value = null
|
|
44
|
+
}
|
|
45
|
+
|
|
37
46
|
function openDatePicker() {
|
|
38
47
|
setTimeout(() => {
|
|
39
48
|
dateInput.value?.showPicker?.()
|
|
@@ -83,6 +83,56 @@
|
|
|
83
83
|
</v-expansion-panel-title>
|
|
84
84
|
|
|
85
85
|
<v-expansion-panel-text>
|
|
86
|
+
<v-container fluid class="px-0">
|
|
87
|
+
<v-row dense>
|
|
88
|
+
<v-col cols="6">
|
|
89
|
+
<v-switch
|
|
90
|
+
v-model="ANPRSwitches.enableUnregistered"
|
|
91
|
+
:disabled="!canManageSiteSettings"
|
|
92
|
+
label="Enable Unregistered Vehicles"
|
|
93
|
+
color="primary"
|
|
94
|
+
hide-details
|
|
95
|
+
></v-switch>
|
|
96
|
+
</v-col>
|
|
97
|
+
|
|
98
|
+
<v-col cols="6">
|
|
99
|
+
<v-switch
|
|
100
|
+
v-model="ANPRSwitches.openBarrierPickUpDropOff"
|
|
101
|
+
:disabled="!canManageSiteSettings"
|
|
102
|
+
label="Open Barrier for Pick-up and Drop-off"
|
|
103
|
+
color="primary"
|
|
104
|
+
hide-details
|
|
105
|
+
></v-switch>
|
|
106
|
+
</v-col>
|
|
107
|
+
|
|
108
|
+
<v-col cols="6">
|
|
109
|
+
<v-switch
|
|
110
|
+
v-model="ANPRSwitches.vehicleSnapshot"
|
|
111
|
+
:disabled="!canManageSiteSettings"
|
|
112
|
+
label="Capture Vehicle Snapshot"
|
|
113
|
+
color="primary"
|
|
114
|
+
hide-details
|
|
115
|
+
></v-switch>
|
|
116
|
+
</v-col>
|
|
117
|
+
</v-row>
|
|
118
|
+
|
|
119
|
+
<v-row v-if="canManageSiteSettings">
|
|
120
|
+
<v-col cols="auto">
|
|
121
|
+
<v-btn
|
|
122
|
+
variant="flat"
|
|
123
|
+
color="primary"
|
|
124
|
+
class="text-none"
|
|
125
|
+
:loading="isSaving"
|
|
126
|
+
@click="saveANPRSettings"
|
|
127
|
+
>
|
|
128
|
+
Save
|
|
129
|
+
</v-btn>
|
|
130
|
+
</v-col>
|
|
131
|
+
</v-row>
|
|
132
|
+
</v-container>
|
|
133
|
+
|
|
134
|
+
<v-divider class="my-4"></v-divider>
|
|
135
|
+
|
|
86
136
|
<CameraMain :site="siteId" :read-only="!canManageSiteSettings"
|
|
87
137
|
:guard-posts="siteData?.metadata?.guardPosts" />
|
|
88
138
|
</v-expansion-panel-text>
|
|
@@ -154,9 +204,11 @@ import useFile from '../composables/useFile';
|
|
|
154
204
|
import useSiteSettings from '../composables/useSiteSettings';
|
|
155
205
|
import useUtils from '../composables/useUtils';
|
|
156
206
|
import useWorkOrder from '../composables/useWorkOrder';
|
|
207
|
+
import useANPRSettings from '../composables/useANPRSettings';
|
|
157
208
|
|
|
158
209
|
const props = defineProps({
|
|
159
210
|
siteId: { type: String, required: true },
|
|
211
|
+
createdBy: { type: String },
|
|
160
212
|
canManageSiteSettings: { type: Boolean, default: false },
|
|
161
213
|
showAnprCamera: { type: Boolean, default: false },
|
|
162
214
|
showCctvCamera: { type: Boolean, default: false },
|
|
@@ -173,6 +225,7 @@ const { requiredRule } = useUtils();
|
|
|
173
225
|
const { getFileUrl } = useFile();
|
|
174
226
|
const { getWorkOrderSettings, createWorkOrderSettings } = useWorkOrder();
|
|
175
227
|
|
|
228
|
+
|
|
176
229
|
const blocks = ref(0);
|
|
177
230
|
const guardPosts = ref(0);
|
|
178
231
|
const gracePeriod = ref(0);
|
|
@@ -358,4 +411,56 @@ function show(message: string, color: string) {
|
|
|
358
411
|
toast.message = message;
|
|
359
412
|
toast.color = color;
|
|
360
413
|
}
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
// ANPR Settings
|
|
417
|
+
const {getANPRSettingBySite, createUpdateANPRSettings} = useANPRSettings();
|
|
418
|
+
const isSaving = ref(false)
|
|
419
|
+
|
|
420
|
+
// Reactive object to hold the state of the 3 switches
|
|
421
|
+
const ANPRSwitches = ref({
|
|
422
|
+
enableUnregistered: false,
|
|
423
|
+
openBarrierPickUpDropOff: false,
|
|
424
|
+
vehicleSnapshot: false
|
|
425
|
+
})
|
|
426
|
+
|
|
427
|
+
await useLazyAsyncData(
|
|
428
|
+
`anpr-settings-${props.siteId}`,
|
|
429
|
+
async () => {
|
|
430
|
+
const data = await getANPRSettingBySite({
|
|
431
|
+
site: props.siteId,
|
|
432
|
+
userType: "site",
|
|
433
|
+
});
|
|
434
|
+
console.log('data', data)
|
|
435
|
+
if(typeof data?.ANPRSwitches == "object"){
|
|
436
|
+
ANPRSwitches.value = data?.ANPRSwitches
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
const saveANPRSettings = async () => {
|
|
442
|
+
try {
|
|
443
|
+
isSaving.value = true
|
|
444
|
+
|
|
445
|
+
// Replace this with your actual API endpoint / store action
|
|
446
|
+
// await $fetch(`/api/sites/${siteId.value}/camera-settings`, {
|
|
447
|
+
// method: 'POST',
|
|
448
|
+
// body: cameraSettings.value
|
|
449
|
+
// })
|
|
450
|
+
const data = {
|
|
451
|
+
site: props.siteId,
|
|
452
|
+
ANPRSwitches: ANPRSwitches.value,
|
|
453
|
+
updatedBy: props.createdBy,
|
|
454
|
+
userType: "site",
|
|
455
|
+
};
|
|
456
|
+
console.log('saveANPRSettings data:', data)
|
|
457
|
+
await createUpdateANPRSettings(data);
|
|
458
|
+
show("Saved successfully", "success");
|
|
459
|
+
} catch (error) {
|
|
460
|
+
console.error('Failed to save settings:', error)
|
|
461
|
+
} finally {
|
|
462
|
+
isSaving.value = false
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
361
466
|
</script>
|
|
@@ -17,13 +17,9 @@
|
|
|
17
17
|
<template #item.block="{ value }">
|
|
18
18
|
{{ value ? `Blk ${value}` : "" }}
|
|
19
19
|
</template>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
</v-chip>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
</template> -->
|
|
20
|
+
<template #item.nric="{ value }">
|
|
21
|
+
{{ maskNRIC(value) }}
|
|
22
|
+
</template>
|
|
27
23
|
|
|
28
24
|
<template #item.plates="{ value, item }">
|
|
29
25
|
<PlateNumberDisplay :plate-numbers="value" :default-value="item.plateNumber" />
|
|
@@ -61,7 +57,12 @@
|
|
|
61
57
|
<v-row no-gutters class="ga-1 mb-5">
|
|
62
58
|
|
|
63
59
|
<template v-for="(label, key) in formattedFields" :key="key">
|
|
64
|
-
|
|
60
|
+
|
|
61
|
+
<v-col v-if="key === 'nric' && selectedVehicleObject['nric']" cols="12">
|
|
62
|
+
<span class="d-flex ga-3 align-center"><strong>{{ label }}:</strong> {{ maskNRIC(selectedVehicleObject[key]) }}</span>
|
|
63
|
+
</v-col>
|
|
64
|
+
|
|
65
|
+
<v-col v-else-if="key === 'plates'" class="d-flex flex-column ga-2">
|
|
65
66
|
<span class="d-flex ga-3 align-center"><strong>{{ label }}:</strong></span>
|
|
66
67
|
<!-- <PlateNumberDisplay :plate-numbers="selectedVehicleObject[key]" show-all :default-value="selectedVehicleObject.plateNumber" /> -->
|
|
67
68
|
<v-col cols="12">
|
|
@@ -186,7 +187,7 @@ const plateHeaders = [
|
|
|
186
187
|
|
|
187
188
|
|
|
188
189
|
|
|
189
|
-
const { formatCamelCaseToWords, formatDate, debounce } = useUtils();
|
|
190
|
+
const { formatCamelCaseToWords, formatDate, debounce, maskNRIC } = useUtils();
|
|
190
191
|
const { getVehicles, deleteVehicle, formatVehicleStatus, approveVehicle, updateVehicle, getSpecificVehicleById } = useVehicle();
|
|
191
192
|
|
|
192
193
|
const items = ref<Array<Record<string, any>>>([]);
|
|
@@ -340,6 +340,11 @@
|
|
|
340
340
|
:disabled="loading.checkingOut" @click="handleCheckout(selectedVisitorId as string)" />
|
|
341
341
|
</span>
|
|
342
342
|
|
|
343
|
+
<span v-else-if="
|
|
344
|
+
key === 'nric' && selectedVisitorObject['nric']" class="d-flex align-center">
|
|
345
|
+
<strong>{{ label }}:</strong>
|
|
346
|
+
<span class="ml-2">{{ maskNRIC(selectedVisitorObject[key]) }}</span>
|
|
347
|
+
</span>
|
|
343
348
|
<span v-else-if="key === 'attachments' && selectedVisitorObject?.[key]?.length > 0"
|
|
344
349
|
class="d-flex flex-column">
|
|
345
350
|
<strong class="w-full">{{ label }}:</strong>
|
|
@@ -349,7 +354,7 @@
|
|
|
349
354
|
</div>
|
|
350
355
|
</span>
|
|
351
356
|
|
|
352
|
-
<span v-else-if="selectedVisitorObject[key]" class="d-flex ga-3 align-
|
|
357
|
+
<span v-else-if="selectedVisitorObject[key]" class="d-flex ga-3 align-start"><strong>{{ label
|
|
353
358
|
}}:</strong>
|
|
354
359
|
{{ formatValues(key, selectedVisitorObject[key]) }}
|
|
355
360
|
<TooltipInfo v-if="key === 'checkOut'" text="Manual Checkout" density="compact" size="x-small" />
|
|
@@ -602,6 +607,7 @@ const {
|
|
|
602
607
|
formatDate,
|
|
603
608
|
UTCToLocalTIme,
|
|
604
609
|
standardFormatDate,
|
|
610
|
+
maskNRIC,
|
|
605
611
|
} = useUtils();
|
|
606
612
|
const { formatLocation } = useSecurityUtils();
|
|
607
613
|
const { getFileUrlAnpr } = useFile();
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export default function () {
|
|
2
|
+
async function getANPRSettingBySite(data: any) {
|
|
3
|
+
return await useNuxtApp().$api<Record<string, any>>(
|
|
4
|
+
`/api/anpr-settings/get/v1`,
|
|
5
|
+
{
|
|
6
|
+
method: "POST",
|
|
7
|
+
body: data,
|
|
8
|
+
}
|
|
9
|
+
);
|
|
10
|
+
}
|
|
11
|
+
async function createUpdateANPRSettings(
|
|
12
|
+
data: any
|
|
13
|
+
) {
|
|
14
|
+
return await useNuxtApp().$api<Record<string, any>>(
|
|
15
|
+
`/api/anpr-settings/v1`,
|
|
16
|
+
{
|
|
17
|
+
method: "POST",
|
|
18
|
+
body: data,
|
|
19
|
+
}
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return {
|
|
24
|
+
getANPRSettingBySite,
|
|
25
|
+
createUpdateANPRSettings,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
@@ -3,20 +3,28 @@ export default function useDocument() {
|
|
|
3
3
|
page = 1,
|
|
4
4
|
search = "",
|
|
5
5
|
limit = 10,
|
|
6
|
-
|
|
6
|
+
type = "all",
|
|
7
7
|
site = "",
|
|
8
8
|
parentId = "",
|
|
9
|
+
}: {
|
|
10
|
+
page?: number;
|
|
11
|
+
search?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
type?: string;
|
|
14
|
+
site?: string;
|
|
15
|
+
parentId?: string;
|
|
9
16
|
} = {}) {
|
|
17
|
+
const query: Record<string, any> = {
|
|
18
|
+
page,
|
|
19
|
+
search,
|
|
20
|
+
limit,
|
|
21
|
+
site,
|
|
22
|
+
parentId,
|
|
23
|
+
};
|
|
24
|
+
|
|
10
25
|
return useNuxtApp().$api<Record<string, any>>(`/api/documents`, {
|
|
11
26
|
method: "GET",
|
|
12
|
-
query
|
|
13
|
-
page,
|
|
14
|
-
search,
|
|
15
|
-
limit,
|
|
16
|
-
status,
|
|
17
|
-
site,
|
|
18
|
-
parentId,
|
|
19
|
-
},
|
|
27
|
+
query,
|
|
20
28
|
});
|
|
21
29
|
}
|
|
22
30
|
|
package/composables/useLocal.ts
CHANGED
|
@@ -50,6 +50,23 @@ export default function useLocal() {
|
|
|
50
50
|
{ title: "Pool Maintenance Services", value: "pool_maintenance_services" },
|
|
51
51
|
];
|
|
52
52
|
|
|
53
|
+
const natureOfBusinessV2 = [
|
|
54
|
+
{
|
|
55
|
+
title: "Property Management Services",
|
|
56
|
+
value: "property_management_agency",
|
|
57
|
+
},
|
|
58
|
+
{ title: "Security Agency", value: "security_agency" },
|
|
59
|
+
{ title: "Cleaning Services", value: "cleaning_services" },
|
|
60
|
+
{
|
|
61
|
+
title: "M&E Services",
|
|
62
|
+
value: "mechanical_electrical_services",
|
|
63
|
+
},
|
|
64
|
+
{ title: "Landscaping Services", value: "landscaping_services" },
|
|
65
|
+
{ title: "Pest Control Services", value: "pest_control_services" },
|
|
66
|
+
{ title: "Pool Maintenance Services", value: "pool_maintenance_services" },
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
|
|
53
70
|
const landingPage = useCookie("landing-page", cookieConfig);
|
|
54
71
|
|
|
55
72
|
const subjects = [
|
|
@@ -125,6 +142,7 @@ export default function useLocal() {
|
|
|
125
142
|
apps,
|
|
126
143
|
headerSearch,
|
|
127
144
|
natureOfBusiness,
|
|
145
|
+
natureOfBusinessV2,
|
|
128
146
|
landingPage,
|
|
129
147
|
subjects,
|
|
130
148
|
};
|
package/composables/useMember.ts
CHANGED
|
@@ -132,6 +132,20 @@ export default function useMember() {
|
|
|
132
132
|
)
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
+
function getByUserTypeOrg(
|
|
136
|
+
user: string,
|
|
137
|
+
type: string,
|
|
138
|
+
org: string
|
|
139
|
+
) {
|
|
140
|
+
return useNuxtApp().$api<TMember>(
|
|
141
|
+
`/api/members/user/${user}/app/${type}/org`,
|
|
142
|
+
{
|
|
143
|
+
method: "GET",
|
|
144
|
+
query: { org },
|
|
145
|
+
}
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
135
149
|
return {
|
|
136
150
|
members,
|
|
137
151
|
member,
|
|
@@ -149,6 +163,7 @@ export default function useMember() {
|
|
|
149
163
|
updateMemberRole,
|
|
150
164
|
createMemberDirect,
|
|
151
165
|
updateMemberSite,
|
|
152
|
-
getAllByUserId
|
|
166
|
+
getAllByUserId,
|
|
167
|
+
getByUserTypeOrg
|
|
153
168
|
};
|
|
154
169
|
}
|
package/composables/useUtils.ts
CHANGED
|
@@ -506,6 +506,19 @@ export default function useUtils() {
|
|
|
506
506
|
return word[0].toUpperCase() + word.substring(1);
|
|
507
507
|
}
|
|
508
508
|
|
|
509
|
+
|
|
510
|
+
function maskNRIC(value?: string) {
|
|
511
|
+
if(!value) return "";
|
|
512
|
+
if (value.length <= 4) {
|
|
513
|
+
return value;
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
const visiblePart = value.slice(-4);
|
|
517
|
+
const maskedPart = "*".repeat(value.length - 4);
|
|
518
|
+
|
|
519
|
+
return `${maskedPart}${visiblePart}`;
|
|
520
|
+
}
|
|
521
|
+
|
|
509
522
|
return {
|
|
510
523
|
requiredRule,
|
|
511
524
|
emailRule,
|
|
@@ -552,5 +565,6 @@ export default function useUtils() {
|
|
|
552
565
|
getImage,
|
|
553
566
|
standardFormatDate,
|
|
554
567
|
getCapitalized,
|
|
568
|
+
maskNRIC
|
|
555
569
|
};
|
|
556
570
|
}
|