@7365admin1/layer-common 1.10.10 → 1.11.0
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 +6 -0
- package/components/AccessCardAddForm.vue +1 -1
- package/components/AccessCardAssignToUnitForm.vue +10 -13
- package/components/AccessCardQrTagging.vue +2 -2
- package/components/Chat/SkeletonLoader.vue +71 -0
- package/components/DashboardMain.vue +176 -0
- package/components/EntryPassInformation.vue +3 -7
- package/components/FileInputWithList.vue +304 -0
- package/components/IncidentReport/IncidentInformation.vue +14 -2
- package/components/IncidentReport/IncidentInformationDownload.vue +22 -9
- package/components/IncidentReport/affectedEntities.vue +5 -0
- package/components/Signature.vue +133 -0
- package/components/SlideCardGroup.vue +194 -0
- package/components/VisitorForm.vue +17 -1
- package/composables/useAccessManagement.ts +25 -6
- package/composables/useComment.ts +147 -0
- package/composables/useFeedback.ts +79 -29
- package/composables/usePDFDownload.ts +1 -1
- package/composables/useWorkOrder.ts +61 -26
- package/package.json +2 -1
- package/public/default-image.svg +4 -0
- package/public/placeholder-image.svg +6 -0
- package/types/comment.d.ts +38 -0
- package/types/dashboard.d.ts +12 -0
- package/types/feedback.d.ts +56 -20
- package/types/work-order.d.ts +54 -18
- package/utils/data.ts +31 -0
|
@@ -148,7 +148,7 @@
|
|
|
148
148
|
size="small"
|
|
149
149
|
color="blue"
|
|
150
150
|
class="cursor-pointer"
|
|
151
|
-
@click="
|
|
151
|
+
@click="toggleNRICComplainant"
|
|
152
152
|
>
|
|
153
153
|
{{ showNRICComplainant ? "mdi-eye-off" : "mdi-eye" }}
|
|
154
154
|
</v-icon>
|
|
@@ -190,7 +190,7 @@
|
|
|
190
190
|
size="small"
|
|
191
191
|
color="blue"
|
|
192
192
|
class="cursor-pointer"
|
|
193
|
-
@click="
|
|
193
|
+
@click="toggleNRICRecipient"
|
|
194
194
|
>
|
|
195
195
|
{{ showNRICRecipient ? "mdi-eye-off" : "mdi-eye" }}
|
|
196
196
|
</v-icon>
|
|
@@ -262,6 +262,8 @@ const props = defineProps({
|
|
|
262
262
|
},
|
|
263
263
|
});
|
|
264
264
|
|
|
265
|
+
const emit = defineEmits(["showNRICComplainant", "showNRICRecipient"]);
|
|
266
|
+
|
|
265
267
|
// utilities
|
|
266
268
|
const { getSiteById } = useSiteSettings();
|
|
267
269
|
|
|
@@ -293,6 +295,16 @@ function toLocalDate(utcString: string) {
|
|
|
293
295
|
});
|
|
294
296
|
}
|
|
295
297
|
|
|
298
|
+
const toggleNRICComplainant = () => {
|
|
299
|
+
showNRICComplainant.value = !showNRICComplainant.value;
|
|
300
|
+
emit("showNRICComplainant", showNRICComplainant.value);
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const toggleNRICRecipient = () => {
|
|
304
|
+
showNRICRecipient.value = !showNRICRecipient.value;
|
|
305
|
+
emit("showNRICRecipient", showNRICRecipient.value);
|
|
306
|
+
};
|
|
307
|
+
|
|
296
308
|
const maskNRIC = (value: any) => {
|
|
297
309
|
if (!value) return "NA";
|
|
298
310
|
|
|
@@ -98,15 +98,11 @@
|
|
|
98
98
|
<v-col cols="4" class="pa-3 border-b">
|
|
99
99
|
<p class="font-weight-bold">NRIC/WP No.</p>
|
|
100
100
|
<p>
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
@click="showNRICComplainant = !showNRICComplainant"
|
|
107
|
-
>
|
|
108
|
-
{{ showNRICComplainant ? "mdi-eye-off" : "mdi-eye" }}
|
|
109
|
-
</v-icon>
|
|
101
|
+
{{
|
|
102
|
+
showNRICComplainant
|
|
103
|
+
? incidentInformation?.complaintInfo?.nric || "-"
|
|
104
|
+
: maskNRIC(incidentInformation?.complaintInfo?.nric)
|
|
105
|
+
}}
|
|
110
106
|
</p>
|
|
111
107
|
</v-col>
|
|
112
108
|
|
|
@@ -175,6 +171,7 @@ const props = defineProps({
|
|
|
175
171
|
|
|
176
172
|
// utilities
|
|
177
173
|
const { getSiteById } = useSiteSettings();
|
|
174
|
+
const route = useRoute();
|
|
178
175
|
|
|
179
176
|
// state
|
|
180
177
|
const siteName = ref("");
|
|
@@ -184,6 +181,22 @@ const siteId = computed(() => props.incidentInformation?.siteInfo?.site);
|
|
|
184
181
|
const showNRICComplainant = ref(false);
|
|
185
182
|
const showNRICRecipient = ref(false);
|
|
186
183
|
|
|
184
|
+
watch(
|
|
185
|
+
() => route.query.nricComp,
|
|
186
|
+
(val) => {
|
|
187
|
+
showNRICComplainant.value = val === "true";
|
|
188
|
+
},
|
|
189
|
+
{ immediate: true }
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
watch(
|
|
193
|
+
() => route.query.nricRec,
|
|
194
|
+
(val) => {
|
|
195
|
+
showNRICRecipient.value = val === "true";
|
|
196
|
+
},
|
|
197
|
+
{ immediate: true }
|
|
198
|
+
);
|
|
199
|
+
|
|
187
200
|
watch(
|
|
188
201
|
siteId,
|
|
189
202
|
async (newSiteId) => {
|
|
@@ -27,6 +27,7 @@
|
|
|
27
27
|
<div class="d-flex align-center">
|
|
28
28
|
NRIC
|
|
29
29
|
<v-icon
|
|
30
|
+
v-if="isShowEyeIcon"
|
|
30
31
|
class="cursor-pointer ml-1"
|
|
31
32
|
size="18"
|
|
32
33
|
color="blue"
|
|
@@ -90,6 +91,10 @@ const props = defineProps({
|
|
|
90
91
|
type: Object as PropType<Record<string, any> | null>,
|
|
91
92
|
required: true,
|
|
92
93
|
},
|
|
94
|
+
isShowEyeIcon: {
|
|
95
|
+
type: Boolean,
|
|
96
|
+
required: true,
|
|
97
|
+
},
|
|
93
98
|
});
|
|
94
99
|
|
|
95
100
|
// emits
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-dialog max-width="700" v-model="isDialogVisible" persistent>
|
|
3
|
+
<v-card>
|
|
4
|
+
<v-toolbar>
|
|
5
|
+
<v-toolbar-title>Signature </v-toolbar-title>
|
|
6
|
+
<v-spacer />
|
|
7
|
+
<v-btn icon="mdi-close" @click="hideModal"></v-btn>
|
|
8
|
+
</v-toolbar>
|
|
9
|
+
<v-card-text>
|
|
10
|
+
<v-row no-gutters>
|
|
11
|
+
<v-col cols="12">
|
|
12
|
+
<div class="text-subtitle-1 text-medium-emphasis ml-2">
|
|
13
|
+
Your signature here
|
|
14
|
+
</div>
|
|
15
|
+
|
|
16
|
+
<NuxtSignaturePad
|
|
17
|
+
ref="signature"
|
|
18
|
+
:options="state.option"
|
|
19
|
+
:width="'100%'"
|
|
20
|
+
:height="'400px'"
|
|
21
|
+
:disabled="state.disabled"
|
|
22
|
+
class="border"
|
|
23
|
+
/>
|
|
24
|
+
</v-col>
|
|
25
|
+
|
|
26
|
+
<v-col cols="12" class="text-center">
|
|
27
|
+
<v-row class="d-flex">
|
|
28
|
+
<v-col class="w-50 px-3">
|
|
29
|
+
<v-btn
|
|
30
|
+
text="clear"
|
|
31
|
+
color="warning"
|
|
32
|
+
type="submit"
|
|
33
|
+
class="my-4 w-100 rounded-lg"
|
|
34
|
+
height="40px"
|
|
35
|
+
@click="clear()"
|
|
36
|
+
/>
|
|
37
|
+
</v-col>
|
|
38
|
+
|
|
39
|
+
<v-col class="w-50 px-3">
|
|
40
|
+
<v-btn
|
|
41
|
+
text="submit"
|
|
42
|
+
color="#1867C0"
|
|
43
|
+
type="submit"
|
|
44
|
+
class="my-4 w-100 rounded-lg"
|
|
45
|
+
height="40px"
|
|
46
|
+
:loading="loading"
|
|
47
|
+
@click="submit"
|
|
48
|
+
:disabled="loading"
|
|
49
|
+
/>
|
|
50
|
+
</v-col>
|
|
51
|
+
</v-row>
|
|
52
|
+
</v-col>
|
|
53
|
+
</v-row>
|
|
54
|
+
</v-card-text>
|
|
55
|
+
</v-card>
|
|
56
|
+
</v-dialog>
|
|
57
|
+
</template>
|
|
58
|
+
|
|
59
|
+
<script setup lang="ts">
|
|
60
|
+
const loading = ref(false);
|
|
61
|
+
// const { isValid } = useAudit();
|
|
62
|
+
// const { uiRequiredInput, uiSetSnackbar } = useUtils();
|
|
63
|
+
|
|
64
|
+
const message = ref("");
|
|
65
|
+
const messageColor = ref("");
|
|
66
|
+
const messageSnackbar = ref(false);
|
|
67
|
+
|
|
68
|
+
function showMessage(msg: string, color: string) {
|
|
69
|
+
message.value = msg;
|
|
70
|
+
messageColor.value = color;
|
|
71
|
+
messageSnackbar.value = true;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const signature = ref(null);
|
|
75
|
+
const state = ref({
|
|
76
|
+
count: 0,
|
|
77
|
+
option: {
|
|
78
|
+
penColor: "rgb(0, 0, 0)",
|
|
79
|
+
backgroundColor: "rgb(255,255,255)",
|
|
80
|
+
},
|
|
81
|
+
disabled: false,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
const emit = defineEmits<{
|
|
85
|
+
(event: "onSubmit", payload: string): void;
|
|
86
|
+
(event: "onCloseDialog"): void;
|
|
87
|
+
}>();
|
|
88
|
+
let props = defineProps({
|
|
89
|
+
isShowDialog: {
|
|
90
|
+
type: Boolean,
|
|
91
|
+
default: false,
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const isDialogVisible = computed(() => props.isShowDialog);
|
|
96
|
+
|
|
97
|
+
const hideModal = () => {
|
|
98
|
+
emit("onCloseDialog");
|
|
99
|
+
};
|
|
100
|
+
const file = ref<File | null>(null);
|
|
101
|
+
const { addFile } = useFile();
|
|
102
|
+
const submit = async () => {
|
|
103
|
+
try {
|
|
104
|
+
loading.value = true;
|
|
105
|
+
const base64 = signature.value.saveSignature();
|
|
106
|
+
const blob = await (await fetch(base64)).blob();
|
|
107
|
+
|
|
108
|
+
file.value = new File([blob], "signature.jpg", { type: "image/jpeg" });
|
|
109
|
+
|
|
110
|
+
const uploadItem = {
|
|
111
|
+
data: file.value,
|
|
112
|
+
name: file.value.name,
|
|
113
|
+
url: URL.createObjectURL(file.value),
|
|
114
|
+
progress: 0,
|
|
115
|
+
type: file.value.type,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const response = await addFile(uploadItem.data);
|
|
119
|
+
|
|
120
|
+
if (response && response.length > 0) {
|
|
121
|
+
emit("onSubmit", response[0]._id);
|
|
122
|
+
}
|
|
123
|
+
} catch (error) {
|
|
124
|
+
showMessage("Error uploading signature. Please try again.", "error");
|
|
125
|
+
} finally {
|
|
126
|
+
loading.value = false;
|
|
127
|
+
}
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const clear = () => {
|
|
131
|
+
signature.value.clearCanvas();
|
|
132
|
+
};
|
|
133
|
+
</script>
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-sheet class="mx-auto">
|
|
3
|
+
<v-slide-group
|
|
4
|
+
class="pa-1"
|
|
5
|
+
show-arrows
|
|
6
|
+
:style="{ backgroundColor: '#FFFF' }"
|
|
7
|
+
>
|
|
8
|
+
<v-slide-group-item
|
|
9
|
+
v-for="(data, idx) in localDataFiles"
|
|
10
|
+
:key="idx"
|
|
11
|
+
v-slot="{ isSelected }"
|
|
12
|
+
>
|
|
13
|
+
<v-card
|
|
14
|
+
:class="['ma-1', isSelected && 'selected']"
|
|
15
|
+
width="150"
|
|
16
|
+
height="100"
|
|
17
|
+
@click="clickable && emit('onClickCarousel', data.path)"
|
|
18
|
+
class="rounded-lg border-thin"
|
|
19
|
+
border=" opacity-50 thin "
|
|
20
|
+
>
|
|
21
|
+
<v-btn
|
|
22
|
+
icon
|
|
23
|
+
variant="text"
|
|
24
|
+
size="x-small"
|
|
25
|
+
class="remove-btn pa-0 ma-0"
|
|
26
|
+
@click.stop="removeFile(data)"
|
|
27
|
+
>
|
|
28
|
+
<v-icon color="red" class="pa-0 ma-0" size="x-large"
|
|
29
|
+
>mdi-close-circle</v-icon
|
|
30
|
+
>
|
|
31
|
+
</v-btn>
|
|
32
|
+
<!-- File Icons -->
|
|
33
|
+
|
|
34
|
+
<v-img :src="data.url" cover height="200">
|
|
35
|
+
<template #placeholder>
|
|
36
|
+
<v-skeleton-loader height="100%" width="100%" />
|
|
37
|
+
</template>
|
|
38
|
+
</v-img>
|
|
39
|
+
<!-- <template v-if="url.type !== 'image'">
|
|
40
|
+
<div
|
|
41
|
+
class="d-flex fill-height align-center justify-center flex-column"
|
|
42
|
+
>
|
|
43
|
+
<v-icon
|
|
44
|
+
:icon="getFileIcon(url.type)"
|
|
45
|
+
size="64"
|
|
46
|
+
:color="getFileColor(url.type)"
|
|
47
|
+
></v-icon>
|
|
48
|
+
</div>
|
|
49
|
+
</template>
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
<template v-else>
|
|
54
|
+
<v-img :src="url.path" cover height="200">
|
|
55
|
+
<template #placeholder>
|
|
56
|
+
<v-skeleton-loader height="100%" width="100%" />
|
|
57
|
+
</template>
|
|
58
|
+
</v-img>
|
|
59
|
+
</template> -->
|
|
60
|
+
|
|
61
|
+
<!-- Edit Button -->
|
|
62
|
+
<!-- <div v-if="imgEditable" class="edit-btn">
|
|
63
|
+
<v-btn
|
|
64
|
+
color="primary"
|
|
65
|
+
size="small"
|
|
66
|
+
@click.stop="onImageEdit(url.path, idx)"
|
|
67
|
+
>
|
|
68
|
+
<v-icon start>mdi-pencil</v-icon>
|
|
69
|
+
Edit
|
|
70
|
+
</v-btn>
|
|
71
|
+
</div> -->
|
|
72
|
+
</v-card>
|
|
73
|
+
</v-slide-group-item>
|
|
74
|
+
</v-slide-group>
|
|
75
|
+
</v-sheet>
|
|
76
|
+
|
|
77
|
+
<DrawImage
|
|
78
|
+
v-if="isShowImageEdit"
|
|
79
|
+
:is-show-dialog="isShowImageEdit"
|
|
80
|
+
:image-url="imageUrl"
|
|
81
|
+
:image-idx="imageIdx"
|
|
82
|
+
@on-submit="onImageSubmitEdit"
|
|
83
|
+
@on-close-dialog="isShowImageEdit = false"
|
|
84
|
+
/>
|
|
85
|
+
</template>
|
|
86
|
+
|
|
87
|
+
<script setup lang="ts">
|
|
88
|
+
const { getImage } = useUtils();
|
|
89
|
+
const { attachedFiles } = useUploadFiles();
|
|
90
|
+
|
|
91
|
+
const emit = defineEmits<{
|
|
92
|
+
(event: "onClickCarousel", url: string): void;
|
|
93
|
+
(event: "onImageEdit", url: string, idx: number): void;
|
|
94
|
+
(event: "onFileRemove"): void;
|
|
95
|
+
}>();
|
|
96
|
+
|
|
97
|
+
const props = defineProps<{
|
|
98
|
+
clickable?: boolean;
|
|
99
|
+
imgEditable?: boolean;
|
|
100
|
+
dataFiles?: File[];
|
|
101
|
+
}>();
|
|
102
|
+
const localDataFiles = ref([...props.dataFiles]);
|
|
103
|
+
const isShowImageEdit = ref(false);
|
|
104
|
+
const imageUrl = ref("");
|
|
105
|
+
const imageIdx = ref(0);
|
|
106
|
+
|
|
107
|
+
const getFileType = (url: string) => {
|
|
108
|
+
const extension = url.split(".").pop()?.toLowerCase() || "";
|
|
109
|
+
if (["jpg", "jpeg", "png", "gif", "webp"].includes(extension)) return "image";
|
|
110
|
+
if (["pdf"].includes(extension)) return "pdf";
|
|
111
|
+
if (["doc", "docx"].includes(extension)) return "word";
|
|
112
|
+
return "other";
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
const getFileIcon = (type: string) => {
|
|
116
|
+
switch (type) {
|
|
117
|
+
case "pdf":
|
|
118
|
+
return "mdi-file-pdf-box";
|
|
119
|
+
case "word":
|
|
120
|
+
return "mdi-file-word-box";
|
|
121
|
+
default:
|
|
122
|
+
return "mdi-file-outline";
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const getFileColor = (type: string) => {
|
|
127
|
+
switch (type) {
|
|
128
|
+
case "pdf":
|
|
129
|
+
return "red";
|
|
130
|
+
case "word":
|
|
131
|
+
return "blue";
|
|
132
|
+
default:
|
|
133
|
+
return "grey";
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const onImageEdit = (url: string, idx: number) => {
|
|
138
|
+
isShowImageEdit.value = true;
|
|
139
|
+
imageUrl.value = url;
|
|
140
|
+
imageIdx.value = idx;
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const onImageSubmitEdit = async (url: string, idx: number) => {
|
|
144
|
+
const response = await getImage(url);
|
|
145
|
+
if (!response) return;
|
|
146
|
+
attachedFiles.value[idx].data = new File(
|
|
147
|
+
[response],
|
|
148
|
+
attachedFiles.value[idx].data?.name
|
|
149
|
+
);
|
|
150
|
+
attachedFiles.value[idx].url = url;
|
|
151
|
+
isShowImageEdit.value = false;
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const isImageFile = (type: string) => {
|
|
155
|
+
return type?.startsWith("image/");
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
watch(
|
|
159
|
+
() => props.dataFiles,
|
|
160
|
+
(newDataFiles) => {
|
|
161
|
+
localDataFiles.value = [...newDataFiles];
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
|
|
165
|
+
const removeFile = (file) => {
|
|
166
|
+
const index = localDataFiles.value.findIndex(
|
|
167
|
+
(dataFile) => dataFile.id === file.id
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
if (index !== -1) {
|
|
171
|
+
localDataFiles.value.splice(index, 1);
|
|
172
|
+
emit("onFileRemove", file);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
175
|
+
</script>
|
|
176
|
+
|
|
177
|
+
<style scoped>
|
|
178
|
+
.edit-btn {
|
|
179
|
+
position: absolute;
|
|
180
|
+
bottom: 8px;
|
|
181
|
+
right: 8px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.selected {
|
|
185
|
+
border: 2px solid var(--v-theme-primary);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.remove-btn {
|
|
189
|
+
position: absolute;
|
|
190
|
+
top: 0px;
|
|
191
|
+
right: 0px;
|
|
192
|
+
z-index: 2;
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
@@ -322,6 +322,7 @@ const { requiredRule, debounce, UTCToLocalTIme } = useUtils();
|
|
|
322
322
|
const { getSiteById, getSiteLevels, getSiteUnits } = useSiteSettings();
|
|
323
323
|
const { createVisitor, typeFieldMap, contractorTypes, getVisitors, updateVisitor } = useVisitor();
|
|
324
324
|
const { getBySiteId: getEntryPassSettingsBySiteId } = useSiteEntryPassSettings();
|
|
325
|
+
const { createVisitorPass } = useAccessManagement();
|
|
325
326
|
const { findPersonByNRIC, findPersonByContact, searchCompanyList, findUsersByPlateNumber } = usePeople()
|
|
326
327
|
const { getById: getUnitDataById } = useBuildingUnit()
|
|
327
328
|
|
|
@@ -354,7 +355,7 @@ const visitor = reactive<Partial<TVisitorPayload>>({
|
|
|
354
355
|
|
|
355
356
|
const passType = ref("");
|
|
356
357
|
const passQuantity = ref<number | null>(1);
|
|
357
|
-
const passCards = ref<string[]>([]);
|
|
358
|
+
const passCards = ref<{ _id: string; cardNo: string }[]>([]);
|
|
358
359
|
|
|
359
360
|
const registeredUnitCompanyName = ref('N/A')
|
|
360
361
|
|
|
@@ -906,6 +907,21 @@ async function submit() {
|
|
|
906
907
|
try {
|
|
907
908
|
const res = await createVisitor(payload);
|
|
908
909
|
if (res) {
|
|
910
|
+
if (prop.type === "contractor" && passType.value) {
|
|
911
|
+
const visitorId = res?._id;
|
|
912
|
+
const acmUrl = entryPassSettings.value?.data?.settings?.acm_url;
|
|
913
|
+
if (visitorId && acmUrl) {
|
|
914
|
+
await createVisitorPass({
|
|
915
|
+
site: prop.site,
|
|
916
|
+
unitId: visitor.unit!,
|
|
917
|
+
quantity: passQuantity.value ?? 1,
|
|
918
|
+
type: passType.value === "QR" ? "QRCODE" : passType.value,
|
|
919
|
+
nfcCards: passCards.value,
|
|
920
|
+
acm_url: acmUrl,
|
|
921
|
+
visitorId,
|
|
922
|
+
});
|
|
923
|
+
}
|
|
924
|
+
}
|
|
909
925
|
if (createMore.value) {
|
|
910
926
|
emit("done:more");
|
|
911
927
|
} else emit("done");
|
|
@@ -111,8 +111,8 @@ export default function useAccessManagement() {
|
|
|
111
111
|
site: string;
|
|
112
112
|
userType: string;
|
|
113
113
|
type: string;
|
|
114
|
-
accessLevel: string;
|
|
115
|
-
liftAccessLevel: string;
|
|
114
|
+
accessLevel: string | null;
|
|
115
|
+
liftAccessLevel: string | null;
|
|
116
116
|
}) {
|
|
117
117
|
return useNuxtApp().$api<Record<string, any>>(
|
|
118
118
|
`/api/access-management/access-and-lift-cards`,
|
|
@@ -122,8 +122,8 @@ export default function useAccessManagement() {
|
|
|
122
122
|
site: params.site,
|
|
123
123
|
userType: params.userType,
|
|
124
124
|
type: params.type,
|
|
125
|
-
accessLevel: params.accessLevel,
|
|
126
|
-
liftAccessLevel: params.liftAccessLevel,
|
|
125
|
+
...(params.accessLevel != null ? { accessLevel: params.accessLevel } : {}),
|
|
126
|
+
...(params.liftAccessLevel != null ? { liftAccessLevel: params.liftAccessLevel } : {}),
|
|
127
127
|
},
|
|
128
128
|
}
|
|
129
129
|
);
|
|
@@ -135,8 +135,8 @@ export default function useAccessManagement() {
|
|
|
135
135
|
type: string;
|
|
136
136
|
site: string;
|
|
137
137
|
userType: string;
|
|
138
|
-
accessLevel: string;
|
|
139
|
-
liftAccessLevel: string;
|
|
138
|
+
accessLevel: string | null;
|
|
139
|
+
liftAccessLevel: string | null;
|
|
140
140
|
}) {
|
|
141
141
|
return useNuxtApp().$api<Record<string, any>>(
|
|
142
142
|
`/api/access-management/assign-access-card`,
|
|
@@ -261,6 +261,24 @@ export default function useAccessManagement() {
|
|
|
261
261
|
);
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
function createVisitorPass(payload: {
|
|
265
|
+
site: string;
|
|
266
|
+
unitId: string;
|
|
267
|
+
quantity: number;
|
|
268
|
+
type: string;
|
|
269
|
+
nfcCards: { _id: string; cardNo: string }[];
|
|
270
|
+
acm_url: string;
|
|
271
|
+
visitorId: string;
|
|
272
|
+
}) {
|
|
273
|
+
return useNuxtApp().$api<Record<string, any>>(
|
|
274
|
+
`/api/access-management/visitor`,
|
|
275
|
+
{
|
|
276
|
+
method: "POST",
|
|
277
|
+
body: payload,
|
|
278
|
+
}
|
|
279
|
+
);
|
|
280
|
+
}
|
|
281
|
+
|
|
264
282
|
return {
|
|
265
283
|
getDoorAccessLevels,
|
|
266
284
|
getLiftAccessLevels,
|
|
@@ -280,5 +298,6 @@ export default function useAccessManagement() {
|
|
|
280
298
|
getAllVisitorAccessCardsQrTags,
|
|
281
299
|
getAvailableContractorCards,
|
|
282
300
|
generateQrVms,
|
|
301
|
+
createVisitorPass,
|
|
283
302
|
};
|
|
284
303
|
}
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
const { currentUser } = useLocalAuth();
|
|
2
|
+
export default function useComment() {
|
|
3
|
+
class MComment implements TCommentChat {
|
|
4
|
+
_id?: string;
|
|
5
|
+
comment: string;
|
|
6
|
+
attachments?: TAttachment[];
|
|
7
|
+
createdBy: string;
|
|
8
|
+
workOrder: string;
|
|
9
|
+
createdAt?: string;
|
|
10
|
+
updatedAt?: string;
|
|
11
|
+
createdByName?: string;
|
|
12
|
+
createdByType?: string;
|
|
13
|
+
feedback?: string;
|
|
14
|
+
justify?: string | undefined;
|
|
15
|
+
seenByNames: string;
|
|
16
|
+
|
|
17
|
+
constructor(comment: Partial<TCommentChat> = {}) {
|
|
18
|
+
this._id = comment._id || "";
|
|
19
|
+
this.comment = comment.comment || "";
|
|
20
|
+
this.attachments = comment.attachments || [];
|
|
21
|
+
this.workOrder = comment.workOrder || "";
|
|
22
|
+
this.createdAt = comment.createdAt
|
|
23
|
+
? comment.createdAt.toString()
|
|
24
|
+
: undefined;
|
|
25
|
+
this.updatedAt = comment.updatedAt || "";
|
|
26
|
+
this.createdBy = comment.createdBy || "";
|
|
27
|
+
this.createdByName = comment.createdByName || "";
|
|
28
|
+
this.createdByType = comment.createdByType || "";
|
|
29
|
+
this.justify = comment.justify || "";
|
|
30
|
+
this.feedback = comment.feedback || "";
|
|
31
|
+
this.seenByNames = comment.seenByNames ?? "";
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const comment = useState("comment", () => new MComment());
|
|
36
|
+
const comments = useState("comments", (): TCommentChat[] => []);
|
|
37
|
+
const page = useState("commentPage", () => 1);
|
|
38
|
+
const itemsPerPage = useState("itemsPerPage", () => 2);
|
|
39
|
+
const search = useState("commentSearch", () => "");
|
|
40
|
+
const pageRange = useState("commentPageRange", () => "-- - -- of --");
|
|
41
|
+
const isCommentsLoaded = useState("isCommentsLoaded", () => false);
|
|
42
|
+
|
|
43
|
+
function addComment(payload: TCommentChat) {
|
|
44
|
+
delete payload._id;
|
|
45
|
+
return useNuxtApp().$api<{ value?: { message: string } }>(
|
|
46
|
+
"/api/comments/v1",
|
|
47
|
+
{
|
|
48
|
+
method: "POST",
|
|
49
|
+
body: payload,
|
|
50
|
+
}
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function getCommentById(id: string) {
|
|
55
|
+
return useNuxtApp().$api(`/api/auth/comments/id/${id}`, {
|
|
56
|
+
method: "GET",
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function setComment(data?: TCommentChat) {
|
|
61
|
+
comment.value = new MComment(data);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const isInviteValid = useState("isInviteValid", () => false);
|
|
65
|
+
|
|
66
|
+
function getCommentsByWorkOrder(id: string) {
|
|
67
|
+
return useNuxtApp().$api(`/api/comments/v1/work-order/${id}`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function getCommentsByWorkOrderId(id: string) {
|
|
71
|
+
return useNuxtApp().$api(`/api/comments/v1/work-order/${id}`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function getCommentsByFeedBackId(id: string, type?: string) {
|
|
75
|
+
const commentFeedbackOrWOD =
|
|
76
|
+
type === "workOrder" ? "work-order" : "feedback";
|
|
77
|
+
return useNuxtApp().$api(`/api/comments/v1/${commentFeedbackOrWOD}/${id}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async function setComments(id: string, type?: string) {
|
|
81
|
+
try {
|
|
82
|
+
isCommentsLoaded.value = true;
|
|
83
|
+
const _comments = (await getCommentsByFeedBackId(
|
|
84
|
+
id,
|
|
85
|
+
type
|
|
86
|
+
)) as TCommentPaginated;
|
|
87
|
+
|
|
88
|
+
let updateSeenIds: any = [];
|
|
89
|
+
|
|
90
|
+
comments.value = _comments.items
|
|
91
|
+
.map((comment: any) => {
|
|
92
|
+
if (comment.createdBy === currentUser.value._id) {
|
|
93
|
+
comment.justify = "end";
|
|
94
|
+
} else {
|
|
95
|
+
comment.justify = "start";
|
|
96
|
+
if (
|
|
97
|
+
((Array.isArray(comment.seenBy) &&
|
|
98
|
+
!comment.seenBy.includes(currentUser.value._id)) ||
|
|
99
|
+
!comment?.seenBy) &&
|
|
100
|
+
comment?._id
|
|
101
|
+
) {
|
|
102
|
+
updateSeenIds.push(comment._id);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return comment;
|
|
106
|
+
})
|
|
107
|
+
.sort(
|
|
108
|
+
(firstComment: any, secondComment: any) =>
|
|
109
|
+
new Date(firstComment.createdAt).getTime() -
|
|
110
|
+
new Date(secondComment.createdAt).getTime()
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
if (Array.isArray(updateSeenIds) && updateSeenIds.length > 0) {
|
|
114
|
+
const seenBy = await updateSeenBy(updateSeenIds, currentUser.value._id);
|
|
115
|
+
}
|
|
116
|
+
} catch (error) {
|
|
117
|
+
console.log("error :", error);
|
|
118
|
+
} finally {
|
|
119
|
+
isCommentsLoaded.value = false;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function updateSeenBy(ids: string[], seenById: string) {
|
|
124
|
+
return useNuxtApp().$api(`/api/comments/v1/seen-by`, {
|
|
125
|
+
method: "PUT",
|
|
126
|
+
body: { seenById, ids },
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return {
|
|
131
|
+
comment,
|
|
132
|
+
comments,
|
|
133
|
+
page,
|
|
134
|
+
itemsPerPage,
|
|
135
|
+
pageRange,
|
|
136
|
+
addComment,
|
|
137
|
+
setComment,
|
|
138
|
+
search,
|
|
139
|
+
isCommentsLoaded,
|
|
140
|
+
isInviteValid,
|
|
141
|
+
setComments,
|
|
142
|
+
getCommentById,
|
|
143
|
+
getCommentsByWorkOrderId,
|
|
144
|
+
updateSeenBy,
|
|
145
|
+
getCommentsByFeedBackId,
|
|
146
|
+
};
|
|
147
|
+
}
|