@7365admin1/layer-common 1.10.6 → 1.10.8

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 (54) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AccessCardQrTagging.vue +314 -34
  3. package/components/AccessCardQrTaggingPrintQr.vue +75 -0
  4. package/components/{AddSupplyForm.vue → AddEqupmentForm.vue} +5 -5
  5. package/components/AreaChecklistHistoryLogs.vue +9 -0
  6. package/components/BuildingForm.vue +36 -5
  7. package/components/BuildingManagement/buildings.vue +18 -9
  8. package/components/BuildingManagement/units.vue +13 -115
  9. package/components/BuildingUnitFormAdd.vue +42 -33
  10. package/components/BuildingUnitFormEdit.vue +334 -139
  11. package/components/CleaningScheduleMain.vue +60 -13
  12. package/components/Dialog/DeleteConfirmation.vue +2 -2
  13. package/components/Dialog/UpdateMoreAction.vue +2 -2
  14. package/components/EntryPassInformation.vue +443 -0
  15. package/components/{CheckoutItemMain.vue → EquipmentItemMain.vue} +88 -85
  16. package/components/{SupplyManagement.vue → EquipmentManagement.vue} +3 -3
  17. package/components/Input/DateTimePicker.vue +17 -11
  18. package/components/Input/InputPhoneNumberV2.vue +8 -0
  19. package/components/ManageChecklistMain.vue +400 -36
  20. package/components/ScheduleAreaMain.vue +56 -0
  21. package/components/TableHygiene.vue +47 -430
  22. package/components/UnitPersonCard.vue +123 -0
  23. package/components/VehicleAddSelection.vue +2 -2
  24. package/components/VehicleForm.vue +78 -19
  25. package/components/VehicleManagement.vue +164 -40
  26. package/components/VisitorForm.vue +95 -20
  27. package/components/VisitorFormSelection.vue +13 -2
  28. package/components/VisitorManagement.vue +83 -55
  29. package/composables/useAccessManagement.ts +52 -0
  30. package/composables/useCleaningPermission.ts +7 -7
  31. package/composables/useDashboardData.ts +2 -2
  32. package/composables/{useSupply.ts → useEquipment.ts} +11 -11
  33. package/composables/{useCheckout.ts → useEquipmentItem.ts} +7 -7
  34. package/composables/{useCheckoutPermission.ts → useEquipmentItemPermission.ts} +13 -13
  35. package/composables/useEquipmentManagementPermission.ts +96 -0
  36. package/composables/{useSupplyPermission.ts → useEquipmentPermission.ts} +9 -9
  37. package/composables/usePeople.ts +4 -3
  38. package/composables/useVehicle.ts +35 -2
  39. package/composables/useVisitor.ts +3 -3
  40. package/composables/useWorkOrder.ts +25 -3
  41. package/package.json +3 -2
  42. package/types/building.d.ts +1 -1
  43. package/types/cleaner-schedule.d.ts +1 -0
  44. package/types/{checkout-item.d.ts → equipment-item.d.ts} +3 -3
  45. package/types/{supply.d.ts → equipment.d.ts} +2 -2
  46. package/types/html2pdf.d.ts +19 -0
  47. package/types/people.d.ts +5 -2
  48. package/types/site.d.ts +8 -0
  49. package/types/vehicle.d.ts +4 -3
  50. package/types/visitor.d.ts +2 -1
  51. package/.playground/app.vue +0 -41
  52. package/.playground/eslint.config.mjs +0 -6
  53. package/.playground/nuxt.config.ts +0 -22
  54. package/.playground/pages/feedback.vue +0 -30
@@ -95,45 +95,43 @@
95
95
  Set {{ group.set }}
96
96
  </span>
97
97
  <v-chip
98
- v-if="group.completedByName && isGroupComplete(group)"
98
+ v-if="group.isScheduleTask"
99
99
  size="x-small"
100
- color="success"
100
+ color="info"
101
101
  variant="tonal"
