@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.
Files changed (86) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AcceptDialog.vue +44 -0
  3. package/components/AccessCard/AvailableStats.vue +55 -0
  4. package/components/AccessCardAddForm.vue +284 -19
  5. package/components/AccessCardAssignToUnitForm.vue +440 -0
  6. package/components/AccessManagement.vue +218 -85
  7. package/components/AddSupplyForm.vue +165 -0
  8. package/components/AreaChecklistHistoryLogs.vue +235 -0
  9. package/components/AreaChecklistHistoryMain.vue +176 -0
  10. package/components/AreaFormDialog.vue +266 -0
  11. package/components/AreaMain.vue +863 -0
  12. package/components/AttendanceCheckInOutDialog.vue +416 -0
  13. package/components/AttendanceDetailsDialog.vue +184 -0
  14. package/components/AttendanceMain.vue +155 -0
  15. package/components/AttendanceMapSearchDialog.vue +393 -0
  16. package/components/AttendanceSettingsDialog.vue +398 -0
  17. package/components/BuildingManagement/buildings.vue +5 -5
  18. package/components/BuildingManagement/units.vue +5 -5
  19. package/components/BulletinBoardManagement.vue +322 -0
  20. package/components/ChecklistItemRow.vue +54 -0
  21. package/components/CheckoutItemMain.vue +705 -0
  22. package/components/CleaningScheduleMain.vue +271 -0
  23. package/components/DocumentManagement.vue +4 -0
  24. package/components/EntryPass/QrTemplatePreview.vue +104 -0
  25. package/components/EntryPassMain.vue +252 -200
  26. package/components/HygieneUpdateMoreAction.vue +238 -0
  27. package/components/ManageChecklistMain.vue +384 -0
  28. package/components/MemberMain.vue +48 -20
  29. package/components/MyAttendanceMain.vue +224 -0
  30. package/components/OnlineFormsConfiguration.vue +9 -2
  31. package/components/PhotoUpload.vue +410 -0
  32. package/components/ScheduleAreaMain.vue +313 -0
  33. package/components/ScheduleTaskAreaFormDialog.vue +144 -0
  34. package/components/ScheduleTaskAreaUpdateMoreAction.vue +109 -0
  35. package/components/ScheduleTaskForm.vue +471 -0
  36. package/components/ScheduleTaskMain.vue +345 -0
  37. package/components/ScheduleTastTicketMain.vue +182 -0
  38. package/components/SignaturePad.vue +17 -5
  39. package/components/StockCard.vue +191 -0
  40. package/components/SupplyManagementMain.vue +557 -0
  41. package/components/TableHygiene.vue +617 -0
  42. package/components/UnitMain.vue +451 -0
  43. package/components/VisitorManagement.vue +28 -15
  44. package/composables/useAccessManagement.ts +163 -0
  45. package/composables/useAreaPermission.ts +51 -0
  46. package/composables/useAreas.ts +99 -0
  47. package/composables/useAttendance.ts +89 -0
  48. package/composables/useAttendancePermission.ts +68 -0
  49. package/composables/useBuilding.ts +2 -2
  50. package/composables/useBuildingUnit.ts +2 -2
  51. package/composables/useBulletin.ts +82 -0
  52. package/composables/useCard.ts +2 -0
  53. package/composables/useCheckout.ts +61 -0
  54. package/composables/useCheckoutPermission.ts +80 -0
  55. package/composables/useCleaningPermission.ts +229 -0
  56. package/composables/useCleaningSchedulePermission.ts +58 -0
  57. package/composables/useCleaningSchedules.ts +233 -0
  58. package/composables/useCountry.ts +8 -0
  59. package/composables/useDashboardData.ts +2 -2
  60. package/composables/useFeedback.ts +1 -1
  61. package/composables/useLocation.ts +78 -0
  62. package/composables/useOnlineForm.ts +16 -9
  63. package/composables/usePeople.ts +87 -72
  64. package/composables/useQR.ts +29 -0
  65. package/composables/useScheduleTask.ts +89 -0
  66. package/composables/useScheduleTaskArea.ts +85 -0
  67. package/composables/useScheduleTaskPermission.ts +68 -0
  68. package/composables/useSiteEntryPassSettings.ts +4 -15
  69. package/composables/useStock.ts +45 -0
  70. package/composables/useSupply.ts +63 -0
  71. package/composables/useSupplyPermission.ts +92 -0
  72. package/composables/useUnitPermission.ts +51 -0
  73. package/composables/useUnits.ts +82 -0
  74. package/composables/useWebUsb.ts +389 -0
  75. package/composables/useWorkOrder.ts +1 -1
  76. package/nuxt.config.ts +3 -0
  77. package/package.json +4 -1
  78. package/types/area.d.ts +22 -0
  79. package/types/attendance.d.ts +38 -0
  80. package/types/checkout-item.d.ts +27 -0
  81. package/types/cleaner-schedule.d.ts +54 -0
  82. package/types/location.d.ts +42 -0
  83. package/types/schedule-task.d.ts +18 -0
  84. package/types/stock.d.ts +16 -0
  85. package/types/supply.d.ts +11 -0
  86. package/utils/acm-crypto.ts +30 -0
