@7365admin1/layer-common 1.8.6 → 1.10.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 +12 -0
- package/components/AccessCardAddForm.vue +235 -27
- package/components/BuildingUnitFormAdd.vue +13 -11
- package/components/Button/Close.vue +16 -0
- package/components/Dialog/UpdateMoreAction.vue +6 -1
- package/components/DocumentManagement.vue +4 -9
- package/components/FeedbackDetail.vue +21 -10
- package/components/IncidentReport/Authorities.vue +226 -0
- package/components/IncidentReport/IncidentInformation.vue +258 -0
- package/components/IncidentReport/affectedEntities.vue +167 -0
- package/components/Input/DatePicker.vue +102 -0
- package/components/InvitationMain.vue +19 -17
- package/components/PasswordConfirmation.vue +95 -0
- package/components/RolePermissionMain.vue +17 -15
- package/components/ServiceProviderMain.vue +27 -7
- package/components/VisitorForm.vue +3 -1
- package/components/VisitorManagement.vue +6 -1
- package/components/WorkOrder/Detail.vue +179 -2
- package/components/WorkOrder/Main.vue +65 -4
- package/composables/useBuildingUnit.ts +1 -1
- package/composables/useDOBEntries.ts +13 -0
- package/composables/useDocument.ts +3 -2
- package/composables/useFeedback.ts +3 -2
- package/composables/useFile.ts +4 -6
- package/composables/useNFCPatrolSettings.ts +1 -1
- package/composables/useRole.ts +3 -2
- package/composables/useWorkOrder.ts +8 -0
- package/package.json +1 -1
- package/types/card.d.ts +5 -0
- package/types/verification.d.ts +1 -1
- /package/components/{NFC/PatrolSettings.vue → Nfc/NFCPatrolSettings.vue} +0 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="d-flex flex-column">
|
|
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">
|
|
5
|
+
<template #append-inner>
|
|
6
|
+
<v-icon icon="mdi-calendar" @click.stop="openDatePicker" />
|
|
7
|
+
</template>
|
|
8
|
+
</v-text-field>
|
|
9
|
+
<div class="w-100 d-flex align-end ga-3 hidden-input">
|
|
10
|
+
<input ref="dateInput" type="date" v-model="date" />
|
|
11
|
+
</div>
|
|
12
|
+
</div>
|
|
13
|
+
</template>
|
|
14
|
+
|
|
15
|
+
<script setup lang="ts">
|
|
16
|
+
|
|
17
|
+
const prop = defineProps({
|
|
18
|
+
rules: {
|
|
19
|
+
type: Array as PropType<Array<any>>,
|
|
20
|
+
default: () => []
|
|
21
|
+
},
|
|
22
|
+
placeholder: {
|
|
23
|
+
type: String,
|
|
24
|
+
default: 'MM/DD/YYYY'
|
|
25
|
+
}
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
const date = defineModel<string | null>({ default: null }) // YYYY-MM-DD format
|
|
29
|
+
|
|
30
|
+
const dateFormattedReadOnly = ref<string | null>(null)
|
|
31
|
+
|
|
32
|
+
const dateInput = ref<HTMLInputElement | null>(null)
|
|
33
|
+
const datePickerRef = ref<HTMLInputElement | null>(null)
|
|
34
|
+
|
|
35
|
+
const isInitialLoad = ref(true)
|
|
36
|
+
|
|
37
|
+
function openDatePicker() {
|
|
38
|
+
setTimeout(() => {
|
|
39
|
+
dateInput.value?.showPicker?.()
|
|
40
|
+
}, 0)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function validate() {
|
|
44
|
+
(datePickerRef.value as any)?.validate()
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function convertToReadableFormat(dateStr: string): string {
|
|
48
|
+
if (!dateStr) return ""
|
|
49
|
+
const dateObj = new Date(dateStr + "T00:00:00")
|
|
50
|
+
const options: Intl.DateTimeFormatOptions = {
|
|
51
|
+
year: 'numeric',
|
|
52
|
+
month: '2-digit',
|
|
53
|
+
day: '2-digit'
|
|
54
|
+
}
|
|
55
|
+
return dateObj.toLocaleDateString('en-US', options)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function handleInitialDate() {
|
|
59
|
+
if (date.value) {
|
|
60
|
+
dateFormattedReadOnly.value = convertToReadableFormat(date.value)
|
|
61
|
+
} else {
|
|
62
|
+
dateFormattedReadOnly.value = null
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
watch(date, (dateVal) => {
|
|
67
|
+
if (isInitialLoad.value) return
|
|
68
|
+
if (!dateVal) {
|
|
69
|
+
dateFormattedReadOnly.value = null
|
|
70
|
+
return
|
|
71
|
+
}
|
|
72
|
+
dateFormattedReadOnly.value = convertToReadableFormat(dateVal)
|
|
73
|
+
}, { immediate: false })
|
|
74
|
+
|
|
75
|
+
watch(date, () => {
|
|
76
|
+
handleInitialDate()
|
|
77
|
+
}, { immediate: true })
|
|
78
|
+
|
|
79
|
+
onMounted(async () => {
|
|
80
|
+
await nextTick()
|
|
81
|
+
isInitialLoad.value = false
|
|
82
|
+
const nativeInput = (datePickerRef.value as any)?.$el?.querySelector('input')
|
|
83
|
+
if (nativeInput) {
|
|
84
|
+
nativeInput.addEventListener('click', (e: MouseEvent) => {
|
|
85
|
+
e.stopPropagation()
|
|
86
|
+
openDatePicker()
|
|
87
|
+
})
|
|
88
|
+
}
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
defineExpose({
|
|
92
|
+
validate
|
|
93
|
+
})
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<style scoped>
|
|
97
|
+
.hidden-input {
|
|
98
|
+
opacity: 0;
|
|
99
|
+
height: 0;
|
|
100
|
+
width: 1px;
|
|
101
|
+
}
|
|
102
|
+
</style>
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
<v-icon v-bind="props">mdi-dots-horizontal</v-icon>
|
|
94
94
|
</template>
|
|
95
95
|
<v-list>
|
|
96
|
-
<v-list-item @click="openConfirmDialog(item._id ?? '')">
|
|
96
|
+
<v-list-item @click="openConfirmDialog(item._id ?? '')" v-if="item.status === 'pending'">
|
|
97
97
|
Cancel Invite
|
|
98
98
|
</v-list-item>
|
|
99
99
|
</v-list>
|
|
@@ -118,21 +118,23 @@
|
|
|
118
118
|
</p>
|
|
119
119
|
</template>
|
|
120
120
|
<template #footer>
|
|
121
|
-
<
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
121
|
+
<div class="d-flex justify-center ga-3 w-100">
|
|
122
|
+
<v-btn
|
|
123
|
+
variant="text"
|
|
124
|
+
@click="confirmDialog = false"
|
|
125
|
+
:disabled="cancelLoading"
|
|
126
|
+
>
|
|
127
|
+
Close
|
|
128
|
+
</v-btn>
|
|
129
|
+
<v-btn
|
|
130
|
+
color="primary"
|
|
131
|
+
variant="flat"
|
|
132
|
+
@click="onConfirmCancel"
|
|
133
|
+
:loading="cancelLoading"
|
|
134
|
+
>
|
|
135
|
+
Cancel Invite
|
|
136
|
+
</v-btn>
|
|
137
|
+
</div>
|
|
136
138
|
</template>
|
|
137
139
|
</ConfirmDialog>
|
|
138
140
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
@@ -245,7 +247,7 @@ const {
|
|
|
245
247
|
status: props.status,
|
|
246
248
|
search: headerSearch.value,
|
|
247
249
|
type: "user-invite,member-invite",
|
|
248
|
-
app: props.app
|
|
250
|
+
app: props.app,
|
|
249
251
|
})
|
|
250
252
|
);
|
|
251
253
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-card width="100%" :disabled="loading || processingPassword" :loading="loading || processingPassword">
|
|
3
|
+
<v-toolbar density="compact">
|
|
4
|
+
<v-row no-gutters class="pa-3 text-h6 font-weight-bold">
|
|
5
|
+
<span>{{ promptTitle }}</span>
|
|
6
|
+
</v-row>
|
|
7
|
+
</v-toolbar>
|
|
8
|
+
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-5 px-7 text-center">
|
|
9
|
+
<v-row no-gutters class="w-100">
|
|
10
|
+
<v-col cols="12">
|
|
11
|
+
<v-icon icon="mdi-lock-outline" :color="'gray'" class="text-h2" />
|
|
12
|
+
</v-col>
|
|
13
|
+
<v-col cols="12" v-if="message" class="mt-2">
|
|
14
|
+
{{ message }}
|
|
15
|
+
</v-col>
|
|
16
|
+
<v-col cols="12" class="mt-5 text-start">
|
|
17
|
+
<v-text-field v-model="passwordInput" label="Password" type="password" density="compact" :error-messages="errorMessage"></v-text-field>
|
|
18
|
+
</v-col>
|
|
19
|
+
</v-row>
|
|
20
|
+
</v-card-text>
|
|
21
|
+
|
|
22
|
+
<v-toolbar class="pa-0" density="compact">
|
|
23
|
+
<v-row no-gutters>
|
|
24
|
+
<v-col cols="6" class="pa-0">
|
|
25
|
+
<v-btn block variant="text" class="text-none" size="large" tile @click="emit('cancel')" height="48">
|
|
26
|
+
Cancel
|
|
27
|
+
</v-btn>
|
|
28
|
+
</v-col>
|
|
29
|
+
|
|
30
|
+
<v-col cols="6" class="pa-0">
|
|
31
|
+
<v-btn block tile variant="flat" class="text-none" size="large" height="48" color="red" :disabled="!passwordInput"
|
|
32
|
+
@click="handleConfirm">
|
|
33
|
+
Confirm
|
|
34
|
+
</v-btn>
|
|
35
|
+
</v-col>
|
|
36
|
+
</v-row>
|
|
37
|
+
</v-toolbar>
|
|
38
|
+
</v-card>
|
|
39
|
+
</template>
|
|
40
|
+
|
|
41
|
+
<script setup lang="ts">
|
|
42
|
+
|
|
43
|
+
const props = defineProps({
|
|
44
|
+
message: {
|
|
45
|
+
type: String,
|
|
46
|
+
default: "Confirm your digital signature with password to proceed."
|
|
47
|
+
},
|
|
48
|
+
promptTitle: {
|
|
49
|
+
type: String,
|
|
50
|
+
default: "Confirm Action"
|
|
51
|
+
},
|
|
52
|
+
loading: {
|
|
53
|
+
type: Boolean,
|
|
54
|
+
default: false
|
|
55
|
+
},
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
const { confirmPassword } = useDOBEntries();
|
|
59
|
+
const { currentUser } = useLocalAuth();
|
|
60
|
+
|
|
61
|
+
const emit = defineEmits(["cancel", "confirm"])
|
|
62
|
+
|
|
63
|
+
const processingPassword = ref(false)
|
|
64
|
+
const errorMessage = ref('')
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
const passwordInput = ref('')
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
async function handleConfirm() {
|
|
72
|
+
errorMessage.value = ''
|
|
73
|
+
try {
|
|
74
|
+
processingPassword.value = true;
|
|
75
|
+
const userId = currentUser.value?._id;
|
|
76
|
+
if (!userId) {
|
|
77
|
+
throw new Error('User not found. Please login again.');
|
|
78
|
+
}
|
|
79
|
+
const res = await confirmPassword({ userId, password: passwordInput.value });
|
|
80
|
+
if (res) {
|
|
81
|
+
emit('confirm');
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
} catch (err: any) {
|
|
85
|
+
const errMessage = err?.response?._data?.message || 'Something went wrong. Please try again.';
|
|
86
|
+
console.log('[ERROR]', err)
|
|
87
|
+
errorMessage.value = errMessage;
|
|
88
|
+
} finally {
|
|
89
|
+
processingPassword.value = false;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
</script>
|
|
94
|
+
|
|
95
|
+
<style scoped></style>
|
|
@@ -119,21 +119,23 @@
|
|
|
119
119
|
</template>
|
|
120
120
|
|
|
121
121
|
<template #footer>
|
|
122
|
-
<
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
122
|
+
<div class="d-flex justify-center ga-3 w-100">
|
|
123
|
+
<v-btn
|
|
124
|
+
variant="text"
|
|
125
|
+
@click="confirmDialog = false"
|
|
126
|
+
:disabled="deleteLoading"
|
|
127
|
+
>
|
|
128
|
+
Close
|
|
129
|
+
</v-btn>
|
|
130
|
+
<v-btn
|
|
131
|
+
color="primary"
|
|
132
|
+
variant="flat"
|
|
133
|
+
@click="handleDeleteRole"
|
|
134
|
+
:loading="deleteLoading"
|
|
135
|
+
>
|
|
136
|
+
Delete Role
|
|
137
|
+
</v-btn>
|
|
138
|
+
</div>
|
|
137
139
|
</template>
|
|
138
140
|
</ConfirmDialog>
|
|
139
141
|
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
</div>
|
|
41
41
|
|
|
42
42
|
<v-text-field
|
|
43
|
+
v-model="searchText"
|
|
43
44
|
placeholder="Search..."
|
|
44
45
|
variant="outlined"
|
|
45
46
|
density="comfortable"
|
|
@@ -420,19 +421,27 @@ const {
|
|
|
420
421
|
invite: inviteServiceProvider,
|
|
421
422
|
} = useServiceProvider();
|
|
422
423
|
|
|
423
|
-
const loading = ref(true);
|
|
424
|
-
|
|
425
424
|
const { getSiteById } = useSite();
|
|
426
425
|
|
|
426
|
+
const { getColorStatus, debounce } = useUtils();
|
|
427
|
+
|
|
427
428
|
const { data: site } = await useLazyAsyncData(
|
|
428
429
|
"get-site-by-id-" + props.siteId,
|
|
429
430
|
() => getSiteById(props.siteId)
|
|
430
431
|
);
|
|
431
432
|
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
433
|
+
const loading = computed(() => getAllReqStatus.value === "pending");
|
|
434
|
+
|
|
435
|
+
const {
|
|
436
|
+
data: serviceProviderReq,
|
|
437
|
+
refresh: _getAllServiceProvider,
|
|
438
|
+
status: getAllReqStatus,
|
|
439
|
+
} = await useLazyAsyncData("get-all-service-providers", () =>
|
|
440
|
+
getAllServiceProvider({
|
|
441
|
+
siteId: props.siteId,
|
|
442
|
+
search: searchText.value || "",
|
|
443
|
+
})
|
|
444
|
+
);
|
|
436
445
|
|
|
437
446
|
watchEffect(() => {
|
|
438
447
|
// console.log("serviceProviderReq", serviceProviderReq.value);
|
|
@@ -441,12 +450,14 @@ watchEffect(() => {
|
|
|
441
450
|
pages.value = serviceProviderReq.value.pages;
|
|
442
451
|
pageRange.value = serviceProviderReq.value.pageRange;
|
|
443
452
|
}
|
|
444
|
-
loading.value = false;
|
|
453
|
+
// loading.value = false;
|
|
445
454
|
});
|
|
446
455
|
|
|
447
456
|
const createDialog = ref(false);
|
|
448
457
|
const createInviteDialog = ref(false);
|
|
449
458
|
|
|
459
|
+
const searchText = ref("");
|
|
460
|
+
|
|
450
461
|
const success = () => {
|
|
451
462
|
createDialog.value = false;
|
|
452
463
|
// getServiceProvider();
|
|
@@ -544,4 +555,13 @@ async function submitServiceProviderInvite() {
|
|
|
544
555
|
disableServiceProvider.value = false;
|
|
545
556
|
}
|
|
546
557
|
}
|
|
558
|
+
|
|
559
|
+
const debounceSearch = debounce(_getAllServiceProvider, 500);
|
|
560
|
+
|
|
561
|
+
watch(
|
|
562
|
+
() => searchText.value,
|
|
563
|
+
() => {
|
|
564
|
+
debounceSearch();
|
|
565
|
+
}
|
|
566
|
+
);
|
|
547
567
|
</script>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-card width="100%" :loading="processing">
|
|
3
3
|
<v-toolbar>
|
|
4
|
-
<v-row no-gutters class="fill-height px-6" align="center">
|
|
4
|
+
<v-row no-gutters class="fill-height px-6 d-flex justify-space-between align-center" align="center">
|
|
5
5
|
<span class="font-weight-bold text-h5 text-capitalize">
|
|
6
6
|
{{ prop.mode }} {{ formatVisitorType(type) }}
|
|
7
7
|
</span>
|
|
8
|
+
<ButtonClose @click="emit('close:all')" />
|
|
8
9
|
</v-row>
|
|
9
10
|
</v-toolbar>
|
|
10
11
|
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-3">
|
|
@@ -238,6 +239,7 @@ const emit = defineEmits([
|
|
|
238
239
|
"done:more",
|
|
239
240
|
"error",
|
|
240
241
|
"close",
|
|
242
|
+
"close:all"
|
|
241
243
|
]);
|
|
242
244
|
|
|
243
245
|
const visitor = reactive<Partial<TVisitorPayload>>({
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
|
|
115
115
|
<v-dialog v-model="dialog.addVisitor" v-if="activeVisitorFormType" width="450" persistent>
|
|
116
116
|
<VisitorForm mode="add" :org="orgId" :site="siteId" :type="activeVisitorFormType" @back="handleClickBack"
|
|
117
|
-
@done="handleVisitorFormDone" @done:more="handleVisitorFormCreateMore" />
|
|
117
|
+
@done="handleVisitorFormDone" @done:more="handleVisitorFormCreateMore" @close:all="handleCloseAll" />
|
|
118
118
|
</v-dialog>
|
|
119
119
|
|
|
120
120
|
<v-dialog v-model="dialog.viewVisitor" width="450" persistent>
|
|
@@ -385,6 +385,11 @@ function showMessage(msg: string, color: string) {
|
|
|
385
385
|
messageSnackbar.value = true;
|
|
386
386
|
}
|
|
387
387
|
|
|
388
|
+
function handleCloseAll() {
|
|
389
|
+
dialog.showSelection = false;
|
|
390
|
+
dialog.addVisitor = false;
|
|
391
|
+
}
|
|
392
|
+
|
|
388
393
|
function handleUpdateAutofillDetails(people: TPeople){
|
|
389
394
|
dialog.vehicleNumberUsersList = false
|
|
390
395
|
console.log('people', people)
|
|
@@ -20,19 +20,51 @@
|
|
|
20
20
|
</v-col>
|
|
21
21
|
</v-row>
|
|
22
22
|
</div>
|
|
23
|
+
<WorkOrderCreate
|
|
24
|
+
v-model="showCreateDialog"
|
|
25
|
+
created-from="workOrder"
|
|
26
|
+
:work-order="_workOrder"
|
|
27
|
+
@update:work-order="(val: TWorkOrderCreate) => (_workOrder = val)"
|
|
28
|
+
:is-edit-mode="isEditMode"
|
|
29
|
+
:loading="isSubmitting"
|
|
30
|
+
:categories="serviceProviders"
|
|
31
|
+
:theme="theme"
|
|
32
|
+
:errored-images="erroredImages"
|
|
33
|
+
:max-files="5"
|
|
34
|
+
:message-fn="showMessage"
|
|
35
|
+
@close="handleCloseDialog"
|
|
36
|
+
@file-added="handleFileAdded"
|
|
37
|
+
@file-deleted="deleteFile"
|
|
38
|
+
@submit="submitWorkOrder"
|
|
39
|
+
/>
|
|
40
|
+
|
|
41
|
+
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
23
42
|
</template>
|
|
24
43
|
<script lang="ts" setup>
|
|
44
|
+
import { useTheme } from "vuetify";
|
|
25
45
|
const route = useRoute();
|
|
26
46
|
const id = route.params.id;
|
|
27
47
|
|
|
28
48
|
const {
|
|
29
49
|
workOrder,
|
|
30
|
-
workOrders,
|
|
31
50
|
getWorkOrderById,
|
|
32
51
|
getWorkOrders: _getWorkOrders,
|
|
52
|
+
updateWorkOrder,
|
|
53
|
+
createWorkOrder,
|
|
33
54
|
} = useWorkOrder();
|
|
34
55
|
|
|
35
56
|
const { getServiceProviderNames } = useServiceProvider();
|
|
57
|
+
const erroredImages = ref<string[]>([]);
|
|
58
|
+
|
|
59
|
+
const message = ref("");
|
|
60
|
+
const messageColor = ref("");
|
|
61
|
+
const messageSnackbar = ref(false);
|
|
62
|
+
|
|
63
|
+
function showMessage(msg: string, color: string) {
|
|
64
|
+
message.value = msg;
|
|
65
|
+
messageColor.value = color;
|
|
66
|
+
messageSnackbar.value = true;
|
|
67
|
+
}
|
|
36
68
|
|
|
37
69
|
const _getWorkOrderById = async () => {
|
|
38
70
|
try {
|
|
@@ -67,5 +99,150 @@ const showCreateDialog = ref(false);
|
|
|
67
99
|
const showCompleteDialog = ref(false);
|
|
68
100
|
const showDeleteDialog = ref(false);
|
|
69
101
|
|
|
70
|
-
|
|
102
|
+
const _workOrder = ref<TWorkOrderCreate>({
|
|
103
|
+
attachments: [],
|
|
104
|
+
category: "",
|
|
105
|
+
subject: "",
|
|
106
|
+
location: "",
|
|
107
|
+
description: "",
|
|
108
|
+
highPriority: false,
|
|
109
|
+
block: "",
|
|
110
|
+
level: "",
|
|
111
|
+
unit: "",
|
|
112
|
+
serviceProvider: "",
|
|
113
|
+
assignee: "",
|
|
114
|
+
organization: "",
|
|
115
|
+
site: "",
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const isEditMode = ref(false);
|
|
119
|
+
const isSubmitting = ref(false);
|
|
120
|
+
const theme = useTheme().name;
|
|
121
|
+
|
|
122
|
+
const _workOrderId = ref<string | null>(null);
|
|
123
|
+
|
|
124
|
+
const serviceProviders = ref<
|
|
125
|
+
Array<{ title: string; value: string; subtitle: string }>
|
|
126
|
+
>([]);
|
|
127
|
+
|
|
128
|
+
const { getBySiteAsServiceProvider } = useCustomerSite();
|
|
129
|
+
|
|
130
|
+
const { data: getAllReq } = useLazyAsyncData(
|
|
131
|
+
"get-by-site-as-service-provider",
|
|
132
|
+
() => getBySiteAsServiceProvider(useRoute().params.site as string)
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
watchEffect(() => {
|
|
136
|
+
if (getAllReq.value) {
|
|
137
|
+
serviceProviders.value = getAllReq.value.map((i: any) => ({
|
|
138
|
+
title: i.nature.replace(/_/g, " "),
|
|
139
|
+
subtitle: i.title,
|
|
140
|
+
value: i.nature,
|
|
141
|
+
}));
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
function openEditDialog() {
|
|
146
|
+
isEditMode.value = true;
|
|
147
|
+
_workOrder.value = { ...workOrder.value };
|
|
148
|
+
showCreateDialog.value = true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function handleCloseDialog() {
|
|
152
|
+
resetWorkOrderForm();
|
|
153
|
+
isEditMode.value = false;
|
|
154
|
+
showCreateDialog.value = false;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function resetWorkOrderForm() {
|
|
158
|
+
_workOrder.value = {
|
|
159
|
+
attachments: [],
|
|
160
|
+
category: "",
|
|
161
|
+
subject: "",
|
|
162
|
+
location: "",
|
|
163
|
+
description: "",
|
|
164
|
+
highPriority: false,
|
|
165
|
+
block: "",
|
|
166
|
+
level: "",
|
|
167
|
+
unit: "",
|
|
168
|
+
serviceProvider: "",
|
|
169
|
+
assignee: "",
|
|
170
|
+
organization: "",
|
|
171
|
+
site: "",
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const { addFile, deleteFile: _deleteFile } = useFile();
|
|
176
|
+
|
|
177
|
+
const API_DO_STORAGE_ENDPOINT =
|
|
178
|
+
useRuntimeConfig().public.API_DO_STORAGE_ENDPOINT;
|
|
179
|
+
|
|
180
|
+
async function handleFileAdded(file: File) {
|
|
181
|
+
try {
|
|
182
|
+
const res = await addFile(file);
|
|
183
|
+
const uploadedId = res?.id;
|
|
184
|
+
if (uploadedId) {
|
|
185
|
+
const url = `${API_DO_STORAGE_ENDPOINT}/${uploadedId}`;
|
|
186
|
+
_workOrder.value.attachments = _workOrder.value.attachments ?? [];
|
|
187
|
+
_workOrder.value.attachments.push(url);
|
|
188
|
+
}
|
|
189
|
+
} catch (error) {
|
|
190
|
+
console.error("Error uploading file:", error);
|
|
191
|
+
showMessage("Failed to upload file", "error");
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
async function deleteFile(value: string) {
|
|
196
|
+
try {
|
|
197
|
+
await _deleteFile(value);
|
|
198
|
+
_workOrder.value.attachments = (_workOrder.value.attachments ?? []).filter(
|
|
199
|
+
(file) => file !== value
|
|
200
|
+
);
|
|
201
|
+
} catch (error) {
|
|
202
|
+
console.log(error);
|
|
203
|
+
showMessage("Failed to delete file", "error");
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function submitWorkOrder() {
|
|
208
|
+
if (!isEditMode.value || !workOrder.value?._id) {
|
|
209
|
+
console.warn("Update called without valid edit mode or work order ID.");
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
isSubmitting.value = true;
|
|
214
|
+
|
|
215
|
+
const payload = {
|
|
216
|
+
// ..._workOrder.value,
|
|
217
|
+
attachments: _workOrder.value.attachments,
|
|
218
|
+
category: _workOrder.value.category,
|
|
219
|
+
description: _workOrder.value.description,
|
|
220
|
+
highPriority: _workOrder.value.highPriority,
|
|
221
|
+
organization: route.params.org as string,
|
|
222
|
+
site: route.params.site as string,
|
|
223
|
+
subject: _workOrder.value.subject,
|
|
224
|
+
unit: _workOrder.value.unit,
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
let res: Record<string, any> = {};
|
|
228
|
+
|
|
229
|
+
if (isEditMode.value) {
|
|
230
|
+
console.log("updating...");
|
|
231
|
+
res = await updateWorkOrder(workOrder.value._id as string, payload);
|
|
232
|
+
} else {
|
|
233
|
+
res = await createWorkOrder(payload);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
showMessage(res.message, "success");
|
|
237
|
+
showCreateDialog.value = false;
|
|
238
|
+
|
|
239
|
+
await _getWorkOrderById();
|
|
240
|
+
|
|
241
|
+
resetWorkOrderForm();
|
|
242
|
+
} catch (err) {
|
|
243
|
+
showMessage((err as Error).message, "error");
|
|
244
|
+
} finally {
|
|
245
|
+
isSubmitting.value = false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
71
248
|
</script>
|