102
- prepend-icon="mdi-check-circle-outline"
102
+ prepend-icon="mdi-calendar-clock"
103
103
  class="text-none"
104
104
  >
105
- Completed · {{ group.completedByName }}
106
- </v-chip>
107
- <v-chip
108
- v-else-if="
109
- group.completedByName && isGroupInProgress(group)
110
- "
111
- size="x-small"
112
- color="warning"
113
- variant="tonal"
114
- prepend-icon="mdi-progress-clock"
115
- class="text-none"
116
- >
117
- Ongoing · {{ group.completedByName }}
105
+ Schedule Task
118
106
  </v-chip>
107
+ <slot name="group-header-chips" :group="group">
108
+ <v-chip
109
+ v-if="group.completedByName && isGroupComplete(group)"
110
+ size="x-small"
111
+ color="success"
112
+ variant="tonal"
113
+ prepend-icon="mdi-check-circle-outline"
114
+ class="text-none"
115
+ >
116
+ Completed · {{ group.completedByName }}
117
+ </v-chip>
118
+ <v-chip
119
+ v-else-if="
120
+ group.completedByName && isGroupInProgress(group)
121
+ "
122
+ size="x-small"
123
+ color="warning"
124
+ variant="tonal"
125
+ prepend-icon="mdi-progress-clock"
126
+ class="text-none"
127
+ >
128
+ Ongoing · {{ group.completedByName }}
129
+ </v-chip>
130
+ </slot>
119
131
  </v-col>
120
132
  <v-spacer />
121
133
  <v-col cols="auto">
122
- <v-btn
123
- v-if="group.attachments && group.attachments.length > 0"
124
- size="x-small"
125
- variant="tonal"
126
- color="primary"
127
- class="text-none"
128
- prepend-icon="mdi-paperclip"
129
- @click.stop="
130
- openAttachmentDialog(group.set, group.attachments)
131
- "
132
- >
133
- {{ group.attachments.length }} attachment{{
134
- group.attachments.length > 1 ? "s" : ""
135
- }}
136
- </v-btn>
134
+ <slot name="group-header-append" :group="group" />
137
135
  </v-col>
138
136
  </v-row>
139
137
  </v-sheet>
@@ -141,45 +139,20 @@
141
139
  <v-sheet
142
140
  v-for="item in group.items"
143
141
  :key="item[itemValue]"
144
- :color="
145
- isItemSelected(item, group.set) ? 'grey-lighten-4' : 'white'
146
- "
142
+ color="white"
147
143
  border="b"
148
144
  >
149
145
  <v-row no-gutters align="center" class="px-4 py-2">
150
146
  <v-col cols="auto" class="mr-3">
151
- <v-icon
152
- size="20"
153
- :color="
154
- activeActions[getKey(item, group.set)] === 'approve'
155
- ? 'success'
156
- : activeActions[getKey(item, group.set)] === 'reject'
157
- ? 'error'
158
- : 'grey-lighten-2'
159
- "
160
- >
161
- {{
162
- activeActions[getKey(item, group.set)] === "approve"
163
- ? "mdi-check-circle"
164
- : activeActions[getKey(item, group.set)] === "reject"
165
- ? "mdi-close-circle"
166
- : "mdi-circle-outline"
167
- }}
168
- </v-icon>
147
+ <slot name="item-prepend" :item="item" :group="group" />
169
148
  </v-col>
149
+
170
150
  <v-col>
171
- <slot name="list-item" :item="item">
172
- <v-row no-gutters align-center>
151
+ <slot name="item-content" :item="item" :group="group">
152
+ <v-row no-gutters>
173
153
  <v-col cols="12">
174
- <span
175
- class="text-body-2 font-weight-medium"
176
- :class="
177
- activeActions[getKey(item, group.set)] === 'approve'
178
- ? 'text-decoration-line-through text-medium-emphasis'
179
- : ''
180
- "
181
- >
182
- {{ getItemValue(item, headers[0].value) }}
154
+ <span class="text-body-2 font-weight-medium">
155
+ {{ getItemValue(item, headers[0]?.value) }}
183
156
  </span>
