@7365admin1/layer-common 3.0.10 → 3.0.12
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 +12 -0
- package/components/DashboardMain.vue +620 -133
- package/components/IncidentReport/IncidentInformation.vue +1 -1
- package/components/IncidentReport/IncidentInformationDownload.vue +1 -1
- package/components/Input/InputPhoneNumberV2.vue +33 -29
- package/components/Input/Password.vue +1 -0
- package/components/Layout/NavigationDrawer.vue +4 -1
- package/components/OnlineFormFill.vue +70 -7
- package/components/VehicleForm.vue +6 -4
- package/components/VehicleManagement.vue +16 -1
- package/components/VisitorDataFromScannedQRCodeForm.vue +15 -2
- package/components/VisitorManagement.vue +19 -4
- package/composables/useAttendancePermission.ts +5 -5
- package/composables/useBulletinBoardPermission.ts +5 -5
- package/composables/useCommonPermission.ts +41 -1
- package/composables/useSettingsPermission.ts +11 -11
- package/composables/useVisitor.ts +1 -0
- package/package.json +1 -1
- package/types/visitor.d.ts +1 -0
|
@@ -5,17 +5,21 @@
|
|
|
5
5
|
v-model="selectedCode"
|
|
6
6
|
:variant="variant"
|
|
7
7
|
:items="countries"
|
|
8
|
-
item-title="
|
|
8
|
+
item-title="dial_code"
|
|
9
9
|
item-value="code"
|
|
10
10
|
hide-details
|
|
11
11
|
class="px-0"
|
|
12
12
|
:density="density"
|
|
13
|
-
style="max-width:
|
|
13
|
+
style="max-width: 110px"
|
|
14
14
|
:rules="[...props.rules]"
|
|
15
15
|
:readonly="props.readOnly"
|
|
16
16
|
:disabled="props.disabled"
|
|
17
17
|
@update:model-value="handleUpdateCountry"
|
|
18
18
|
>
|
|
19
|
+
<template v-slot:selection="{ item }">
|
|
20
|
+
<span>{{ item.raw.dial_code }}</span>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
19
23
|
<template v-slot:item="{ props: itemProps, item }">
|
|
20
24
|
<v-list-item
|
|
21
25
|
v-bind="itemProps"
|
|
@@ -38,7 +42,6 @@
|
|
|
38
42
|
hide-details
|
|
39
43
|
persistent-hint
|
|
40
44
|
return-masked-value
|
|
41
|
-
:prefix="phonePrefix || ''"
|
|
42
45
|
persistent-placeholder
|
|
43
46
|
:density="density"
|
|
44
47
|
:disabled="props.disabled"
|
|
@@ -52,7 +55,7 @@
|
|
|
52
55
|
</template>
|
|
53
56
|
|
|
54
57
|
<script setup lang="ts">
|
|
55
|
-
import { ref, computed, watch,
|
|
58
|
+
import { ref, computed, watch, type PropType } from 'vue'
|
|
56
59
|
//@ts-ignore
|
|
57
60
|
import phoneMasks from '~/utils/phoneMasks'
|
|
58
61
|
import type { ValidationRule } from 'vuetify/lib/types.mjs'
|
|
@@ -101,6 +104,10 @@ const phonePrefix = computed(() => {
|
|
|
101
104
|
return country?.dial_code || ''
|
|
102
105
|
})
|
|
103
106
|
|
|
107
|
+
const countriesByLongestDialCode = computed(() => {
|
|
108
|
+
return [...countries].sort((a: TPhoneMask, b: TPhoneMask) => b.dial_code.length - a.dial_code.length)
|
|
109
|
+
})
|
|
110
|
+
|
|
104
111
|
function generateMaskFromRegex(regex: string): string {
|
|
105
112
|
let pattern = regex.replace(/^\^|\$$/g, '')
|
|
106
113
|
pattern = pattern.replace(/\(\?:\+?\d+\)\?/g, '')
|
|
@@ -125,13 +132,19 @@ const validatePhone = (): boolean | string => {
|
|
|
125
132
|
return isValid
|
|
126
133
|
}
|
|
127
134
|
|
|
135
|
+
function findCountryFromPhone(value: string): TPhoneMask | undefined {
|
|
136
|
+
return countriesByLongestDialCode.value.find((c: TPhoneMask) => value.startsWith(c.dial_code))
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function syncInputFromPhone(value: string) {
|
|
140
|
+
const prefix = phonePrefix.value
|
|
141
|
+
if (!value) input.value = ''
|
|
142
|
+
else input.value = value.startsWith(prefix) ? value.slice(prefix.length) : value
|
|
143
|
+
}
|
|
144
|
+
|
|
128
145
|
function handleUpdateCountry() {
|
|
129
146
|
const prefix = phonePrefix.value
|
|
130
|
-
|
|
131
|
-
input.value = phone.value.slice(prefix.length)
|
|
132
|
-
} else {
|
|
133
|
-
input.value = ''
|
|
134
|
-
}
|
|
147
|
+
phone.value = input.value ? prefix + input.value : ''
|
|
135
148
|
}
|
|
136
149
|
|
|
137
150
|
|
|
@@ -150,28 +163,19 @@ watch(
|
|
|
150
163
|
)
|
|
151
164
|
|
|
152
165
|
|
|
153
|
-
watch(
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
watch(
|
|
167
|
+
phone,
|
|
168
|
+
(newVal) => {
|
|
169
|
+
const country = newVal ? findCountryFromPhone(newVal) : undefined
|
|
170
|
+
if (country && country.code !== selectedCode.value) selectedCode.value = country.code
|
|
171
|
+
syncInputFromPhone(newVal)
|
|
172
|
+
emit('update:modelValue', newVal)
|
|
173
|
+
},
|
|
174
|
+
{ immediate: true }
|
|
175
|
+
)
|
|
159
176
|
|
|
160
177
|
|
|
161
178
|
watch(selectedCode, () => {
|
|
162
|
-
|
|
163
|
-
if (phone.value?.startsWith(prefix)) input.value = phone.value.slice(prefix.length)
|
|
164
|
-
else input.value = phone.value || ''
|
|
165
|
-
})
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
onMounted(() => {
|
|
169
|
-
if (phone.value) {
|
|
170
|
-
const found = countries.find((c: TPhoneMask) => phone.value.startsWith(c.dial_code))
|
|
171
|
-
if (found) selectedCode.value = found.code
|
|
172
|
-
const prefix = phonePrefix.value
|
|
173
|
-
input.value = phone.value.startsWith(prefix) ? phone.value.slice(prefix.length) : phone.value
|
|
174
|
-
maskKey.value++
|
|
175
|
-
}
|
|
179
|
+
maskKey.value++
|
|
176
180
|
})
|
|
177
181
|
</script>
|
|
@@ -62,15 +62,18 @@ const { menuItem: hidFaceReaderMenu } = useHidFaceReaderMenu({
|
|
|
62
62
|
cacheKey: "layout-navigation-hid-site",
|
|
63
63
|
});
|
|
64
64
|
|
|
65
|
+
const { canManageEntryPass } = useSettingsPermission();
|
|
66
|
+
|
|
65
67
|
const navigationItems = computed(() => {
|
|
66
68
|
const items = [...(props.navigationItems as TNavigationItem[])];
|
|
67
69
|
|
|
68
70
|
if (
|
|
71
|
+
canManageEntryPass.value &&
|
|
69
72
|
hidFaceReaderMenu.value &&
|
|
70
73
|
!items.some((item) => item.title === hidFaceReaderMenu.value?.title)
|
|
71
74
|
) {
|
|
72
75
|
const insertAfterIndex = items.findIndex(
|
|
73
|
-
(item) => item.title === "Pass & Key Mgmt"
|
|
76
|
+
(item) => item.title === "Pass & Key Mgmt"
|
|
74
77
|
);
|
|
75
78
|
|
|
76
79
|
if (insertAfterIndex >= 0) {
|
|
@@ -258,13 +258,14 @@
|
|
|
258
258
|
<span class="text-caption text-medium-emphasis d-block mb-1">{{ block.label }}</span>
|
|
259
259
|
<div
|
|
260
260
|
v-if="initialValues[block.key]"
|
|
261
|
-
style="
|
|
262
|
-
display: inline-block
|
|
263
|
-
|
|
264
|
-
background: #
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
261
|
+
:style="{
|
|
262
|
+
display: 'inline-block',
|
|
263
|
+
borderRadius: '8px',
|
|
264
|
+
background: sigBgLight[block.key] === false ? '#1e1e1e' : '#ffffff',
|
|
265
|
+
border: sigBgLight[block.key] === false ? '1px solid #334155' : '1px solid #e0e0e0',
|
|
266
|
+
padding: '8px',
|
|
267
|
+
maxWidth: '100%',
|
|
268
|
+
}"
|
|
268
269
|
>
|
|
269
270
|
<img
|
|
270
271
|
:src="initialValues[block.key]"
|
|
@@ -443,6 +444,68 @@ const { currentUser } = useLocalAuth();
|
|
|
443
444
|
const validForm = ref(false);
|
|
444
445
|
const submitting = ref(false);
|
|
445
446
|
const message = ref("");
|
|
447
|
+
|
|
448
|
+
// Detect signature background: true = light (black ink), false = dark (white ink)
|
|
449
|
+
const sigBgLight = ref<Record<string, boolean>>({});
|
|
450
|
+
|
|
451
|
+
function detectSigBackground(key: string, dataUrl: string) {
|
|
452
|
+
if (!dataUrl?.startsWith("data:image")) return;
|
|
453
|
+
const img = new Image();
|
|
454
|
+
img.onload = () => {
|
|
455
|
+
const SIZE = 32;
|
|
456
|
+
const canvas = document.createElement("canvas");
|
|
457
|
+
canvas.width = SIZE;
|
|
458
|
+
canvas.height = SIZE;
|
|
459
|
+
const ctx = canvas.getContext("2d");
|
|
460
|
+
if (!ctx) return;
|
|
461
|
+
ctx.drawImage(img, 0, 0, SIZE, SIZE);
|
|
462
|
+
const d = ctx.getImageData(0, 0, SIZE, SIZE).data;
|
|
463
|
+
|
|
464
|
+
let opaqueTotal = 0, opaqueCount = 0;
|
|
465
|
+
let inkTotal = 0, inkCount = 0;
|
|
466
|
+
for (let i = 0; i < d.length; i += 4) {
|
|
467
|
+
const alpha = d[i + 3];
|
|
468
|
+
const lum = (d[i] + d[i + 1] + d[i + 2]) / 3;
|
|
469
|
+
if (alpha > 128) {
|
|
470
|
+
opaqueTotal += lum;
|
|
471
|
+
opaqueCount++;
|
|
472
|
+
// Ink pixels are the non-background strokes (not very transparent)
|
|
473
|
+
inkTotal += lum;
|
|
474
|
+
inkCount++;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (opaqueCount === 0) {
|
|
479
|
+
// Fully transparent PNG — can't detect, default to white bg
|
|
480
|
+
sigBgLight.value[key] = true;
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const opaqueRatio = opaqueCount / (d.length / 4);
|
|
485
|
+
if (opaqueRatio > 0.5) {
|
|
486
|
+
// Mostly opaque PNG — has a solid background; use overall brightness
|
|
487
|
+
sigBgLight.value[key] = opaqueTotal / opaqueCount > 128;
|
|
488
|
+
} else {
|
|
489
|
+
// Mostly transparent — ink-only PNG; check ink color
|
|
490
|
+
// Dark ink (< 128) = black strokes = was light mode → show white bg
|
|
491
|
+
// Light ink (≥ 128) = white strokes = was dark mode → show dark bg
|
|
492
|
+
sigBgLight.value[key] = inkTotal / inkCount < 128;
|
|
493
|
+
}
|
|
494
|
+
};
|
|
495
|
+
img.src = dataUrl;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
watch(
|
|
499
|
+
() => props.initialValues,
|
|
500
|
+
(vals) => {
|
|
501
|
+
for (const [key, val] of Object.entries(vals)) {
|
|
502
|
+
if (typeof val === "string" && val.startsWith("data:image")) {
|
|
503
|
+
detectSigBackground(key, val);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
{ immediate: true, deep: true }
|
|
508
|
+
);
|
|
446
509
|
const values = ref<Record<string, any>>({});
|
|
447
510
|
|
|
448
511
|
const selectedAction = ref("");
|
|
@@ -56,8 +56,8 @@
|
|
|
56
56
|
</v-col>
|
|
57
57
|
|
|
58
58
|
<v-col v-if="shouldShowField('nric')" cols="12">
|
|
59
|
-
<InputLabel class="text-capitalize" title="NRIC"
|
|
60
|
-
<InputNRICNumber v-model="vehicle.nric" density="comfortable"
|
|
59
|
+
<InputLabel class="text-capitalize" title="NRIC"/>
|
|
60
|
+
<InputNRICNumber v-model="vehicle.nric" density="comfortable"
|
|
61
61
|
:disabled="disablePrefilledInputs && type !== 'blocklist' && prop.mode !== 'edit'" />
|
|
62
62
|
</v-col>
|
|
63
63
|
|
|
@@ -74,8 +74,8 @@
|
|
|
74
74
|
|
|
75
75
|
|
|
76
76
|
<v-col v-if="shouldShowField('phone')" cols="12">
|
|
77
|
-
<InputLabel class="text-capitalize" title="Phone Number"
|
|
78
|
-
<InputPhoneNumberV2 v-model="vehicle.phoneNumber" density="comfortable"
|
|
77
|
+
<InputLabel class="text-capitalize" title="Phone Number"/>
|
|
78
|
+
<InputPhoneNumberV2 v-model="vehicle.phoneNumber" density="comfortable" />
|
|
79
79
|
</v-col>
|
|
80
80
|
|
|
81
81
|
<v-col v-if="shouldShowField('plateNumber')" cols="12">
|
|
@@ -770,6 +770,8 @@ watch(
|
|
|
770
770
|
async (newUnit) => {
|
|
771
771
|
if (prefillingRecords.value) return;
|
|
772
772
|
|
|
773
|
+
if(prop.mode === 'edit') return;
|
|
774
|
+
|
|
773
775
|
resetVehicleDetails();
|
|
774
776
|
matchingPeople.value = [];
|
|
775
777
|
if (!newUnit) return;
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
</v-chip>
|
|
79
79
|
</template>
|
|
80
80
|
<template #item.action="{ item: plateNumberItem, value }">
|
|
81
|
-
<v-menu>
|
|
81
|
+
<v-menu v-if="hasVehicleActions(plateNumberItem as TPlateNumber)">
|
|
82
82
|
<template #activator="{ props: menuProps }">
|
|
83
83
|
<v-btn icon="mdi-dots-vertical" v-bind="menuProps" flat size="x-small" />
|
|
84
84
|
</template>
|
|
@@ -382,6 +382,21 @@ function handleUpdateType(item: TPlateNumber) {
|
|
|
382
382
|
dialog.updateVehicleType = true;
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
+
function hasVehicleActions(item: TPlateNumber) {
|
|
386
|
+
const isActive = item?.status === "active";
|
|
387
|
+
const isPending = item?.status === "pending";
|
|
388
|
+
const isDeleted = item?.status === "deleted";
|
|
389
|
+
const canUpdateType = item?.type === "whitelist" || item?.type === "blocklist";
|
|
390
|
+
|
|
391
|
+
return (
|
|
392
|
+
isActive ||
|
|
393
|
+
(props.app === "property_management_agency" && isPending) ||
|
|
394
|
+
isDeleted ||
|
|
395
|
+
(canUpdateType && isActive) ||
|
|
396
|
+
(props.canDeleteVehicle && isActive)
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
|
|
385
400
|
function handleSelectVehicleStatus(value: TVehicleType) {
|
|
386
401
|
vehicleType.value = value;
|
|
387
402
|
dialog.showSelection = false;
|
|
@@ -101,7 +101,7 @@
|
|
|
101
101
|
<v-text-field v-model="location" density="comfortable" readonly />
|
|
102
102
|
</v-col>
|
|
103
103
|
|
|
104
|
-
<v-col
|
|
104
|
+
<v-col cols="12">
|
|
105
105
|
<InputLabel class="text-capitalize" title="Purpose" />
|
|
106
106
|
<v-textarea
|
|
107
107
|
v-model="visitorInfo.purpose"
|
|
@@ -253,7 +253,20 @@ const emit = defineEmits(["check-in-out", "close"]);
|
|
|
253
253
|
|
|
254
254
|
const { standardFormatDateTime } = useUtils();
|
|
255
255
|
|
|
256
|
-
const visitorInfo = ref(
|
|
256
|
+
const visitorInfo = ref<Partial<TVisitor>>({});
|
|
257
|
+
|
|
258
|
+
watch(
|
|
259
|
+
() => prop.visitorData,
|
|
260
|
+
(visitorData) => {
|
|
261
|
+
visitorInfo.value = {
|
|
262
|
+
...visitorData,
|
|
263
|
+
visitorPass: Array.isArray(visitorData?.visitorPass)
|
|
264
|
+
? [...visitorData.visitorPass]
|
|
265
|
+
: [],
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
{ immediate: true }
|
|
269
|
+
);
|
|
257
270
|
|
|
258
271
|
const visitorNric = computed({
|
|
259
272
|
get: () =>
|
|
@@ -557,7 +557,7 @@
|
|
|
557
557
|
color="success"
|
|
558
558
|
text="Checkin"
|
|
559
559
|
:disabled="isVisitorDataFromScannedQRCodeCheckingInOut"
|
|
560
|
-
@click="handleCheckin(
|
|
560
|
+
@click="handleCheckin(selectedVisitorDataObject)"
|
|
561
561
|
:loading="isVisitorDataFromScannedQRCodeCheckingInOut"
|
|
562
562
|
/>
|
|
563
563
|
</span>
|
|
@@ -1298,6 +1298,7 @@ const formattedFields = {
|
|
|
1298
1298
|
block: "Block",
|
|
1299
1299
|
level: "Level",
|
|
1300
1300
|
unit: "Unit",
|
|
1301
|
+
purpose: "Purpose",
|
|
1301
1302
|
checkIn: "Check In",
|
|
1302
1303
|
snapshotEntryImage: "Entry Image",
|
|
1303
1304
|
checkOut: "Check Out",
|
|
@@ -1481,18 +1482,32 @@ async function handleVisitorDataFromScannedQRCodeCheckInOut(
|
|
|
1481
1482
|
try {
|
|
1482
1483
|
isVisitorDataFromScannedQRCodeCheckingInOut.value = true;
|
|
1483
1484
|
if (type != "checkOut") {
|
|
1485
|
+
const visitorId =
|
|
1486
|
+
visitorData._id ??
|
|
1487
|
+
visitorDataFromScannedQRCode.value._id ??
|
|
1488
|
+
selectedVisitorId.value;
|
|
1489
|
+
|
|
1490
|
+
if (!visitorId) {
|
|
1491
|
+
showMessage("Unable to check in visitor: missing visitor ID.", "error");
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
|
|
1495
|
+
const visitorPass = Array.isArray(visitorData.visitorPass)
|
|
1496
|
+
? visitorData.visitorPass
|
|
1497
|
+
: [];
|
|
1498
|
+
|
|
1484
1499
|
await visitorDataFromScannedQRCodeCheckIn({
|
|
1485
1500
|
type,
|
|
1486
1501
|
filter: {
|
|
1487
|
-
_id:
|
|
1502
|
+
_id: visitorId,
|
|
1488
1503
|
},
|
|
1489
1504
|
update: {
|
|
1490
1505
|
updatedBy: currentUser.value._id,
|
|
1491
|
-
visitorPass:
|
|
1506
|
+
visitorPass: visitorPass.find(
|
|
1492
1507
|
(i: Record<string, any>) => i.keyId === null
|
|
1493
1508
|
)
|
|
1494
1509
|
? undefined
|
|
1495
|
-
:
|
|
1510
|
+
: visitorPass,
|
|
1496
1511
|
nric: visitorData.nric ?? "",
|
|
1497
1512
|
plateNumber: visitorData.plateNumber ?? "",
|
|
1498
1513
|
},
|
|
@@ -4,7 +4,7 @@ export function useAttendancePermission() {
|
|
|
4
4
|
const { userAppRole } = useLocalSetup();
|
|
5
5
|
|
|
6
6
|
const canViewAllAttendance = computed(() => {
|
|
7
|
-
if (!userAppRole.value) return
|
|
7
|
+
if (!userAppRole.value) return false;
|
|
8
8
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
9
9
|
return hasPermission(
|
|
10
10
|
userAppRole.value,
|
|
@@ -15,7 +15,7 @@ export function useAttendancePermission() {
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
const canViewAttendanceDetails = computed(() => {
|
|
18
|
-
if (!userAppRole.value) return
|
|
18
|
+
if (!userAppRole.value) return false;
|
|
19
19
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
20
20
|
return hasPermission(
|
|
21
21
|
userAppRole.value,
|
|
@@ -26,7 +26,7 @@ export function useAttendancePermission() {
|
|
|
26
26
|
});
|
|
27
27
|
|
|
28
28
|
const canManageAttendanceSettings = computed(() => {
|
|
29
|
-
if (!userAppRole.value) return
|
|
29
|
+
if (!userAppRole.value) return false;
|
|
30
30
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
31
31
|
return hasPermission(
|
|
32
32
|
userAppRole.value,
|
|
@@ -37,7 +37,7 @@ export function useAttendancePermission() {
|
|
|
37
37
|
});
|
|
38
38
|
|
|
39
39
|
const canViewOwnAttendance = computed(() => {
|
|
40
|
-
if (!userAppRole.value) return
|
|
40
|
+
if (!userAppRole.value) return false;
|
|
41
41
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
42
42
|
return hasPermission(
|
|
43
43
|
userAppRole.value,
|
|
@@ -48,7 +48,7 @@ export function useAttendancePermission() {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
const canCheckInOut = computed(() => {
|
|
51
|
-
if (!userAppRole.value) return
|
|
51
|
+
if (!userAppRole.value) return false;
|
|
52
52
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
53
53
|
return hasPermission(
|
|
54
54
|
userAppRole.value,
|
|
@@ -7,25 +7,25 @@ export function useBulletinBoardPermission() {
|
|
|
7
7
|
const { userAppRole } = useLocalSetup();
|
|
8
8
|
|
|
9
9
|
const canViewBulletinBoard = computed(() => {
|
|
10
|
-
if (!userAppRole.value) return
|
|
10
|
+
if (!userAppRole.value) return false;
|
|
11
11
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
12
12
|
return hasPermission(userAppRole.value, permissions, "bulletin-board", "see-all-bulletin-boards");
|
|
13
13
|
});
|
|
14
14
|
|
|
15
15
|
const canCreateBulletinBoard = computed(() => {
|
|
16
|
-
if (!userAppRole.value) return
|
|
16
|
+
if (!userAppRole.value) return false;
|
|
17
17
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
18
18
|
return hasPermission(userAppRole.value, permissions, "bulletin-board", "add-bulletin-board");
|
|
19
19
|
});
|
|
20
20
|
|
|
21
21
|
const canUpdateBulletinBoard = computed(() => {
|
|
22
|
-
if (!userAppRole.value) return
|
|
22
|
+
if (!userAppRole.value) return false;
|
|
23
23
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
24
24
|
return hasPermission(userAppRole.value, permissions, "bulletin-board", "update-bulletin-board");
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
const canViewBulletinBoardDetails = computed(() => {
|
|
28
|
-
if (!userAppRole.value) return
|
|
28
|
+
if (!userAppRole.value) return false;
|
|
29
29
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
30
30
|
return hasPermission(userAppRole.value, permissions, "bulletin-board", "see-bulletin-board-details");
|
|
31
31
|
});
|
|
@@ -33,7 +33,7 @@ export function useBulletinBoardPermission() {
|
|
|
33
33
|
|
|
34
34
|
|
|
35
35
|
const canDeleteBulletinBoard = computed(() => {
|
|
36
|
-
if (!userAppRole.value) return
|
|
36
|
+
if (!userAppRole.value) return false;
|
|
37
37
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
38
38
|
return hasPermission(userAppRole.value, permissions, "bulletin-board", "delete-bulletin-board");
|
|
39
39
|
});
|
|
@@ -278,6 +278,45 @@ export function useCommonPermissions() {
|
|
|
278
278
|
},
|
|
279
279
|
|
|
280
280
|
}
|
|
281
|
+
|
|
282
|
+
const dashboardPermissions: Record<string, TPermission> = {
|
|
283
|
+
"view-activity": {
|
|
284
|
+
check: true,
|
|
285
|
+
description: "Allows the user to view the activity chart widget.",
|
|
286
|
+
},
|
|
287
|
+
"view-task-schedule": {
|
|
288
|
+
check: true,
|
|
289
|
+
description: "Allows the user to view the task schedule widget.",
|
|
290
|
+
},
|
|
291
|
+
"view-attendance-widget": {
|
|
292
|
+
check: true,
|
|
293
|
+
description: "Allows the user to view the attendance/staff status widget.",
|
|
294
|
+
},
|
|
295
|
+
"view-feedbacks-widget": {
|
|
296
|
+
check: true,
|
|
297
|
+
description: "Allows the user to view the feedbacks widget.",
|
|
298
|
+
},
|
|
299
|
+
"view-upcoming-events": {
|
|
300
|
+
check: true,
|
|
301
|
+
description: "Allows the user to view the upcoming events widget.",
|
|
302
|
+
},
|
|
303
|
+
"view-today-reminders": {
|
|
304
|
+
check: true,
|
|
305
|
+
description: "Allows the user to view the today's reminders widget.",
|
|
306
|
+
},
|
|
307
|
+
"view-today-attentions": {
|
|
308
|
+
check: true,
|
|
309
|
+
description: "Allows the user to view the today's attentions widget.",
|
|
310
|
+
},
|
|
311
|
+
"view-work-order-status": {
|
|
312
|
+
check: true,
|
|
313
|
+
description: "Allows the user to view the work order status widget.",
|
|
314
|
+
},
|
|
315
|
+
"view-active-patrol": {
|
|
316
|
+
check: true,
|
|
317
|
+
description: "Allows the user to view the active patrol widget.",
|
|
318
|
+
},
|
|
319
|
+
}
|
|
281
320
|
|
|
282
321
|
return {
|
|
283
322
|
invitationPermissions,
|
|
@@ -289,6 +328,7 @@ export function useCommonPermissions() {
|
|
|
289
328
|
buildingManagementPermissions,
|
|
290
329
|
buildingUnitManagementPermissions,
|
|
291
330
|
bulletinBoardPermissions,
|
|
292
|
-
siteSettingsPermissions
|
|
331
|
+
siteSettingsPermissions,
|
|
332
|
+
dashboardPermissions
|
|
293
333
|
};
|
|
294
334
|
}
|
|
@@ -6,7 +6,7 @@ export function useSettingsPermission() {
|
|
|
6
6
|
const { userAppRole } = useLocalSetup();
|
|
7
7
|
|
|
8
8
|
const canManageSiteInformation = computed(() => {
|
|
9
|
-
if (!userAppRole.value) return
|
|
9
|
+
if (!userAppRole.value) return false;
|
|
10
10
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
11
11
|
return hasPermission(
|
|
12
12
|
userAppRole.value,
|
|
@@ -16,7 +16,7 @@ export function useSettingsPermission() {
|
|
|
16
16
|
);
|
|
17
17
|
});
|
|
18
18
|
const canManageANPRCamera = computed(() => {
|
|
19
|
-
if (!userAppRole.value) return
|
|
19
|
+
if (!userAppRole.value) return false;
|
|
20
20
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
21
21
|
return hasPermission(
|
|
22
22
|
userAppRole.value,
|
|
@@ -26,7 +26,7 @@ export function useSettingsPermission() {
|
|
|
26
26
|
);
|
|
27
27
|
});
|
|
28
28
|
const canManageCCTVCamera = computed(() => {
|
|
29
|
-
if (!userAppRole.value) return
|
|
29
|
+
if (!userAppRole.value) return false;
|
|
30
30
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
31
31
|
return hasPermission(
|
|
32
32
|
userAppRole.value,
|
|
@@ -36,7 +36,7 @@ export function useSettingsPermission() {
|
|
|
36
36
|
);
|
|
37
37
|
});
|
|
38
38
|
const canManageDeliveryCompanies = computed(() => {
|
|
39
|
-
if (!userAppRole.value) return
|
|
39
|
+
if (!userAppRole.value) return false;
|
|
40
40
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
41
41
|
return hasPermission(
|
|
42
42
|
userAppRole.value,
|
|
@@ -46,7 +46,7 @@ export function useSettingsPermission() {
|
|
|
46
46
|
);
|
|
47
47
|
});
|
|
48
48
|
const canManageResidentVisitors = computed(() => {
|
|
49
|
-
if (!userAppRole.value) return
|
|
49
|
+
if (!userAppRole.value) return false;
|
|
50
50
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
51
51
|
return hasPermission(
|
|
52
52
|
userAppRole.value,
|
|
@@ -56,7 +56,7 @@ export function useSettingsPermission() {
|
|
|
56
56
|
);
|
|
57
57
|
});
|
|
58
58
|
const canManageWorkOrderSettings = computed(() => {
|
|
59
|
-
if (!userAppRole.value) return
|
|
59
|
+
if (!userAppRole.value) return false;
|
|
60
60
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
61
61
|
return hasPermission(
|
|
62
62
|
userAppRole.value,
|
|
@@ -66,7 +66,7 @@ export function useSettingsPermission() {
|
|
|
66
66
|
);
|
|
67
67
|
});
|
|
68
68
|
const canManageBillingAndSOAConfiguration = computed(() => {
|
|
69
|
-
if (!userAppRole.value) return
|
|
69
|
+
if (!userAppRole.value) return false;
|
|
70
70
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
71
71
|
return hasPermission(
|
|
72
72
|
userAppRole.value,
|
|
@@ -76,7 +76,7 @@ export function useSettingsPermission() {
|
|
|
76
76
|
);
|
|
77
77
|
});
|
|
78
78
|
const canManageNFCManagement = computed(() => {
|
|
79
|
-
if (!userAppRole.value) return
|
|
79
|
+
if (!userAppRole.value) return false;
|
|
80
80
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
81
81
|
return hasPermission(
|
|
82
82
|
userAppRole.value,
|
|
@@ -86,7 +86,7 @@ export function useSettingsPermission() {
|
|
|
86
86
|
);
|
|
87
87
|
});
|
|
88
88
|
const canManageNFCPatrolSettings = computed(() => {
|
|
89
|
-
if (!userAppRole.value) return
|
|
89
|
+
if (!userAppRole.value) return false;
|
|
90
90
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
91
91
|
return hasPermission(
|
|
92
92
|
userAppRole.value,
|
|
@@ -96,7 +96,7 @@ export function useSettingsPermission() {
|
|
|
96
96
|
);
|
|
97
97
|
});
|
|
98
98
|
const canManageEntryPass = computed(() => {
|
|
99
|
-
if (!userAppRole.value) return
|
|
99
|
+
if (!userAppRole.value) return false;
|
|
100
100
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
101
101
|
return hasPermission(
|
|
102
102
|
userAppRole.value,
|
|
@@ -106,7 +106,7 @@ export function useSettingsPermission() {
|
|
|
106
106
|
);
|
|
107
107
|
});
|
|
108
108
|
const canManageRedDotSettings = computed(() => {
|
|
109
|
-
if (!userAppRole.value) return
|
|
109
|
+
if (!userAppRole.value) return false;
|
|
110
110
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
111
111
|
return hasPermission(
|
|
112
112
|
userAppRole.value,
|
package/package.json
CHANGED