@7365admin1/layer-common 1.10.6 → 1.10.7

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 (35) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components/AccessCardQrTagging.vue +314 -34
  3. package/components/AccessCardQrTaggingPrintQr.vue +75 -0
  4. package/components/AreaChecklistHistoryLogs.vue +9 -0
  5. package/components/BuildingForm.vue +36 -5
  6. package/components/BuildingManagement/buildings.vue +18 -9
  7. package/components/BuildingManagement/units.vue +12 -114
  8. package/components/BuildingUnitFormAdd.vue +39 -30
  9. package/components/BuildingUnitFormEdit.vue +265 -116
  10. package/components/CleaningScheduleMain.vue +60 -13
  11. package/components/Dialog/DeleteConfirmation.vue +2 -2
  12. package/components/Dialog/UpdateMoreAction.vue +2 -2
  13. package/components/EntryPassInformation.vue +215 -0
  14. package/components/Input/InputPhoneNumberV2.vue +8 -0
  15. package/components/ManageChecklistMain.vue +29 -3
  16. package/components/ScheduleAreaMain.vue +56 -0
  17. package/components/TableHygiene.vue +27 -0
  18. package/components/UnitPersonCard.vue +63 -0
  19. package/components/VehicleAddSelection.vue +2 -2
  20. package/components/VehicleForm.vue +78 -19
  21. package/components/VehicleManagement.vue +164 -40
  22. package/components/VisitorForm.vue +19 -0
  23. package/composables/useAccessManagement.ts +52 -0
  24. package/composables/usePeople.ts +4 -3
  25. package/composables/useVehicle.ts +15 -1
  26. package/package.json +3 -2
  27. package/types/cleaner-schedule.d.ts +1 -0
  28. package/types/html2pdf.d.ts +19 -0
  29. package/types/people.d.ts +2 -1
  30. package/types/site.d.ts +8 -0
  31. package/types/vehicle.d.ts +2 -3
  32. package/.playground/app.vue +0 -41
  33. package/.playground/eslint.config.mjs +0 -6
  34. package/.playground/nuxt.config.ts +0 -22
  35. package/.playground/pages/feedback.vue +0 -30
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @iservice365/layer-common
2
2
 
3
+ ## 1.10.7
4
+
5
+ ### Patch Changes
6
+
7
+ - a17cf02: Update Changes for march 11, 2026
8
+
3
9
  ## 1.10.6
4
10
 
5
11
  ### Patch Changes
@@ -9,6 +9,7 @@
9
9
  variant="tonal"
10
10
  size="large"
11
11
  :disabled="!items.length"
12
+ @click="handlePrintAll"
12
13
  >
13
14
  Print All
14
15
  </v-btn>
@@ -17,7 +18,8 @@
17
18
  rounded="pill"
18
19
  variant="tonal"
19
20
  size="large"
20
- :disabled="!selected.length"
21
+ :disabled="!qrCodeAccessCards.length"
22
+ @click="generateQrCodes"
21
23
  >
22
24
  Generate QR Code
23
25
  </v-btn>
@@ -26,23 +28,14 @@
26
28
  rounded="pill"
27
29
  variant="tonal"
28
30
  size="large"
29
- :disabled="!selected.length"
31
+ :disabled="!qrCodeAccessCardsToPrint.length"
32
+ @click="handlePrintQrCode"
30
33
  >
31
34
  Print QR Code
32
35
  </v-btn>
33
36
  </v-row>
34
37
 
35
38
  <v-row no-gutters class="ga-2" justify="end" style="max-width: 420px">
36
- <v-select
37
- v-model="typeFilter"
38
- :items="typeOptions"
39
- placeholder="Type"
40
- variant="outlined"
41
- density="comfortable"
42
- hide-details
43
- clearable
44
- style="max-width: 160px"
45
- />
46
39
  <v-text-field
47
40
  v-model="searchText"
48
41
  placeholder="Search Card..."
@@ -86,55 +79,133 @@
86
79
  </v-toolbar>
87
80
 
88
81
  <v-data-table
89
- v-model="selected"
90
82
  :headers="tableHeaders"
91
83
  :items="items"
92
84
  item-value="_id"
93
85
  items-per-page="10"
94
86
  fixed-header
95
87
  hide-default-footer
96
- show-select
97
88
  style="max-height: calc(100vh - 200px)"
98
89
  >
