@7365admin1/layer-common 1.10.3 → 1.10.4

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 CHANGED
@@ -1,5 +1,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.10.4
4
+
5
+ ### Patch Changes
6
+
7
+ - a78dff9: Update Layer-common Changes for March 4, 2026
8
+
3
9
  ## 1.10.3
4
10
 
5
11
  ### Patch Changes
@@ -0,0 +1,297 @@
1
+ <template>
2
+ <v-dialog :model-value="modelValue" width="450" persistent>
3
+ <v-card width="100%">
4
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pb-0">
5
+ <v-row no-gutters class="mb-4">
6
+ <v-col cols="12">
7
+ <strong>Building:</strong> {{ unit?.block?.name ?? "N/A" }}
8
+ </v-col>
9
+ <v-col cols="12">
10
+ <strong>Level:</strong> {{ unit?.level?.level ?? "N/A" }}
11
+ </v-col>
12
+ <v-col cols="12">
13
+ <strong>Unit:</strong> {{ unit?.name ?? "N/A" }}
14
+ </v-col>
15
+
16
+ <!-- Available Physical -->
17
+ <v-col cols="12" class="mt-3">
18
+ <strong>Available Physical</strong>
19
+ <div v-if="unit?.available?.physical?.length" class="mt-1">
20
+ <v-chip
21
+ v-for="card in unit.available.physical"
22
+ :key="card._id"
23
+ size="small"
24
+ class="mr-1 mb-1"
25
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
26
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
27
+ style="cursor: pointer"
28
+ @click="toggleCard(card)"
29
+ >
30
+ {{ card.cardNo }}
31
+ </v-chip>
32
+ </div>
33
+ <span v-else class="text-caption text-grey ml-1">None</span>
34
+ </v-col>
35
+
36
+ <!-- Available Non-Physical -->
37
+ <v-col cols="12" class="mt-2">
38
+ <strong>Available Non-Physical</strong>
39
+ <div v-if="unit?.available?.non_physical?.length" class="mt-1">
40
+ <v-chip
41
+ v-for="card in unit.available.non_physical"
42
+ :key="card._id"
43
+ size="small"
44
+ class="mr-1 mb-1"
45
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
46
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
47
+ style="cursor: pointer"
48
+ @click="toggleCard(card)"
49
+ >
50
+ {{ card.cardNo }}
51
+ </v-chip>
52
+ </div>
53
+ <span v-else class="text-caption text-grey ml-1">None</span>
54
+ </v-col>
55
+
56
+ <!-- Assigned Physical -->
57
+ <v-col cols="12" class="mt-2">
58
+ <strong>Assigned Physical</strong>
59
+ <div v-if="unit?.assigned?.physical?.length" class="mt-1">
60
+ <v-chip
61
+ v-for="card in unit.assigned.physical"
62
+ :key="card._id"
63
+ size="small"
64
+ class="mr-1 mb-1"
65
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
66
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
67
+ style="cursor: pointer"
68
+ @click="toggleCard(card)"
69
+ >
70
+ {{ card.cardNo }}
71
+ </v-chip>
72
+ </div>
73
+ <span v-else class="text-caption text-grey ml-1">None</span>
74
+ </v-col>
75
+
76
+ <!-- Assigned Non-Physical -->
77
+ <v-col cols="12" class="mt-2">
78
+ <strong>Assigned Non-Physical</strong>
79
+ <div v-if="unit?.assigned?.non_physical?.length" class="mt-1">
80
+ <v-chip
81
+ v-for="card in unit.assigned.non_physical"
82
+ :key="card._id"
83
+ size="small"
84
+ class="mr-1 mb-1"
85
+ :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
86
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
87
+ style="cursor: pointer"
88
+ @click="toggleCard(card)"
89
+ >
90
+ {{ card.cardNo }}
91
+ </v-chip>
92
+ </div>
93
+ <span v-else class="text-caption text-grey ml-1">None</span>
94
+ </v-col>
95
+
96
+ <!-- Replaced Physical -->
97
+ <v-col cols="12" class="mt-2">
98
+ <strong>Replaced Physical</strong>
99
+ <div v-if="unit?.replaced?.physical?.length" class="mt-1">
100
+ <v-chip
101
+ v-for="card in unit.replaced.physical"
102
+ :key="card._id"
103
+ size="small"
104
+ class="mr-1 mb-1"
105
+ :color="selectedCardInUnit?._id === card._id ? 'orange' : 'orange'"
106
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
107
+ style="cursor: pointer"
108
+ @click="toggleCard(card)"
109
+ >
110
+ {{ card.cardNo }}
111
+ </v-chip>
112
+ </div>
113
+ <span v-else class="text-caption text-grey ml-1">None</span>
114
+ </v-col>
115
+
116
+ <!-- Replaced Non-Physical -->
117
+ <v-col cols="12" class="mt-2">
118
+ <strong>Replaced Non-Physical</strong>
119
+ <div v-if="unit?.replaced?.non_physical?.length" class="mt-1">
120
+ <v-chip
121
+ v-for="card in unit.replaced.non_physical"
122
+ :key="card._id"
123
+ size="small"
124
+ class="mr-1 mb-1"
125
+ :color="selectedCardInUnit?._id === card._id ? 'orange' : 'orange'"
126
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
127
+ style="cursor: pointer"
128
+ @click="toggleCard(card)"
129
+ >
130
+ {{ card.cardNo }}
131
+ </v-chip>
132
+ </div>
133
+ <span v-else class="text-caption text-grey ml-1">None</span>
134
+ </v-col>
135
+
136
+ <!-- Deleted Physical -->
137
+ <v-col cols="12" class="mt-2">
138
+ <strong>Deleted Physical</strong>
139
+ <div v-if="unit?.deleted?.physical?.length" class="mt-1">
140
+ <v-chip
141
+ v-for="card in unit.deleted.physical"
142
+ :key="card._id"
143
+ size="small"
144
+ class="mr-1 mb-1"
145
+ :color="selectedCardInUnit?._id === card._id ? 'red' : 'red'"
146
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
147
+ style="cursor: pointer"
148
+ @click="toggleCard(card)"
149
+ >
150
+ {{ card.cardNo }}
151
+ </v-chip>
152
+ </div>
153
+ <span v-else class="text-caption text-grey ml-1">None</span>
154
+ </v-col>
155
+
156
+ <!-- Deleted Non-Physical -->
157
+ <v-col cols="12" class="mt-2">
158
+ <strong>Deleted Non-Physical</strong>
159
+ <div v-if="unit?.deleted?.non_physical?.length" class="mt-1">
160
+ <v-chip
161
+ v-for="card in unit.deleted.non_physical"
162
+ :key="card._id"
163
+ size="small"
164
+ class="mr-1 mb-1"
165
+ :color="selectedCardInUnit?._id === card._id ? 'red' : 'red'"
166
+ :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
167
+ style="cursor: pointer"
168
+ @click="toggleCard(card)"
169
+ >
170
+ {{ card.cardNo }}
171
+ </v-chip>
172
+ </div>
173
+ <span v-else class="text-caption text-grey ml-1">None</span>
174
+ </v-col>
175
+ </v-row>
176
+ </v-card-text>
177
+
178
+ <v-toolbar class="pa-0" density="compact">
179
+ <v-row no-gutters>
180
+ <v-col cols="6" class="pa-0">
181
+ <v-btn
182
+ block
183
+ variant="text"
184
+ class="text-none"
185
+ size="large"
186
+ height="48"
187
+ @click="emit('update:modelValue', false)"
188
+ >
189
+ Close
190
+ </v-btn>
191
+ </v-col>
192
+ <v-col cols="6" class="pa-0" v-if="canUpdate">
193
+ <v-menu>
194
+ <template #activator="{ props }">
195
+ <v-btn
196
+ block
197
+ variant="flat"
198
+ color="black"
199
+ class="text-none"
200
+ height="48"
201
+ v-bind="props"
202
+ tile
203
+ >
204
+ More actions
205
+ </v-btn>
206
+ </template>
207
+ <v-list class="pa-0">
208
+ <v-list-item
209
+ :disabled="!isSelectedCardAssignedPhysical"
210
+ @click="emit('replace')"
211
+ v-if="canReplaceAccessCard && isSelectedCardAssignedPhysical"
212
+ >
213
+ <v-list-item-title class="text-subtitle-2">
214
+ Replace Card
215
+ </v-list-item-title>
216
+ </v-list-item>
217
+ <v-list-item :disabled="selectedCardInUnit === null">
218
+ <v-list-item-title class="text-subtitle-2 cursor-pointer">
219
+ Card History
220
+ </v-list-item-title>
221
+ </v-list-item>
222
+ <v-list-item
223
+ @click="emit('delete')"
224
+ class="text-red"
225
+ :disabled="!isSelectedCardDeletable"
226
+ v-if="canDeleteAccessCard"
227
+ >
228
+ <v-list-item-title class="text-subtitle-2">
229
+ Delete Card
230
+ </v-list-item-title>
231
+ </v-list-item>
232
+ </v-list>
233
+ </v-menu>
234
+ </v-col>
235
+ </v-row>
236
+ </v-toolbar>
237
+ </v-card>
238
+ </v-dialog>
239
+ </template>
240
+
241
+ <script setup lang="ts">
242
+ const props = defineProps({
243
+ modelValue: {
244
+ type: Boolean,
245
+ default: false,
246
+ },
247
+ unit: {
248
+ type: Object as PropType<Record<string, any>>,
249
+ default: () => ({}),
250
+ },
251
+ selectedCardInUnit: {
252
+ type: Object as PropType<Record<string, any> | null>,
253
+ default: null,
254
+ },
255
+ canUpdate: {
256
+ type: Boolean,
257
+ default: true,
258
+ },
259
+ canReplaceAccessCard: {
260
+ type: Boolean,
261
+ default: true,
262
+ },
263
+ canDeleteAccessCard: {
264
+ type: Boolean,
265
+ default: true,
266
+ },
267
+ isSelectedCardAssignedPhysical: {
268
+ type: Boolean,
269
+ default: false,
270
+ },
271
+ });
272
+
273
+ const emit = defineEmits<{
274
+ "update:modelValue": [value: boolean];
275
+ "update:selectedCardInUnit": [value: Record<string, any> | null];
276
+ replace: [];
277
+ delete: [];
278
+ }>();
279
+
280
+ const isSelectedCardDeletable = computed(() => {
281
+ if (!props.selectedCardInUnit?._id) return false;
282
+ const id = props.selectedCardInUnit._id;
283
+ return [
284
+ ...(props.unit?.available?.physical ?? []),
285
+ ...(props.unit?.available?.non_physical ?? []),
286
+ ...(props.unit?.assigned?.physical ?? []),
287
+ ...(props.unit?.assigned?.non_physical ?? []),
288
+ ].some((c) => c._id === id);
289
+ });
290
+
291
+ function toggleCard(card: Record<string, any>) {
292
+ emit(
293
+ "update:selectedCardInUnit",
294
+ props.selectedCardInUnit?._id === card._id ? null : card
295
+ );
296
+ }
297
+ </script>
@@ -0,0 +1,179 @@
1
+ <template>
2
+ <v-card width="100%">
3
+ <v-toolbar>
4
+ <v-row no-gutters class="fill-height px-6" align="center">
5
+ <span class="font-weight-bold text-h5">Replace Access Card</span>
6
+ </v-row>
7
+ </v-toolbar>
8
+ <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-0">
9
+ <v-form v-model="validForm" :disabled="loading">
10
+ <v-row no-gutters class="px-6 pt-4 pb-6">
11
+ <!-- Card being replaced -->
12
+ <v-col cols="12" class="px-1 mb-4">
13
+ <div class="text-caption text-grey mb-1 font-weight-bold">
14
+ Card being replaced
15
+ </div>
16
+ <v-chip size="small" variant="tonal" color="warning">
17
+ {{ card.cardNo }}
18
+ </v-chip>
19
+ </v-col>
20
+
21
+ <!-- Replacement card -->
22
+ <v-col cols="12" class="px-1">
23
+ <InputLabel
24
+ class="text-capitalize font-weight-bold"
25
+ title="Replacement Card"
26
+ required
27
+ />
28
+ <v-autocomplete
29
+ v-model="form.replacementCardId"
30
+ density="compact"
31
+ :items="replacementOptions"
32
+ hide-details="auto"
33
+ item-title="cardNo"
34
+ item-value="_id"
35
+ placeholder="Select replacement card..."
36
+ persistent-placeholder
37
+ :rules="[requiredRule]"
38
+ :no-data-text="noDataText"
39
+ />
40
+ </v-col>
41
+
42
+ <!-- Remarks -->
43
+ <v-col cols="12" class="px-1 mt-2">
44
+ <InputLabel
45
+ class="text-capitalize font-weight-bold"
46
+ title="Remarks"
47
+ required
48
+ />
49
+ <v-textarea
50
+ v-model="form.remarks"
51
+ density="compact"
52
+ hide-details="auto"
53
+ placeholder="Enter remarks..."
54
+ persistent-placeholder
55
+ rows="3"
56
+ auto-grow
57
+ :rules="[requiredRule]"
58
+ />
59
+ </v-col>
60
+ </v-row>
61
+ </v-form>
62
+ </v-card-text>
63
+
64
+ <v-toolbar density="compact">
65
+ <v-row no-gutters>
66
+ <v-col cols="6">
67
+ <v-btn
68
+ tile
69
+ block
70
+ variant="text"
71
+ class="text-none"
72
+ size="48"
73
+ @click="cancel"
74
+ :disabled="loading"
75
+ >
76
+ Cancel
77
+ </v-btn>
78
+ </v-col>
79
+ <v-col cols="6">
80
+ <v-btn
81
+ tile
82
+ block
83
+ variant="flat"
84
+ color="black"
85
+ class="text-none"
86
+ size="48"
87
+ :disabled="!validForm || loading"
88
+ :loading="loading"
89
+ @click="submit"
90
+ >
91
+ Replace
92
+ </v-btn>
93
+ </v-col>
94
+ </v-row>
95
+ </v-toolbar>
96
+ </v-card>
97
+ </template>
98
+ <script setup lang="ts">
99
+ const props = defineProps({
100
+ card: {
101
+ type: Object as PropType<Record<string, any>>,
102
+ required: true,
103
+ },
104
+ unit: {
105
+ type: Object as PropType<Record<string, any>>,
106
+ required: true,
107
+ },
108
+ siteId: {
109
+ type: String,
110
+ default: "",
111
+ },
112
+ });
113
+ const emit = defineEmits(["cancel", "success", "error"]);
114
+
115
+ const { requiredRule } = useUtils();
116
+
117
+ const validForm = ref(false);
118
+ const loading = ref(false);
119
+
120
+ const form = ref({
121
+ replacementCardId: null as string | null,
122
+ remarks: "",
123
+ });
124
+
125
+ const cardType = computed(() => {
126
+ const id = props.card._id;
127
+ if (props.unit.available?.physical?.some((c: any) => c._id === id)) return "physical";
128
+ if (props.unit.assigned?.physical?.some((c: any) => c._id === id)) return "physical";
129
+ if (props.unit.available?.non_physical?.some((c: any) => c._id === id)) return "non_physical";
130
+ if (props.unit.assigned?.non_physical?.some((c: any) => c._id === id)) return "non_physical";
131
+ return null;
132
+ });
133
+
134
+ const replacementOptions = computed(() => {
135
+ const id = props.card._id;
136
+ if (cardType.value === "physical") {
137
+ return (props.unit.available?.physical ?? []).filter((c: any) => c._id !== id);
138
+ }
139
+ if (cardType.value === "non_physical") {
140
+ return (props.unit.available?.non_physical ?? []).filter((c: any) => c._id !== id);
141
+ }
142
+ return [];
143
+ });
144
+
145
+ const noDataText = computed(() => {
146
+ if (!cardType.value) return "Unable to determine card type";
147
+ const label = cardType.value === "physical" ? "physical" : "non-physical";
148
+ return `No available ${label} cards for replacement`;
149
+ });
150
+
151
+ function cancel() {
152
+ emit("cancel");
153
+ }
154
+
155
+ async function submit() {
156
+ loading.value = true;
157
+ try {
158
+ const { replaceCard } = useCard();
159
+ const { currentUser } = useLocalAuth();
160
+ // @TODO: userId should be the ID of the unit owner, not the current user
161
+ await replaceCard({
162
+ cardId: props.card._id,
163
+ issuedCardId: form.value.replacementCardId!,
164
+ unitId: props.unit._id,
165
+ remarks: form.value.remarks,
166
+ userId: currentUser.value?._id ?? "",
167
+ });
168
+ emit("success");
169
+ } catch (error: any) {
170
+ const msg =
171
+ error?.response?._data?.message ||
172
+ error?.data?.message ||
173
+ "Failed to replace access card.";
174
+ emit("error", msg);
175
+ } finally {
176
+ loading.value = false;
177
+ }
178
+ }
179
+ </script>