@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.
- package/CHANGELOG.md +6 -0
- package/components/AccessCard/AvailableStats.vue +55 -0
- package/components/AccessCardAddForm.vue +185 -8
- package/components/AccessCardAssignToUnitForm.vue +440 -0
- package/components/AccessManagement.vue +106 -56
- package/components/AreaMain.vue +26 -4
- package/components/BulletinBoardManagement.vue +322 -0
- package/components/SignaturePad.vue +17 -5
- package/components/SupplyManagementMain.vue +1 -1
- package/composables/useAccessManagement.ts +73 -0
- package/composables/useBulletin.ts +82 -0
- package/package.json +1 -1
|
@@ -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-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
<!--
|
|
78
|
-
<v-dialog v-model="
|
|
79
|
-
<
|
|
80
|
-
|
|
81
|
-
@cancel="
|
|
82
|
-
@success="
|
|
83
|
-
|
|
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
|
|
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="
|
|
112
|
-
|
|
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="
|
|
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
|
|
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="
|
|
132
|
-
|
|
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="
|
|
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="
|
|
152
|
-
|
|
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="
|
|
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
|
|
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="
|
|
172
|
-
|
|
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="
|
|
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
|
-
|
|
215
|
-
|
|
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
|
-
|
|
415
|
+
canDeleteAccessCard: {
|
|
364
416
|
type: Boolean,
|
|
365
417
|
default: true,
|
|
366
418
|
},
|
|
367
|
-
|
|
419
|
+
canReplaceAccessCard: {
|
|
368
420
|
type: Boolean,
|
|
369
421
|
default: true,
|
|
370
422
|
},
|
|
371
|
-
|
|
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
|
|
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
|
-
|
|
397
|
-
const siteId = computed(() =>
|
|
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
|
|
459
|
-
|
|
460
|
-
previewDialog.value = false;
|
|
511
|
+
function successAssign() {
|
|
512
|
+
assignDialog.value = false;
|
|
461
513
|
getCards();
|
|
462
|
-
|
|
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;
|
package/components/AreaMain.vue
CHANGED
|
@@ -330,7 +330,7 @@
|
|
|
330
330
|
color="black"
|
|
331
331
|
class="text-none font-weight-bold rounded-0"
|
|
332
332
|
height="48"
|
|
333
|
-
:loading="
|
|
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
|
|
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
|
|