184
157
  </v-col>
185
158
  <v-col
@@ -229,48 +202,7 @@
229
202
  </v-col>
230
203
 
231
204
  <v-col cols="auto">
232
- <slot
233
- name="list-item-append"
234
- :item="item"
235
- :isSelected="isItemSelected(item, group.set)"
236
- >
237
- <v-row
238
- v-if="canManageScheduleTasks"
239
- no-gutters
240
- align="center"
241
- >
242
- <v-col cols="auto">
243
- <v-btn
244
- icon="mdi-close"
245
- size="small"
246
- :variant="
247
- activeActions[getKey(item, group.set)] === 'reject'
248
- ? 'flat'
249
- : 'text'
250
- "
251
- color="error"
252
- @click.stop="
253
- handleActionClick(item, group.set, 'reject')
254
- "
255
- />
256
- </v-col>
257
- <v-col cols="auto">
258
- <v-btn
259
- icon="mdi-check"
260
- size="small"
261
- :variant="
262
- activeActions[getKey(item, group.set)] === 'approve'
263
- ? 'flat'
264
- : 'text'
265
- "
266
- color="success"
267
- @click.stop="
268
- handleActionClick(item, group.set, 'approve')
269
- "
270
- />
271
- </v-col>
272
- </v-row>
273
- </slot>
205
+ <slot name="item-append" :item="item" :group="group" />
274
206
  </v-col>
275
207
  </v-row>
276
208
  </v-sheet>
@@ -281,81 +213,6 @@
281
213
  </v-card>
282
214
  </v-col>
283
215
  </v-row>
284
-
285
- <v-dialog v-model="showAttachmentDialog" max-width="700" scrollable>
286
- <v-card>
287
- <v-card-title class="d-flex align-center pa-4">
288
- <span class="text-h6 font-weight-bold">
289
- Set {{ attachmentDialogSet }} — Attachments
290
- </span>
291
- <v-spacer />
292
- <v-btn
293
- icon="mdi-close"
294
- variant="text"
295
- size="small"
296
- @click="showAttachmentDialog = false"
297
- />
298
- </v-card-title>
299
-
300
- <v-divider />
301
-
302
- <v-card-text class="pa-4">
303
- <v-row>
304
- <v-col
305
- v-for="(id, index) in attachmentDialogIds"
306
- :key="index"
307
- cols="6"
308
- sm="4"
309
- >
310
- <v-sheet
311
- rounded="lg"
312
- class="overflow-hidden"
313
- style="aspect-ratio: 1"
314
- >
315
- <v-img
316
- :src="getFileUrl(id)"
317
- aspect-ratio="1"
318
- cover
319
- class="rounded-lg"
320
- @click="openFullImage(getFileUrl(id))"
321
- style="cursor: zoom-in"
322
- >
323
- <template v-slot:placeholder>
324
- <v-row
325
- class="fill-height ma-0"
326
- align="center"
327
- justify="center"
328
- >
329
- <v-progress-circular indeterminate color="grey-lighten-4" />
330
- </v-row>
331
- </template>
332
- <template v-slot:error>
333
- <v-row
334
- class="fill-height ma-0"
335
- align="center"
336
- justify="center"
337
- >
338
- <v-icon icon="mdi-image-broken" size="40" color="grey" />
339
- </v-row>
340
- </template>
341
- </v-img>
342
- </v-sheet>
343
- </v-col>
344
- </v-row>
345
- </v-card-text>
346
- </v-card>
347
- </v-dialog>
348
-
349
- <v-dialog v-model="showLightbox" max-width="900">
350
- <v-card>
351
- <v-card-actions class="pa-2 justify-end">
352
- <v-btn icon="mdi-close" variant="text" @click="showLightbox = false" />
353
- </v-card-actions>
354
- <v-card-text class="pa-2 pt-0">
355
- <v-img :src="lightboxSrc" contain max-height="80vh" />
356
- </v-card-text>
357
- </v-card>
358
- </v-dialog>
359
216
  </template>
