@7365admin1/layer-common 3.0.25 → 3.0.26
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 +7 -0
- package/components/AccessCardDetailsDialog.vue +31 -1
- package/components/AddPassKeyToVisitor.vue +346 -225
- package/components/DashboardMain.vue +95 -59
- package/components/DocumentManagement.vue +71 -5
- package/components/HidIntercomManagement.vue +186 -0
- package/components/HidReaderManagement.vue +21 -1
- package/components/RolePermissionMain.vue +14 -8
- package/components/SiteSettings.vue +3 -1
- package/components/VisitorForm.vue +13 -1
- package/components/VisitorManagement.vue +302 -679
- package/components/VisitorSocketPopUp.vue +29 -1
- package/composables/useANPRSettings.ts +57 -8
- package/composables/useAccessManagement.ts +9 -1
- package/composables/useCleaningPermission.ts +2 -2
- package/composables/useCommonPermission.ts +2 -39
- package/composables/useDocument.ts +21 -0
- package/composables/useLocalAuth.ts +3 -1
- package/package.json +1 -1
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/hid-users/index.vue +2 -0
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +2 -0
|
@@ -1542,72 +1542,108 @@ const {
|
|
|
1542
1542
|
} = useDashboard();
|
|
1543
1543
|
|
|
1544
1544
|
const { userAppRole } = useLocalSetup();
|
|
1545
|
-
const { hasPermission } = usePermission();
|
|
1546
|
-
const { dashboardPermissions } = useCommonPermissions();
|
|
1547
1545
|
const { downloadPDF } = usePDFDownload();
|
|
1548
1546
|
const route = useRoute();
|
|
1549
1547
|
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
"virtual-patrol:see-all-virtual-patrol-logs",
|
|
1564
|
-
"virtual-patrol:see-all-virtual-patrol-route",
|
|
1565
|
-
],
|
|
1566
|
-
staffStatus: ["attendance:see-all-attendance"],
|
|
1567
|
-
attendance: ["attendance:see-all-attendance"],
|
|
1568
|
-
feedbacks: ["feedbacks:see-all-feedbacks"],
|
|
1569
|
-
upcomingEvents: [
|
|
1570
|
-
"bulletin-boards:see-all-bulletin-boards",
|
|
1571
|
-
"bulletin-board:see-all-bulletin-board",
|
|
1572
|
-
],
|
|
1573
|
-
workOrderStatus: ["work-orders:see-all-work-orders"],
|
|
1574
|
-
activePatrol: [
|
|
1575
|
-
"virtual-patrol:see-all-virtual-patrol-logs",
|
|
1576
|
-
"virtual-patrol:see-all-virtual-patrol-route",
|
|
1577
|
-
],
|
|
1578
|
-
taskSchedule: ["cleaning-schedule-mgmt:see-all-schedules"],
|
|
1579
|
-
};
|
|
1548
|
+
const dashboardWidgetPermissions: Record<string, string> = {
|
|
1549
|
+
activity: "dashboard:view-activity",
|
|
1550
|
+
recentActivity: "dashboard:view-activity",
|
|
1551
|
+
taskSchedule: "dashboard:view-task-schedule",
|
|
1552
|
+
staffStatus: "dashboard:view-attendance-widget",
|
|
1553
|
+
attendance: "dashboard:view-attendance-widget",
|
|
1554
|
+
feedbacks: "dashboard:view-feedbacks-widget",
|
|
1555
|
+
workOrderStatus: "dashboard:view-work-order-status",
|
|
1556
|
+
activePatrol: "dashboard:view-active-patrol",
|
|
1557
|
+
upcomingEvents: "dashboard:view-upcoming-events",
|
|
1558
|
+
todayReminders: "dashboard:view-today-reminders",
|
|
1559
|
+
todayAttentions: "dashboard:view-today-attentions",
|
|
1560
|
+
};
|
|
1580
1561
|
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1562
|
+
const propertyWidgetPermissions: Record<string, string[]> = {
|
|
1563
|
+
workOrders: ["workOrder:see-all-work-orders"],
|
|
1564
|
+
incidents: ["incident-reports:see-incident-reports"],
|
|
1565
|
+
visitors: ["visitorManagement:see-all-visitor"],
|
|
1566
|
+
facilityBookings: ["facility-booking-mgmt:see-all-facility-booking"],
|
|
1567
|
+
activity: ["workOrder:see-all-work-orders"],
|
|
1568
|
+
upcomingEvents: ["event-mgmt:see-all-event"],
|
|
1569
|
+
todayReminders: ["event-mgmt:see-all-event"],
|
|
1570
|
+
todayAttentions: [
|
|
1571
|
+
"incident-reports:see-incident-reports",
|
|
1572
|
+
"facility-booking-mgmt:see-all-facility-booking",
|
|
1573
|
+
],
|
|
1574
|
+
workOrderStatus: ["workOrder:see-all-work-orders"],
|
|
1575
|
+
feedbacks: ["feedbacks:see-all-feedback"],
|
|
1576
|
+
activePatrol: ["virtual-patrol:see-all-virtual-patrol-logs"],
|
|
1577
|
+
};
|
|
1588
1578
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1579
|
+
const moduleWidgetPermissions: Record<string, string[]> = {
|
|
1580
|
+
openWorkOrder: [
|
|
1581
|
+
"work_order:see-all-work-orders",
|
|
1582
|
+
"work-order:see-all-work-orders",
|
|
1583
|
+
"work-orders:see-all-work-orders",
|
|
1584
|
+
],
|
|
1585
|
+
workOrders: [
|
|
1586
|
+
"work_order:see-all-work-orders",
|
|
1587
|
+
"work-order:see-all-work-orders",
|
|
1588
|
+
"work-orders:see-all-work-orders",
|
|
1589
|
+
],
|
|
1590
|
+
taskSchedule: [
|
|
1591
|
+
"cleaning-schedule-mgmt:see-all-schedules",
|
|
1592
|
+
"schedule-task-mgmt:see-all-schedule-tasks",
|
|
1593
|
+
],
|
|
1594
|
+
taskCompleted: [
|
|
1595
|
+
"cleaning-schedule-mgmt:see-all-schedules",
|
|
1596
|
+
"schedule-task-mgmt:see-all-schedule-tasks",
|
|
1597
|
+
],
|
|
1598
|
+
staffStatus: ["attendance-mgmt:see-all-attendance"],
|
|
1599
|
+
attendance: ["attendance-mgmt:see-all-attendance"],
|
|
1600
|
+
supplyAlert: ["supply-mgmt:see-all-supplies"],
|
|
1601
|
+
feedbacks: [
|
|
1602
|
+
"feedbacks:see-all-feedback",
|
|
1603
|
+
"feedback:see-all-feedback",
|
|
1604
|
+
"feedbacks:see-all-feedbacks",
|
|
1605
|
+
],
|
|
1606
|
+
incidents: [
|
|
1607
|
+
"incident-reports:see-incident-reports",
|
|
1608
|
+
"incident-reports:see-all-incident-reports",
|
|
1609
|
+
],
|
|
1610
|
+
visitors: ["visitor:see-all-visitor"],
|
|
1611
|
+
facilityBookings: ["facility-booking:see-all-facility-booking"],
|
|
1612
|
+
patrolCompliance: [
|
|
1613
|
+
"virtual-patrol:see-all-virtual-patrol-logs",
|
|
1614
|
+
"virtual-patrol:see-all-virtual-patrol-route",
|
|
1615
|
+
],
|
|
1616
|
+
activePatrol: ["virtual-patrol:see-all-virtual-patrol-logs"],
|
|
1617
|
+
};
|
|
1618
|
+
|
|
1619
|
+
function canViewWidget(key: string) {
|
|
1620
|
+
const permissions = userAppRole.value?.permissions;
|
|
1621
|
+
const dashboardPermission = dashboardWidgetPermissions[key];
|
|
1622
|
+
const modulePermissions = isPropertyMode.value
|
|
1623
|
+
? propertyWidgetPermissions[key]
|
|
1624
|
+
: moduleWidgetPermissions[key];
|
|
1625
|
+
|
|
1626
|
+
if (!permissions) return true;
|
|
1627
|
+
if (permissions.includes("*")) return true;
|
|
1628
|
+
|
|
1629
|
+
if (isPropertyMode.value && modulePermissions) {
|
|
1630
|
+
return modulePermissions.some((permission) =>
|
|
1631
|
+
permissions.includes(permission)
|
|
1632
|
+
);
|
|
1633
|
+
}
|
|
1601
1634
|
|
|
1602
|
-
|
|
1603
|
-
|
|
1635
|
+
if (dashboardPermission || modulePermissions) {
|
|
1636
|
+
return (
|
|
1637
|
+
(dashboardPermission && permissions.includes(dashboardPermission)) ||
|
|
1638
|
+
Boolean(
|
|
1639
|
+
modulePermissions?.some((permission) =>
|
|
1640
|
+
permissions.includes(permission)
|
|
1641
|
+
)
|
|
1642
|
+
)
|
|
1643
|
+
);
|
|
1644
|
+
}
|
|
1604
1645
|
|
|
1605
|
-
return
|
|
1606
|
-
userAppRole.value,
|
|
1607
|
-
{ dashboard: dashboardPermissions },
|
|
1608
|
-
"dashboard",
|
|
1609
|
-
action
|
|
1610
|
-
);
|
|
1646
|
+
return true;
|
|
1611
1647
|
}
|
|
1612
1648
|
|
|
1613
1649
|
// ─── State ────────────────────────────────────────────────────────────────────
|
|
@@ -332,6 +332,7 @@
|
|
|
332
332
|
class="text-none"
|
|
333
333
|
size="large"
|
|
334
334
|
@click="editFolderDialog = false"
|
|
335
|
+
:disabled="folderUpdateLoading"
|
|
335
336
|
height="48"
|
|
336
337
|
>
|
|
337
338
|
Cancel
|
|
@@ -345,6 +346,7 @@
|
|
|
345
346
|
class="text-none"
|
|
346
347
|
size="large"
|
|
347
348
|
@click="handleApplyFolderEdit"
|
|
349
|
+
:loading="folderUpdateLoading"
|
|
348
350
|
height="48"
|
|
349
351
|
>
|
|
350
352
|
Apply
|
|
@@ -382,6 +384,7 @@
|
|
|
382
384
|
variant="text"
|
|
383
385
|
class="text-none"
|
|
384
386
|
@click="deleteFolderDialog = false"
|
|
387
|
+
:disabled="folderDeleteLoading"
|
|
385
388
|
>
|
|
386
389
|
Cancel
|
|
387
390
|
</v-btn>
|
|
@@ -395,6 +398,7 @@
|
|
|
395
398
|
variant="flat"
|
|
396
399
|
class="text-none"
|
|
397
400
|
@click="handleDeleteFolderUiConfirm"
|
|
401
|
+
:loading="folderDeleteLoading"
|
|
398
402
|
>
|
|
399
403
|
Confirm
|
|
400
404
|
</v-btn>
|
|
@@ -684,6 +688,8 @@ const documentService = useDocument();
|
|
|
684
688
|
const {
|
|
685
689
|
getAll: _getAllDocuments,
|
|
686
690
|
deleteById,
|
|
691
|
+
deleteFolderById,
|
|
692
|
+
updateFolderById,
|
|
687
693
|
createFolder,
|
|
688
694
|
create,
|
|
689
695
|
} = documentService;
|
|
@@ -848,6 +854,8 @@ const selectedFolder = ref<(Partial<TDocument> & Record<string, any>) | null>(
|
|
|
848
854
|
);
|
|
849
855
|
const editFolderDialog = ref(false);
|
|
850
856
|
const deleteFolderDialog = ref(false);
|
|
857
|
+
const folderUpdateLoading = ref(false);
|
|
858
|
+
const folderDeleteLoading = ref(false);
|
|
851
859
|
const editFolderTitle = ref("");
|
|
852
860
|
const editFolderRemarks = ref("");
|
|
853
861
|
const validEditFolderForm = ref(false);
|
|
@@ -1022,7 +1030,10 @@ function closeFolderActionMenu(folderId?: string) {
|
|
|
1022
1030
|
function openEditFolderDialog(
|
|
1023
1031
|
folder: Partial<TDocument> & Record<string, any>
|
|
1024
1032
|
) {
|
|
1025
|
-
selectedFolder.value =
|
|
1033
|
+
selectedFolder.value = {
|
|
1034
|
+
...folder,
|
|
1035
|
+
parentId: folder.parentId || activeParentId.value,
|
|
1036
|
+
};
|
|
1026
1037
|
editFolderTitle.value = String(folder.name || "");
|
|
1027
1038
|
editFolderRemarks.value = String(folder.remarks || "");
|
|
1028
1039
|
editFolderDialog.value = true;
|
|
@@ -1037,12 +1048,67 @@ function openDeleteFolderDialog(
|
|
|
1037
1048
|
closeFolderActionMenu(String(folder._id || ""));
|
|
1038
1049
|
}
|
|
1039
1050
|
|
|
1040
|
-
function handleApplyFolderEdit() {
|
|
1041
|
-
|
|
1051
|
+
async function handleApplyFolderEdit() {
|
|
1052
|
+
const folderId = String(selectedFolder.value?._id || "");
|
|
1053
|
+
|
|
1054
|
+
if (!folderId) {
|
|
1055
|
+
showMessage("Failed to update folder", "error");
|
|
1056
|
+
return;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
if (!editFolderTitle.value) {
|
|
1060
|
+
showMessage("Folder title is required", "error");
|
|
1061
|
+
return;
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
folderUpdateLoading.value = true;
|
|
1065
|
+
try {
|
|
1066
|
+
const response: any = await updateFolderById(folderId, {
|
|
1067
|
+
name: editFolderTitle.value,
|
|
1068
|
+
remarks: editFolderRemarks.value,
|
|
1069
|
+
parentId: selectedFolder.value?.parentId || activeParentId.value,
|
|
1070
|
+
});
|
|
1071
|
+
|
|
1072
|
+
showMessage(
|
|
1073
|
+
response?.message || "Folder updated successfully!",
|
|
1074
|
+
"success"
|
|
1075
|
+
);
|
|
1076
|
+
await getDocuments();
|
|
1077
|
+
selectedFolder.value = null;
|
|
1078
|
+
editFolderDialog.value = false;
|
|
1079
|
+
} catch (error: any) {
|
|
1080
|
+
showMessage(
|
|
1081
|
+
error?.response?._data?.message || "Failed to update folder",
|
|
1082
|
+
"error"
|
|
1083
|
+
);
|
|
1084
|
+
} finally {
|
|
1085
|
+
folderUpdateLoading.value = false;
|
|
1086
|
+
}
|
|
1042
1087
|
}
|
|
1043
1088
|
|
|
1044
|
-
function handleDeleteFolderUiConfirm() {
|
|
1045
|
-
|
|
1089
|
+
async function handleDeleteFolderUiConfirm() {
|
|
1090
|
+
const folderId = String(selectedFolder.value?._id || "");
|
|
1091
|
+
|
|
1092
|
+
if (!folderId) {
|
|
1093
|
+
showMessage("Failed to delete folder", "error");
|
|
1094
|
+
return;
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
folderDeleteLoading.value = true;
|
|
1098
|
+
try {
|
|
1099
|
+
const response = await deleteFolderById(folderId);
|
|
1100
|
+
showMessage(response.message || "Folder deleted successfully!", "success");
|
|
1101
|
+
await getDocuments();
|
|
1102
|
+
selectedFolder.value = null;
|
|
1103
|
+
deleteFolderDialog.value = false;
|
|
1104
|
+
} catch (error: any) {
|
|
1105
|
+
showMessage(
|
|
1106
|
+
error?.response?._data?.message || "Failed to delete folder",
|
|
1107
|
+
"error"
|
|
1108
|
+
);
|
|
1109
|
+
} finally {
|
|
1110
|
+
folderDeleteLoading.value = false;
|
|
1111
|
+
}
|
|
1046
1112
|
}
|
|
1047
1113
|
|
|
1048
1114
|
async function handleCreateFolder() {
|
|
@@ -104,6 +104,7 @@
|
|
|
104
104
|
<v-list-item title="SIP Settings" @click="openSipDialog(row)" />
|
|
105
105
|
<v-list-item title="Call Settings" @click="openCallDialog(row)" />
|
|
106
106
|
<v-list-item title="Dial Code" @click="openDialCodeDialog(row)" />
|
|
107
|
+
<v-list-item title="Make a call" @click="openMakeCallDialog(row)" />
|
|
107
108
|
</v-list>
|
|
108
109
|
</v-menu>
|
|
109
110
|
</td>
|
|
@@ -344,6 +345,55 @@
|
|
|
344
345
|
</v-card>
|
|
345
346
|
</v-dialog>
|
|
346
347
|
|
|
348
|
+
<v-dialog v-model="makeCallDialog" max-width="520" persistent>
|
|
349
|
+
<v-card class="form-dialog make-call-dialog" rounded="lg">
|
|
350
|
+
<v-card-title class="dialog-title">Make a Call</v-card-title>
|
|
351
|
+
<v-card-text class="dialog-body make-call-body">
|
|
352
|
+
<div class="make-call-field">
|
|
353
|
+
<div class="call-label">Address to dial</div>
|
|
354
|
+
<v-text-field
|
|
355
|
+
v-model="makeCallForm.target"
|
|
356
|
+
placeholder="Enter extension, address, or contact"
|
|
357
|
+
variant="outlined"
|
|
358
|
+
density="compact"
|
|
359
|
+
hide-details="auto"
|
|
360
|
+
class="call-input"
|
|
361
|
+
@keyup.enter="runMakeCall"
|
|
362
|
+
/>
|
|
363
|
+
</div>
|
|
364
|
+
<div class="make-call-actions">
|
|
365
|
+
<v-btn
|
|
366
|
+
class="text-none make-call-primary"
|
|
367
|
+
variant="flat"
|
|
368
|
+
:loading="calling"
|
|
369
|
+
:disabled="!makeCallForm.target.trim()"
|
|
370
|
+
@click="runMakeCall"
|
|
371
|
+
>
|
|
372
|
+
Make a call
|
|
373
|
+
</v-btn>
|
|
374
|
+
<v-btn
|
|
375
|
+
class="text-none make-call-danger"
|
|
376
|
+
variant="flat"
|
|
377
|
+
:loading="hangingUp"
|
|
378
|
+
@click="runHangupCall"
|
|
379
|
+
>
|
|
380
|
+
Hang up call
|
|
381
|
+
</v-btn>
|
|
382
|
+
</div>
|
|
383
|
+
<div v-if="makeCallForm.status" class="make-call-status" :class="`is-${makeCallForm.statusType}`">
|
|
384
|
+
{{ makeCallForm.status }}
|
|
385
|
+
</div>
|
|
386
|
+
<div class="make-call-note">
|
|
387
|
+
The call will only be made if the equipment is on the idle or identification screen.
|
|
388
|
+
</div>
|
|
389
|
+
</v-card-text>
|
|
390
|
+
<v-card-actions class="dialog-actions pa-0">
|
|
391
|
+
<v-btn class="text-none action-cancel" variant="flat" height="44" @click="makeCallDialog = false">Cancel</v-btn>
|
|
392
|
+
<v-btn class="text-none action-submit" variant="flat" height="44" @click="makeCallDialog = false">Close</v-btn>
|
|
393
|
+
</v-card-actions>
|
|
394
|
+
</v-card>
|
|
395
|
+
</v-dialog>
|
|
396
|
+
|
|
347
397
|
<v-dialog v-model="contactDialog" max-width="360" persistent>
|
|
348
398
|
<v-card class="form-dialog" rounded="lg">
|
|
349
399
|
<v-card-title class="dialog-title">{{ contactForm.id ? 'Edit Contact' : 'Add Contact' }}</v-card-title>
|
|
@@ -461,6 +511,8 @@ const {
|
|
|
461
511
|
setReaderConfiguration,
|
|
462
512
|
runObjectOperation,
|
|
463
513
|
getIntercomStatus,
|
|
514
|
+
makeIntercomCall,
|
|
515
|
+
finalizeIntercomCall,
|
|
464
516
|
} = useHidAmico();
|
|
465
517
|
|
|
466
518
|
const activeTab = ref<IntercomTab>("intercom");
|
|
@@ -470,6 +522,8 @@ const contacts = ref<ContactRow[]>([]);
|
|
|
470
522
|
const loading = ref(false);
|
|
471
523
|
const saving = ref(false);
|
|
472
524
|
const contactsSaving = ref(false);
|
|
525
|
+
const calling = ref(false);
|
|
526
|
+
const hangingUp = ref(false);
|
|
473
527
|
const search = ref("");
|
|
474
528
|
const statusFilter = ref("Status");
|
|
475
529
|
const page = ref(1);
|
|
@@ -484,6 +538,7 @@ const rebootConfirmDialog = ref(false);
|
|
|
484
538
|
const rebootingDialog = ref(false);
|
|
485
539
|
const callDialog = ref(false);
|
|
486
540
|
const dialCodeDialog = ref(false);
|
|
541
|
+
const makeCallDialog = ref(false);
|
|
487
542
|
const contactDialog = ref(false);
|
|
488
543
|
const contactViewDialog = ref(false);
|
|
489
544
|
const deleteContactDialog = ref(false);
|
|
@@ -556,6 +611,12 @@ const dialForm = reactive({
|
|
|
556
611
|
code: "",
|
|
557
612
|
});
|
|
558
613
|
|
|
614
|
+
const makeCallForm = reactive({
|
|
615
|
+
target: "",
|
|
616
|
+
status: "",
|
|
617
|
+
statusType: "info" as MessageType,
|
|
618
|
+
});
|
|
619
|
+
|
|
559
620
|
const contactForm = reactive({
|
|
560
621
|
id: undefined as number | undefined,
|
|
561
622
|
readerId: "",
|
|
@@ -882,6 +943,14 @@ function openDialCodeDialog(row: IntercomRow) {
|
|
|
882
943
|
dialCodeDialog.value = true;
|
|
883
944
|
}
|
|
884
945
|
|
|
946
|
+
function openMakeCallDialog(row: IntercomRow) {
|
|
947
|
+
selectedRow.value = row;
|
|
948
|
+
makeCallForm.target = row.extension !== "N/A" ? row.extension : "";
|
|
949
|
+
makeCallForm.status = "";
|
|
950
|
+
makeCallForm.statusType = "info";
|
|
951
|
+
makeCallDialog.value = true;
|
|
952
|
+
}
|
|
953
|
+
|
|
885
954
|
function openContactDialog(contact?: ContactRow) {
|
|
886
955
|
selectedContact.value = contact || null;
|
|
887
956
|
contactForm.id = contact?.id;
|
|
@@ -947,6 +1016,51 @@ async function saveDialCode() {
|
|
|
947
1016
|
dialCodeDialog.value = false;
|
|
948
1017
|
}
|
|
949
1018
|
|
|
1019
|
+
async function runMakeCall() {
|
|
1020
|
+
if (!selectedRow.value) return;
|
|
1021
|
+
const target = makeCallForm.target.trim();
|
|
1022
|
+
if (!target) {
|
|
1023
|
+
makeCallForm.status = "Please enter an address to dial.";
|
|
1024
|
+
makeCallForm.statusType = "warning";
|
|
1025
|
+
return;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
calling.value = true;
|
|
1029
|
+
makeCallForm.status = "";
|
|
1030
|
+
try {
|
|
1031
|
+
await makeIntercomCall(selectedRow.value.reader._id, target);
|
|
1032
|
+
makeCallForm.status = `Calling ${target}.`;
|
|
1033
|
+
makeCallForm.statusType = "success";
|
|
1034
|
+
showMessage(`Calling ${target}.`, "success");
|
|
1035
|
+
} catch (error: any) {
|
|
1036
|
+
const message = getErrorMessage(error);
|
|
1037
|
+
makeCallForm.status = message;
|
|
1038
|
+
makeCallForm.statusType = "error";
|
|
1039
|
+
showMessage(message, "error");
|
|
1040
|
+
} finally {
|
|
1041
|
+
calling.value = false;
|
|
1042
|
+
}
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
async function runHangupCall() {
|
|
1046
|
+
if (!selectedRow.value) return;
|
|
1047
|
+
hangingUp.value = true;
|
|
1048
|
+
makeCallForm.status = "";
|
|
1049
|
+
try {
|
|
1050
|
+
await finalizeIntercomCall(selectedRow.value.reader._id);
|
|
1051
|
+
makeCallForm.status = "Call ended.";
|
|
1052
|
+
makeCallForm.statusType = "success";
|
|
1053
|
+
showMessage("Intercom call ended.", "success");
|
|
1054
|
+
} catch (error: any) {
|
|
1055
|
+
const message = getErrorMessage(error);
|
|
1056
|
+
makeCallForm.status = message;
|
|
1057
|
+
makeCallForm.statusType = "error";
|
|
1058
|
+
showMessage(message, "error");
|
|
1059
|
+
} finally {
|
|
1060
|
+
hangingUp.value = false;
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
950
1064
|
async function savePjsip(readerId: string, payload: Record<string, string>, options: { showSuccessMessage?: boolean } = {}) {
|
|
951
1065
|
saving.value = true;
|
|
952
1066
|
try {
|
|
@@ -1745,6 +1859,78 @@ export default {
|
|
|
1745
1859
|
margin-bottom: 0;
|
|
1746
1860
|
}
|
|
1747
1861
|
|
|
1862
|
+
.make-call-dialog {
|
|
1863
|
+
width: 520px;
|
|
1864
|
+
max-width: 94vw;
|
|
1865
|
+
}
|
|
1866
|
+
|
|
1867
|
+
.make-call-body {
|
|
1868
|
+
display: grid;
|
|
1869
|
+
gap: 18px;
|
|
1870
|
+
padding: 20px 24px 26px;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
.make-call-field {
|
|
1874
|
+
max-width: 320px;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
.make-call-actions {
|
|
1878
|
+
display: grid;
|
|
1879
|
+
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
|
1880
|
+
gap: 18px;
|
|
1881
|
+
align-items: center;
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
.make-call-primary,
|
|
1885
|
+
.make-call-danger {
|
|
1886
|
+
min-height: 46px;
|
|
1887
|
+
border-radius: 0;
|
|
1888
|
+
color: #fff;
|
|
1889
|
+
font-size: 14px;
|
|
1890
|
+
letter-spacing: 0;
|
|
1891
|
+
}
|
|
1892
|
+
|
|
1893
|
+
.make-call-primary {
|
|
1894
|
+
background: #06324f;
|
|
1895
|
+
}
|
|
1896
|
+
|
|
1897
|
+
.make-call-danger {
|
|
1898
|
+
background: #d91345;
|
|
1899
|
+
}
|
|
1900
|
+
|
|
1901
|
+
.make-call-status {
|
|
1902
|
+
padding: 10px 12px;
|
|
1903
|
+
border-radius: 4px;
|
|
1904
|
+
font-size: 12px;
|
|
1905
|
+
line-height: 1.4;
|
|
1906
|
+
}
|
|
1907
|
+
|
|
1908
|
+
.make-call-status.is-success {
|
|
1909
|
+
background: #e7f7ec;
|
|
1910
|
+
color: #17833b;
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
.make-call-status.is-warning {
|
|
1914
|
+
background: #fff7db;
|
|
1915
|
+
color: #8a5b00;
|
|
1916
|
+
}
|
|
1917
|
+
|
|
1918
|
+
.make-call-status.is-error {
|
|
1919
|
+
background: #fde8e8;
|
|
1920
|
+
color: #b42318;
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
.make-call-status.is-info {
|
|
1924
|
+
background: #edf4ff;
|
|
1925
|
+
color: #075fb7;
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1928
|
+
.make-call-note {
|
|
1929
|
+
color: #5d6673;
|
|
1930
|
+
font-size: 13px;
|
|
1931
|
+
line-height: 1.45;
|
|
1932
|
+
}
|
|
1933
|
+
|
|
1748
1934
|
.call-two-col {
|
|
1749
1935
|
gap: 12px;
|
|
1750
1936
|
}
|
|
@@ -859,12 +859,32 @@ function getFriendlyHidError(error: any) {
|
|
|
859
859
|
return "Unable to verify the HID reader session. Please check the reader username and password, then try again.";
|
|
860
860
|
}
|
|
861
861
|
|
|
862
|
+
if (
|
|
863
|
+
message.includes("status 401") ||
|
|
864
|
+
message.includes("401 Authorization Required") ||
|
|
865
|
+
lowerMessage.includes("authorization required")
|
|
866
|
+
) {
|
|
867
|
+
return "Unable to authorize the HID gateway connection. Please check the relay credentials on the server, then try again.";
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
if (message.includes("status 403") || lowerMessage.includes("forbidden")) {
|
|
871
|
+
return "The HID gateway rejected this request. Please check the relay access permissions and try again.";
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
if (message.includes("status 404") || lowerMessage.includes("not found")) {
|
|
875
|
+
return "The HID gateway endpoint could not be found. Please check the reader Base URL and try again.";
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
if (lowerMessage.includes("<html") || lowerMessage.includes("<!doctype")) {
|
|
879
|
+
return "The HID gateway returned an unexpected response. Please check the relay configuration and try again.";
|
|
880
|
+
}
|
|
881
|
+
|
|
862
882
|
if (message.includes("reader is not active")) {
|
|
863
883
|
return "This HID reader is not active. Please enable the reader before syncing.";
|
|
864
884
|
}
|
|
865
885
|
|
|
866
886
|
if (message.includes("HID Amico request failed:")) {
|
|
867
|
-
return
|
|
887
|
+
return "The HID device rejected the request. Please check the reader connection and try again.";
|
|
868
888
|
}
|
|
869
889
|
|
|
870
890
|
return message || "The HID device rejected the request. Please check the reader connection and try again.";
|
|
@@ -145,11 +145,6 @@
|
|
|
145
145
|
</template>
|
|
146
146
|
|
|
147
147
|
<script setup lang="ts">
|
|
148
|
-
import {
|
|
149
|
-
getPermissionCount,
|
|
150
|
-
updateRolePermissionsInList,
|
|
151
|
-
} from "../utils/rolePermissionCounts";
|
|
152
|
-
|
|
153
148
|
const props = defineProps({
|
|
154
149
|
orgId: {
|
|
155
150
|
type: String,
|
|
@@ -270,10 +265,22 @@ const selectedPermissions = ref<Array<string>>([]);
|
|
|
270
265
|
const originalPermissions = ref<Array<string>>([]);
|
|
271
266
|
const edit = ref(false);
|
|
272
267
|
|
|
273
|
-
const { convertPermissionsToArray } = useUtils();
|
|
274
|
-
|
|
275
268
|
const roleId = ref("");
|
|
276
269
|
|
|
270
|
+
function getPermissionCount(permissions: unknown): number {
|
|
271
|
+
return Array.isArray(permissions) ? permissions.length : 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function updateRolePermissionsInList(
|
|
275
|
+
roles: Array<Record<string, any>>,
|
|
276
|
+
roleId: string,
|
|
277
|
+
permissions: string[]
|
|
278
|
+
) {
|
|
279
|
+
return roles.map((role) =>
|
|
280
|
+
role._id === roleId ? { ...role, permissions } : role
|
|
281
|
+
);
|
|
282
|
+
}
|
|
283
|
+
|
|
277
284
|
const { getRoleById: _getRoleById, deleteRole } = useRole();
|
|
278
285
|
|
|
279
286
|
const { data: role, refresh: getRoleById } = useLazyAsyncData(
|
|
@@ -340,7 +347,6 @@ function successfulUpdate() {
|
|
|
340
347
|
savedPermissions
|
|
341
348
|
);
|
|
342
349
|
void refreshRolesAfterUpdate(savedPermissions);
|
|
343
|
-
useLocalSetup(props.type);
|
|
344
350
|
}
|
|
345
351
|
|
|
346
352
|
async function refreshRolesAfterUpdate(savedPermissions: string[]) {
|
|
@@ -371,7 +371,9 @@ function show(message: string, color: string) {
|
|
|
371
371
|
}
|
|
372
372
|
|
|
373
373
|
// ANPR Settings
|
|
374
|
-
const { getANPRSettingBySite, createUpdateANPRSettings } = useANPRSettings(
|
|
374
|
+
const { getANPRSettingBySite, createUpdateANPRSettings } = useANPRSettings(
|
|
375
|
+
props.siteId
|
|
376
|
+
);
|
|
375
377
|
const isSaving = ref(false);
|
|
376
378
|
|
|
377
379
|
// Reactive object to hold the state of the 3 switches
|
|
@@ -990,6 +990,10 @@ async function handleNextPage() {
|
|
|
990
990
|
return;
|
|
991
991
|
}
|
|
992
992
|
const step = contractorStep.value;
|
|
993
|
+
if (step === 2 && passType.value === "NFC" && passCards.value.length === 0) {
|
|
994
|
+
errorMessage.value = "Please select at least one NFC card before proceeding.";
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
993
997
|
if (step < 3) contractorStep.value++;
|
|
994
998
|
else await submit();
|
|
995
999
|
}
|
|
@@ -1098,7 +1102,15 @@ async function submit() {
|
|
|
1098
1102
|
type: passType.value === "QR" ? "QRCODE" : passType.value,
|
|
1099
1103
|
nfcCards: passCards.value,
|
|
1100
1104
|
acm_url: acmUrl,
|
|
1101
|
-
|
|
1105
|
+
user: {
|
|
1106
|
+
visitorId,
|
|
1107
|
+
name: visitor.name,
|
|
1108
|
+
nric: visitor.nric,
|
|
1109
|
+
contact: visitor.contact,
|
|
1110
|
+
company: visitor.company,
|
|
1111
|
+
type: visitor.type,
|
|
1112
|
+
plateNumber: visitor.plateNumber,
|
|
1113
|
+
},
|
|
1102
1114
|
});
|
|
1103
1115
|
|
|
1104
1116
|
if (passType.value === "QR") {
|