@7365admin1/layer-common 1.10.3 → 1.10.5

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 (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/components/AccessCardDeleteDialog.vue +109 -0
  3. package/components/AccessCardHistoryDialog.vue +133 -0
  4. package/components/AccessCardPreviewDialog.vue +308 -0
  5. package/components/AccessCardQrTagging.vue +183 -0
  6. package/components/AccessCardReplaceForm.vue +179 -0
  7. package/components/AccessManagement.vue +61 -251
  8. package/components/AreaChecklistHistoryLogs.vue +1 -1
  9. package/components/BuildingManagement/units.vue +33 -2
  10. package/components/BuildingUnitFormAdd.vue +45 -99
  11. package/components/BuildingUnitFormEdit.vue +59 -148
  12. package/components/BulletinBoardManagement.vue +6 -1
  13. package/components/BulletinBoardView.vue +2 -2
  14. package/components/Button/Close.vue +3 -1
  15. package/components/CleaningScheduleMain.vue +20 -9
  16. package/components/IncidentReport/IncidentInformation.vue +45 -6
  17. package/components/IncidentReport/affectedEntities.vue +29 -0
  18. package/components/PeopleForm.vue +1 -1
  19. package/components/PlateNumberDisplay.vue +45 -0
  20. package/components/ScheduleAreaMain.vue +5 -2
  21. package/components/ScheduleTaskForm.vue +59 -114
  22. package/components/ScheduleTaskMain.vue +19 -15
  23. package/components/VehicleAddSelection.vue +58 -0
  24. package/components/VehicleForm.vue +600 -0
  25. package/components/VehicleManagement.vue +298 -0
  26. package/components/VisitorForm.vue +1 -1
  27. package/composables/useAccessManagement.ts +16 -0
  28. package/composables/useBulletin.ts +11 -9
  29. package/composables/useCard.ts +14 -0
  30. package/composables/useScheduleTask.ts +4 -8
  31. package/composables/useVehicle.ts +114 -0
  32. package/package.json +1 -1
  33. package/types/bulletin-board.d.ts +1 -1
  34. package/types/checkout-item.d.ts +1 -0
  35. package/types/cleaner-schedule.d.ts +1 -1
  36. package/types/people.d.ts +1 -1
  37. package/types/vehicle.d.ts +43 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.10.5
4
+
5
+ ### Patch Changes
6
+
7
+ - eaec446: Update changes for March 6, 2024
8
+
9
+ ## 1.10.4
10
+
11
+ ### Patch Changes
12
+
13
+ - a78dff9: Update Layer-common Changes for March 4, 2026
14
+
3
15
  ## 1.10.3
4
16
 
5
17
  ### Patch Changes
@@ -0,0 +1,109 @@
1
+ <template>
2
+ <v-dialog :model-value="modelValue" width="450" persistent>
3
+ <v-card width="100%">
4
+ <v-toolbar density="compact" class="pl-4">
5
+ <span class="font-weight-medium text-h5">Delete Card</span>
6
+ </v-toolbar>
7
+
8
+ <v-card-text>
9
+ <p class="text-subtitle-2 text-center mb-4">
10
+ Are you sure you want to delete this card? This action cannot be
11
+ undone.
12
+ </p>
13
+
14
+ <v-form v-model="validForm" :disabled="loading">
15
+ <InputLabel class="text-capitalize font-weight-bold" title="Remarks" required />
16
+ <v-textarea
17
+ v-model="remarks"
18
+ placeholder="Enter remarks..."
19
+ persistent-placeholder
20
+ rows="3"
21
+ auto-grow
22
+ hide-details="auto"
23
+ :rules="[requiredRule]"
24
+ />
25
+ </v-form>
26
+
27
+ <v-row v-if="error" no-gutters justify="center" class="mt-3">
28
+ <span class="text-caption text-error text-center">{{ error }}</span>
29
+ </v-row>
30
+ </v-card-text>
31
+
32
+ <v-toolbar density="compact">
33
+ <v-row no-gutters>
34
+ <v-col cols="6">
35
+ <v-btn
36
+ tile
37
+ block
38
+ size="48"
39
+ variant="text"
40
+ class="text-none"
41
+ :disabled="loading"
42
+ @click="handleClose"
43
+ >
44
+ Close
45
+ </v-btn>
46
+ </v-col>
47
+ <v-col cols="6">
48
+ <v-btn
49
+ tile
50
+ block
51
+ size="48"
52
+ color="black"
53
+ variant="flat"
54
+ class="text-none"
55
+ :loading="loading"
56
+ :disabled="!validForm || loading"
57
+ @click="handleConfirm"
58
+ >
59
+ Delete Card
60
+ </v-btn>
61
+ </v-col>
62
+ </v-row>
63
+ </v-toolbar>
64
+ </v-card>
65
+ </v-dialog>
66
+ </template>
67
+
68
+ <script setup lang="ts">
69
+ const props = defineProps({
70
+ modelValue: {
71
+ type: Boolean,
72
+ default: false,
73
+ },
74
+ loading: {
75
+ type: Boolean,
76
+ default: false,
77
+ },
78
+ error: {
79
+ type: String,
80
+ default: "",
81
+ },
82
+ });
83
+
84
+ const emit = defineEmits<{
85
+ "update:modelValue": [value: boolean];
86
+ confirm: [remarks: string];
87
+ }>();
88
+
89
+ const { requiredRule } = useUtils();
90
+
91
+ const validForm = ref(false);
92
+ const remarks = ref("");
93
+
94
+ watch(
95
+ () => props.modelValue,
96
+ (val) => {
97
+ if (val) remarks.value = "";
98
+ }
99
+ );
100
+
101
+ function handleClose() {
102
+ emit("update:modelValue", false);
103
+ }
104
+
105
+ function handleConfirm() {
106
+ if (!validForm.value) return;
107
+ emit("confirm", remarks.value.trim());
108
+ }
109
+ </script>
@@ -0,0 +1,133 @@
1
+ <template>
2
+ <v-dialog :model-value="modelValue" width="480" persistent>
3
+ <v-card width="100%">
4
+ <v-toolbar density="compact" color="black">
5
+ <v-toolbar-title class="text-subtitle-1 font-weight-medium">
6
+ Card History — {{ card?.cardNo ?? "N/A" }}
7
+ </v-toolbar-title>
8
+ <v-btn icon @click="emit('update:modelValue', false)">
9
+ <v-icon>mdi-close</v-icon>
10
+ </v-btn>
11
+ </v-toolbar>
12
+
13
+ <v-card-text style="max-height: 70vh; overflow-y: auto" class="pa-4">
14
+ <div v-if="pending" class="d-flex justify-center align-center py-8">
15
+ <v-progress-circular indeterminate color="black" />
16
+ </div>
17
+
18
+ <div v-else-if="!history.length" class="text-center text-grey py-8">
19
+ No history available.
20
+ </div>
21
+
22
+ <v-timeline v-else side="end" density="compact" truncate-line="both">
23
+ <v-timeline-item
24
+ v-for="(item, index) in history"
25
+ :key="index"
26
+ :dot-color="actionColor(item.action)"
27
+ size="small"
28
+ >
29
+ <template #icon>
30
+ <v-icon size="14" color="white">{{ actionIcon(item.action) }}</v-icon>
31
+ </template>
32
+
33
+ <div class="mb-4">
34
+ <div class="d-flex align-center ga-2 mb-1">
35
+ <v-chip
36
+ :color="actionColor(item.action)"
37
+ variant="flat"
38
+ size="x-small"
39
+ class="text-capitalize font-weight-medium"
40
+ >
41
+ {{ actionLabel(item.action) }}
42
+ </v-chip>
43
+ </div>
44
+ <div class="text-body-2">{{ item.performedBy?.name ?? "N/A" }}</div>
45
+ <div class="text-caption text-grey">{{ formatDate(item.date) }}</div>
46
+ </div>
47
+ </v-timeline-item>
48
+ </v-timeline>
49
+ </v-card-text>
50
+ </v-card>
51
+ </v-dialog>
52
+ </template>
53
+
54
+ <script setup lang="ts">
55
+ const props = defineProps({
56
+ modelValue: {
57
+ type: Boolean,
58
+ default: false,
59
+ },
60
+ card: {
61
+ type: Object as PropType<Record<string, any> | null>,
62
+ default: null,
63
+ },
64
+ });
65
+
66
+ const emit = defineEmits<{
67
+ "update:modelValue": [value: boolean];
68
+ }>();
69
+
70
+ const { getCardHistory } = useAccessManagement();
71
+
72
+ const history = ref<Record<string, any>[]>([]);
73
+ const pending = ref(false);
74
+
75
+ watch(
76
+ () => props.modelValue,
77
+ async (val) => {
78
+ if (val && props.card?._id) {
79
+ pending.value = true;
80
+ try {
81
+ const data = await getCardHistory(props.card._id);
82
+ history.value = Array.isArray(data) ? data : [];
83
+ } finally {
84
+ pending.value = false;
85
+ }
86
+ } else {
87
+ history.value = [];
88
+ }
89
+ }
90
+ );
91
+
92
+ function actionLabel(action: string) {
93
+ const map: Record<string, string> = {
94
+ available: "Created",
95
+ assign: "Assigned",
96
+ replace: "Replaced",
97
+ deleted: "Deleted",
98
+ };
99
+ return map[action] ?? action;
100
+ }
101
+
102
+ function actionColor(action: string) {
103
+ const map: Record<string, string> = {
104
+ available: "success",
105
+ assign: "primary",
106
+ replace: "orange",
107
+ deleted: "error",
108
+ };
109
+ return map[action] ?? "grey";
110
+ }
111
+
112
+ function actionIcon(action: string) {
113
+ const map: Record<string, string> = {
114
+ available: "mdi-plus",
115
+ assign: "mdi-account-check",
116
+ replace: "mdi-swap-horizontal",
117
+ deleted: "mdi-delete",
118
+ };
119
+ return map[action] ?? "mdi-circle-small";
120
+ }
121
+
122
+ function formatDate(date: string) {
123
+ if (!date) return "N/A";
124
+ return new Intl.DateTimeFormat("en-GB", {
125
+ day: "2-digit",
126
+ month: "short",
127
+ year: "numeric",
128
+ hour: "2-digit",
129
+ minute: "2-digit",
130
+ hour12: true,
131
+ }).format(new Date(date));
132
+ }
133
+ </script>
@@ -0,0 +1,308 @@
1
+ <template>
2
+ <AccessCardHistoryDialog
3
+ v-model="historyDialog"
4
+ :card="selectedCardInUnit"
5
+ />
6
+
7
+ <v-dialog :model-value="modelValue" width="450" persistent>
8
+ <v-card width="100%">
9
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
10
+ <v-row no-gutters class="mb-4">
11
+ <v-col cols="12">
12
+ <strong>Building:</strong> {{ unit?.block?.name ?? "N/A" }}
13
+ </v-col>
14
+ <v-col cols="12">
15
+ <strong>Level:</strong> {{ unit?.level?.level ?? "N/A" }}
16
+ </v-col>
17
+ <v-col cols="12">
18
+ <strong>Unit:</strong> {{ unit?.name ?? "N/A" }}
19
+ </v-col>
20
+
21
+ <!-- Available Physical -->
22
+ <v-col cols="12" class="mt-3">
23
+ <strong>Available Physical</strong>
24
+ <div v-if="unit?.available?.physical?.length" class="mt-1">
25
+ <v-chip
26
+ v-for="card in unit.available.physical"
27
+ :key="card._id"
28
+ size="small"
29
+ class="mr-1 mb-1"
30
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
31
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
32
+ style="cursor: pointer"
33
+ @click="toggleCard(card)"
34
+ >
35
+ {{ card.cardNo }}
36
+ </v-chip>
37
+ </div>
38
+ <span v-else class="text-caption text-grey ml-1">None</span>
39
+ </v-col>
40
+
41
+ <!-- Available Non-Physical -->
42
+ <v-col cols="12" class="mt-2">
43
+ <strong>Available Non-Physical</strong>
44
+ <div v-if="unit?.available?.non_physical?.length" class="mt-1">
45
+ <v-chip
46
+ v-for="card in unit.available.non_physical"
47
+ :key="card._id"
48
+ size="small"
49
+ class="mr-1 mb-1"
50
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
51
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
52
+ style="cursor: pointer"
53
+ @click="toggleCard(card)"
54
+ >
55
+ {{ card.cardNo }}
56
+ </v-chip>
57
+ </div>
58
+ <span v-else class="text-caption text-grey ml-1">None</span>
59
+ </v-col>
60
+
61
+ <!-- Assigned Physical -->
62
+ <v-col cols="12" class="mt-2">
63
+ <strong>Assigned Physical</strong>
64
+ <div v-if="unit?.assigned?.physical?.length" class="mt-1">
65
+ <v-chip
66
+ v-for="card in unit.assigned.physical"
67
+ :key="card._id"
68
+ size="small"
69
+ class="mr-1 mb-1"
70
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
71
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
72
+ style="cursor: pointer"
73
+ @click="toggleCard(card)"
74
+ >
75
+ {{ card.cardNo }}
76
+ </v-chip>
77
+ </div>
78
+ <span v-else class="text-caption text-grey ml-1">None</span>
79
+ </v-col>
80
+
81
+ <!-- Assigned Non-Physical -->
82
+ <v-col cols="12" class="mt-2">
83
+ <strong>Assigned Non-Physical</strong>
84
+ <div v-if="unit?.assigned?.non_physical?.length" class="mt-1">
85
+ <v-chip
86
+ v-for="card in unit.assigned.non_physical"
87
+ :key="card._id"
88
+ size="small"
89
+ class="mr-1 mb-1"
90
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
91
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
92
+ style="cursor: pointer"
93
+ @click="toggleCard(card)"
94
+ >
95
+ {{ card.cardNo }}
96
+ </v-chip>
97
+ </div>
98
+ <span v-else class="text-caption text-grey ml-1">None</span>
99
+ </v-col>
100
+
101
+ <!-- Replaced Physical -->
102
+ <v-col cols="12" class="mt-2">
103
+ <strong>Replaced Physical</strong>
104
+ <div v-if="unit?.replaced?.physical?.length" class="mt-1">
105
+ <v-chip
106
+ v-for="card in unit.replaced.physical"
107
+ :key="card._id"
108
+ size="small"
109
+ class="mr-1 mb-1"
110
+ :color="selectedCardInUnit?._id === card._id ? 'orange' : 'orange'"
111
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
112
+ style="cursor: pointer"
113
+ @click="toggleCard(card)"
114
+ >
115
+ {{ card.cardNo }}
116
+ </v-chip>
117
+ </div>
118
+ <span v-else class="text-caption text-grey ml-1">None</span>
119
+ </v-col>
120
+
121
+ <!-- Replaced Non-Physical -->
122
+ <v-col cols="12" class="mt-2">
123
+ <strong>Replaced Non-Physical</strong>
124
+ <div v-if="unit?.replaced?.non_physical?.length" class="mt-1">
125
+ <v-chip
126
+ v-for="card in unit.replaced.non_physical"
127
+ :key="card._id"
128
+ size="small"
129
+ class="mr-1 mb-1"
130
+ :color="selectedCardInUnit?._id === card._id ? 'orange' : 'orange'"
131
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
132
+ style="cursor: pointer"
133
+ @click="toggleCard(card)"
134
+ >
135
+ {{ card.cardNo }}
136
+ </v-chip>
137
+ </div>
138
+ <span v-else class="text-caption text-grey ml-1">None</span>
139
+ </v-col>
140
+
141
+ <!-- Deleted Physical -->
142
+ <v-col cols="12" class="mt-2">
143
+ <strong>Deleted Physical</strong>
144
+ <div v-if="unit?.deleted?.physical?.length" class="mt-1">
145
+ <v-chip
146
+ v-for="card in unit.deleted.physical"
147
+ :key="card._id"
148
+ size="small"
149
+ class="mr-1 mb-1"
150
+ :color="selectedCardInUnit?._id === card._id ? 'red' : 'red'"
151
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
152
+ style="cursor: pointer"
153
+ @click="toggleCard(card)"
154
+ >
155
+ {{ card.cardNo }}
156
+ </v-chip>
157
+ </div>
158
+ <span v-else class="text-caption text-grey ml-1">None</span>
159
+ </v-col>
160
+
161
+ <!-- Deleted Non-Physical -->
162
+ <v-col cols="12" class="mt-2">
163
+ <strong>Deleted Non-Physical</strong>
164
+ <div v-if="unit?.deleted?.non_physical?.length" class="mt-1">
165
+ <v-chip
166
+ v-for="card in unit.deleted.non_physical"
167
+ :key="card._id"
168
+ size="small"
169
+ class="mr-1 mb-1"
170
+ :color="selectedCardInUnit?._id === card._id ? 'red' : 'red'"
171
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
172
+ style="cursor: pointer"
173
+ @click="toggleCard(card)"
174
+ >
175
+ {{ card.cardNo }}
176
+ </v-chip>
177
+ </div>
178
+ <span v-else class="text-caption text-grey ml-1">None</span>
179
+ </v-col>
180
+ </v-row>
181
+ </v-card-text>
182
+
183
+ <v-toolbar class="pa-0" density="compact">
184
+ <v-row no-gutters>
185
+ <v-col cols="6" class="pa-0">
186
+ <v-btn
187
+ block
188
+ variant="text"
189
+ class="text-none"
190
+ size="large"
191
+ height="48"
192
+ @click="emit('update:modelValue', false)"
193
+ >
194
+ Close
195
+ </v-btn>
196
+ </v-col>
197
+ <v-col cols="6" class="pa-0" v-if="canUpdate">
198
+ <v-menu>
199
+ <template #activator="{ props }">
200
+ <v-btn
201
+ block
202
+ variant="flat"
203
+ color="black"
204
+ class="text-none"
205
+ height="48"
206
+ v-bind="props"
207
+ tile
208
+ >
209
+ More actions
210
+ </v-btn>
211
+ </template>
212
+ <v-list class="pa-0">
213
+ <v-list-item
214
+ :disabled="!isSelectedCardAssignedPhysical"
215
+ @click="emit('replace')"
216
+ v-if="canReplaceAccessCard && isSelectedCardAssignedPhysical"
217
+ >
218
+ <v-list-item-title class="text-subtitle-2">
219
+ Replace Card
220
+ </v-list-item-title>
221
+ </v-list-item>
222
+ <v-list-item :disabled="!isSelectedCardPhysical" @click="historyDialog = true">
223
+ <v-list-item-title class="text-subtitle-2 cursor-pointer">
224
+ Card History
225
+ </v-list-item-title>
226
+ </v-list-item>
227
+ <v-list-item
228
+ @click="emit('delete')"
229
+ class="text-red"
230
+ :disabled="!isSelectedCardDeletable"
231
+ v-if="canDeleteAccessCard"
232
+ >
233
+ <v-list-item-title class="text-subtitle-2">
234
+ Delete Card
235
+ </v-list-item-title>
236
+ </v-list-item>
237
+ </v-list>
238
+ </v-menu>
239
+ </v-col>
240
+ </v-row>
241
+ </v-toolbar>
242
+ </v-card>
243
+ </v-dialog>
244
+ </template>
245
+
246
+ <script setup lang="ts">
247
+ const props = defineProps({
248
+ modelValue: {
249
+ type: Boolean,
250
+ default: false,
251
+ },
252
+ unit: {
253
+ type: Object as PropType<Record<string, any>>,
254
+ default: () => ({}),
255
+ },
256
+ selectedCardInUnit: {
257
+ type: Object as PropType<Record<string, any> | null>,
258
+ default: null,
259
+ },
260
+ canUpdate: {
261
+ type: Boolean,
262
+ default: true,
263
+ },
264
+ canReplaceAccessCard: {
265
+ type: Boolean,
266
+ default: true,
267
+ },
268
+ canDeleteAccessCard: {
269
+ type: Boolean,
270
+ default: true,
271
+ },
272
+ isSelectedCardAssignedPhysical: {
273
+ type: Boolean,
274
+ default: false,
275
+ },
276
+ isSelectedCardPhysical: {
277
+ type: Boolean,
278
+ default: false,
279
+ },
280
+ });
281
+
282
+ const emit = defineEmits<{
283
+ "update:modelValue": [value: boolean];
284
+ "update:selectedCardInUnit": [value: Record<string, any> | null];
285
+ replace: [];
286
+ delete: [];
287
+ }>();
288
+
289
+ const historyDialog = ref(false);
290
+
291
+ const isSelectedCardDeletable = computed(() => {
292
+ if (!props.selectedCardInUnit?._id) return false;
293
+ const id = props.selectedCardInUnit._id;
294
+ return [
295
+ ...(props.unit?.available?.physical ?? []),
296
+ ...(props.unit?.available?.non_physical ?? []),
297
+ ...(props.unit?.assigned?.physical ?? []),
298
+ ...(props.unit?.assigned?.non_physical ?? []),
299
+ ].some((c) => c._id === id);
300
+ });
301
+
302
+ function toggleCard(card: Record<string, any>) {
303
+ emit(
304
+ "update:selectedCardInUnit",
305
+ props.selectedCardInUnit?._id === card._id ? null : card
306
+ );
307
+ }
308
+ </script>