99
- <template #item.card="{ item }">
90
+ <template #header.selectGenerate>
91
+ <v-checkbox
92
+ :model-value="isAllGenerateSelected"
93
+ @update:model-value="toggleSelectAllGenerate"
94
+ hide-details
95
+ density="compact"
96
+ :disabled="!hasGeneratableItems"
97
+ />
98
+ </template>
99
+
100
+ <template #header.selectPrint>
101
+ <v-checkbox
102
+ :model-value="isAllPrintSelected"
103
+ @update:model-value="toggleSelectAllPrint"
104
+ hide-details
105
+ density="compact"
106
+ :disabled="!hasPrintableItems"
107
+ style="margin-left: -12px"
108
+ />
109
+ </template>
110
+
111
+ <template #item.selectGenerate="{ item }">
112
+ <v-checkbox
113
+ v-if="!item.qrTag"
114
+ :model-value="qrCodeAccessCards.includes(item._id)"
115
+ @update:model-value="toggleGenerateSelection(item._id)"
116
+ hide-details
117
+ density="compact"
118
+ />
119
+ </template>
120
+
121
+ <template #item.cardNo="{ item }">
100
122
  <v-row no-gutters align="center" class="ga-2">
101
123
  <v-icon size="18" color="grey-darken-1">mdi-card-account-details</v-icon>
102
- <span>{{ item.hid ?? "N/A" }}</span>
124
+ <span>{{ item.cardNo ?? "N/A" }}</span>
103
125
  </v-row>
104
126
  </template>
105
127
 
106
- <template #item.qrCode="{ item }">
128
+ <template #item.qrTagCardNo="{ item }">
129
+ <span>{{ item.qrTagCardNo != null ? `C${item.qrTagCardNo}` : "N/A" }}</span>
130
+ </template>
131
+
132
+ <template #item.qrTag="{ item }">
107
133
  <v-icon
108
- :color="item.qrCode ? 'success' : 'grey-lighten-1'"
134
+ :color="item.qrTag ? 'success' : 'grey-lighten-1'"
109
135
  size="22"
110
136
  >
111
- {{ item.qrCode ? "mdi-check-circle" : "mdi-circle-outline" }}
137
+ {{ item.qrTag ? "mdi-check-circle" : "mdi-circle-outline" }}
112
138
  </v-icon>
113
139
  </template>
140
+
141
+ <template #item.selectPrint="{ item }">
142
+ <v-checkbox
143
+ v-if="item.qrTag"
144
+ :model-value="qrCodeAccessCardsToPrint.includes(item._id)"
145
+ @update:model-value="togglePrintSelection(item._id)"
146
+ hide-details
147
+ density="compact"
148
+ style="margin-left: -12px"
149
+ />
150
+ </template>
114
151
  </v-data-table>
115
152
  </v-card>
116
153
  </v-col>
117
154
 
155
+ <!-- Hidden QR canvases for generation -->
156
+ <ClientOnly>
157
+ <div style="display: none">
158
+ <qrcode-vue
159
+ v-for="qrCode in qrCodesToGenerate"
160
+ :key="qrCode._id"
161
+ :value="qrCode.qrData"
162
+ :size="150"
163
+ :data-card-id="qrCode._id"
164
+ />
165
+ </div>
166
+ </ClientOnly>
167
+
168
+ <!-- Print dialog -->
169
+ <v-dialog v-model="showPrintDialog" max-width="fit-content">
170
+ <AccessCardQrTaggingPrintQr
171
+ ref="printQrRef"
172
+ :access-cards="printPayload"
173
+ @close="showPrintDialog = false"
174
+ />
175
+ </v-dialog>
176
+
177
+ <!-- Processing loader -->
178
+ <v-dialog v-model="isGenerating" persistent width="300">
179
+ <v-card>
180
+ <v-card-text class="text-center">
181
+ <v-progress-circular indeterminate color="deep-purple" size="50" />
182
+ <div class="mt-3">Processing QR Codes...</div>
183
+ </v-card-text>
184
+ </v-card>
185
+ </v-dialog>
186
+
118
187
  <Snackbar v-model="messageSnackbar" :text="message" :color="messageColor" />
119
188
  </v-row>
120
189
  </template>
121
190
 
122
191
  <script setup lang="ts">
192
+ import QrcodeVue from "qrcode.vue";
193
+
123
194
  definePageMeta({
124
195
  middleware: ["01-auth", "02-org"],
125
196
  memberOnly: true,
126
197
  });
127
198
 
