@7365admin1/layer-common 1.10.1 → 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.
@@ -2,16 +2,28 @@
2
2
  <v-row no-gutters>
3
3
  <v-col cols="12" class="mb-2">
4
4
  <v-row no-gutters align="center" justify="space-between">
5
- <v-btn
6
- class="text-none"
7
- rounded="pill"
8
- variant="tonal"
9
- size="large"
10
- @click="setCard()"
11
- v-if="canCreate && canCreateAccessCard"
12
- >
13
- Add Access Card
14
- </v-btn>
5
+ <v-row no-gutters class="ga-2">
6
+ <v-btn
7
+ class="text-none"
8
+ rounded="pill"
9
+ variant="tonal"
10
+ size="large"
11
+ @click="setCard()"
12
+ v-if="canCreate && canCreateAccessCard"
13
+ >
14
+ Add Access Card
15
+ </v-btn>
16
+ <v-btn
17
+ class="text-none"
18
+ rounded="pill"
19
+ variant="tonal"
20
+ size="large"
21
+ @click="assignDialog = true"
22
+ v-if="canCreate && canAssignAccessCard"
23
+ >
24
+ Assign to Unit
25
+ </v-btn>
26
+ </v-row>
15
27
 
16
28
  <v-text-field
17
29
  v-model="searchText"
@@ -25,6 +37,11 @@
25
37
  />
26
38
  </v-row>
27
39
  </v-col>
40
+ <!-- Available Card Stats -->
41
+ <v-col cols="12" class="mb-2">
42
+ <AccessCardAvailableStats ref="statsRef" :site-id="siteId" />
43
+ </v-col>
44
+
28
45
  <v-col cols="12">
29
46
  <v-card
30
47
  width="100%"
@@ -71,16 +88,17 @@
71
88
  <AccessCardAddForm
72
89
  @cancel="createDialog = false"
73
90
  @success="successCreate()"
91
+ @error="(msg) => showMessage(msg, 'error')"
74
92
  />
75
93
  </v-dialog>
76
94
 
77
- <!-- Edit Dialog -->
78
- <v-dialog v-model="editDialog" width="650" persistent>
79
- <AccessCardAddForm
80
- mode="edit"
81
- @cancel="editDialog = false"
82
- @success="successUpdate()"
83
- :card="selectedCard as TCard"
95
+ <!-- Assign to Unit Dialog -->
96
+ <v-dialog v-model="assignDialog" width="650" persistent>
97
+ <AccessCardAssignToUnitForm
98
+ :site-id="siteId"
99
+ @cancel="assignDialog = false"
100
+ @success="successAssign()"
101
+ @error="(msg: any) => showMessage(msg, 'error')"
84
102
  />
85
103
  </v-dialog>
86
104
 
@@ -102,16 +120,26 @@
102
120
  <!-- Available Physical -->
103
121
  <v-col cols="12" class="mt-3">
104
122
  <strong>Available Physical</strong>
105
- <div v-if="selectedCard?.available?.physical?.length" class="mt-1">
123
+ <div
124
+ v-if="selectedCard?.available?.physical?.length"
125
+ class="mt-1"
126
+ >
106
127
  <v-chip
107
128
  v-for="card in selectedCard.available.physical"
108
129
  :key="card._id"
109
130
  size="small"
110
131
  class="mr-1 mb-1"
111
- :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
112
- :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
132
+ :color="
133
+ selectedCardInUnit?._id === card._id ? 'primary' : undefined
134
+ "
135
+ :variant="
136
+ selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
137
+ "
113
138
  style="cursor: pointer"
114
- @click="selectedCardInUnit = selectedCardInUnit?._id === card._id ? null : card"
139
+ @click="
140
+ selectedCardInUnit =
141
+ selectedCardInUnit?._id === card._id ? null : card
142
+ "
115
143
  >
116
144
  {{ card.cardNo }}