@@ -0,0 +1,705 @@
1
+ <template>
2
+ <v-row no-gutters align="center" justify="center">
3
+ <v-col cols="12" lg="12">
4
+ <TableMain
5
+ :title="`${siteName} Units`"
6
+ :items="items"
7
+ :headers="headers"
8
+ :loading="loading"
9
+ :show-header="true"
10
+ v-model:page="page"
11
+ :pages="pages"
12
+ :pageRange="pageRange"
13
+ :no-data-text="`No supplies found for ${siteName}.`"
14
+ @refresh="getCheckoutItemsRefresh"
15
+ :canCreate="canCreateCheckout"
16
+ createLabel="Checkout Item"
17
+ @row-click="handleRowClick"
18
+ >
19
+ <template #actions>
20
+ <v-row no-gutters align="center" class="w-100">
21
+ <v-col cols="auto" v-if="canCreateCheckout">
22
+ <v-btn
23
+ class="text-none"
24
+ rounded="pill"
25
+ variant="tonal"
26
+ size="large"
27
+ @click="openCheckoutDialog()"
28
+ >
29
+ Checkout Item
30
+ </v-btn>
31
+ </v-col>
32
+
33
+ <v-spacer />
34
+
35
+ <v-col cols="auto">
36
+ <v-text-field
37
+ v-model="searchInput"
38
+ density="compact"
39
+ placeholder="Search"
40
+ clearable
41
+ width="300"
42
+ append-inner-icon="mdi-magnify"
43
+ hide-details
44
+ />
45
+ </v-col>
46
+ </v-row>
47
+ </template>
48
+
49
+ <template #item.createdAt="{ value }">
50
+ <span>{{ formatDate(value) }}</span>
51
+ </template>
52
+ </TableMain>
53
+ </v-col>
54
+ </v-row>
55
+
56
+ <v-dialog v-model="dialogShowMoreActions" width="400" persistent>
57
+ <HygieneUpdateMoreAction
58
+ :title="selectedSupply?.name"
59
+ :canDelete="false"
60
+ :canUpdate="false"
61
+ :canApprove="false"
62
+ :canDisapprove="false"
63
+ :disabled="true"
64
+ @close="dialogShowMoreActions = false"
65
+ >
66
+ <template #content>
67
+ <v-row no-gutters>
68
+ <v-col v-if="selectedSupply" cols="12" class="mb-2">
69
+ <v-row
70
+ no-gutters
71
+ class="mb-3"
72
+ v-if="
73
+ selectedSupply.attachment && selectedSupply.attachment.length
74
+ "
75
+ >
76
+ <v-col cols="12" class="d-flex justify-center">
77
+ <v-sheet
78
+ rounded="lg"
79
+ class="overflow-hidden"
80
+ style="width: 100%; max-width: 360px; cursor: pointer"
81
+ >
82
+ <v-img
83
+ :src="getFileUrl(selectedSupply.attachment[0])"
84
+ height="180"
85
+ cover
86
+ class="rounded-lg"
87
+ @click="viewAttachment(selectedSupply.attachment[0])"
88
+ >
89
+ <template v-slot:placeholder>
90
+ <v-row
91
+ class="fill-height ma-0"
92
+ align="center"
93
+ justify="center"
94
+ >
95
+ <v-progress-circular
96
+ indeterminate
97
+ color="grey-lighten-4"
98
+ />
99
+ </v-row>
100
+ </template>
101
+ </v-img>
102
+ </v-sheet>
103
+ </v-col>
104
+ </v-row>
105
+
106
+ <v-row
107
+ no-gutters
108
+ class="mb-3"
109
+ v-if="
110
+ selectedSupply.attachment &&
111
+ selectedSupply.attachment.length > 1
112
+ "
113
+ >
114
+ <v-col cols="12">
115
+ <v-row no-gutters>
116
+ <v-col
117
+ v-for="(attId, i) in selectedSupply.attachment.slice(1)"
118
+ :key="attId + '-' + i"
119
+ cols="auto"
120
+ class="pa-1"
121
+ >
122
+ <v-sheet
123
+ rounded="sm"
124
+ style="
125
+ width: 72px;
126
+ height: 56px;
127
+ overflow: hidden;
128
+ cursor: pointer;
129
+ "
130
+ @click="viewAttachment(attId)"
131
+ >
132
+ <v-img :src="getFileUrl(attId)" height="56" cover />
133
+ </v-sheet>
134
+ </v-col>
135
+ </v-row>
136
+ </v-col>
137
+ </v-row>
138
+
139
+ <v-row no-gutters class="mb-2">
140
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
141
+ Name:
142
+ </v-col>
143
+ <v-col cols="7" class="text-subtitle-2">
144
+ {{ selectedSupply.supplyName || "N/A" }}
145
+ </v-col>
146
+ </v-row>
147
+
148
+ <v-row no-gutters class="mb-2">
149
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
150
+ Checkout Quantity:
151
+ </v-col>
152
+ <v-col cols="7" class="text-subtitle-2">
153
+ {{ selectedSupply.qty || "N/A" }}
154
+ </v-col>
155
+ </v-row>
156
+
157
+ <v-row no-gutters class="mb-2">
158
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
159
+ Unit of Measurement:
160
+ </v-col>
161
+ <v-col cols="7" class="text-subtitle-2">
162
+ {{ selectedSupply.unitOfMeasurement || "N/A" }}
163
+ </v-col>
164
+ </v-row>
165
+
166
+ <v-row no-gutters class="mb-2">
167
+ <v-col cols="5" class="text-subtitle-2 font-weight-bold">
168
+ Stock Quantity:
169
+ </v-col>
170
+ <v-col cols="7" class="text-subtitle-2">
171
+ {{ selectedSupply.stockQty || "N/A" }}
172
+ </v-col>
173
+ </v-row>
174
+ </v-col>
175
+ </v-row>
176
+ </template>
177
+ </HygieneUpdateMoreAction>
178
+ </v-dialog>
179
+
180
+ <!-- Main Checkout Dialog -->
181
+ <v-dialog v-model="dialogCheckoutItem" max-width="540" persistent>
182
+ <v-card>
183
+ <v-card-title class="d-flex align-center pa-4">
184
+ <span class="text-h6 font-weight-bold">Checkout Item</span>
185
+ <v-spacer />
186
+ <v-btn
187
+ icon="mdi-close"
188
+ variant="text"
189
+ size="small"
190
+ @click="dialogCheckoutItem = false"
191
+ />
192
+ </v-card-title>
193
+
194
+ <v-divider />
195
+
196
+ <v-card-text class="pa-6">
197
+ <v-form ref="checkoutItemFormRef">
198
+ <!-- Add Supply Section -->
199
+ <v-row no-gutters class="mb-4">
200
+ <v-col cols="12">
201
+ <InputLabel for="supply" title="Add Supply" class="mb-2" />
202
+ <v-autocomplete
203
+ v-model="selectedSupplyForAdd"
204
+ :items="supplyItems"
205
+ :loading="loading"
206
+ item-title="name"
207
+ item-value="value"
208
+ density="comfortable"
209
+ variant="outlined"
210
+ placeholder="Select supply"
211
+ @update:model-value="addSupplyToList"
212
+ />
213
+ </v-col>
214
+ </v-row>
215
+
216
+ <!-- Selected Supplies List -->
217
+ <v-row no-gutters v-if="checkoutItems.length > 0" class="mb-4">
218
+ <v-col cols="12">
219
+ <v-divider class="mb-4" />
220
+
221
+ <div class="text-subtitle-2 font-weight-bold mb-3">
222
+ Selected Supplies
223
+ </div>
224
+
225
+ <v-sheet
226
+ v-for="(item, index) in checkoutItems"
227
+ :key="index"
228
+ rounded="lg"
229
+ variant="outlined"
230
+ class="pa-3 mb-3"
231
+ >
232
+ <v-row no-gutters align="center">
233
+ <v-col cols="12" md="8" class="pr-md-4 mb-2 mb-md-0">
234
+ <div class="text-body-2 font-weight-medium">
235
+ <span class="text-capitalize">{{ item.supplyName }}</span>
236
+ - (Available:
237
+ <span class="font-weight-bold">{{ item.qty }}</span
238
+ >)
239
+ </div>
240
+ </v-col>
241
+
242
+ <v-col
243
+ cols="12"
244
+ md="4"
245
+ class="d-flex justify-end align-center"
246
+ >
247
+ <v-text-field
248
+ v-model.number="item.qty"
249
+ :rules="[requiredRule]"
250
+ density="compact"
251
+ variant="outlined"
252
+ type="number"
253
+ min="1"
254
+ hide-details
255
+ style="max-width: 120px"
256
+ class="mr-2"
257
+ />
258
+
259
+ <v-menu location="bottom end">
260
+ <template #activator="{ props }">
261
+ <v-btn
262
+ v-bind="props"
263
+ icon="mdi-dots-vertical"
264
+ variant="text"
265
+ size="small"
266
+ />
267
+ </template>
268
+
269
+ <v-list density="compact" min-width="180">
270
+ <v-list-item @click="openPhotoDialog(index)">
271
+ <template #prepend>
272
+ <v-icon size="18">mdi-camera-outline</v-icon>
273
+ </template>
274
+ <v-list-item-title>Upload Photo/s</v-list-item-title>
275
+ </v-list-item>
276
+
277
+ <v-list-item
278
+ @click="removeSupplyFromList(index)"
279
+ class="text-error"
280
+ >
281
+ <template #prepend>
282
+ <v-icon size="18" color="error"
283
+ >mdi-delete-outline</v-icon
284
+ >
285
+ </template>
286
+ <v-list-item-title>Remove Supply</v-list-item-title>
287
+ </v-list-item>
288
+ </v-list>
289
+ </v-menu>
290
+ </v-col>
291
+ </v-row>
292
+
293
+ <!-- Photo Preview -->
294
+ <v-row
295
+ v-if="item.photoIds && item.photoIds.length"
296
+ no-gutters
297
+ class="mt-3"
298
+ >
299
+ <v-col
300
+ v-for="(pid, pidx) in item.photoIds"
301
+ :key="pid + '-' + pidx"
302
+ cols="auto"
303
+ class="pa-1"
304
+ >
305
+ <v-sheet
306
+ rounded="sm"
307
+ class="photo-thumb"
308
+ @click="viewAttachment(pid)"
309
+ >
310
+ <v-img
311
+ :src="getFileUrl(pid)"
312
+ height="64"
313
+ width="88"
314
+ cover
315
+ />
316
+ </v-sheet>
317
+ </v-col>
318
+ </v-row>
319
+
320
+ <!-- Empty photo hint -->
321
+ <v-row v-else no-gutters class="mt-2">
322
+ <v-col cols="12">
323
+ <div class="text-caption text-medium-emphasis">
324
+ No photos attached
325
+ </div>
326
+ </v-col>
327
+ </v-row>
328
+ </v-sheet>
329
+ </v-col>
330
+ </v-row>
331
+ </v-form>
332
+ </v-card-text>
333
+
334
+ <v-divider />
335
+
336
+ <v-card-actions class="pa-0">
337
+ <v-row no-gutters>
338
+ <v-col cols="6">
339
+ <v-btn
340
+ block
341
+ variant="text"
342
+ size="large"
343
+ height="56"
344
+ class="text-none font-weight-medium rounded-0"
345
+ @click="dialogCheckoutItem = false"
346
+ >
347
+ Cancel
348
+ </v-btn>
349
+ </v-col>
350
+ <v-col cols="6">
351
+ <v-btn
352
+ block
353
+ variant="flat"
354
+ color="black"
355
+ size="large"
356
+ height="56"
357
+ class="text-none font-weight-bold rounded-0"
358
+ :loading="submitting"
359
+ @click="_checkoutItem()"
360
+ >
361
+ Checkout Item
362
+ </v-btn>
363
+ </v-col>
364
+ </v-row>
365
+ </v-card-actions>
366
+ </v-card>
367
+ </v-dialog>
368
+
369
+ <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
370
+
371
+ <v-dialog v-model="dialogPhotoForItem" max-width="720" persistent>
372
+ <v-card rounded="lg">
373
+ <v-card-title class="d-flex align-center pa-4">
374
+ <div>
375
+ <div class="text-h6 font-weight-bold">Item Photos</div>
376
+ <div class="text-caption text-medium-emphasis">
377
+ {{ checkoutItems[activePhotoIndex ?? 0]?.supplyName || "Item" }}
378
+ </div>
379
+ </div>
380
+
381
+ <v-spacer />
382
+
383
+ <v-btn
384
+ icon="mdi-close"
385
+ variant="text"
386
+ size="small"
387
+ @click="dialogPhotoForItem = false"
388
+ />
389
+ </v-card-title>
390
+
391
+ <v-divider />
392
+
393
+ <!-- Body -->
394
+ <v-card-text class="pa-6" v-if="activePhotoIndex !== null">
395
+ <PhotoUpload
396
+ title="Add / Take Photos"
397
+ v-model="currentPhotos"
398
+ v-model:photo-ids="currentPhotoIds"
399
+ @error="showMessage($event, 'error')"
400
+ />
401
+ </v-card-text>
402
+
403
+ <v-divider />
404
+
405
+ <!-- Footer -->
406
+ <v-card-actions class="pa-0">
407
+ <v-row no-gutters class="w-100">
408
+ <v-col cols="6">
409
+ <v-btn
410
+ block
411
+ variant="text"
412
+ size="large"
413
+ height="56"
414
+ class="text-none font-weight-medium rounded-0"
415
+ @click="dialogPhotoForItem = false"
416
+ >
417
+ Cancel
418
+ </v-btn>
419
+ </v-col>
420
+
421
+ <v-col cols="6">
422
+ <v-btn
423
+ block
424
+ variant="flat"
425
+ color="black"
426
+ size="large"
427
+ height="56"
428
+ class="text-none font-weight-bold rounded-0"
429
+ @click="dialogPhotoForItem = false"
430
+ >
431
+ Done
432
+ </v-btn>
433
+ </v-col>
434
+ </v-row>
435
+ </v-card-actions>
436
+ </v-card>
437
+ </v-dialog>
438
+ </template>
439
+
440
+ <script setup lang="ts">
441
+ import useCheckout from "../composables/useCheckout";
442
+ import { useCheckoutPermission } from "../composables/useCheckoutPermission";
443
+ import useSupply from "../composables/useSupply";
444
+
445
+ const props = defineProps({
446
+ orgId: { type: String, default: "" },
447
+ site: { type: String, default: "" },
448
+ });
449
+
450
+ const { canCreateCheckout } = useCheckoutPermission();
451
+
452
+ const items = ref<Array<Record<string, any>>>([]);
453
+ const siteName = ref<string>("");
454
+ const submitting = ref(false);
455
+
456
+ const headers = [
457
+ { title: "Date", value: "createdAt" },
458
+ { title: "Name", value: "supplyName" },
459
+ { title: "Checkout Qty", value: "checkOutQty" },
460
+ { title: "Checkout By", value: "checkOutByName" },
461
+ { title: "Available Stock Qty", value: "supplyQty" },
462
+ ];
463
+
464
+ const {
465
+ getCheckoutItems,
466
+ getCheckoutItemById,
467
+ checkoutItems: checkoutItemApi,
468
+ } = useCheckout();
469
+ const { debounce, requiredRule, formatDate } = useUtils();
470
+
471
+ const searchInput = ref("");
472
+ const startDate = ref("");
473
+ const endDate = ref("");
474
+ const page = ref(1);
475
+ const pages = ref(0);
476
+ const pageRange = ref("-- - -- of --");
477
+
478
+ const selectedCheckoutItemId = ref("");
479
+
480
+ const {
481
+ data: getCheckoutItemsReq,
482
+ refresh: getCheckoutItemsRefresh,
483
+ pending: loading,
484
+ } = await useLazyAsyncData(
485
+ "get-all-checkout-items",
486
+ () =>
487
+ getCheckoutItems({
488
+ page: page.value,
489
+ search: searchInput.value,
490
+ site: props.site,
491
+ }),
492
+ {
493
+ watch: [page, () => props.site],
494
+ }
495
+ );
496
+
497
+ watchEffect(() => {
498
+ if (getCheckoutItemsReq.value) {
499
+ items.value = getCheckoutItemsReq.value.items;
500
+ pages.value = getCheckoutItemsReq.value.pages;
501
+ pageRange.value = getCheckoutItemsReq.value.pageRange;
502
+ }
503
+ });
504
+
505
+ const debounceSearch = debounce(getCheckoutItemsRefresh, 500);
506
+ watch(
507
+ [searchInput, endDate, startDate],
508
+ ([]) => {
509
+ debounceSearch();
510
+ },
511
+ { immediate: false, deep: true }
512
+ );
513
+
514
+ const { data: getCheckoutItemByIdReq, refresh: getCheckoutItemByIdRefresh } =
515
+ await useLazyAsyncData(
516
+ "get-checkout-item-by-id",
517
+ () => getCheckoutItemById(selectedCheckoutItemId.value),
518
+ {
519
+ immediate: false,
520
+ }
521
+ );
522
+
523
+ watchEffect(() => {
524
+ if (getCheckoutItemByIdReq.value) {
525
+ selectedSupply.value = {
526
+ _id: getCheckoutItemByIdReq.value._id,
527
+ supplyName: getCheckoutItemByIdReq.value.supplyName || "N/A",
528
+ qty: getCheckoutItemByIdReq.value.qty || 0,
529
+ unitOfMeasurement:
530
+ getCheckoutItemByIdReq.value.supply?.unitOfMeasurement || "N/A",
531
+ status: getCheckoutItemByIdReq.value.status || "N/A",
532
+ site: getCheckoutItemByIdReq.value.site || "N/A",
533
+ supply:
534
+ getCheckoutItemByIdReq.value.supply ||
535
+ getCheckoutItemByIdReq.value.supply?._id ||
536
+ "N/A",
537
+ attachment: Array.isArray(getCheckoutItemByIdReq.value.attachment)
538
+ ? [...getCheckoutItemByIdReq.value.attachment]
539
+ : [],
540
+ stockQty:
541
+ getCheckoutItemByIdReq.value.stockQty ||
542
+ getCheckoutItemByIdReq.value.supply?.stockQty ||
543
+ "N/A",
544
+ };
545
+ }
546
+ });
547
+
548
+ const { getSupplies } = useSupply();
549
+
550
+ const { data: getSuppliesReq } = await useLazyAsyncData(
551
+ "get-supplies-for-checkout-item",
552
+ () =>
553
+ getSupplies({
554
+ site: props.site,
555
+ limit: 100,
556
+ }),
557
+ {}
558
+ );
559
+
560
+ watchEffect(() => {
561
+ if (getSuppliesReq.value) {
562
+ supplyItems.value = getSuppliesReq.value.items.map((item: any) => ({
563
+ name: item.name,
564
+ value: item._id,
565
+ }));
566
+ }
567
+ });
568
+
569
+ const supplyItems = ref<Array<{ name: string; value: string }>>([]);
570
+ const selectedSupplyForAdd = ref<string>("");
571
+ const checkoutItems = ref<TCheckoutItem[]>([]);
572
+
573
+ const selectedSupply = ref<Record<string, any>>({});
574
+ const message = ref("");
575
+ const messageSnackbar = ref(false);
576
+ const messageColor = ref("");
577
+
578
+ const dialogShowMoreActions = ref(false);
579
+ const dialogCheckoutItem = ref(false);
580
+ const dialogPhotoForItem = ref(false);
581
+ const activePhotoIndex = ref<number | null>(null);
582
+
583
+ function showMessage(msg: string, color: string = "error") {
584
+ message.value = msg;
585
+ messageColor.value = color;
586
+ messageSnackbar.value = true;
587
+ }
588
+
589
+ function openCheckoutDialog() {
590
+ checkoutItems.value = [];
591
+ selectedSupplyForAdd.value = "";
592
+ dialogCheckoutItem.value = true;
593
+ }
594
+
595
+ function addSupplyToList(supplyId: string) {
596
+ if (!supplyId) return;
597
+
598
+ const exists = checkoutItems.value.find((item) => item.supply === supplyId);
599
+ if (exists) {
600
+ showMessage("Supply already added.");
601
+ selectedSupplyForAdd.value = "";
602
+ return;
603
+ }
604
+
605
+ const supply = supplyItems.value.find((s) => s.value === supplyId);
606
+ if (supply) {
607
+ checkoutItems.value.push({
608
+ supply: supplyId,
609
+ supplyName: supply.name,
610
+ qty: 1,
611
+ photos: [],
612
+ photoIds: [],
613
+ });
614
+ }
615
+ selectedSupplyForAdd.value = "";
616
+ }
617
+
618
+ function removeSupplyFromList(index: number) {
619
+ checkoutItems.value.splice(index, 1);
620
+ }
621
+
622
+ function openPhotoDialog(index: number) {
623
+ activePhotoIndex.value = index;
624
+ dialogPhotoForItem.value = true;
625
+ }
626
+
627
+ async function handleRowClick(data: any) {
628
+ message.value = "";
629
+
630
+ selectedCheckoutItemId.value = data?.item?._id;
631
+
632
+ if (selectedCheckoutItemId.value) {
633
+ await getCheckoutItemByIdRefresh();
634
+ }
635
+
636
+ dialogShowMoreActions.value = true;
637
+ }
638
+
639
+ async function _checkoutItem() {
640
+ submitting.value = true;
641
+ try {
642
+ if (!checkoutItems.value.length) {
643
+ showMessage("Add at least one supply.");
644
+ submitting.value = false;
645
+ return;
646
+ }
647
+
648
+ const items = checkoutItems.value.map((item) => ({
649
+ supply: item.supply,
650
+ qty: item.qty,
651
+ ...(item.photoIds && item.photoIds.length
652
+ ? { attachment: [...item.photoIds] }
653
+ : {}),
654
+ }));
655
+
656
+ await checkoutItemApi(props.site, { items });
657
+
658
+ showMessage(
659
+ `Checked out ${checkoutItems.value.length} item(s).`,
660
+ "success"
661
+ );
662
+ dialogCheckoutItem.value = false;
663
+ checkoutItems.value = [];
664
+
665
+ await getCheckoutItemsRefresh();
666
+ } catch (error: any) {
667
+ console.error(error);
668
+ showMessage(error?.data?.message, "error");
669
+ } finally {
670
+ submitting.value = false;
671
+ }
672
+ }
673
+
674
+ const { getFileUrl } = useFile();
675
+
676
+ function viewAttachment(attachmentId: string) {
677
+ if (!attachmentId) return;
678
+ const url = getFileUrl(attachmentId);
679
+ window.open(url, "_blank");
680
+ }
681
+
682
+ const currentPhotos = computed({
683
+ get() {
684
+ if (activePhotoIndex.value === null) return [];
685
+ return checkoutItems.value[activePhotoIndex.value]?.photos || [];
686
+ },
687
+ set(val) {
688
+ if (activePhotoIndex.value === null) return;
689
+ checkoutItems.value[activePhotoIndex.value].photos = val || [];
690
+ },
691
+ });
692
+
693
+ const currentPhotoIds = computed({
694
+ get() {
695
+ if (activePhotoIndex.value === null) return [];
696
+ return checkoutItems.value[activePhotoIndex.value]?.photoIds || [];
697
+ },
698
+ set(val) {
699
+ if (activePhotoIndex.value === null) return;
700
+ checkoutItems.value[activePhotoIndex.value].photoIds = val || [];
701
+ },
702
+ });
703
+ </script>
704
+
705
+ <style scoped></style>