128
199
  const tableHeaders = [
129
- { title: "Card", key: "card", sortable: false },
130
- { title: "Location", key: "location", sortable: false },
131
- { title: "Card No.", key: "cardNo", sortable: false },
132
- { title: "HID", key: "hid", sortable: false },
133
- { title: "QR Code", key: "qrCode", sortable: false },
200
+ { title: "", key: "selectGenerate", sortable: false, width: "50px" },
201
+ { title: "Card", key: "cardNo", sortable: false },
202
+ { title: "Location", key: "accessLevel", sortable: false },
203
+ { title: "Card No.", key: "qrTagCardNo", sortable: false },
204
+ { title: "HID", key: "extractedCardNo", sortable: false },
205
+ { title: "QR Code", key: "qrTag", sortable: false },
206
+ { title: "", key: "selectPrint", sortable: false, width: "50px" },
134
207
  ];
135
208
 
136
- const typeOptions = ["Physical", "Non-Physical"];
137
-
138
209
  const route = useRoute();
139
210
  const siteId = computed(() => route.params.site as string);
140
211
  const orgId = computed(() => route.params.org as string);
@@ -148,9 +219,24 @@ const messageSnackbar = ref(false);
148
219
  const messageColor = ref("");
149
220
 
150
221
  const items = ref<Record<string, any>[]>([]);
151
- const selected = ref<string[]>([]);
152
222
  const searchText = ref("");
153
- const typeFilter = ref<string | null>(null);
223
+
224
+ const qrCodeAccessCards = ref<string[]>([]);
225
+ const qrCodeAccessCardsToPrint = ref<string[]>([]);
226
+ const qrCodesToGenerate = ref<{ _id: string; cardNo: string; qrData: string; qrTagCardNo: string }[]>([]);
227
+
228
+ const isGenerating = ref(false);
229
+ const showPrintDialog = ref(false);
230
+ const printPayload = ref<any[]>([]);
231
+ const printQrRef = ref();
232
+
233
+ const {
234
+ getVisitorAccessCards,
235
+ saveVisitorAccessCardQrTag,
236
+ getAllVisitorAccessCardsQrTags,
237
+ } = useAccessManagement();
238
+
239
+ const { addFile, getFileUrl } = useFile();
154
240
 