117
145
  </v-chip>
@@ -122,16 +150,26 @@
122
150
  <!-- Available Non-Physical -->
123
151
  <v-col cols="12" class="mt-2">
124
152
  <strong>Available Non-Physical</strong>
125
- <div v-if="selectedCard?.available?.non_physical?.length" class="mt-1">
153
+ <div
154
+ v-if="selectedCard?.available?.non_physical?.length"
155
+ class="mt-1"
156
+ >
126
157
  <v-chip
127
158
  v-for="card in selectedCard.available.non_physical"
128
159
  :key="card._id"
129
160
  size="small"
130
161
  class="mr-1 mb-1"
131
- :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
132
- :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
162
+ :color="
163
+ selectedCardInUnit?._id === card._id ? 'primary' : undefined
164
+ "
165
+ :variant="
166
+ selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
167
+ "
133
168
  style="cursor: pointer"
134
- @click="selectedCardInUnit = selectedCardInUnit?._id === card._id ? null : card"
169
+ @click="
170
+ selectedCardInUnit =
171
+ selectedCardInUnit?._id === card._id ? null : card
172
+ "
135
173
  >
136
174
  {{ card.cardNo }}
137
175
  </v-chip>
@@ -148,10 +186,17 @@
148
186
  :key="card._id"
149
187
  size="small"
150
188
  class="mr-1 mb-1"
151
- :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
152
- :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
189
+ :color="
190
+ selectedCardInUnit?._id === card._id ? 'primary' : undefined
191
+ "
192
+ :variant="
193
+ selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
194
+ "
153
195
  style="cursor: pointer"
154
- @click="selectedCardInUnit = selectedCardInUnit?._id === card._id ? null : card"
196
+ @click="
197
+ selectedCardInUnit =
198
+ selectedCardInUnit?._id === card._id ? null : card
199
+ "
155
200
  >
156
201
  {{ card.cardNo }}
157
202
  </v-chip>
@@ -162,16 +207,26 @@
162
207
  <!-- Assigned Non-Physical -->
163
208
  <v-col cols="12" class="mt-2">
164
209
  <strong>Assigned Non-Physical</strong>
165
- <div v-if="selectedCard?.assigned?.non_physical?.length" class="mt-1">
210
+ <div
211
+ v-if="selectedCard?.assigned?.non_physical?.length"
212
+ class="mt-1"
213
+ >
166
214
  <v-chip
167
215
  v-for="card in selectedCard.assigned.non_physical"
168
216
  :key="card._id"
169
217
  size="small"
170
218
  class="mr-1 mb-1"
171
- :color="selectedCardInUnit?._id === card._id ? 'primary' : undefined"
172
- :variant="selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'"
219
+ :color="
220
+ selectedCardInUnit?._id === card._id ? 'primary' : undefined
221
+ "
222
+ :variant="
223
+ selectedCardInUnit?._id === card._id ? 'flat' : 'tonal'
224
+ "
173
225
  style="cursor: pointer"
174
- @click="selectedCardInUnit = selectedCardInUnit?._id === card._id ? null : card"
226
+ @click="
227
+ selectedCardInUnit =
228
+ selectedCardInUnit?._id === card._id ? null : card
229
+ "
175
230
  >
176
231
  {{ card.cardNo }}
177
232
  </v-chip>
@@ -211,14 +266,10 @@
211
266
  </template>
212
267
  <v-list class="pa-0">
213
268
  <v-list-item
214
- @click="openEditDialog()"
215
- v-if="canUpdateAccessCard"
269
+ :disabled="selectedCardInUnit === null"
270
+ @click="openReplaceDialog()"
271
+ v-if="canReplaceAccessCard"
216
272
  >
217
- <v-list-item-title class="text-subtitle-2">
218
- Edit Card
219
- </v-list-item-title>
220
- </v-list-item>
221
- <v-list-item @click="openReplaceDialog()" v-if="canReplaceAccessCard">
222
273
  <v-list-item-title class="text-subtitle-2">
