@7365admin1/layer-common 1.10.0 → 1.10.2
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/AcceptDialog.vue +44 -0
- package/components/AccessCard/AvailableStats.vue +55 -0
- package/components/AccessCardAddForm.vue +284 -19
- package/components/AccessCardAssignToUnitForm.vue +440 -0
- package/components/AccessManagement.vue +218 -85
- package/components/AddSupplyForm.vue +165 -0
- package/components/AreaChecklistHistoryLogs.vue +235 -0
- package/components/AreaChecklistHistoryMain.vue +176 -0
- package/components/AreaFormDialog.vue +266 -0
- package/components/AreaMain.vue +863 -0
- package/components/AttendanceCheckInOutDialog.vue +416 -0
- package/components/AttendanceDetailsDialog.vue +184 -0
- package/components/AttendanceMain.vue +155 -0
- package/components/AttendanceMapSearchDialog.vue +393 -0
- package/components/AttendanceSettingsDialog.vue +398 -0
- package/components/BuildingManagement/buildings.vue +5 -5
- package/components/BuildingManagement/units.vue +5 -5
- package/components/BulletinBoardManagement.vue +322 -0
- package/components/ChecklistItemRow.vue +54 -0
- package/components/CheckoutItemMain.vue +705 -0
- package/components/CleaningScheduleMain.vue +271 -0
- package/components/DocumentManagement.vue +4 -0
- package/components/EntryPass/QrTemplatePreview.vue +104 -0
- package/components/EntryPassMain.vue +252 -200
- package/components/HygieneUpdateMoreAction.vue +238 -0
- package/components/ManageChecklistMain.vue +384 -0
- package/components/MemberMain.vue +48 -20
- package/components/MyAttendanceMain.vue +224 -0
- package/components/OnlineFormsConfiguration.vue +9 -2
- package/components/PhotoUpload.vue +410 -0
- package/components/ScheduleAreaMain.vue +313 -0
- package/components/ScheduleTaskAreaFormDialog.vue +144 -0
- package/components/ScheduleTaskAreaUpdateMoreAction.vue +109 -0
- package/components/ScheduleTaskForm.vue +471 -0
- package/components/ScheduleTaskMain.vue +345 -0
- package/components/ScheduleTastTicketMain.vue +182 -0
- package/components/SignaturePad.vue +17 -5
- package/components/StockCard.vue +191 -0
- package/components/SupplyManagementMain.vue +557 -0
- package/components/TableHygiene.vue +617 -0
- package/components/UnitMain.vue +451 -0
- package/components/VisitorManagement.vue +28 -15
- package/composables/useAccessManagement.ts +163 -0
- package/composables/useAreaPermission.ts +51 -0
- package/composables/useAreas.ts +99 -0
- package/composables/useAttendance.ts +89 -0
- package/composables/useAttendancePermission.ts +68 -0
- package/composables/useBuilding.ts +2 -2
- package/composables/useBuildingUnit.ts +2 -2
- package/composables/useBulletin.ts +82 -0
- package/composables/useCard.ts +2 -0
- package/composables/useCheckout.ts +61 -0
- package/composables/useCheckoutPermission.ts +80 -0
- package/composables/useCleaningPermission.ts +229 -0
- package/composables/useCleaningSchedulePermission.ts +58 -0
- package/composables/useCleaningSchedules.ts +233 -0
- package/composables/useCountry.ts +8 -0
- package/composables/useDashboardData.ts +2 -2
- package/composables/useFeedback.ts +1 -1
- package/composables/useLocation.ts +78 -0
- package/composables/useOnlineForm.ts +16 -9
- package/composables/usePeople.ts +87 -72
- package/composables/useQR.ts +29 -0
- package/composables/useScheduleTask.ts +89 -0
- package/composables/useScheduleTaskArea.ts +85 -0
- package/composables/useScheduleTaskPermission.ts +68 -0
- package/composables/useSiteEntryPassSettings.ts +4 -15
- package/composables/useStock.ts +45 -0
- package/composables/useSupply.ts +63 -0
- package/composables/useSupplyPermission.ts +92 -0
- package/composables/useUnitPermission.ts +51 -0
- package/composables/useUnits.ts +82 -0
- package/composables/useWebUsb.ts +389 -0
- package/composables/useWorkOrder.ts +1 -1
- package/nuxt.config.ts +3 -0
- package/package.json +4 -1
- package/types/area.d.ts +22 -0
- package/types/attendance.d.ts +38 -0
- package/types/checkout-item.d.ts +27 -0
- package/types/cleaner-schedule.d.ts +54 -0
- package/types/location.d.ts +42 -0
- package/types/schedule-task.d.ts +18 -0
- package/types/stock.d.ts +16 -0
- package/types/supply.d.ts +11 -0
- package/utils/acm-crypto.ts +30 -0
|
@@ -2,16 +2,28 @@
|
|
|
2
2
|
<v-row no-gutters>
|
|
3
3
|
<v-col cols="12" class="mb-2">
|
|
4
4
|
<v-row no-gutters align="center" justify="space-between">
|
|
5
|
-
<v-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
<v-row no-gutters class="ga-2">
|
|
6
|
+
<v-btn
|
|
7
|
+
class="text-none"
|
|
8
|
+
rounded="pill"
|
|
9
|
+
variant="tonal"
|
|
10
|
+
size="large"
|
|
11
|
+
@click="setCard()"
|
|
12
|
+
v-if="canCreate && canCreateAccessCard"
|
|
13
|
+
>
|
|
14
|
+
Add Access Card
|
|
15
|
+
</v-btn>
|
|
16
|
+
<v-btn
|
|
17
|
+
class="text-none"
|
|
18
|
+
rounded="pill"
|
|
19
|
+
variant="tonal"
|
|
20
|
+
size="large"
|
|
21
|
+
@click="assignDialog = true"
|
|
22
|
+
v-if="canCreate && canAssignAccessCard"
|
|
23
|
+
>
|
|
24
|
+
Assign to Unit
|
|
25
|
+
</v-btn>
|
|
26
|
+
</v-row>
|
|
15
27
|
|
|
16
28
|
<v-text-field
|
|
17
29
|
v-model="searchText"
|
|
@@ -25,6 +37,11 @@
|
|
|
25
37
|
/>
|
|
26
38
|
</v-row>
|
|
27
39
|
</v-col>
|
|
40
|
+
<!-- Available Card Stats -->
|
|
41
|
+
<v-col cols="12" class="mb-2">
|
|
42
|
+
<AccessCardAvailableStats ref="statsRef" :site-id="siteId" />
|
|
43
|
+
</v-col>
|
|
44
|
+
|
|
28
45
|
<v-col cols="12">
|
|
29
46
|
<v-card
|
|
30
47
|
width="100%"
|
|
@@ -71,16 +88,17 @@
|
|
|
71
88
|
<AccessCardAddForm
|
|
72
89
|
@cancel="createDialog = false"
|
|
73
90
|
@success="successCreate()"
|
|
91
|
+
@error="(msg) => showMessage(msg, 'error')"
|
|
74
92
|
/>
|
|
75
93
|
</v-dialog>
|
|
76
94
|
|
|
77
|
-
<!--
|
|
78
|
-
<v-dialog v-model="
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
@cancel="
|
|
82
|
-
@success="
|
|
83
|
-
|
|
95
|
+
<!-- Assign to Unit Dialog -->
|
|
96
|
+
<v-dialog v-model="assignDialog" width="650" persistent>
|
|
97
|
+
<AccessCardAssignToUnitForm
|
|
98
|
+
:site-id="siteId"
|
|
99
|
+
@cancel="assignDialog = false"
|
|
100
|
+
@success="successAssign()"
|
|
101
|
+
@error="(msg: any) => showMessage(msg, 'error')"
|
|
84
102
|
/>
|
|
85
103
|
</v-dialog>
|
|
86
104
|
|
|
@@ -90,21 +108,130 @@
|
|
|
90
108
|
<v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
|
|
91
109
|
<v-row no-gutters class="mb-4">
|
|
92
110
|
<v-col cols="12">
|
|
93
|
-
<strong>
|
|
111
|
+
<strong>Unit:</strong> {{ selectedCard?.name ?? "N/A" }}
|
|
94
112
|
</v-col>
|
|
95
113
|
<v-col cols="12">
|
|
96
|
-
<strong>
|
|
97
|
-
{{ selectedCard?.accessCardType ?? "N/A" }}
|
|
114
|
+
<strong>Block:</strong> {{ selectedCard?.block?.name ?? "N/A" }}
|
|
98
115
|
</v-col>
|
|
99
116
|
<v-col cols="12">
|
|
100
|
-
<strong>
|
|
101
|
-
{{ selectedCard?.unit || "N/A" }}
|
|
117
|
+
<strong>Level:</strong> {{ selectedCard?.level?.level ?? "N/A" }}
|
|
102
118
|
</v-col>
|
|
103
|
-
|
|
104
|
-
|
|
119
|
+
|
|
120
|
+
<!-- Available Physical -->
|
|
121
|
+
<v-col cols="12" class="mt-3">
|
|
122
|
+
<strong>Available Physical</strong>
|
|
123
|
+
<div
|
|
124
|
+
v-if="selectedCard?.available?.physical?.length"
|
|
125
|
+
class="mt-1"
|
|
126
|
+
>
|
|
127
|
+
<v-chip
|
|
128
|
+
v-for="card in selectedCard.available.physical"
|
|
129
|
+
:key="card._id"
|
|
130
|
+
size="small"
|
|
131
|
+
class="mr-1 mb-1"
|
|
132
|
+
:color="
|
|
133
|
+
selectedCardInUnit?._id === card._id ? 'primary' : undefined
|
|
134
|
+
"
|
|
135
|
+
:variant="
|
|
136
|
+
selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
|
|
137
|
+
"
|
|
138
|
+
style="cursor: pointer"
|
|
139
|
+
@click="
|
|
140
|
+
selectedCardInUnit =
|
|
141
|
+
selectedCardInUnit?._id === card._id ? null : card
|
|
142
|
+
"
|
|
143
|
+
>
|
|
144
|
+
{{ card.cardNo }}
|
|
145
|
+
</v-chip>
|
|
146
|
+
</div>
|
|
147
|
+
<span v-else class="text-caption text-grey ml-1">None</span>
|
|
105
148
|
</v-col>
|
|
106
|
-
|
|
107
|
-
|
|
149
|
+
|
|
150
|
+
<!-- Available Non-Physical -->
|
|
151
|
+
<v-col cols="12" class="mt-2">
|
|
152
|
+
<strong>Available Non-Physical</strong>
|
|
153
|
+
<div
|
|
154
|
+
v-if="selectedCard?.available?.non_physical?.length"
|
|
155
|
+
class="mt-1"
|
|
156
|
+
>
|
|
157
|
+
<v-chip
|
|
158
|
+
v-for="card in selectedCard.available.non_physical"
|
|
159
|
+
:key="card._id"
|
|
160
|
+
size="small"
|
|
161
|
+
class="mr-1 mb-1"
|
|
162
|
+
:color="
|
|
163
|
+
selectedCardInUnit?._id === card._id ? 'primary' : undefined
|
|
164
|
+
"
|
|
165
|
+
:variant="
|
|
166
|
+
selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
|
|
167
|
+
"
|
|
168
|
+
style="cursor: pointer"
|
|
169
|
+
@click="
|
|
170
|
+
selectedCardInUnit =
|
|
171
|
+
selectedCardInUnit?._id === card._id ? null : card
|
|
172
|
+
"
|
|
173
|
+
>
|
|
174
|
+
{{ card.cardNo }}
|
|
175
|
+
</v-chip>
|
|
176
|
+
</div>
|
|
177
|
+
<span v-else class="text-caption text-grey ml-1">None</span>
|
|
178
|
+
</v-col>
|
|
179
|
+
|
|
180
|
+
<!-- Assigned Physical -->
|
|
181
|
+
<v-col cols="12" class="mt-2">
|
|
182
|
+
<strong>Assigned Physical</strong>
|
|
183
|
+
<div v-if="selectedCard?.assigned?.physical?.length" class="mt-1">
|
|
184
|
+
<v-chip
|
|
185
|
+
v-for="card in selectedCard.assigned.physical"
|
|
186
|
+
:key="card._id"
|
|
187
|
+
size="small"
|
|
188
|
+
class="mr-1 mb-1"
|
|
189
|
+
:color="
|
|
190
|
+
selectedCardInUnit?._id === card._id ? 'primary' : undefined
|
|
191
|
+
"
|
|
192
|
+
:variant="
|
|
193
|
+
selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
|
|
194
|
+
"
|
|
195
|
+
style="cursor: pointer"
|
|
196
|
+
@click="
|
|
197
|
+
selectedCardInUnit =
|
|
198
|
+
selectedCardInUnit?._id === card._id ? null : card
|
|
199
|
+
"
|
|
200
|
+
>
|
|
201
|
+
{{ card.cardNo }}
|
|
202
|
+
</v-chip>
|
|
203
|
+
</div>
|
|
204
|
+
<span v-else class="text-caption text-grey ml-1">None</span>
|
|
205
|
+
</v-col>
|
|
206
|
+
|
|
207
|
+
<!-- Assigned Non-Physical -->
|
|
208
|
+
<v-col cols="12" class="mt-2">
|
|
209
|
+
<strong>Assigned Non-Physical</strong>
|
|
210
|
+
<div
|
|
211
|
+
v-if="selectedCard?.assigned?.non_physical?.length"
|
|
212
|
+
class="mt-1"
|
|
213
|
+
>
|
|
214
|
+
<v-chip
|
|
215
|
+
v-for="card in selectedCard.assigned.non_physical"
|
|
216
|
+
:key="card._id"
|
|
217
|
+
size="small"
|
|
218
|
+
class="mr-1 mb-1"
|
|
219
|
+
:color="
|
|
220
|
+
selectedCardInUnit?._id === card._id ? 'primary' : undefined
|
|
221
|
+
"
|
|
222
|
+
:variant="
|
|
223
|
+
selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
|
|
224
|
+
"
|
|
225
|
+
style="cursor: pointer"
|
|
226
|
+
@click="
|
|
227
|
+
selectedCardInUnit =
|
|
228
|
+
selectedCardInUnit?._id === card._id ? null : card
|
|
229
|
+
"
|
|
230
|
+
>
|
|
231
|
+
{{ card.cardNo }}
|
|
232
|
+
</v-chip>
|
|
233
|
+
</div>
|
|
234
|
+
<span v-else class="text-caption text-grey ml-1">None</span>
|
|
108
235
|
</v-col>
|
|
109
236
|
</v-row></v-card-text
|
|
110
237
|
>
|
|
@@ -139,14 +266,10 @@
|
|
|
139
266
|
</template>
|
|
140
267
|
<v-list class="pa-0">
|
|
141
268
|
<v-list-item
|
|
142
|
-
|
|
143
|
-
|
|
269
|
+
:disabled="selectedCardInUnit === null"
|
|
270
|
+
@click="openReplaceDialog()"
|
|
271
|
+
v-if="canReplaceAccessCard"
|
|
144
272
|
>
|
|
145
|
-
<v-list-item-title class="text-subtitle-2">
|
|
146
|
-
Edit Card
|
|
147
|
-
</v-list-item-title>
|
|
148
|
-
</v-list-item>
|
|
149
|
-
<v-list-item @click="openReplaceDialog()" v-if="canReplaceAccessCard">
|
|
150
273
|
<v-list-item-title class="text-subtitle-2">
|
|
151
274
|
Replace Card
|
|
152
275
|
</v-list-item-title>
|
|
@@ -154,6 +277,7 @@
|
|
|
154
277
|
<v-list-item
|
|
155
278
|
@click="openDeleteDialog()"
|
|
156
279
|
class="text-red"
|
|
280
|
+
:disabled="selectedCardInUnit === null"
|
|
157
281
|
v-if="canDeleteAccessCard"
|
|
158
282
|
>
|
|
159
283
|
<v-list-item-title class="text-subtitle-2">
|
|
@@ -238,18 +362,38 @@ const props = defineProps({
|
|
|
238
362
|
type: Array as PropType<Array<Record<string, any>>>,
|
|
239
363
|
default: () => [
|
|
240
364
|
{
|
|
241
|
-
title: "
|
|
242
|
-
value: "
|
|
365
|
+
title: "Unit",
|
|
366
|
+
value: "name",
|
|
243
367
|
},
|
|
244
368
|
{
|
|
245
|
-
title: "
|
|
246
|
-
value: "
|
|
369
|
+
title: "Block",
|
|
370
|
+
value: "block.name",
|
|
247
371
|
},
|
|
248
372
|
{
|
|
249
|
-
title: "
|
|
250
|
-
value: "
|
|
373
|
+
title: "Level",
|
|
374
|
+
value: "level.level",
|
|
251
375
|
},
|
|
252
|
-
{
|
|
376
|
+
{
|
|
377
|
+
title: "Avail. (P)",
|
|
378
|
+
value: "cardCounts.available.physical",
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
title: "Avail. (NP)",
|
|
382
|
+
value: "cardCounts.available.non_physical",
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
title: "Asgn. (P)",
|
|
386
|
+
value: "cardCounts.assigned.physical",
|
|
387
|
+
},
|
|
388
|
+
{
|
|
389
|
+
title: "Asgn. (NP)",
|
|
390
|
+
value: "cardCounts.assigned.non_physical",
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
title: "Total",
|
|
394
|
+
value: "totalCardCount",
|
|
395
|
+
},
|
|
396
|
+
// { title: "Action", value: "action-table" },
|
|
253
397
|
],
|
|
254
398
|
},
|
|
255
399
|
canCreate: {
|
|
@@ -268,15 +412,15 @@ const props = defineProps({
|
|
|
268
412
|
type: Boolean,
|
|
269
413
|
default: true,
|
|
270
414
|
},
|
|
271
|
-
|
|
415
|
+
canDeleteAccessCard: {
|
|
272
416
|
type: Boolean,
|
|
273
417
|
default: true,
|
|
274
418
|
},
|
|
275
|
-
|
|
419
|
+
canReplaceAccessCard: {
|
|
276
420
|
type: Boolean,
|
|
277
421
|
default: true,
|
|
278
422
|
},
|
|
279
|
-
|
|
423
|
+
canAssignAccessCard: {
|
|
280
424
|
type: Boolean,
|
|
281
425
|
default: true,
|
|
282
426
|
},
|
|
@@ -293,39 +437,31 @@ const messageColor = ref("");
|
|
|
293
437
|
|
|
294
438
|
const items = ref<Array<Record<string, any>>>([]);
|
|
295
439
|
const createDialog = ref(false);
|
|
296
|
-
const
|
|
440
|
+
const assignDialog = ref(false);
|
|
297
441
|
const previewDialog = ref(false);
|
|
298
442
|
const deleteLoading = ref(false);
|
|
299
443
|
const confirmDialog = ref(false);
|
|
300
444
|
const searchText = ref("");
|
|
301
445
|
const replaceDialog = ref(false);
|
|
302
446
|
|
|
303
|
-
const
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
endDate: "",
|
|
312
|
-
door: "",
|
|
313
|
-
accessGroup: [],
|
|
314
|
-
cardType: "",
|
|
315
|
-
pinNo: "",
|
|
316
|
-
useAsLiftCard: false,
|
|
317
|
-
liftAccessLevel: "",
|
|
318
|
-
isActivate: true,
|
|
319
|
-
isAntiPassBack: false,
|
|
320
|
-
status: "",
|
|
321
|
-
org: "",
|
|
322
|
-
site: "",
|
|
323
|
-
unit: "",
|
|
324
|
-
assign: "",
|
|
325
|
-
});
|
|
447
|
+
const route = useRoute();
|
|
448
|
+
//@TODO
|
|
449
|
+
const siteId = computed(() => route.params.site as string);
|
|
450
|
+
// const siteId = computed(() => "66ab2f1381856008f1887971" as string);
|
|
451
|
+
const orgId = computed(() => route.params.org as string);
|
|
452
|
+
|
|
453
|
+
const selectedCard = ref<Record<string, any>>({});
|
|
454
|
+
const selectedCardInUnit = ref<Record<string, any> | null>(null);
|
|
326
455
|
const selectedCardId = ref<string | null>(null);
|
|
327
456
|
|
|
328
|
-
|
|
457
|
+
watch(previewDialog, (val) => {
|
|
458
|
+
if (!val) selectedCardInUnit.value = null;
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
const { deleteById: _deleteCard } = useCard();
|
|
462
|
+
const { getUserTypeAccessCards } = useAccessManagement();
|
|
463
|
+
|
|
464
|
+
const statsRef = ref<{ refresh: () => void } | null>(null);
|
|
329
465
|
|
|
330
466
|
const {
|
|
331
467
|
data: getCardReq,
|
|
@@ -334,10 +470,12 @@ const {
|
|
|
334
470
|
} = useLazyAsyncData(
|
|
335
471
|
"get-all-cards",
|
|
336
472
|
() =>
|
|
337
|
-
|
|
473
|
+
getUserTypeAccessCards({
|
|
338
474
|
page: page.value,
|
|
339
475
|
search: searchText.value,
|
|
340
|
-
|
|
476
|
+
organization: orgId.value,
|
|
477
|
+
site: siteId.value,
|
|
478
|
+
userType: "Visitor/Resident",
|
|
341
479
|
}),
|
|
342
480
|
{
|
|
343
481
|
watch: [page, searchText],
|
|
@@ -347,19 +485,16 @@ const {
|
|
|
347
485
|
const loading = computed(() => getAllReqStatus.value === "pending");
|
|
348
486
|
|
|
349
487
|
watchEffect(() => {
|
|
350
|
-
if (getCardReq.value) {
|
|
351
|
-
items.value = getCardReq.value.items;
|
|
352
|
-
pages.value = getCardReq.value.pages;
|
|
353
|
-
pageRange.value = getCardReq.value.pageRange;
|
|
488
|
+
if (getCardReq.value?.data) {
|
|
489
|
+
items.value = getCardReq.value.data.items;
|
|
490
|
+
pages.value = getCardReq.value.data.pages;
|
|
491
|
+
pageRange.value = getCardReq.value.data.pageRange;
|
|
354
492
|
}
|
|
355
493
|
});
|
|
356
494
|
|
|
357
495
|
function setCard({ mode = "create", dialog = true, data = {} as TCard } = {}) {
|
|
358
496
|
if (mode === "create") {
|
|
359
497
|
createDialog.value = dialog;
|
|
360
|
-
} else if (mode === "edit") {
|
|
361
|
-
editDialog.value = dialog;
|
|
362
|
-
selectedCard.value = data;
|
|
363
498
|
} else if (mode === "preview") {
|
|
364
499
|
previewDialog.value = dialog;
|
|
365
500
|
selectedCard.value = data;
|
|
@@ -369,14 +504,15 @@ function setCard({ mode = "create", dialog = true, data = {} as TCard } = {}) {
|
|
|
369
504
|
function successCreate() {
|
|
370
505
|
createDialog.value = false;
|
|
371
506
|
getCards();
|
|
507
|
+
statsRef.value?.refresh();
|
|
372
508
|
showMessage("Card created successfully!", "success");
|
|
373
509
|
}
|
|
374
510
|
|
|
375
|
-
function
|
|
376
|
-
|
|
377
|
-
previewDialog.value = false;
|
|
511
|
+
function successAssign() {
|
|
512
|
+
assignDialog.value = false;
|
|
378
513
|
getCards();
|
|
379
|
-
|
|
514
|
+
statsRef.value?.refresh();
|
|
515
|
+
showMessage("Access card assigned successfully!", "success");
|
|
380
516
|
}
|
|
381
517
|
|
|
382
518
|
function showMessage(msg: string, color: string) {
|
|
@@ -390,10 +526,6 @@ function tableRowClickHandler(_: any, data: any) {
|
|
|
390
526
|
previewDialog.value = true;
|
|
391
527
|
}
|
|
392
528
|
|
|
393
|
-
function openEditDialog() {
|
|
394
|
-
editDialog.value = true;
|
|
395
|
-
}
|
|
396
|
-
|
|
397
529
|
function openDeleteDialog() {
|
|
398
530
|
confirmDialog.value = true;
|
|
399
531
|
message.value = "";
|
|
@@ -408,6 +540,7 @@ async function handleDeleteCard() {
|
|
|
408
540
|
try {
|
|
409
541
|
await _deleteCard(selectedCard.value._id ?? "");
|
|
410
542
|
await getCards();
|
|
543
|
+
statsRef.value?.refresh();
|
|
411
544
|
selectedCardId.value = null;
|
|
412
545
|
confirmDialog.value = false;
|
|
413
546
|
previewDialog.value = false;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<v-dialog v-model="showDialog" max-width="450">
|
|
3
|
+
<v-card>
|
|
4
|
+
<v-toolbar>
|
|
5
|
+
<v-row no-gutters class="fill-height px-6" align="center">
|
|
6
|
+
<span class="font-weight-bold text-h6 text-capitalize">
|
|
7
|
+
{{ prop.mode === "edit" ? "Edit Supply" : "Add New Supply" }}
|
|
8
|
+
</span>
|
|
9
|
+
</v-row>
|
|
10
|
+
</v-toolbar>
|
|
11
|
+
|
|
12
|
+
<v-card-text class="pa-4">
|
|
13
|
+
<v-form ref="formRef" v-model="valid">
|
|
14
|
+
<v-row no-gutters class="pa-0">
|
|
15
|
+
<v-col cols="12" class="pa-0 mb-2">
|
|
16
|
+
<InputLabel for="name" title="Name" required class="mb-1" />
|
|
17
|
+
<v-text-field
|
|
18
|
+
v-model="nameModel"
|
|
19
|
+
:rules="[requiredRule]"
|
|
20
|
+
density="comfortable"
|
|
21
|
+
variant="outlined"
|
|
22
|
+
placeholder="Enter supply name"
|
|
23
|
+
hide-details="auto"
|
|
24
|
+
class="mb-0"
|
|
25
|
+
/>
|
|
26
|
+
</v-col>
|
|
27
|
+
|
|
28
|
+
<v-col cols="12" class="pa-0 mb-0">
|
|
29
|
+
<InputLabel
|
|
30
|
+
for="unitOfMeasurement"
|
|
31
|
+
title="Unit of Measurement"
|
|
32
|
+
required
|
|
33
|
+
class="mb-1"
|
|
34
|
+
/>
|
|
35
|
+
<v-select
|
|
36
|
+
v-model="unitOfMeasurementModel"
|
|
37
|
+
:items="unitOfMeasurementOptions"
|
|
38
|
+
:rules="[requiredRule]"
|
|
39
|
+
density="comfortable"
|
|
40
|
+
variant="outlined"
|
|
41
|
+
placeholder="Select unitOfMeasurement"
|
|
42
|
+
hide-details="auto"
|
|
43
|
+
class="mb-0"
|
|
44
|
+
/>
|
|
45
|
+
</v-col>
|
|
46
|
+
</v-row>
|
|
47
|
+
</v-form>
|
|
48
|
+
</v-card-text>
|
|
49
|
+
|
|
50
|
+
<v-toolbar class="pa-0" density="compact">
|
|
51
|
+
<v-row no-gutters>
|
|
52
|
+
<v-col cols="6" class="pa-0">
|
|
53
|
+
<v-btn
|
|
54
|
+
block
|
|
55
|
+
variant="text"
|
|
56
|
+
class="text-none"
|
|
57
|
+
size="large"
|
|
58
|
+
@click="close"
|
|
59
|
+
height="48"
|
|
60
|
+
>
|
|
61
|
+
Cancel
|
|
62
|
+
</v-btn>
|
|
63
|
+
</v-col>
|
|
64
|
+
|
|
65
|
+
<v-col cols="6" class="pa-0">
|
|
66
|
+
<v-btn
|
|
67
|
+
block
|
|
68
|
+
variant="flat"
|
|
69
|
+
color="black"
|
|
70
|
+
class="text-none font-weight-bold rounded-0"
|
|
71
|
+
height="48"
|
|
72
|
+
:loading="submitting"
|
|
73
|
+
@click="submit"
|
|
74
|
+
>
|
|
75
|
+
{{ prop.mode === "edit" ? "Save" : "Create" }}
|
|
76
|
+
</v-btn>
|
|
77
|
+
</v-col>
|
|
78
|
+
</v-row>
|
|
79
|
+
</v-toolbar>
|
|
80
|
+
</v-card>
|
|
81
|
+
</v-dialog>
|
|
82
|
+
</template>
|
|
83
|
+
|
|
84
|
+
<script setup lang="ts">
|
|
85
|
+
const nameModel = defineModel("name", { type: String, default: "" });
|
|
86
|
+
const unitOfMeasurementModel = defineModel("unitOfMeasurement", {
|
|
87
|
+
type: String,
|
|
88
|
+
default: "",
|
|
89
|
+
});
|
|
90
|
+
const showDialog = defineModel({ type: Boolean, default: false });
|
|
91
|
+
|
|
92
|
+
const prop = defineProps({
|
|
93
|
+
mode: {
|
|
94
|
+
type: String,
|
|
95
|
+
default: "add",
|
|
96
|
+
},
|
|
97
|
+
supplyData: {
|
|
98
|
+
type: Object,
|
|
99
|
+
default: null,
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const emit = defineEmits(["saved", "close"]);
|
|
104
|
+
|
|
105
|
+
const formRef = ref<any>(null);
|
|
106
|
+
const valid = ref(false);
|
|
107
|
+
const submitting = ref(false);
|
|
108
|
+
|
|
109
|
+
const { requiredRule } = useUtils();
|
|
110
|
+
|
|
111
|
+
const unitOfMeasurementOptions = [
|
|
112
|
+
"Pieces",
|
|
113
|
+
"Boxes",
|
|
114
|
+
"Bottles",
|
|
115
|
+
"Liters",
|
|
116
|
+
"Kilograms",
|
|
117
|
+
"Grams",
|
|
118
|
+
"Rolls",
|
|
119
|
+
"Packs",
|
|
120
|
+
"Sets",
|
|
121
|
+
"Units",
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
watchEffect(() => {
|
|
125
|
+
nameModel.value = prop.supplyData?.name || "";
|
|
126
|
+
unitOfMeasurementModel.value = prop.supplyData?.unitOfMeasurement || "";
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
function close() {
|
|
130
|
+
showDialog.value = false;
|
|
131
|
+
emit("close");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async function submit() {
|
|
135
|
+
const form = formRef.value as any;
|
|
136
|
+
let ok = valid.value;
|
|
137
|
+
if (form && typeof form.validate === "function") {
|
|
138
|
+
const result = await form.validate();
|
|
139
|
+
ok = result.valid;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (!ok) return;
|
|
143
|
+
|
|
144
|
+
submitting.value = true;
|
|
145
|
+
try {
|
|
146
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
147
|
+
|
|
148
|
+
emit("saved", {
|
|
149
|
+
name: nameModel.value,
|
|
150
|
+
|
|
151
|
+
unitOfMeasurement: unitOfMeasurementModel.value,
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
nameModel.value = "";
|
|
155
|
+
unitOfMeasurementModel.value = "";
|
|
156
|
+
|
|
157
|
+
showDialog.value = false;
|
|
158
|
+
emit("close");
|
|
159
|
+
} catch (e) {
|
|
160
|
+
console.error("Error submitting form:", e);
|
|
161
|
+
} finally {
|
|
162
|
+
submitting.value = false;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
</script>
|