155
241
  const {
156
242
  data: qrTaggingReq,
@@ -158,23 +244,217 @@ const {
158
244
  status: fetchStatus,
159
245
  } = useLazyAsyncData(
160
246
  "get-qr-tagging",
161
- () => {
162
- // TODO: wire up QR tagging API
163
- return Promise.resolve({ data: { items: [], pages: 0, pageRange: "0 - 0 of 0" } });
164
- },
165
- { watch: [page, searchText, typeFilter] }
247
+ () =>
248
+ getVisitorAccessCards({
249
+ page: page.value,
250
+ limit: 10,
251
+ site: siteId.value,
252
+ search: searchText.value || undefined,
253
+ }),
254
+ { watch: [page, searchText] }
166
255
  );
167
256
 
168
257
  const loading = computed(() => fetchStatus.value === "pending");
169
258
 
170
259
  watchEffect(() => {
171
260
  if (qrTaggingReq.value?.data) {
172
- items.value = qrTaggingReq.value.data.items;
261
+ items.value = qrTaggingReq.value.data.items.map((item: any) => ({
262
+ ...item,
263
+ qrTagCardNo: item.qrTagCardNo ?? item.extractedCardNo,
264
+ }));
173
265
  pages.value = qrTaggingReq.value.data.pages;
174
266
  pageRange.value = qrTaggingReq.value.data.pageRange;
175
267
  }
176
268
  });
177
269
 
270
+ const hasGeneratableItems = computed(() =>
271
+ items.value.some((item) => !item.qrTag)
272
+ );
273
+ const hasPrintableItems = computed(() =>
274
+ items.value.some((item) => !!item.qrTag)
275
+ );
276
+
277
+ const isAllGenerateSelected = computed(() => {
278
+ const generatable = items.value.filter((item) => !item.qrTag);
279
+ return (
280
+ generatable.length > 0 &&
281
+ generatable.every((item) => qrCodeAccessCards.value.includes(item._id))
282
+ );
283
+ });
284
+
285
+ const isAllPrintSelected = computed(() => {
286
+ const printable = items.value.filter((item) => !!item.qrTag);
287
+ return (
288
+ printable.length > 0 &&
289
+ printable.every((item) =>
290
+ qrCodeAccessCardsToPrint.value.includes(item._id)
291
+ )
292
+ );
293
+ });
294
+
295
+ function toggleSelectAllGenerate(value: boolean | null) {
296
+ if (value) {
297
+ qrCodeAccessCards.value = items.value
298
+ .filter((item) => !item.qrTag)
299
+ .map((item) => item._id);
300
+ } else {
301
+ qrCodeAccessCards.value = [];
302
+ }
303
+ }
304
+
305
+ function toggleSelectAllPrint(value: boolean | null) {
306
+ if (value) {
307
+ qrCodeAccessCardsToPrint.value = items.value
308
+ .filter((item) => !!item.qrTag)
309
+ .map((item) => item._id);
310
+ } else {
311
+ qrCodeAccessCardsToPrint.value = [];
312
+ }
313
+ }
314
+
315
+ function toggleGenerateSelection(id: string) {
316
+ if (qrCodeAccessCards.value.includes(id)) {
317
+ qrCodeAccessCards.value = qrCodeAccessCards.value.filter((i) => i !== id);
318
+ } else {
319
+ qrCodeAccessCards.value.push(id);
320
+ }
321
+ }
322
+
323
+ function togglePrintSelection(id: string) {
324
+ if (qrCodeAccessCardsToPrint.value.includes(id)) {
325
+ qrCodeAccessCardsToPrint.value = qrCodeAccessCardsToPrint.value.filter(
326
+ (i) => i !== id
327
+ );
328
+ } else {
329
+ qrCodeAccessCardsToPrint.value.push(id);
330
+ }
331
+ }
332
+
333
+ async function generateQrCodes() {
334
+ isGenerating.value = true;
335
+
336
+ const filteredCards = items.value.filter((card) =>
337
+ qrCodeAccessCards.value.includes(card._id)
338
+ );
339
+
340
+ qrCodesToGenerate.value = filteredCards.map((card) => ({
341
+ _id: card._id,
342
+ cardNo: card.cardNo,
343
+ qrData: card.qrData,
344
+ qrTagCardNo: card.qrTagCardNo ?? "",
345
+ }));
346
+
347
+ await nextTick();
348
+ await nextTick();
349
+
350
+ const payloads: Array<{ _id: string; cardNo: string; qrTag: string; qrTagCardNo: string }> = [];
351
+
352
+ await Promise.all(
353
+ qrCodesToGenerate.value.map(async (qrCode) => {
354
+ const fileUrl = await saveQrCodeToStorage(qrCode._id);
355
+ if (fileUrl) {
356
+ payloads.push({
357
+ _id: qrCode._id,
358
+ cardNo: qrCode.cardNo,
359
+ qrTag: fileUrl,
360
+ qrTagCardNo: qrCode.qrTagCardNo,
361
+ });
362
+ }
363
+ })
364
+ );
365
+
366
+ try {
367
+ if (payloads.length > 0) {
368
+ await saveVisitorAccessCardQrTag(siteId.value, payloads);
369
+ showMessage(`${payloads.length} QR code(s) generated successfully`, "success");
370
+ } else {
371
+ showMessage("No QR codes could be generated", "warning");
372
+ }
373
+ } catch {
374
+ showMessage("Failed to save QR codes", "error");
375
+ }
376
+
377
+ await fetchItems();
378
+
379
+ qrCodesToGenerate.value = [];
380
+ qrCodeAccessCards.value = [];
381
+ isGenerating.value = false;
382
+ }
383
+
384
+ async function saveQrCodeToStorage(_id: string): Promise<string | null> {
385
+ const canvas = document.querySelector<HTMLCanvasElement>(
386
+ `canvas[data-card-id="${_id}"]`
387
+ );
388
+ if (!canvas) return null;
389
+
390
+ return new Promise((resolve) => {
391
+ canvas.toBlob(async (blob) => {
392
+ if (!blob) return resolve(null);
393
+ const file = new File([blob], `${_id}.png`, { type: "image/png" });
394
+ try {
395
+ const res = await addFile(file);
396
+ resolve(res?.id ? getFileUrl(res.id) : null);
397
+ } catch {
398
+ resolve(null);
399
+ }
400
+ }, "image/png");
401
+ });
402
+ }
403
+
404
+ async function handlePrintQrCode() {
405
+ const payload = items.value
406
+ .filter((card) => qrCodeAccessCardsToPrint.value.includes(card._id))
407
+ .map((card) => ({
408
+ accessLevel: card.accessLevel,
409
+ cardNumber: card.extractedCardNo,
410
+ qrDataImage: card.qrTag,
411
+ qrTagCardNo: card.qrTagCardNo,
412
+ }));
413
+
414
+ if (!payload.length) return;
415
+
416
+ printPayload.value = payload;
417
+ showPrintDialog.value = true;
418
+
419
+ await nextTick();
420
+ await nextTick();
421
+
422
+ printQrRef.value?.downloadPDF();
423
+ qrCodeAccessCardsToPrint.value = [];
424
+ showMessage("Printing QR codes...", "success");
425
+ }
426
+
427
+ async function handlePrintAll() {
428
+ isGenerating.value = true;
429
+ try {
430
+ const response: any = await getAllVisitorAccessCardsQrTags(siteId.value);
431
+
432
+ if (!response?.data?.items?.length) {
433
+ showMessage("No QR codes found to print", "warning");
434
+ return;
435
+ }
436
+
437
+ printPayload.value = response.data.items.map((card: any) => ({
438
+ accessLevel: card.accessLevel,
439
+ cardNumber: card.extractedCardNo,
440
+ qrDataImage: card.qrTag,
441
+ qrTagCardNo: card.qrTagCardNo,
442
+ }));
443
+
444
+ showPrintDialog.value = true;
445
+
446
+ await nextTick();
447
+ await nextTick();
448
+
449
+ printQrRef.value?.downloadPDF();
450
+ showMessage("Printing all QR codes...", "success");
451
+ } catch (error: any) {
452
+ showMessage(error || "Failed to fetch QR codes", "error");
453
+ } finally {
454
+ isGenerating.value = false;
455
+ }
456
+ }
457
+
178
458
  function showMessage(msg: string, color: string) {
179
459
  message.value = msg;
180
460
  messageColor.value = color;
@@ -0,0 +1,75 @@
1
+ <template>
2
+ <v-container ref="printContent" fluid style="overflow-y: auto">
3
+ <v-row no-gutters class="main-container">
4
+ <v-col v-for="(card, idx) in accessCards" :key="idx" cols="auto">
5
+ <div class="qr-box">
6
+ <p><strong>Access Level:</strong> {{ card.accessLevel }}</p>
7
+ <p><strong>HID NO:</strong> {{ card.cardNumber }}</p>
8
+ <img :src="card.qrDataImage" alt="QR Code" />
9
+ <p><strong>Card No.:</strong> C{{ card.qrTagCardNo }}</p>
10
+ </div>
11
+ </v-col>
12
+ </v-row>
13
+ </v-container>
14
+ </template>
15
+
16
+ <script setup lang="ts">
17
+ import type { ComponentPublicInstance } from "vue";
18
+
19
+ defineProps<{ accessCards?: any[] }>();
20
+ defineEmits(["close"]);
21
+
22
+ const printContent = ref<HTMLElement | null>(null);
23
+
24
+ async function downloadPDF() {
25
+ const html2pdf = (await import("html2pdf.js")).default;
26
+ const component = printContent.value as unknown as ComponentPublicInstance;
27
+ const el = component.$el ?? printContent.value;
28
+
29
+ if (!el || !(el instanceof HTMLElement)) return;
30
+
31
+ const opt = {
32
+ margin: 0.1,
33
+ filename: `Access-cards-qr.pdf`,
34
+ image: { type: "jpeg", quality: 0.98 },
35
+ html2canvas: { scale: 2, useCORS: true },
36
+ jsPDF: { unit: "in", format: "a4", orientation: "portrait" },
37
+ };
38
+
39
+ await html2pdf().set(opt).from(el).save();
40
+ }
41
+
42
+ defineExpose({ downloadPDF });
43
+ </script>
44
+
45
+ <style scoped>
46
+ .main-container {
47
+ background-color: #fff;
48
+ padding: 8px;
49
+ display: grid;
50
+ grid-template-columns: repeat(6, 1fr);
51
+ gap: 4px;
52
+ width: 100%;
53
+ }
54
+ .qr-box {
55
+ height: 3cm;
56
+ width: 2.7cm;
57
+ border: 1px solid #ccc;
58
+ padding: 8px;
59
+ text-align: center;
60
+ page-break-inside: avoid;
61
+ margin: 0 auto;
62
+ display: flex;
63
+ flex-direction: column;
64
+ align-items: center;
65
+ gap: 6px;
66
+ }
67
+ .qr-box p {
68
+ font-size: 8px;
69
+ line-height: 1.2;
70
+ }
71
+ img {
72
+ width: 1cm;
73
+ height: 1cm;
74
+ }
75
+ </style>
@@ -119,6 +119,15 @@
119
119
  class="text-subtitle-2 font-weight-bold px-0"
120
120
  >
121
121
  Set {{ checklistSet.set }}
122
+ <v-chip
123
+ v-if="checklistSet.isScheduleTask"
124
+ size="x-small"
125
+ color="info"
126
+ variant="tonal"
127
+ class="text-none ml-2"
128
+ >
129
+ Schedule Task
130
+ </v-chip>
122
131
  </v-list-subheader>
123
132
 
124
133
  <v-list-item
@@ -3,7 +3,7 @@
3
3
  <v-toolbar>
4
4
  <v-row no-gutters class="fill-height px-6" align="center">
5
5
  <span class="font-weight-bold text-h5 text-capitalize">
6
- {{ prop.mode }} Building
6
+ {{ prop.mode }} Block
7
7
  </span>
8
8
  </v-row>
9
9
  </v-toolbar>
@@ -40,7 +40,10 @@
40
40
  <v-select
41
41
  v-model.number="building.block"
42
42
  :items="blocks"
43
+ item-title="label"
44
+ item-value="value"
43
45
  density="comfortable"
46
+ :disabled="prop.mode === 'edit'"
44
47
  :rules="[
45
48
  requiredRule,
46
49
  () =>
@@ -48,7 +51,12 @@
48
51
  'Block is required',
49
52
  ]"
50
53
  type="number"
51
- ></v-select>
54
+ >
55
+ <template v-slot:item="{ props: itemProps, item}">
56
+ <v-list-item v-bind="itemProps" :disabled="item?.raw?.disabled" ></v-list-item>
57
+ </template>
58
+
59
+ </v-select>
52
60
  </v-col>
53
61
  </v-row>
54
62
  </v-col>
@@ -114,10 +122,10 @@
114
122
  </v-row>
115
123
  </v-expand-transition>
116
124
 
117
- <v-col cols="12" class="px-6">
125
+ <!-- <v-col cols="12" class="px-6">
118
126
  <InputLabel class="text-capitalize" title="Building Floor plan" />
119
127
  <InputFileV2 v-model="building.buildingFloorPlan" :multiple="true" :max-length="10" title="Upload Images" />
120
- </v-col>
128
+ </v-col> -->
121
129
 
122
130
  <v-col cols="12" class="mt-2">
123
131
  <v-checkbox v-model="createMore" density="comfortable" hide-details>
@@ -199,6 +207,10 @@ const prop = defineProps({
199
207
  buildingFloorPlan: []
200
208
  }),
201
209
  },
