@7365admin1/layer-common 1.8.6 → 1.9.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 +235 -27
- package/components/BuildingUnitFormAdd.vue +13 -11
- package/components/Button/Close.vue +16 -0
- package/components/Dialog/UpdateMoreAction.vue +6 -1
- package/components/FeedbackDetail.vue +21 -10
- package/components/Input/DatePicker.vue +102 -0
- 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/useFeedback.ts +3 -2
- package/composables/useNFCPatrolSettings.ts +1 -1
- package/composables/useWorkOrder.ts +8 -0
- package/package.json +1 -1
- package/types/card.d.ts +5 -0
- /package/components/{NFC/PatrolSettings.vue → Nfc/NFCPatrolSettings.vue} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -24,7 +24,24 @@
|
|
|
24
24
|
type="text"
|
|
25
25
|
></v-select>
|
|
26
26
|
</v-col>
|
|
27
|
-
<v-col cols="12" class="px-1
|
|
27
|
+
<v-col cols="12" class="px-1" v-if="isNonPhysicalCard">
|
|
28
|
+
<InputLabel
|
|
29
|
+
class="text-capitalize font-weight-bold"
|
|
30
|
+
title="Quantity"
|
|
31
|
+
required
|
|
32
|
+
/>
|
|
33
|
+
<v-text-field
|
|
34
|
+
v-model.number="card.quantity"
|
|
35
|
+
density="compact"
|
|
36
|
+
hide-details
|
|
37
|
+
:rules="[requiredRule]"
|
|
38
|
+
type="number"
|
|
39
|
+
:min="1"
|
|
40
|
+
hide-spin-buttons
|
|
41
|
+
@input="enforceMinQuantity"
|
|
42
|
+
/>
|
|
43
|
+
</v-col>
|
|
44
|
+
<v-col cols="12" class="px-1 pb-5" v-if="!isNonPhysicalCard">
|
|
28
45
|
<InputLabel
|
|
29
46
|
class="text-capitalize font-weight-bold"
|
|
30
47
|
:title="`Card No (${cardNoMaxLength} digits)`"
|
|
@@ -35,36 +52,38 @@
|
|
|
35
52
|
density="compact"
|
|
36
53
|
hide-details
|
|
37
54
|
:rules="[requiredRule]"
|
|
38
|
-
maxlength="
|
|
55
|
+
:maxlength="cardNoMaxLength"
|
|
39
56
|
type="number"
|
|
40
57
|
hide-spin-buttons
|
|
58
|
+
:suffix="`${card.cardNumber?.toString().length || 0}/${cardNoMaxLength}`"
|
|
59
|
+
@input="limitCardNumber"
|
|
41
60
|
>
|
|
42
61
|
</v-text-field>
|
|
43
62
|
</v-col>
|
|
44
|
-
<v-col cols="12" class="px-1">
|
|
63
|
+
<v-col cols="12" class="px-1" v-if="!isNonPhysicalCard">
|
|
45
64
|
<InputLabel
|
|
46
65
|
class="text-capitalize font-weight-bold"
|
|
47
66
|
title="Start Date"
|
|
48
67
|
required
|
|
49
68
|
/>
|
|
50
|
-
<
|
|
51
|
-
ref="startDateRef"
|
|
69
|
+
<InputDatePicker
|
|
52
70
|
v-model="card.startDate"
|
|
53
71
|
placeholder="Start Date"
|
|
54
72
|
:rules="[requiredRule]"
|
|
73
|
+
@update:model-value="updateEndDate"
|
|
55
74
|
/>
|
|
56
75
|
</v-col>
|
|
57
|
-
<v-col cols="12" class="px-1 pb-4">
|
|
76
|
+
<v-col cols="12" class="px-1 pb-4" v-if="!isNonPhysicalCard">
|
|
58
77
|
<InputLabel
|
|
59
78
|
class="text-capitalize font-weight-bold"
|
|
60
79
|
title="End Date"
|
|
61
80
|
required
|
|
62
81
|
/>
|
|
63
|
-
<
|
|
64
|
-
ref="endDateRef"
|
|
82
|
+
<InputDatePicker
|
|
65
83
|
v-model="card.endDate"
|
|
66
84
|
placeholder="End Date"
|
|
67
85
|
:rules="[requiredRule]"
|
|
86
|
+
readonly
|
|
68
87
|
/>
|
|
69
88
|
</v-col>
|
|
70
89
|
<v-divider />
|
|
@@ -102,7 +121,7 @@
|
|
|
102
121
|
>
|
|
103
122
|
</v-select>
|
|
104
123
|
</v-col>
|
|
105
|
-
<v-col cols="12">
|
|
124
|
+
<v-col cols="12" v-if="!isNonPhysicalCard">
|
|
106
125
|
<InputLabel
|
|
107
126
|
class="text-capitalize font-weight-bold"
|
|
108
127
|
title="Card Type"
|
|
@@ -119,7 +138,7 @@
|
|
|
119
138
|
>
|
|
120
139
|
</v-select>
|
|
121
140
|
</v-col>
|
|
122
|
-
<v-col cols="12">
|
|
141
|
+
<v-col cols="12" v-if="!isNonPhysicalCard">
|
|
123
142
|
<InputLabel
|
|
124
143
|
class="text-capitalize font-weight-bold"
|
|
125
144
|
:title="`Pin No (${cardPinMaxLength} digits)`"
|
|
@@ -130,26 +149,27 @@
|
|
|
130
149
|
density="compact"
|
|
131
150
|
hide-details
|
|
132
151
|
:rules="[requiredRule]"
|
|
133
|
-
maxlength="
|
|
152
|
+
:maxlength="cardPinMaxLength"
|
|
134
153
|
type="number"
|
|
135
154
|
hide-spin-buttons
|
|
155
|
+
:suffix="`${card.pinNo?.toString().length || 0}/${cardPinMaxLength}`"
|
|
156
|
+
@input="limitPinNo"
|
|
136
157
|
>
|
|
137
158
|
</v-text-field>
|
|
138
159
|
</v-col>
|
|
139
160
|
<v-col cols="12" class="pt-5">
|
|
140
|
-
<v-
|
|
141
|
-
|
|
161
|
+
<v-switch
|
|
162
|
+
v-model="card.useAsLiftCard"
|
|
163
|
+
label="Use as Lift Card"
|
|
142
164
|
color="primary"
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
@click="card.useAsLiftCard = !card.useAsLiftCard"
|
|
147
|
-
></v-btn>
|
|
165
|
+
hide-details
|
|
166
|
+
density="compact"
|
|
167
|
+
/>
|
|
148
168
|
</v-col>
|
|
149
169
|
<v-col cols="12" v-if="card.useAsLiftCard">
|
|
150
170
|
<InputLabel
|
|
151
171
|
class="text-capitalize font-weight-bold"
|
|
152
|
-
title="Lift Access
|
|
172
|
+
title="Lift Access Level"
|
|
153
173
|
:required="card.useAsLiftCard"
|
|
154
174
|
/>
|
|
155
175
|
<v-select
|
|
@@ -187,6 +207,67 @@
|
|
|
187
207
|
</span>
|
|
188
208
|
</div>
|
|
189
209
|
</v-col>
|
|
210
|
+
<v-col cols="12">
|
|
211
|
+
<v-divider class="my-4" />
|
|
212
|
+
</v-col>
|
|
213
|
+
<v-col cols="12">
|
|
214
|
+
<v-switch
|
|
215
|
+
v-model="card.showAssign"
|
|
216
|
+
label="Assign (Optional)"
|
|
217
|
+
color="primary"
|
|
218
|
+
hide-details
|
|
219
|
+
density="compact"
|
|
220
|
+
/>
|
|
221
|
+
</v-col>
|
|
222
|
+
<template v-if="card.showAssign">
|
|
223
|
+
<v-col cols="12">
|
|
224
|
+
<InputLabel
|
|
225
|
+
class="text-capitalize font-weight-bold"
|
|
226
|
+
title="Building"
|
|
227
|
+
/>
|
|
228
|
+
<v-autocomplete
|
|
229
|
+
v-model="card.assignBuilding"
|
|
230
|
+
density="compact"
|
|
231
|
+
:items="buildingItems"
|
|
232
|
+
hide-details
|
|
233
|
+
item-title="name"
|
|
234
|
+
item-value="value"
|
|
235
|
+
placeholder="Search building..."
|
|
236
|
+
/>
|
|
237
|
+
</v-col>
|
|
238
|
+
<v-col cols="12">
|
|
239
|
+
<InputLabel
|
|
240
|
+
class="text-capitalize font-weight-bold"
|
|
241
|
+
title="Level"
|
|
242
|
+
/>
|
|
243
|
+
<v-autocomplete
|
|
244
|
+
v-model="card.assignLevel"
|
|
245
|
+
density="compact"
|
|
246
|
+
:items="levelItems"
|
|
247
|
+
hide-details
|
|
248
|
+
item-title="name"
|
|
249
|
+
item-value="value"
|
|
250
|
+
placeholder="Search level..."
|
|
251
|
+
:disabled="!card.assignBuilding"
|
|
252
|
+
/>
|
|
253
|
+
</v-col>
|
|
254
|
+
<v-col cols="12">
|
|
255
|
+
<InputLabel
|
|
256
|
+
class="text-capitalize font-weight-bold"
|
|
257
|
+
title="Unit"
|
|
258
|
+
/>
|
|
259
|
+
<v-autocomplete
|
|
260
|
+
v-model="card.assignUnit"
|
|
261
|
+
density="compact"
|
|
262
|
+
:items="unitItems"
|
|
263
|
+
hide-details
|
|
264
|
+
item-title="name"
|
|
265
|
+
item-value="value"
|
|
266
|
+
placeholder="Search unit..."
|
|
267
|
+
:disabled="!card.assignLevel"
|
|
268
|
+
/>
|
|
269
|
+
</v-col>
|
|
270
|
+
</template>
|
|
190
271
|
</v-row>
|
|
191
272
|
</v-row>
|
|
192
273
|
</v-form>
|
|
@@ -244,6 +325,8 @@ const prop = defineProps({
|
|
|
244
325
|
});
|
|
245
326
|
|
|
246
327
|
const { add: _addCard, updateById: _updateCardById } = useCard();
|
|
328
|
+
const { getAll: _getBuildings } = useBuilding();
|
|
329
|
+
const { getAllUnits: _getUnits } = useBuildingUnit();
|
|
247
330
|
const emit = defineEmits(["cancel", "success"]);
|
|
248
331
|
|
|
249
332
|
const validForm = ref(false);
|
|
@@ -252,27 +335,43 @@ const message = ref("");
|
|
|
252
335
|
|
|
253
336
|
const { requiredRule } = useUtils();
|
|
254
337
|
|
|
338
|
+
function formatDate(date: Date): string {
|
|
339
|
+
return date.toISOString().split("T")[0];
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const today = new Date();
|
|
343
|
+
const tenYearsLater = new Date(today);
|
|
344
|
+
tenYearsLater.setFullYear(today.getFullYear() + 10);
|
|
345
|
+
|
|
255
346
|
const card = ref<Record<string, any>>({
|
|
256
|
-
accessCardType: "",
|
|
347
|
+
accessCardType: "Physical Access Card",
|
|
257
348
|
type: "door-setup",
|
|
258
349
|
cardNumber: "",
|
|
259
|
-
startDate:
|
|
260
|
-
endDate:
|
|
350
|
+
startDate: formatDate(today),
|
|
351
|
+
endDate: formatDate(tenYearsLater),
|
|
261
352
|
door: "",
|
|
262
353
|
accessGroup: [],
|
|
263
|
-
cardType: "",
|
|
354
|
+
cardType: "Normal",
|
|
264
355
|
pinNo: "",
|
|
265
356
|
isActivate: true,
|
|
266
357
|
isAntiPassBack: false,
|
|
267
358
|
useAsLiftCard: false,
|
|
359
|
+
isWinsland: false,
|
|
268
360
|
status: "active",
|
|
269
361
|
liftAccessLevel: "",
|
|
270
362
|
org: "",
|
|
271
363
|
site: "",
|
|
272
364
|
unit: "",
|
|
273
365
|
assign: "",
|
|
366
|
+
quantity: 1,
|
|
367
|
+
showAssign: false,
|
|
368
|
+
assignBuilding: "",
|
|
369
|
+
assignLevel: "",
|
|
370
|
+
assignUnit: "",
|
|
274
371
|
});
|
|
275
372
|
|
|
373
|
+
const isNonPhysicalCard = computed(() => card.value.accessCardType === "Non Physical Access Card");
|
|
374
|
+
|
|
276
375
|
if (prop.mode === "edit") {
|
|
277
376
|
card.value.accessCardType = prop.card.accessCardType;
|
|
278
377
|
card.value.type = prop.card.type;
|
|
@@ -292,15 +391,19 @@ if (prop.mode === "edit") {
|
|
|
292
391
|
card.value.site = prop.card.site;
|
|
293
392
|
card.value.unit = prop.card.unit;
|
|
294
393
|
card.value.assign = prop.card.assign;
|
|
394
|
+
card.value.quantity = prop.card.quantity;
|
|
395
|
+
card.value.showAssign = prop.card.showAssign;
|
|
396
|
+
card.value.assignBuilding = prop.card.assignBuilding;
|
|
397
|
+
card.value.assignLevel = prop.card.assignLevel;
|
|
398
|
+
card.value.assignUnit = prop.card.assignUnit;
|
|
295
399
|
}
|
|
296
400
|
|
|
297
401
|
const accessCardTypes = ["Physical Access Card", "Non Physical Access Card"];
|
|
298
402
|
const typeOfVisitors = ["Contractor", "Visitor"];
|
|
299
403
|
|
|
300
404
|
const accessLevelItems = ref<{ name: string; no: string }[]>([
|
|
301
|
-
{ name: "All access", no: "1" },
|
|
302
405
|
]);
|
|
303
|
-
const accessGroupItems = ref([
|
|
406
|
+
const accessGroupItems = ref([]);
|
|
304
407
|
const cardTypeItems = ref([
|
|
305
408
|
{
|
|
306
409
|
name: "Normal - Access Card",
|
|
@@ -321,14 +424,119 @@ const cardTypeItems = ref([
|
|
|
321
424
|
]);
|
|
322
425
|
const cardNoMaxLength = ref(10);
|
|
323
426
|
const cardPinMaxLength = ref(6);
|
|
324
|
-
const startDateRef = ref<HTMLInputElement | null>(null);
|
|
325
|
-
const endDateRef = ref<HTMLInputElement | null>(null);
|
|
326
427
|
const liftAccessLevelItems = ref<{ name: string; no: string }[]>([]);
|
|
428
|
+
const buildingItems = ref<{ name: string; value: string }[]>([]);
|
|
429
|
+
const levelItems = ref<{ name: string; value: string }[]>([]);
|
|
430
|
+
const unitItems = ref<{ name: string; value: string }[]>([]);
|
|
431
|
+
const buildingsData = ref<Record<string, any>[]>([]);
|
|
327
432
|
|
|
328
433
|
const route = useRoute();
|
|
329
434
|
const siteId = route.params.site as string;
|
|
330
435
|
const orgId = route.params.org as string;
|
|
331
436
|
|
|
437
|
+
function limitCardNumber() {
|
|
438
|
+
if (card.value.cardNumber && card.value.cardNumber.toString().length > cardNoMaxLength.value) {
|
|
439
|
+
card.value.cardNumber = card.value.cardNumber.toString().slice(0, cardNoMaxLength.value);
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
function limitPinNo() {
|
|
444
|
+
if (card.value.pinNo && card.value.pinNo.toString().length > cardPinMaxLength.value) {
|
|
445
|
+
card.value.pinNo = card.value.pinNo.toString().slice(0, cardPinMaxLength.value);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
function enforceMinQuantity() {
|
|
450
|
+
if (card.value.quantity < 1 || !card.value.quantity) {
|
|
451
|
+
card.value.quantity = 1;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function updateEndDate(startDate: string | null) {
|
|
456
|
+
if (startDate) {
|
|
457
|
+
const date = new Date(startDate);
|
|
458
|
+
date.setFullYear(date.getFullYear() + 10);
|
|
459
|
+
card.value.endDate = formatDate(date);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
// Watch for Assign toggle to fetch buildings
|
|
464
|
+
watch(() => card.value.showAssign, async (newVal) => {
|
|
465
|
+
if (newVal) {
|
|
466
|
+
try {
|
|
467
|
+
const response = await _getBuildings({
|
|
468
|
+
page: 1,
|
|
469
|
+
status: "active",
|
|
470
|
+
site: siteId,
|
|
471
|
+
});
|
|
472
|
+
buildingsData.value = response.items || [];
|
|
473
|
+
buildingItems.value = buildingsData.value.map((building: any) => ({
|
|
474
|
+
name: building.name,
|
|
475
|
+
value: building._id,
|
|
476
|
+
}));
|
|
477
|
+
} catch (error) {
|
|
478
|
+
console.error("Failed to fetch buildings:", error);
|
|
479
|
+
}
|
|
480
|
+
} else {
|
|
481
|
+
// Reset selections when toggled off
|
|
482
|
+
card.value.assignBuilding = "";
|
|
483
|
+
card.value.assignLevel = "";
|
|
484
|
+
card.value.assignUnit = "";
|
|
485
|
+
buildingItems.value = [];
|
|
486
|
+
levelItems.value = [];
|
|
487
|
+
unitItems.value = [];
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
// Watch for building selection to populate levels
|
|
492
|
+
watch(() => card.value.assignBuilding, async (newVal) => {
|
|
493
|
+
if (newVal) {
|
|
494
|
+
const selectedBuilding = buildingsData.value.find((b: any) => b._id === newVal);
|
|
495
|
+
if (selectedBuilding && selectedBuilding.levels) {
|
|
496
|
+
levelItems.value = selectedBuilding.levels.map((level: string) => ({
|
|
497
|
+
name: level,
|
|
498
|
+
value: level,
|
|
499
|
+
}));
|
|
500
|
+
}
|
|
501
|
+
// Reset level and unit when building changes
|
|
502
|
+
card.value.assignLevel = "";
|
|
503
|
+
card.value.assignUnit = "";
|
|
504
|
+
unitItems.value = [];
|
|
505
|
+
} else {
|
|
506
|
+
levelItems.value = [];
|
|
507
|
+
card.value.assignLevel = "";
|
|
508
|
+
card.value.assignUnit = "";
|
|
509
|
+
unitItems.value = [];
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
// Watch for level selection to fetch units
|
|
514
|
+
watch(() => card.value.assignLevel, async (newVal) => {
|
|
515
|
+
if (newVal && card.value.assignBuilding) {
|
|
516
|
+
try {
|
|
517
|
+
const response = await _getUnits({
|
|
518
|
+
page: 1,
|
|
519
|
+
status: "active",
|
|
520
|
+
site: siteId,
|
|
521
|
+
building: card.value.assignBuilding,
|
|
522
|
+
});
|
|
523
|
+
const units = response.items || [];
|
|
524
|
+
// Filter units by selected level
|
|
525
|
+
const filteredUnits = units.filter((unit: any) => unit.level === newVal);
|
|
526
|
+
unitItems.value = filteredUnits.map((unit: any) => ({
|
|
527
|
+
name: unit.name,
|
|
528
|
+
value: unit._id,
|
|
529
|
+
}));
|
|
530
|
+
} catch (error) {
|
|
531
|
+
console.error("Failed to fetch units:", error);
|
|
532
|
+
}
|
|
533
|
+
card.value.assignUnit = "";
|
|
534
|
+
} else {
|
|
535
|
+
unitItems.value = [];
|
|
536
|
+
card.value.assignUnit = "";
|
|
537
|
+
}
|
|
538
|
+
});
|
|
539
|
+
|
|
332
540
|
function cancel() {
|
|
333
541
|
// createMore.value = false;
|
|
334
542
|
message.value = "";
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
<v-card-text style="max-height: 100vh; overflow-y: auto">
|
|
9
9
|
<v-form v-model="validForm" :disabled="disable">
|
|
10
10
|
<v-row no-gutters>
|
|
11
|
-
<v-col cols="12" class="mt-2">
|
|
11
|
+
<!-- <v-col cols="12" class="mt-2">
|
|
12
12
|
<v-row no-gutters>
|
|
13
13
|
<InputLabel class="text-capitalize" title="Name" required />
|
|
14
14
|
<v-col cols="12">
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
></v-text-field>
|
|
20
20
|
</v-col>
|
|
21
21
|
</v-row>
|
|
22
|
-
</v-col>
|
|
22
|
+
</v-col> -->
|
|
23
23
|
|
|
24
24
|
<v-col cols="12">
|
|
25
25
|
<v-row>
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
</v-row>
|
|
49
49
|
</v-col>
|
|
50
50
|
|
|
51
|
-
<v-col cols="12">
|
|
51
|
+
<!-- <v-col cols="12">
|
|
52
52
|
<v-row>
|
|
53
53
|
<v-col cols="12" class="mt-2">
|
|
54
54
|
<v-row no-gutters>
|
|
@@ -67,9 +67,9 @@
|
|
|
67
67
|
</v-row>
|
|
68
68
|
</v-col>
|
|
69
69
|
</v-row>
|
|
70
|
-
</v-col>
|
|
70
|
+
</v-col> -->
|
|
71
71
|
|
|
72
|
-
<v-col cols="12">
|
|
72
|
+
<!-- <v-col cols="12">
|
|
73
73
|
<v-row>
|
|
74
74
|
<v-col cols="12" class="mt-2">
|
|
75
75
|
<v-row no-gutters>
|
|
@@ -87,9 +87,9 @@
|
|
|
87
87
|
</v-row>
|
|
88
88
|
</v-col>
|
|
89
89
|
</v-row>
|
|
90
|
-
</v-col>
|
|
90
|
+
</v-col> -->
|
|
91
91
|
|
|
92
|
-
<v-col cols="12">
|
|
92
|
+
<!-- <v-col cols="12">
|
|
93
93
|
<v-row>
|
|
94
94
|
<v-col cols="12" class="mt-2">
|
|
95
95
|
<v-row no-gutters>
|
|
@@ -129,7 +129,7 @@
|
|
|
129
129
|
</v-row>
|
|
130
130
|
</v-col>
|
|
131
131
|
</v-row>
|
|
132
|
-
</v-col>
|
|
132
|
+
</v-col> -->
|
|
133
133
|
|
|
134
134
|
<v-col cols="12" class="mt-2">
|
|
135
135
|
<v-row no-gutters>
|
|
@@ -221,7 +221,7 @@
|
|
|
221
221
|
</v-row>
|
|
222
222
|
</v-expand-transition>
|
|
223
223
|
|
|
224
|
-
<v-col cols="12" class="mt-5">
|
|
224
|
+
<!-- <v-col cols="12" class="mt-5">
|
|
225
225
|
<InputLabel
|
|
226
226
|
class="text-capitalize"
|
|
227
227
|
title="Upload Files (Lease of Contract, Cert. of Occupancy)"
|
|
@@ -233,7 +233,7 @@
|
|
|
233
233
|
title="Upload PDF Files"
|
|
234
234
|
accept="application/pdf"
|
|
235
235
|
/>
|
|
236
|
-
</v-col>
|
|
236
|
+
</v-col> -->
|
|
237
237
|
|
|
238
238
|
<v-col cols="12" class="mt-2">
|
|
239
239
|
<v-checkbox v-model="createMore" density="comfortable" hide-details>
|
|
@@ -343,6 +343,7 @@ const buildingUnit = ref<TBuildingUnit>({
|
|
|
343
343
|
leaseEnd: "",
|
|
344
344
|
});
|
|
345
345
|
|
|
346
|
+
|
|
346
347
|
const startDateRef = ref<HTMLInputElement | null>(null);
|
|
347
348
|
const expiryDateRef = ref<HTMLInputElement | null>(null);
|
|
348
349
|
|
|
@@ -408,10 +409,11 @@ function setBuildingUnit() {
|
|
|
408
409
|
|
|
409
410
|
async function submit() {
|
|
410
411
|
disable.value = true;
|
|
412
|
+
const {buildingUnitFiles, companyName, companyRegistrationNumber, leaseStart, leaseEnd, ...rest} = buildingUnit.value;
|
|
411
413
|
try {
|
|
412
414
|
await add({
|
|
413
415
|
labels: unitLabels.value,
|
|
414
|
-
unit:
|
|
416
|
+
unit: rest,
|
|
415
417
|
qty: buildingUnitQty.value,
|
|
416
418
|
});
|
|
417
419
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-btn text="Close" class="bg-grey-lighten-2 text-capitalize" :height="prop.height" @click="emit('click')" />
|
|
3
|
+
</template>
|
|
4
|
+
|
|
5
|
+
<script setup lang="ts">
|
|
6
|
+
const prop = defineProps<{
|
|
7
|
+
height?: string | number;
|
|
8
|
+
}>();
|
|
9
|
+
|
|
10
|
+
const emit = defineEmits<{
|
|
11
|
+
(e: 'click'): void;
|
|
12
|
+
}>();
|
|
13
|
+
|
|
14
|
+
</script>
|
|
15
|
+
|
|
16
|
+
<style scoped></style>
|
|
@@ -41,6 +41,7 @@
|
|
|
41
41
|
color="black"
|
|
42
42
|
class="text-none"
|
|
43
43
|
height="48"
|
|
44
|
+
:disabled="disabled"
|
|
44
45
|
v-bind="props"
|
|
45
46
|
tile
|
|
46
47
|
>
|
|
@@ -78,6 +79,10 @@ const prop = defineProps({
|
|
|
78
79
|
type: Boolean,
|
|
79
80
|
default: true,
|
|
80
81
|
},
|
|
82
|
+
disabled: {
|
|
83
|
+
type: Boolean,
|
|
84
|
+
default: false,
|
|
85
|
+
},
|
|
81
86
|
editButtonLabel: {
|
|
82
87
|
type: String,
|
|
83
88
|
default: "Edit",
|
|
@@ -97,7 +102,7 @@ const prop = defineProps({
|
|
|
97
102
|
});
|
|
98
103
|
|
|
99
104
|
const emit = defineEmits(["close", "edit", "delete"]);
|
|
100
|
-
const { canUpdate, editButtonLabel, deleteButtonLabel, title } = prop;
|
|
105
|
+
const { canUpdate, editButtonLabel, deleteButtonLabel, title, disabled } = prop;
|
|
101
106
|
</script>
|
|
102
107
|
|
|
103
108
|
<style scoped></style>
|
|
@@ -305,21 +305,32 @@ async function handleCompletionFileAdded(file: File) {
|
|
|
305
305
|
|
|
306
306
|
const res = await addFile(file);
|
|
307
307
|
|
|
308
|
-
|
|
309
|
-
const url = `https://seven365-storage.sgp1.cdn.digitaloceanspaces.com/dev/${res.id}`;
|
|
308
|
+
const uploadedId = res?.id;
|
|
310
309
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
310
|
+
if (uploadedId) {
|
|
311
|
+
const url = `${uploadedId}`;
|
|
314
312
|
|
|
313
|
+
completionAttachments.value = completionAttachments.value ?? [];
|
|
315
314
|
completionAttachments.value.push(url);
|
|
315
|
+
}
|
|
316
316
|
|
|
317
|
-
|
|
317
|
+
// if (res?.id && res?.name) {
|
|
318
|
+
// const url = `https://seven365-storage.sgp1.cdn.digitaloceanspaces.com/dev/${res.id}`;
|
|
318
319
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
320
|
+
// if (!completionAttachments.value) {
|
|
321
|
+
// completionAttachments.value = [];
|
|
322
|
+
// }
|
|
323
|
+
|
|
324
|
+
// completionAttachments.value.push(url);
|
|
325
|
+
|
|
326
|
+
// completionFileNames.value[url] = res.name;
|
|
327
|
+
|
|
328
|
+
// if (completionFileInput.value) {
|
|
329
|
+
// completionFileInput.value.updateFileName(url, res.name);
|
|
330
|
+
// }
|
|
331
|
+
// }
|
|
332
|
+
|
|
333
|
+
console.log("completionAttachments.value", completionAttachments.value);
|
|
323
334
|
} catch (error) {
|
|
324
335
|
console.error("Error uploading completion file:", error);
|
|
325
336
|
showMessage("Failed to upload file", "error");
|
|
@@ -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>
|
|
@@ -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>
|
|
@@ -65,6 +65,9 @@
|
|
|
65
65
|
@click:row="tableRowClickHandler"
|
|
66
66
|
style="max-height: calc(100vh - (200px))"
|
|
67
67
|
>
|
|
68
|
+
<template v-slot:item.createdAt="{ item }">
|
|
69
|
+
{{ formatDateDDMMYYYYLocal(item.createdAt) }}
|
|
70
|
+
</template>
|
|
68
71
|
</v-data-table>
|
|
69
72
|
</v-card>
|
|
70
73
|
</v-col>
|
|
@@ -88,6 +91,39 @@
|
|
|
88
91
|
@submit="submitWorkOrder"
|
|
89
92
|
/>
|
|
90
93
|
|
|
94
|
+
<ConfirmDialog
|
|
95
|
+
v-model="showDeleteDialog"
|
|
96
|
+
:loading="submitting"
|
|
97
|
+
@submit="submitDelete"
|
|
98
|
+
:image-src="'/images/icons/delete-icon.png'"
|
|
99
|
+
>
|
|
100
|
+
<template #image>
|
|
101
|
+
<v-img
|
|
102
|
+
height="120"
|
|
103
|
+
src="/images/icons/delete-icon.png"
|
|
104
|
+
alt="Delete Icon"
|
|
105
|
+
contain
|
|
106
|
+
/>
|
|
107
|
+
</template>
|
|
108
|
+
|
|
109
|
+
<template #title>
|
|
110
|
+
Are you sure you want to delete this work order?
|
|
111
|
+
</template>
|
|
112
|
+
|
|
113
|
+
<template #footer>
|
|
114
|
+
<v-btn
|
|
115
|
+
color="primary-button"
|
|
116
|
+
variant="flat"
|
|
117
|
+
class="font-weight-bold py-5 d-flex align-center justify-center"
|
|
118
|
+
@click="submitDelete"
|
|
119
|
+
:loading="submitting"
|
|
120
|
+
block
|
|
121
|
+
>
|
|
122
|
+
Confirm
|
|
123
|
+
</v-btn>
|
|
124
|
+
</template>
|
|
125
|
+
</ConfirmDialog>
|
|
126
|
+
|
|
91
127
|
<Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
|
|
92
128
|
|
|
93
129
|
<!-- Preview Dialog -->
|
|
@@ -235,7 +271,8 @@ const message = ref("");
|
|
|
235
271
|
const messageColor = ref("");
|
|
236
272
|
const messageSnackbar = ref(false);
|
|
237
273
|
|
|
238
|
-
const { getColorStatus, formatDate, debounce } =
|
|
274
|
+
const { getColorStatus, formatDate, debounce, formatDateDDMMYYYYLocal } =
|
|
275
|
+
useUtils();
|
|
239
276
|
|
|
240
277
|
const _workOrder = ref<TWorkOrderCreate>({
|
|
241
278
|
attachments: [],
|
|
@@ -272,12 +309,14 @@ const submitting = ref(false);
|
|
|
272
309
|
const selected = ref<string[]>([]);
|
|
273
310
|
const route = useRoute();
|
|
274
311
|
const { customers } = useCustomer();
|
|
312
|
+
const showDeleteDialog = ref(false);
|
|
275
313
|
|
|
276
314
|
const {
|
|
277
315
|
getWorkOrders: _getWorkOrders,
|
|
278
316
|
createWorkOrder,
|
|
279
317
|
getWorkOrderById,
|
|
280
318
|
updateWorkOrder,
|
|
319
|
+
deleteWorkOrder,
|
|
281
320
|
} = useWorkOrder();
|
|
282
321
|
|
|
283
322
|
const page = ref(1);
|
|
@@ -444,8 +483,8 @@ async function submitWorkOrder() {
|
|
|
444
483
|
|
|
445
484
|
showMessage(res.message, "success");
|
|
446
485
|
showCreateDialog.value = false;
|
|
486
|
+
await getAllReqRefresh();
|
|
447
487
|
resetWorkOrderForm();
|
|
448
|
-
getAllReqRefresh();
|
|
449
488
|
} catch (err) {
|
|
450
489
|
showMessage((err as Error).message, "error");
|
|
451
490
|
} finally {
|
|
@@ -468,7 +507,6 @@ function onViewWorkOrder(item: any) {
|
|
|
468
507
|
async function editWorkOrder(item: any) {
|
|
469
508
|
try {
|
|
470
509
|
const _workOrders = await getWorkOrderById(item._id);
|
|
471
|
-
console.log(_workOrders);
|
|
472
510
|
_workOrder.value = {
|
|
473
511
|
attachments: (_workOrders.attachments || []) as string[],
|
|
474
512
|
category: _workOrders.category || "",
|
|
@@ -488,7 +526,30 @@ async function editWorkOrder(item: any) {
|
|
|
488
526
|
|
|
489
527
|
async function _updateWorkOrder() {}
|
|
490
528
|
|
|
491
|
-
function confirmDeleteWorkOrder(item: any) {
|
|
529
|
+
function confirmDeleteWorkOrder(item: any) {
|
|
530
|
+
workOrderToDelete.value = item;
|
|
531
|
+
showDeleteDialog.value = true;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
const workOrderToDelete = ref<any>(null);
|
|
535
|
+
|
|
536
|
+
async function submitDelete() {
|
|
537
|
+
if (!workOrderToDelete.value) return;
|
|
538
|
+
submitting.value = true;
|
|
539
|
+
try {
|
|
540
|
+
const response = await deleteWorkOrder(workOrderToDelete.value._id);
|
|
541
|
+
showMessage(response.message, "success");
|
|
542
|
+
await getAllReqRefresh();
|
|
543
|
+
} catch (error) {
|
|
544
|
+
console.error("Failed to delete work order:", error);
|
|
545
|
+
showMessage("Failed to delete work order!", "error");
|
|
546
|
+
} finally {
|
|
547
|
+
submitting.value = false;
|
|
548
|
+
showDeleteDialog.value = false;
|
|
549
|
+
workOrderToDelete.value = null;
|
|
550
|
+
dialogPreview.value = false;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
492
553
|
|
|
493
554
|
function tableRowClickHandler(_: any, data: any) {
|
|
494
555
|
selectedWorkOrder.value = data.item;
|
|
@@ -73,7 +73,7 @@ export default function useBuildingUnit() {
|
|
|
73
73
|
});
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
function add(data: { labels: string[]; unit: TBuildingUnit
|
|
76
|
+
function add(data: { labels: string[]; unit: Partial<TBuildingUnit>; qty?: number }) {
|
|
77
77
|
return useNuxtApp().$api("/api/building-units", {
|
|
78
78
|
method: "POST",
|
|
79
79
|
body: data,
|
|
@@ -75,8 +75,9 @@ export default function useFeedback() {
|
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
function deleteFeedback(id: string) {
|
|
78
|
-
return useNuxtApp().$api<Record<string, any>>(`/api/feedbacks
|
|
78
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/feedbacks/deleted/feedback`, {
|
|
79
79
|
method: "DELETE",
|
|
80
|
+
query: { id },
|
|
80
81
|
});
|
|
81
82
|
}
|
|
82
83
|
|
|
@@ -94,7 +95,7 @@ export default function useFeedback() {
|
|
|
94
95
|
|
|
95
96
|
function updateStatusComplete(id: string, payload: TFeedbackStatusComplete) {
|
|
96
97
|
return useNuxtApp().$api<Record<string, any>>(
|
|
97
|
-
`/api/feedbacks/${id}/status
|
|
98
|
+
`/api/feedbacks/${id}/status/complete`,
|
|
98
99
|
{
|
|
99
100
|
method: "PUT",
|
|
100
101
|
body: payload,
|
|
@@ -71,6 +71,13 @@ export default function useWorkOrder() {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
+
function deleteWorkOrder(id: string) {
|
|
75
|
+
return useNuxtApp().$api<Record<string, any>>(`/api/work-orders/deleted/work-order`, {
|
|
76
|
+
method: "DELETE",
|
|
77
|
+
query: { id },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
74
81
|
return {
|
|
75
82
|
workOrders,
|
|
76
83
|
workOrder,
|
|
@@ -81,5 +88,6 @@ export default function useWorkOrder() {
|
|
|
81
88
|
getWorkOrders,
|
|
82
89
|
getWorkOrderById,
|
|
83
90
|
updateWorkOrder,
|
|
91
|
+
deleteWorkOrder,
|
|
84
92
|
};
|
|
85
93
|
}
|
package/package.json
CHANGED
package/types/card.d.ts
CHANGED
|
File without changes
|