223
274
  Replace Card
224
275
  </v-list-item-title>
@@ -226,6 +277,7 @@
226
277
  <v-list-item
227
278
  @click="openDeleteDialog()"
228
279
  class="text-red"
280
+ :disabled="selectedCardInUnit === null"
229
281
  v-if="canDeleteAccessCard"
230
282
  >
231
283
  <v-list-item-title class="text-subtitle-2">
@@ -360,15 +412,15 @@ const props = defineProps({
360
412
  type: Boolean,
361
413
  default: true,
362
414
  },
363
- canUpdateAccessCard: {
415
+ canDeleteAccessCard: {
364
416
  type: Boolean,
365
417
  default: true,
366
418
  },
367
- canDeleteAccessCard: {
419
+ canReplaceAccessCard: {
368
420
  type: Boolean,
369
421
  default: true,
370
422
  },
371
- canReplaceAccessCard: {
423
+ canAssignAccessCard: {
372
424
  type: Boolean,
373
425
  default: true,
374
426
  },
@@ -385,7 +437,7 @@ const messageColor = ref("");
385
437
 
386
438
  const items = ref<Array<Record<string, any>>>([]);
387
439
  const createDialog = ref(false);
388
- const editDialog = ref(false);
440
+ const assignDialog = ref(false);
389
441
  const previewDialog = ref(false);
390
442
  const deleteLoading = ref(false);
391
443
  const confirmDialog = ref(false);
@@ -393,8 +445,9 @@ const searchText = ref("");
393
445
  const replaceDialog = ref(false);
394
446
 
395
447
  const route = useRoute();
396
- // const siteId = computed(() => route.params.site as string); @TODO
397
- const siteId = computed(() => '66ab2f1381856008f1887971' as string);
448
+ //@TODO
449
+ const siteId = computed(() => route.params.site as string);
450
+ // const siteId = computed(() => "66ab2f1381856008f1887971" as string);
398
451
  const orgId = computed(() => route.params.org as string);
399
452
 
400
453
  const selectedCard = ref<Record<string, any>>({});
@@ -408,6 +461,8 @@ watch(previewDialog, (val) => {
408
461
  const { deleteById: _deleteCard } = useCard();
409
462
  const { getUserTypeAccessCards } = useAccessManagement();
410
463
 
464
+ const statsRef = ref<{ refresh: () => void } | null>(null);
465
+
411
466
  const {
412
467
  data: getCardReq,
413
468
  refresh: getCards,
@@ -440,9 +495,6 @@ watchEffect(() => {
440
495
  function setCard({ mode = "create", dialog = true, data = {} as TCard } = {}) {
441
496
  if (mode === "create") {
442
497
  createDialog.value = dialog;
443
- } else if (mode === "edit") {
444
- editDialog.value = dialog;
445
- selectedCard.value = data;
446
498
  } else if (mode === "preview") {
447
499
  previewDialog.value = dialog;
448
500
  selectedCard.value = data;
@@ -452,14 +504,15 @@ function setCard({ mode = "create", dialog = true, data = {} as TCard } = {}) {
452
504
  function successCreate() {
453
505
  createDialog.value = false;
454
506
  getCards();
507
+ statsRef.value?.refresh();
455
508
  showMessage("Card created successfully!", "success");
456
509
  }
457
510
 
458
- function successUpdate() {
459
- editDialog.value = false;
460
- previewDialog.value = false;
511
+ function successAssign() {
512
+ assignDialog.value = false;
461
513
  getCards();
462
- showMessage("Card updated successfully!", "success");
514
+ statsRef.value?.refresh();
515
+ showMessage("Access card assigned successfully!", "success");
463
516
  }
464
517
 
465
518
  function showMessage(msg: string, color: string) {
@@ -473,10 +526,6 @@ function tableRowClickHandler(_: any, data: any) {
473
526
  previewDialog.value = true;
474
527
  }
475
528
 
476
- function openEditDialog() {
477
- editDialog.value = true;
478
- }
479
-
480
529
  function openDeleteDialog() {
481
530
  confirmDialog.value = true;
482
531
  message.value = "";
@@ -491,6 +540,7 @@ async function handleDeleteCard() {
491
540
  try {
492
541
  await _deleteCard(selectedCard.value._id ?? "");
493
542
  await getCards();
543
+ statsRef.value?.refresh();
494
544
  selectedCardId.value = null;
495
545
  confirmDialog.value = false;
496
546
  previewDialog.value = false;
@@ -330,7 +330,7 @@
330
330
  color="black"
331
331
  class="text-none font-weight-bold rounded-0"
332
332
  height="48"
333
- :loading="submitting"
333
+ :loading="qrLoading"
334
334
  @click="onDownloadQrCode"
335
335
  >
336
336
  Download
@@ -357,6 +357,7 @@ import QrcodeVue from "qrcode.vue";
357
357
  import useAreas from "../composables/useAreas";
358
358
  import useUnits from "../composables/useUnits";
359
359
  import { useAreaPermission } from "../composables/useAreaPermission";
360
+ import useSiteSettings from "../composables/useSiteSettings";
360
361
 
361
362
  const props = defineProps({
362
363
  orgId: { type: String, default: "" },
@@ -436,6 +437,8 @@ const {
436
437
  exportAreas,
437
438
  } = useAreas();
438
439
 
440
+ const { getSiteById } = useSiteSettings();
441
+
439
442
  const { getUnits } = useUnits();
440
443
 
441
444
  const searchInput = ref("");
@@ -515,6 +518,24 @@ const qrPath = ref("");
515
518
  const qrLoading = ref(false);
516
519
  const qrError = ref("");
517
520
 
521
+ watch(
522
+ () => props.site,
523
+ async (val) => {
524
+ if (!val) {
525
+ siteName.value = "";
526
+ return;
527
+ }
528
+
529
+ try {
530
+ const siteResp = await getSiteById(val);
531
+ siteName.value = (siteResp && (siteResp.name || siteResp.title)) || val;
532
+ } catch (err) {
533
+ siteName.value = val;
534
+ }
535
+ },
536
+ { immediate: true }
537
+ );
538
+
518
539
  function showMessage(msg: string, color: string = "error") {
519
540
  message.value = msg;
520
541
  messageColor.value = color;
@@ -533,7 +554,6 @@ async function handleDownloadExcel() {
533
554
  try {
534
555
  isDownloading.value = true;
535
556
 
536
- // Validate site id (must be a 24-char hex ObjectId)
537
557
  if (!props.site || !/^[0-9a-fA-F]{24}$/.test(props.site)) {
538
558
  showMessage("Invalid site id for export.", "error");
539
559
  return;
@@ -776,7 +796,9 @@ async function onDownloadQrCode() {
776
796
 
777
797
  const areaName = selectedItem.value?.name || "toilet-location";
778
798
  const filename = `qr-${areaName.toLowerCase().replace(/\s+/g, "-")}`;
779
- const title = "Seven365 Pte Ltd";
799
+ const siteDisplay = siteName.value || props.site || "";
800
+ const title = siteDisplay;
801
+ const subtitle = selectedItem.value?.name || "";
780
802
 
781
803
  try {
782
804
  qrLoading.value = true;
@@ -788,7 +810,7 @@ async function onDownloadQrCode() {
788
810
  qrPath.value
789
811
  )}&filename=${encodeURIComponent(filename)}&title=${encodeURIComponent(
790
812
  title
791
- )}&download=true`;
813
+ )}&subtitle=${encodeURIComponent(subtitle)}&download=true`;
792
814
 
793
815
  const response = await fetch(downloadUrl);
794
816