210
+ blocks: {
211
+ type: Array as PropType<TBuilding[]>,
212
+ default: () => [],
213
+ },
202
214
  });
203
215
 
204
216
  const { getSiteById } = useSite();
@@ -218,7 +230,20 @@ watchEffect(() => {
218
230
 
219
231
  const blocks = computed(() => {
220
232
  if (!site.value) return [];
221
- return Array.from({ length: site.value?.metadata?.block || 0 }, (_, i) => i + 1);
233
+ // return
234
+ // return [ { label: "1", value: 1, disabled: true }, { label: "2", value: 2 }, { label: "3", value: 3 } ];
235
+ const array = Array.from({ length: site.value?.metadata?.block || 0 }, (_, i) => i + 1);
236
+
237
+ const formattedArray = array.map((num) => {
238
+ const isDisabled = prop.blocks.some((block) => block.block === num)
239
+ return {
240
+ label: num.toString(),
241
+ value: num,
242
+ disabled: isDisabled
243
+ };
244
+ });
245
+
246
+ return formattedArray;
222
247
  });
223
248
 
224
249
  const emit = defineEmits(["cancel", "success", "success:create-more"]);
@@ -295,6 +320,12 @@ async function submit() {
295
320
  }
296
321
  }
297
322
 
323
+ watch(buildingLevels, (newVal) => {
324
+ setTimeout(() => {
325
+ show.value = newVal > 0 && !!newVal;
326
+ }, 500);
327
+ });
328
+
298
329
  function cancel() {
299
330
  createMore.value = false;
300
331
  message.value = "";