@iservice365/layer-common 1.0.9 → 1.0.11
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/Avatar/Main.vue +68 -0
- package/components/Chat/Information.vue +252 -68
- package/components/Chat/Message.vue +10 -1
- package/components/FeedbackDetail.vue +30 -11
- package/components/FeedbackMain.vue +20 -3
- package/components/Input/DateTimePicker.vue +69 -14
- package/components/Input/File.vue +29 -4
- package/components/Input/FileV2.vue +121 -0
- package/components/InvitationForm.vue +27 -22
- package/components/Layout/Header.vue +22 -66
- package/components/TableMain.vue +15 -3
- package/components/WorkOrder/Create.vue +22 -1
- package/components/WorkOrder/Detail.vue +71 -0
- package/components/WorkOrder/Main.vue +62 -3
- package/composables/useCustomerSite.ts +11 -1
- package/composables/useFeedback.ts +2 -0
- package/composables/useFile.ts +12 -1
- package/composables/useUtils.ts +20 -0
- package/composables/useWorkOrder.ts +10 -1
- package/middleware/02.org.ts +3 -0
- package/package.json +1 -1
- package/plugins/secure-member.client.ts +20 -6
- package/plugins/vuetify.ts +5 -0
- package/types/feedback.d.ts +3 -0
- package/types/work-order.d.ts +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 1.0.11
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 368e98f: Invitation - Add key to API request
|
|
8
|
+
|
|
9
|
+
## 1.0.10
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 4cdf32b: Invitation form - revise app selection
|
|
14
|
+
|
|
3
15
|
## 1.0.9
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-avatar :color="getColor(name || id)" :size="size">
|
|
3
|
+
<v-img v-if="imageSrc" alt="John" :src="imageUrl" />
|
|
4
|
+
<span v-else :style="{
|
|
5
|
+
fontSize: `${fontSize}px`,
|
|
6
|
+
lineHeight: `${size}px`
|
|
7
|
+
}">{{ initials }}</span>
|
|
8
|
+
</v-avatar>
|
|
9
|
+
</template>
|
|
10
|
+
|
|
11
|
+
<script setup lang="ts">
|
|
12
|
+
const props = defineProps({
|
|
13
|
+
name: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: ""
|
|
16
|
+
},
|
|
17
|
+
color: {
|
|
18
|
+
type: String,
|
|
19
|
+
default: 'red'
|
|
20
|
+
},
|
|
21
|
+
size: {
|
|
22
|
+
type: Number,
|
|
23
|
+
default: 40
|
|
24
|
+
},
|
|
25
|
+
id: {
|
|
26
|
+
type: String, //fallback for constant default color without name
|
|
27
|
+
default: ""
|
|
28
|
+
},
|
|
29
|
+
imageSrc:{
|
|
30
|
+
type: String,
|
|
31
|
+
default: ""
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const { getNameInitials } = useUtils();
|
|
38
|
+
const { getFileUrl } = useFile();
|
|
39
|
+
|
|
40
|
+
const initials = computed(() => {
|
|
41
|
+
return getNameInitials(props.name)
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
const fontSize = computed(() => Math.round(props.size * 0.5))
|
|
45
|
+
|
|
46
|
+
function getColor(str?: string): string {
|
|
47
|
+
if(!str) return "grey"
|
|
48
|
+
|
|
49
|
+
let hash = 0
|
|
50
|
+
for (let i = 0; i < str.length; i++) {
|
|
51
|
+
hash = (hash << 5) - hash + str.charCodeAt(i)
|
|
52
|
+
hash |= 0 // Convert to 32-bit integer
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const hue = Math.abs(hash) % 360
|
|
56
|
+
const saturation = 60 + (Math.abs((hash >> 3) % 20)) // 60–80%
|
|
57
|
+
const lightness = 45 + (Math.abs((hash >> 5) % 15)) // 45–60%
|
|
58
|
+
|
|
59
|
+
return `hsl(${hue}, ${saturation}%, ${lightness}%)`
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const imageUrl = computed(() => {
|
|
63
|
+
return getFileUrl(props?.imageSrc || '')
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<style scoped></style>
|
|
@@ -8,51 +8,19 @@
|
|
|
8
8
|
<v-row class="pa-4">
|
|
9
9
|
<v-sheet
|
|
10
10
|
class="overflow-y-auto flex-grow-1"
|
|
11
|
-
:style="{ height: 'calc(100vh -
|
|
11
|
+
:style="{ height: 'calc(100vh - 250px)', overflowX: 'hidden' }"
|
|
12
12
|
>
|
|
13
13
|
<v-col
|
|
14
14
|
cols="12"
|
|
15
15
|
class="mb-2"
|
|
16
16
|
style="position: sticky; top: 0; background-color: white; z-index: 1"
|
|
17
17
|
>
|
|
18
|
-
<h3 class="text-h6">
|
|
18
|
+
<h3 class="text-h6">
|
|
19
|
+
{{ type === "workOrder" ? "Work Order" : "Feedback" }} Information
|
|
20
|
+
</h3>
|
|
19
21
|
</v-col>
|
|
20
22
|
|
|
21
|
-
<v-col cols="12" class="text-center mb-
|
|
22
|
-
<!-- <v-avatar
|
|
23
|
-
size="48"
|
|
24
|
-
class="mb-1"
|
|
25
|
-
v-if="!item.attachments || !item.attachments.length"
|
|
26
|
-
>
|
|
27
|
-
<v-img v-if="currentUser?.profile" width="15" height="15" />
|
|
28
|
-
<span v-else class="text-h5">{{
|
|
29
|
-
getNameInitials(senderName)
|
|
30
|
-
}}</span>
|
|
31
|
-
</v-avatar>
|
|
32
|
-
|
|
33
|
-
<v-img
|
|
34
|
-
v-else-if="item.attachments.length === 1"
|
|
35
|
-
:src="item.attachments[0]"
|
|
36
|
-
height="200"
|
|
37
|
-
cover
|
|
38
|
-
class="rounded mb-2"
|
|
39
|
-
/>
|
|
40
|
-
|
|
41
|
-
<v-carousel
|
|
42
|
-
v-else
|
|
43
|
-
hide-delimiter-background
|
|
44
|
-
height="200"
|
|
45
|
-
class="rounded mb-2"
|
|
46
|
-
show-arrows
|
|
47
|
-
cycle
|
|
48
|
-
>
|
|
49
|
-
<v-carousel-item
|
|
50
|
-
v-for="(attachment, index) in item.attachments"
|
|
51
|
-
:key="index"
|
|
52
|
-
>
|
|
53
|
-
<v-img :src="`/api/public/${attachment}`" cover class="w-100 h-100" />
|
|
54
|
-
</v-carousel-item>
|
|
55
|
-
</v-carousel> -->
|
|
23
|
+
<v-col cols="12" class="text-center mb-0" v-if="item">
|
|
56
24
|
<v-img
|
|
57
25
|
v-if="item.attachments.length === 1"
|
|
58
26
|
:src="`/api/public/${item.attachments[0]}`"
|
|
@@ -61,7 +29,7 @@
|
|
|
61
29
|
class="rounded mb-2"
|
|
62
30
|
/>
|
|
63
31
|
<v-carousel
|
|
64
|
-
v-else
|
|
32
|
+
v-else-if="item.attachments.length > 1"
|
|
65
33
|
hide-delimiter-background
|
|
66
34
|
height="200"
|
|
67
35
|
class="rounded mb-2"
|
|
@@ -82,10 +50,19 @@
|
|
|
82
50
|
</v-col>
|
|
83
51
|
|
|
84
52
|
<v-col cols="12" v-if="item">
|
|
85
|
-
<v-row dense class="my-1">
|
|
53
|
+
<v-row dense class="my-1" v-if="type === 'feedback'">
|
|
54
|
+
<v-col cols="12" class="py-1"><strong>Subject:</strong></v-col>
|
|
55
|
+
<v-col cols="12" class="py-1">
|
|
56
|
+
{{ item.subject }}
|
|
57
|
+
</v-col>
|
|
58
|
+
</v-row>
|
|
59
|
+
|
|
60
|
+
<v-divider />
|
|
61
|
+
|
|
62
|
+
<v-row dense class="my-1 pr-4">
|
|
86
63
|
<v-col cols="6" class="py-1"><strong>Category:</strong></v-col>
|
|
87
64
|
<v-col cols="6" class="py-1 text-right text-capitalize">{{
|
|
88
|
-
item.
|
|
65
|
+
item.categoryInfo
|
|
89
66
|
}}</v-col>
|
|
90
67
|
</v-row>
|
|
91
68
|
|
|
@@ -97,7 +74,7 @@
|
|
|
97
74
|
</v-row>
|
|
98
75
|
<v-divider />
|
|
99
76
|
|
|
100
|
-
<v-row dense class="my-1">
|
|
77
|
+
<v-row dense class="my-1" v-if="type === 'feedback'">
|
|
101
78
|
<v-col cols="12" class="py-1"
|
|
102
79
|
><strong>Description Feedback:</strong></v-col
|
|
103
80
|
>
|
|
@@ -107,22 +84,45 @@
|
|
|
107
84
|
</v-row>
|
|
108
85
|
<v-divider />
|
|
109
86
|
|
|
110
|
-
<v-row dense class="my-1">
|
|
87
|
+
<v-row dense class="my-1" v-if="type === 'workOrder'">
|
|
111
88
|
<v-col cols="12" class="py-1"
|
|
112
89
|
><strong>Description Work Order:</strong></v-col
|
|
113
90
|
>
|
|
114
91
|
<v-col cols="12" class="py-1">
|
|
115
|
-
{{
|
|
116
|
-
item.workOrderDescription ||
|
|
117
|
-
"No work order description available"
|
|
118
|
-
}}
|
|
92
|
+
{{ item.description || "No work order description available" }}
|
|
119
93
|
</v-col>
|
|
120
94
|
</v-row>
|
|
121
95
|
<v-divider />
|
|
122
96
|
|
|
123
|
-
<v-row dense class="my-1">
|
|
124
|
-
<v-col cols="6" class="py-1"
|
|
125
|
-
|
|
97
|
+
<v-row dense class="my-1" v-if="type === 'feedback'">
|
|
98
|
+
<v-col cols="6" class="py-1"
|
|
99
|
+
><strong>Work Order No.:</strong></v-col
|
|
100
|
+
>
|
|
101
|
+
<v-col v-if="item.workOrderNo" cols="6" class="py-1 text-right">
|
|
102
|
+
<NuxtLink :to="showWorkOrderInfo(item.workOrderId)">{{
|
|
103
|
+
item.workOrderNo
|
|
104
|
+
}}</NuxtLink>
|
|
105
|
+
</v-col>
|
|
106
|
+
<v-col v-else cols="6" class="py-1 text-right">
|
|
107
|
+
<v-btn
|
|
108
|
+
class="text-none text-capitalize text-blue"
|
|
109
|
+
rounded="pill"
|
|
110
|
+
variant="flat"
|
|
111
|
+
size="small"
|
|
112
|
+
@click="openWorOrderCreateDialog()"
|
|
113
|
+
>
|
|
114
|
+
Create WorkOrder
|
|
115
|
+
</v-btn>
|
|
116
|
+
</v-col>
|
|
117
|
+
</v-row>
|
|
118
|
+
|
|
119
|
+
<v-row dense class="my-1" v-if="type === 'workOrder'">
|
|
120
|
+
<v-col cols="6" class="py-1"
|
|
121
|
+
><strong>Work Order Number:</strong></v-col
|
|
122
|
+
>
|
|
123
|
+
<v-col v-if="item.workOrderNo" cols="6" class="py-1 text-right"
|
|
124
|
+
>{{ item.workOrderNo }}
|
|
125
|
+
</v-col>
|
|
126
126
|
</v-row>
|
|
127
127
|
|
|
128
128
|
<v-divider />
|
|
@@ -149,22 +149,19 @@
|
|
|
149
149
|
}}</v-col>
|
|
150
150
|
</v-row>
|
|
151
151
|
</v-col>
|
|
152
|
-
|
|
153
|
-
<v-col cols="12">
|
|
154
|
-
<v-btn
|
|
155
|
-
block
|
|
156
|
-
color="primary"
|
|
157
|
-
variant="flat"
|
|
158
|
-
class="mb-2 text-capitalize"
|
|
159
|
-
@click="$emit('edit')"
|
|
160
|
-
>
|
|
161
|
-
Edit
|
|
162
|
-
</v-btn>
|
|
163
|
-
</v-col>
|
|
164
152
|
</v-sheet>
|
|
165
153
|
</v-row>
|
|
166
154
|
|
|
167
155
|
<v-sheet class="px-4 py-2">
|
|
156
|
+
<v-btn
|
|
157
|
+
block
|
|
158
|
+
color="primary"
|
|
159
|
+
variant="flat"
|
|
160
|
+
class="mb-2 text-capitalize"
|
|
161
|
+
@click="$emit('edit')"
|
|
162
|
+
>
|
|
163
|
+
Edit
|
|
164
|
+
</v-btn>
|
|
168
165
|
<v-btn
|
|
169
166
|
v-if="item?.status !== 'Completed'"
|
|
170
167
|
block
|
|
@@ -186,13 +183,34 @@
|
|
|
186
183
|
Delete
|
|
187
184
|
</v-btn>
|
|
188
185
|
</v-sheet>
|
|
186
|
+
|
|
187
|
+
<WorkOrderCreate
|
|
188
|
+
v-model="showCreateDialog"
|
|
189
|
+
:created-from="'feedback'"
|
|
190
|
+
:work-order="_workOrder"
|
|
191
|
+
@update:work-order="(val: TWorkOrderCreate) => (_workOrder = val)"
|
|
192
|
+
:is-edit-mode="isEditMode"
|
|
193
|
+
:loading="isSubmitting"
|
|
194
|
+
:categories="serviceProviders"
|
|
195
|
+
:theme="theme"
|
|
196
|
+
:errored-images="erroredImages"
|
|
197
|
+
:max-files="5"
|
|
198
|
+
:message-fn="showMessage"
|
|
199
|
+
@close="handleCloseDialog"
|
|
200
|
+
@file-added="handleFileAdded"
|
|
201
|
+
@file-deleted="deleteFile"
|
|
202
|
+
@submit="submitWorkOrder"
|
|
203
|
+
/>
|
|
204
|
+
|
|
205
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
189
206
|
</v-sheet>
|
|
190
207
|
</template>
|
|
191
208
|
|
|
192
209
|
<script setup lang="ts">
|
|
210
|
+
import { useTheme } from "vuetify";
|
|
193
211
|
const { getColorStatus, formatDate } = useUtils();
|
|
194
212
|
|
|
195
|
-
defineProps({
|
|
213
|
+
const props = defineProps({
|
|
196
214
|
item: {
|
|
197
215
|
type: Object,
|
|
198
216
|
default: () => ({}),
|
|
@@ -201,15 +219,181 @@ defineProps({
|
|
|
201
219
|
type: String,
|
|
202
220
|
default: "",
|
|
203
221
|
},
|
|
222
|
+
type: {
|
|
223
|
+
type: String,
|
|
224
|
+
default: "feedback",
|
|
225
|
+
},
|
|
204
226
|
});
|
|
205
227
|
|
|
206
|
-
const emit = defineEmits
|
|
207
|
-
"update:provider",
|
|
208
|
-
"mark-complete-request"
|
|
209
|
-
"delete"
|
|
210
|
-
"edit"
|
|
211
|
-
|
|
228
|
+
const emit = defineEmits<{
|
|
229
|
+
(e: "update:provider", serviceProvider: string): void;
|
|
230
|
+
(e: "mark-complete-request"): void;
|
|
231
|
+
(e: "delete"): void;
|
|
232
|
+
(e: "edit"): void;
|
|
233
|
+
(e: "updateWorkOrderId", workOrderId: string): void;
|
|
234
|
+
}>();
|
|
212
235
|
|
|
213
236
|
const { currentUser } = useLocalAuth();
|
|
214
237
|
const { getNameInitials } = useUtils();
|
|
238
|
+
|
|
239
|
+
const showCreateDialog = ref(false);
|
|
240
|
+
const isEditMode = ref(false);
|
|
241
|
+
const isSubmitting = ref(false);
|
|
242
|
+
const theme = useTheme().name;
|
|
243
|
+
const erroredImages = ref<string[]>([]);
|
|
244
|
+
|
|
245
|
+
const message = ref("");
|
|
246
|
+
const messageColor = ref("");
|
|
247
|
+
const messageSnackbar = ref(false);
|
|
248
|
+
|
|
249
|
+
const route = useRoute();
|
|
250
|
+
|
|
251
|
+
const { getWorkOrders: _getWorkOrders, createWorkOrder } = useWorkOrder();
|
|
252
|
+
|
|
253
|
+
const _workOrder = ref<TWorkOrderCreate>({
|
|
254
|
+
attachments: [],
|
|
255
|
+
category: "",
|
|
256
|
+
subject: "",
|
|
257
|
+
location: "",
|
|
258
|
+
description: "",
|
|
259
|
+
highPriority: false,
|
|
260
|
+
block: "",
|
|
261
|
+
level: "",
|
|
262
|
+
unit: "",
|
|
263
|
+
serviceProvider: "",
|
|
264
|
+
assignee: "",
|
|
265
|
+
organization: "",
|
|
266
|
+
site: "",
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
const serviceProviders = ref<
|
|
270
|
+
Array<{ title: string; value: string; subtitle: string }>
|
|
271
|
+
>([]);
|
|
272
|
+
const { getAll: getAllServiceProvider } = useServiceProvider();
|
|
273
|
+
|
|
274
|
+
const { data: getAllReq } = useLazyAsyncData("get-all-service-providers", () =>
|
|
275
|
+
getAllServiceProvider({
|
|
276
|
+
siteId: useRoute().params.site as string,
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
watchEffect(() => {
|
|
281
|
+
if (getAllReq.value) {
|
|
282
|
+
serviceProviders.value = getAllReq.value.items.map((i: any) => ({
|
|
283
|
+
title: i.nature.replace(/_/g, " "),
|
|
284
|
+
value: i.serviceProviderOrgId,
|
|
285
|
+
subtitle: i.name,
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
function showMessage(msg: string, color: string) {
|
|
291
|
+
message.value = msg;
|
|
292
|
+
messageColor.value = color;
|
|
293
|
+
messageSnackbar.value = true;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function handleCloseDialog() {
|
|
297
|
+
resetWorkOrderForm();
|
|
298
|
+
isEditMode.value = false;
|
|
299
|
+
showCreateDialog.value = false;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
function openWorOrderCreateDialog() {
|
|
303
|
+
let newWorkOrder = {
|
|
304
|
+
..._workOrder.value,
|
|
305
|
+
attachments:
|
|
306
|
+
props.item.attachments.length > 0 ? props.item.attachments : [],
|
|
307
|
+
subject: props.item.subject ? props.item.subject : "",
|
|
308
|
+
category: props.item.category ? props.item.category : "",
|
|
309
|
+
location: props.item.location ? props.item.location : "",
|
|
310
|
+
};
|
|
311
|
+
_workOrder.value = newWorkOrder;
|
|
312
|
+
showCreateDialog.value = true;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function resetWorkOrderForm() {
|
|
316
|
+
_workOrder.value = {
|
|
317
|
+
attachments: [],
|
|
318
|
+
category: "",
|
|
319
|
+
subject: "",
|
|
320
|
+
location: "",
|
|
321
|
+
description: "",
|
|
322
|
+
highPriority: false,
|
|
323
|
+
block: "",
|
|
324
|
+
level: "",
|
|
325
|
+
unit: "",
|
|
326
|
+
serviceProvider: "",
|
|
327
|
+
assignee: "",
|
|
328
|
+
organization: "",
|
|
329
|
+
site: "",
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const { addFile, deleteFile: _deleteFile } = useFile();
|
|
334
|
+
|
|
335
|
+
const API_DO_STORAGE_ENDPOINT =
|
|
336
|
+
useRuntimeConfig().public.API_DO_STORAGE_ENDPOINT;
|
|
337
|
+
|
|
338
|
+
async function handleFileAdded(file: File) {
|
|
339
|
+
try {
|
|
340
|
+
const res = await addFile(file);
|
|
341
|
+
const uploadedId = res?.id;
|
|
342
|
+
if (uploadedId) {
|
|
343
|
+
const url = `${API_DO_STORAGE_ENDPOINT}/${uploadedId}`;
|
|
344
|
+
_workOrder.value.attachments = _workOrder.value.attachments ?? [];
|
|
345
|
+
_workOrder.value.attachments.push(url);
|
|
346
|
+
}
|
|
347
|
+
} catch (error) {
|
|
348
|
+
console.error("Error uploading file:", error);
|
|
349
|
+
showMessage("Failed to upload file", "error");
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
async function deleteFile(value: string) {
|
|
354
|
+
try {
|
|
355
|
+
await _deleteFile(value);
|
|
356
|
+
_workOrder.value.attachments = (_workOrder.value.attachments ?? []).filter(
|
|
357
|
+
(file) => file !== value
|
|
358
|
+
);
|
|
359
|
+
} catch (error) {
|
|
360
|
+
console.log(error);
|
|
361
|
+
showMessage("Failed to delete file", "error");
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
async function submitWorkOrder() {
|
|
366
|
+
try {
|
|
367
|
+
isSubmitting.value = true;
|
|
368
|
+
|
|
369
|
+
const payload = {
|
|
370
|
+
..._workOrder.value,
|
|
371
|
+
organization: route.params.org as string,
|
|
372
|
+
site: route.params.site as string,
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const res = await createWorkOrder(payload);
|
|
376
|
+
|
|
377
|
+
showMessage(res.message, "success");
|
|
378
|
+
showCreateDialog.value = false;
|
|
379
|
+
resetWorkOrderForm();
|
|
380
|
+
emit("updateWorkOrderId", res.workOrderId);
|
|
381
|
+
// getAllReqRefresh();
|
|
382
|
+
} catch (err) {
|
|
383
|
+
showMessage((err as Error).message, "error");
|
|
384
|
+
} finally {
|
|
385
|
+
isSubmitting.value = false;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
function showWorkOrderInfo(workOrderId: string) {
|
|
390
|
+
const route = useRoute();
|
|
391
|
+
const org = route.params.org;
|
|
392
|
+
const site = route.params.site;
|
|
393
|
+
const id = workOrderId;
|
|
394
|
+
return {
|
|
395
|
+
name: "org-site-work-orders-id",
|
|
396
|
+
params: { org, site, id },
|
|
397
|
+
};
|
|
398
|
+
}
|
|
215
399
|
</script>
|
|
@@ -6,7 +6,9 @@
|
|
|
6
6
|
style="border-right: 1px solid #e0e0e0; border-top: 1px solid #e0e0e0"
|
|
7
7
|
>
|
|
8
8
|
<div style="border-bottom: 1px solid #e0e0e0" class="px-4 pt-4 pb-2 mb-2">
|
|
9
|
-
<h3 class="text-h6">
|
|
9
|
+
<h3 class="text-h6">
|
|
10
|
+
{{ type === "workOrder" ? "Work Order" : "Feedback" }} Chat
|
|
11
|
+
</h3>
|
|
10
12
|
</div>
|
|
11
13
|
|
|
12
14
|
<v-sheet
|
|
@@ -94,6 +96,13 @@
|
|
|
94
96
|
</template>
|
|
95
97
|
|
|
96
98
|
<script setup lang="ts">
|
|
99
|
+
defineProps({
|
|
100
|
+
type: {
|
|
101
|
+
type: String,
|
|
102
|
+
default: "feedback",
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
97
106
|
const messages = [
|
|
98
107
|
{
|
|
99
108
|
isCurrentUser: true,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="feedback-detail-wrapper">
|
|
3
3
|
<v-row no-gutters class="fill-height">
|
|
4
|
-
|
|
5
|
-
<v-col cols="12" xl="3" lg="4" md="4" class="fill-height">
|
|
4
|
+
<!-- <v-col cols="12" xl="3" lg="4" md="4" class="fill-height">
|
|
6
5
|
<div class="panel-container border-e">
|
|
7
6
|
<ChatNavigation
|
|
8
7
|
:title="'Feedbacks'"
|
|
@@ -11,24 +10,24 @@
|
|
|
11
10
|
@search="_getFeedbacks"
|
|
12
11
|
/>
|
|
13
12
|
</div>
|
|
14
|
-
</v-col>
|
|
15
|
-
|
|
13
|
+
</v-col> -->
|
|
16
14
|
|
|
17
|
-
<v-col cols="12" xl="
|
|
15
|
+
<v-col cols="12" xl="7" lg="7" md="7" class="fill-height">
|
|
18
16
|
<div class="panel-container border-e">
|
|
19
|
-
<ChatMessage />
|
|
17
|
+
<ChatMessage :type="'feedback'" />
|
|
20
18
|
</div>
|
|
21
19
|
</v-col>
|
|
22
20
|
|
|
23
|
-
|
|
24
|
-
<v-col cols="12" xl="3" lg="3" md="3" class="fill-height">
|
|
21
|
+
<v-col cols="12" xl="5" lg="5" md="5" class="fill-height">
|
|
25
22
|
<div class="panel-container">
|
|
26
23
|
<ChatInformation
|
|
27
24
|
:item="feedback"
|
|
28
25
|
:service-providers="_serviceProviders"
|
|
26
|
+
:type="'feedback'"
|
|
29
27
|
@edit="openEditDialog"
|
|
30
28
|
@mark-complete-request="showCompleteDialog = true"
|
|
31
29
|
@delete="showDeleteDialog = true"
|
|
30
|
+
@update-work-order-id="updateWorkOrderId"
|
|
32
31
|
/>
|
|
33
32
|
</div>
|
|
34
33
|
</v-col>
|
|
@@ -473,6 +472,25 @@ async function submitFeedback(payload: TFeedbackUpdate) {
|
|
|
473
472
|
submitting.value = false;
|
|
474
473
|
}
|
|
475
474
|
}
|
|
475
|
+
|
|
476
|
+
async function updateWorkOrderId(workOrderId: string) {
|
|
477
|
+
const payload = {
|
|
478
|
+
category: feedback.value.category,
|
|
479
|
+
subject: feedback.value.subject,
|
|
480
|
+
location: feedback.value.location,
|
|
481
|
+
description: feedback.value.description,
|
|
482
|
+
workOrderId: workOrderId,
|
|
483
|
+
};
|
|
484
|
+
const response = await updateFeedback(feedback.value._id, payload);
|
|
485
|
+
const message =
|
|
486
|
+
response?.message || "Feedback updated with Work Order Number successfully";
|
|
487
|
+
showMessage(message, "success");
|
|
488
|
+
|
|
489
|
+
showCreateDialog.value = false;
|
|
490
|
+
await _getFeedbackById();
|
|
491
|
+
|
|
492
|
+
resetFeedbackForm();
|
|
493
|
+
}
|
|
476
494
|
</script>
|
|
477
495
|
|
|
478
496
|
<style scoped>
|
|
@@ -532,12 +550,13 @@ async function submitFeedback(payload: TFeedbackUpdate) {
|
|
|
532
550
|
height: calc(100vh - 64px); /* Mobile header height */
|
|
533
551
|
max-height: calc(100vh - 64px);
|
|
534
552
|
}
|
|
535
|
-
|
|
553
|
+
|
|
536
554
|
.border-e {
|
|
537
555
|
border-right: none;
|
|
538
|
-
border-bottom: 1px solid
|
|
556
|
+
border-bottom: 1px solid
|
|
557
|
+
rgba(var(--v-border-color), var(--v-border-opacity));
|
|
539
558
|
}
|
|
540
|
-
|
|
559
|
+
|
|
541
560
|
.v-col {
|
|
542
561
|
height: auto;
|
|
543
562
|
min-height: 300px;
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
:items="items"
|
|
22
22
|
:pages="pages"
|
|
23
23
|
:page-range="pageRange"
|
|
24
|
-
:loading="loading"
|
|
24
|
+
:loading="loading || onNextPrevPageLoading"
|
|
25
25
|
:height="'calc(100vh - 175px)'"
|
|
26
26
|
v-model:page="page"
|
|
27
27
|
:selected="selected"
|
|
@@ -31,6 +31,7 @@
|
|
|
31
31
|
:viewPage="{ name: 'org-site-feedbacks-id' }"
|
|
32
32
|
:clickable-rows="true"
|
|
33
33
|
:length="pages"
|
|
34
|
+
@update:pagination="updatePage"
|
|
34
35
|
>
|
|
35
36
|
<template #title>
|
|
36
37
|
<span class="text-h6 font-weight-regular">Feedbacks</span>
|
|
@@ -252,6 +253,7 @@ const {
|
|
|
252
253
|
);
|
|
253
254
|
|
|
254
255
|
const loading = computed(() => getAllReqStatus.value === "pending");
|
|
256
|
+
const onNextPrevPageLoading = ref(false);
|
|
255
257
|
|
|
256
258
|
watchEffect(() => {
|
|
257
259
|
if (getAllFeedbackReq.value) {
|
|
@@ -261,6 +263,22 @@ watchEffect(() => {
|
|
|
261
263
|
}
|
|
262
264
|
});
|
|
263
265
|
|
|
266
|
+
async function updatePage(pageVal: any) {
|
|
267
|
+
onNextPrevPageLoading.value = true;
|
|
268
|
+
page.value = pageVal;
|
|
269
|
+
const response = await _getFeedbacks({
|
|
270
|
+
page: page.value,
|
|
271
|
+
organization: route.params.org as string,
|
|
272
|
+
site: route.params.site as string,
|
|
273
|
+
});
|
|
274
|
+
if (response) {
|
|
275
|
+
items.value = response.items;
|
|
276
|
+
pages.value = response.pages;
|
|
277
|
+
pageRange.value = response.pageRange;
|
|
278
|
+
onNextPrevPageLoading.value = false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
264
282
|
function onSelectedUpdate(newSelected: string[]) {
|
|
265
283
|
selected.value = newSelected;
|
|
266
284
|
}
|
|
@@ -347,7 +365,6 @@ function handleCloseDialog() {
|
|
|
347
365
|
const _feedbackId = ref<string | null>(null);
|
|
348
366
|
|
|
349
367
|
async function _createFeedback(organization: string, site: string) {
|
|
350
|
-
|
|
351
368
|
const userId = getUserFromCookie();
|
|
352
369
|
|
|
353
370
|
const createPayload: TFeedbackCreate = {
|
|
@@ -361,7 +378,7 @@ async function _createFeedback(organization: string, site: string) {
|
|
|
361
378
|
assignee: _feedback.value.assignee || "",
|
|
362
379
|
organization,
|
|
363
380
|
site,
|
|
364
|
-
...(userId !== null ? {createdBy: userId } : {}),
|
|
381
|
+
...(userId !== null ? { createdBy: userId } : {}),
|
|
365
382
|
};
|
|
366
383
|
|
|
367
384
|
const response = await createFeedback(createPayload);
|