360
217
 
361
218
  <script lang="ts" setup>
@@ -414,14 +271,6 @@ const props = defineProps({
414
271
  type: Boolean,
415
272
  default: false,
416
273
  },
417
- canManageScheduleTasks: {
418
- type: Boolean,
419
- default: true,
420
- },
421
- canAddRemarks: {
422
- type: Boolean,
423
- default: true,
424
- },
425
274
  extensionHeight: {
426
275
  type: Number,
427
276
  default: 50,
@@ -430,73 +279,33 @@ const props = defineProps({
430
279
  type: Number,
431
280
  default: 200,
432
281
  },
433
- selected: {
434
- type: Array as PropType<any[]>,
435
- default: () => [],
436
- },
437
282
  });
438
283
 
439
- const emits = defineEmits([
440
- "create",
441
- "refresh",
442
- "update:page",
443
- "row-click",
444
- "update:selected",
445
- "action-click",
446
- "request-completion-dialog",
447
- ]);
448
-
449
- defineExpose({
450
- revertSetApprovals,
451
- });
284
+ const emits = defineEmits(["create", "refresh", "update:page"]);
452
285
 
453
286
  const internalPage = ref(props.page);
454
- const selected = shallowRef<any[]>(props.selected);
455
- const activeActions = reactive<Record<string, "approve" | "reject">>({});
456
- const persistedActions = reactive<Record<string, "approve" | "reject">>({});
457
- const completedSets = ref<Set<number>>(new Set());
458
287
  const itemOrderMap = new Map<string, number>();
459
288
 
460
- const showAttachmentDialog = ref(false);
461
- const attachmentDialogSet = ref<number | undefined>(undefined);
462
- const attachmentDialogIds = ref<string[]>([]);
463
- const showLightbox = ref(false);
464
- const lightboxSrc = ref("");
465
-
466
- const { getFileUrl } = useFile();
467
-
468
- function openAttachmentDialog(setNumber: number, attachments: string[]) {
469
- attachmentDialogSet.value = setNumber;
470
- attachmentDialogIds.value = attachments;
471
- showAttachmentDialog.value = true;
472
- }
473
-
474
- function openFullImage(src: string) {
475
- lightboxSrc.value = src;
476
- showLightbox.value = true;
477
- }
478
-
479
289
  const groupedItems = computed(() => {
480
290
  return props.items.map((item: any) => {
481
291
  const units = item.units || [item];
482
292
 
483
- units.forEach((unit: any, index: number) => {
484
- const key = getKey(unit, item.set);
293
+ units.forEach((unit: any) => {
294
+ const key = `${unit[props.itemValue]}_${item.set}`;
485
295
  if (!itemOrderMap.has(key)) {
486
296
  itemOrderMap.set(key, itemOrderMap.size);
487
297
  }
488
298
  });
489
299
 
490
300
  const sortedUnits = [...units].sort((a: any, b: any) => {
491
- const keyA = getKey(a, item.set);
492
- const keyB = getKey(b, item.set);
493
- const orderA = itemOrderMap.get(keyA) ?? 0;
494
- const orderB = itemOrderMap.get(keyB) ?? 0;
495
- return orderA - orderB;
301
+ const keyA = `${a[props.itemValue]}_${item.set}`;
302
+ const keyB = `${b[props.itemValue]}_${item.set}`;
303
+ return (itemOrderMap.get(keyA) ?? 0) - (itemOrderMap.get(keyB) ?? 0);
496
304
  });
497
305
 
498
306
  return {
499
307
  set: item.set,
308
+ isScheduleTask: item.isScheduleTask ?? false,
500
309
  completedByName: item.completedByName ?? null,
501
310
  attachments:
502
311
  (item.attachment as string[] | undefined) ??
@@ -507,27 +316,6 @@ const groupedItems = computed(() => {
507
316
  });
508
317
  });
509
318
 
510
- const totalItemsCount = computed(() => {
511
- return groupedItems.value.reduce((acc, group) => acc + group.items.length, 0);
512
- });
513
-
514
- const approvedItemsCount = computed(() => {
515
- return Object.values(activeActions).filter((action) => action === "approve")
516
- .length;
517
- });
518
-
519
- const hasRejectedItems = computed(() => {
520
- return Object.values(activeActions).some((action) => action === "reject");
521
- });
522
-
523
- const allItemsApproved = computed(() => {
524
- return (
525
- totalItemsCount.value > 0 &&
526
- approvedItemsCount.value === totalItemsCount.value &&
527
- !hasRejectedItems.value
528
- );
529
- });
530
-
531
319
  function formatTimestamp(ts: string): string {
532
320
  if (!ts) return "";
533
321
  const date = new Date(ts);
@@ -556,187 +344,16 @@ function isGroupInProgress(group: { items: any[] }): boolean {
556
344
  );
557
345
  }
558
346
 
559
- function isSetFullyApproved(setNumber: number): boolean {
560
- const group = groupedItems.value.find((g) => g.set === setNumber);
561
- if (!group) return false;
562
-
563
- return group.items.every((item: any) => {
564
- const key = getKey(item, setNumber);
565
- return activeActions[key] === "approve";
566
- });
567
- }
568
-
569
- function getNewApprovedItemsForSet(
570
- setNumber: number
571
- ): Array<{ key: string; item: any; action: "approve" }> {
572
- const group = groupedItems.value.find((g) => g.set === setNumber);
573
- if (!group) return [];
574
-
575
- const approvedItems: Array<{ key: string; item: any; action: "approve" }> =
576
- [];
577
-
578
- group.items.forEach((item: any) => {
579
- const key = getKey(item, setNumber);
580
- if (activeActions[key] === "approve" && !(key in persistedActions)) {
581
- approvedItems.push({
582
- key,
583
- item: { ...item, set: setNumber },
584
- action: "approve",
585
- });
586
- }
587
- });
588
-
589
- return approvedItems;
347
+ function getItemValue(item: any, key: string): string {
348
+ if (!key) return "";
349
+ return key.split(".").reduce((obj: any, k: string) => obj?.[k], item) ?? "";
590
350
  }
591
351
 
592
- const lastApprovedKey = ref<string | null>(null);
593
-
594
352
  watch(
595
353
  () => props.page,
596
354
  (val) => {
597
355
  internalPage.value = val;
598
-
599
356
  itemOrderMap.clear();
600
357
  }
601
358
  );
602
-
603
- watch(
604
- () => props.selected,
605
- (val) => {
606
- selected.value = val;
607
- }
608
- );
609
-
610
- watch(selected, (val) => {
611
- emits("update:selected", val);
612
- });
613
-
614
- watch(
615
- () => props.items,
616
- (items) => {
617
- if (!items || !Array.isArray(items)) return;
618
-
619
- Object.keys(persistedActions).forEach(
620
- (key) => delete persistedActions[key]
621
- );
622
-
623
- items.forEach((group: any) => {
624
- const set = group.set;
625
- const units = group.units || [];
626
-
627
- units.forEach((unit: any) => {
628
- const key = getKey(unit, set);
629
- if (unit.approve === true) {
630
- activeActions[key] = "approve";
631
- persistedActions[key] = "approve";
632
- } else if (unit.reject === true) {
633
- activeActions[key] = "reject";
634
- persistedActions[key] = "reject";
635
- }
636
- });
637
- });
638
- },
639
- { immediate: true }
640
- );
641
-
642
- function getKey(item: any, set?: number): string {
643
- return `${item[props.itemValue]}_${set ?? ""}`;
644
- }
645
-
646
- function getItemValue(item: any, key: string): string {
647
- if (!key) return "";
648
- return key.split(".").reduce((obj, k) => obj?.[k], item) ?? "";
649
- }
650
-
651
- function isItemSelected(item: any, set?: number): boolean {
652
- if (!Array.isArray(selected.value) || selected.value.length === 0) {
653
- return false;
654
- }
655
-
656
- if (typeof selected.value[0] === "object" && "unit" in selected.value[0]) {
657
- return selected.value.some(
658
- (s: any) => s.unit === item[props.itemValue] && s.set === set
659
- );
660
- }
661
-
662
- return selected.value.includes(item[props.itemValue]);
663
- }
664
-
665
- function handleActionClick(
666
- item: any,
667
- set: number | undefined,
668
- action: "approve" | "reject"
669
- ): void {
670
- const key = getKey(item, set);
671
-
672
- const isPersisted = key in persistedActions;
673
-
674
- if (activeActions[key] === action && !isPersisted) {
675
- delete activeActions[key];
676
- return;
677
- }
678
-
679
- activeActions[key] = action;
680
-
681
- if (action === "reject") {
682
- console.debug("TableHygiene: emitting action-click", {
683
- item: { ...item, set },
684
- action,
685
- });
686
- emits("action-click", { item: { ...item, set }, action });
687
- }
688
-
689
- if (action === "approve") {
690
- lastApprovedKey.value = key;
691
-
692
- if (
693
- set !== undefined &&
694
- isSetFullyApproved(set) &&
695
- !completedSets.value.has(set)
696
- ) {
697
- const newApprovedItems = getNewApprovedItemsForSet(set);
698
-
699
- if (newApprovedItems.length > 0) {
700
- // Mark this set as completed
701
- completedSets.value.add(set);
702
-
703
- // Emit request to open completion dialog
704
- emits("request-completion-dialog", {
705
- setNumber: set,
706
- approvedItems: newApprovedItems,
707
- lastApprovedKey: lastApprovedKey.value,
708
- });
709
- return;
710
- }
711
- }
712
- console.debug("TableHygiene: emitting action-click (approve immediate)", {
713
- item: { ...item, set },
714
- action,
715
- });
716
- emits("action-click", { item: { ...item, set }, action });
717
- }
718
- }
719
-
720
- function revertSetApprovals(setNumber: number): void {
721
- const group = groupedItems.value.find((g) => g.set === setNumber);
722
- if (group) {
723
- group.items.forEach((item: any) => {
724
- const key = getKey(item, setNumber);
725
-
726
- if (!(key in persistedActions)) {
727
- delete activeActions[key];
728
- }
729
- });
730
- }
731
-
732
- completedSets.value.delete(setNumber);
733
- }
734
-
735
- watch(
736
- () => props.items,
737
- () => {
738
- completedSets.value.clear();
739
- },
740
- { deep: true }
741
- );
742
359
  </script>
@@ -0,0 +1,123 @@
1
+ <template>
2
+ <v-card-text>
3
+ <v-row>
4
+ <v-col cols="12" md="4" class="mt-2">
5
+ <InputLabel class="font-weight-bold" title="Name" />
6
+ <v-text-field :model-value="person.name" density="compact" :readonly="readOnly"
7
+ :class="{ 'no-pointer': readOnly }" />
8
+ </v-col>
9
+
10
+ <v-col cols="12" md="4" class="mt-2">
11
+ <InputLabel class="font-weight-bold" title="Email Address" />
12
+ <v-text-field :model-value="person.email" density="compact" :readonly="readOnly"
13
+ :class="{ 'no-pointer': readOnly }" />
14
+ </v-col>
15
+
16
+ <v-col cols="12" md="4" class="mt-2">
17
+ <InputLabel class="font-weight-bold" title="NRIC" />
18
+ <v-text-field :model-value="person.nric" density="compact" :readonly="readOnly"
19
+ :class="{ 'no-pointer': readOnly }" />
20
+ </v-col>
21
+
22
+ <v-col cols="12" md="4" class="mt-2">
23
+ <InputLabel class="font-weight-bold" title="Mobile Number" />
24
+ <v-text-field :model-value="person.contact" density="compact" :readonly="readOnly"
25
+ :class="{ 'no-pointer': readOnly }" />
26
+ </v-col>
27
+ </v-row>
28
+
29
+ <v-row>
30
+ <v-col cols="12" md="4" class="mt-2">
31
+ <InputLabel class="font-weight-bold" title="Other Documents and Files" :viewMode="readOnly" />
32
+
33
+ <p v-if="!person.files?.length" class="text-blue-darken-2 font-weight-thin text-caption ml-2 mt-5">
34
+ **No documents to display**
35
+ </p>
36
+ <v-list v-else dense nav class="mt-2">
37
+ <v-list-item v-for="file in person.files" :key="file.id" class="text-blue-darken-2"
38
+ :href="getFileUrl(file.id)" target="_blank">
39
+ <v-list-item-content>
40
+ <v-list-item-title class="d-flex align-center">
41
+ <span>
42
+ <v-icon size="18" class="mr-1">mdi-file-document-outline</v-icon>
43
+ </span>
44
+ <span> {{ file.name }}</span>
45
+ </v-list-item-title>
46
+ </v-list-item-content>
47
+ </v-list-item>
48
+ </v-list>
49
+
50
+ </v-col>
51
+
52
+ <v-col cols="0" md="4"></v-col>
53
+ <v-col v-if="prop.showOwnerOption" cols="12" md="4">
54
+ <v-checkbox :model-value="person.isOwner" readonly hide-details density="compact" @click="handleClick">
55
+ <template #label>
56
+ <span class="font-weight-bold text-subtitle-2 ml-2">Is Owner?</span>
57
+ </template>
58
+ </v-checkbox>
59
+
60
+ <span class="d-flex text-caption text-gray-5 ml-7">As owner, you will be receiving emails and updates
61
+ related to this unit.</span>
62
+ </v-col>
63
+ </v-row>
64
+
65
+ <v-dialog v-model="dialog.showPrompt" persistent width="540">
66
+ <DialogReusablePrompt :loading="loadingOwnerProcessing"
67
+ :prompt-title="`Are you sure want to ${person.isOwner ? 'remove' : 'add'} (${person.name}) as owner ?`"
68
+ @proceed="handleApprove(person)" @close="dialog.showPrompt = false" />
69
+ </v-dialog>
70
+ </v-card-text>
71
+ </template>
72
+
73
+ <script setup lang="ts">
74
+ import useFile from '../composables/useFile';
75
+
76
+ const prop = defineProps({
77
+ person: {
78
+ type: Object as PropType<TPeople>,
79
+ required: true,
80
+ },
81
+ readOnly: {
82
+ type: Boolean,
83
+ default: false,
84
+ },
85
+ showOwnerOption: {
86
+ type: Boolean,
87
+ default: false,
88
+ },
89
+ loadingOwnerProcessing: {
90
+ type: Boolean,
91
+ default: false,
92
+ }
93
+ })
94
+
95
+ const { getFileUrl } = useFile()
96
+
97
+ const emit = defineEmits(['update:owner'])
98
+
99
+
100
+ const dialog = reactive({
101
+ showPrompt: false,
102
+ })
103
+
104
+
105
+
106
+
107
+
108
+ const handleApprove = (person: TPeople) => {
109
+ dialog.showPrompt = false;
110
+ emit('update:owner', { person, isOwner: !person.isOwner })
111
+ }
112
+
113
+ const handleClick = () => {
114
+ dialog.showPrompt = true;
115
+ }
116
+
117
+ </script>
118
+
119
+ <style lang="scss" scoped>
120
+ .no-pointer {
121
+ pointer-events: none;
122
+ }
123
+ </style>