@7365admin1/layer-common 3.2.1-staging.72 → 3.2.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 +39 -0
- package/components/EntryPassInformation.vue +4 -4
- package/components/FeedbackMain.vue +7 -7
- package/components/HidAccessLogDashboard.vue +44 -119
- package/components/HidIdentityMapping.vue +975 -0
- package/components/HidIntercomManagement.vue +201 -808
- package/components/HidQrCodeConfiguration.vue +95 -907
- package/components/HidServiceSettingsPanel.vue +1 -20
- package/components/HidUserEnrollment.vue +81 -392
- package/components/Layout/NavigationDrawer.vue +2 -43
- package/components/NavigationItem.vue +0 -9
- package/components/Nfc/NFCPatrolRouteMain.vue +7 -52
- package/components/OnlineFormConfigurationForm.vue +224 -620
- package/components/OnlineFormFill.vue +8 -36
- package/components/OnlineFormsConfiguration.vue +2 -6
- package/components/QrTemplate/CreditCardLandscape.vue +12 -19
- package/components/QrTemplate/PrintDialog.vue +196 -209
- package/components/SiteSettings.vue +2 -4
- package/components/VisitorForm.vue +78 -201
- package/components/VisitorManagement.vue +2 -1
- package/composables/useComment.ts +3 -3
- package/composables/useHidAmico.ts +0 -127
- package/composables/useHidNavigation.ts +7 -0
- package/composables/useOnlineFormPages.ts +0 -2
- package/composables/useSiteSettings.ts +0 -50
- package/composables/useWebUsb.ts +37 -127
- package/middleware/member.ts +4 -0
- package/package.json +1 -2
- package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +23 -0
- package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
- package/plugins/secure-member.client.ts +56 -21
- package/plugins/vuetify.ts +9 -37
- package/types/site.d.ts +0 -71
- package/.changeset/camera-wall-gated-endpoint.md +0 -47
- package/.changeset/camera-wall-plain-english.md +0 -60
- package/.changeset/camera-wall-selection-and-guidance.md +0 -65
- package/.changeset/dark-primary-contrast.md +0 -39
- package/components/CameraWall.vue +0 -1034
- package/components/CameraWallTile.vue +0 -818
- package/components/HidCameraCaptureDialog.vue +0 -199
- package/composables/useSipWebPhone.ts +0 -311
- package/utils/camera-wall.test.ts +0 -571
- package/utils/camera-wall.ts +0 -926
- package/utils/theme.test.ts +0 -201
- package/utils/theme.ts +0 -136
|
@@ -496,79 +496,10 @@
|
|
|
496
496
|
</v-card-actions>
|
|
497
497
|
</v-card>
|
|
498
498
|
</v-dialog>
|
|
499
|
-
|
|
500
|
-
<v-dialog v-model="hidQrPreviewDialog" max-width="440" persistent>
|
|
501
|
-
<v-card class="hid-qr-preview-card">
|
|
502
|
-
<v-toolbar color="transparent" density="comfortable" class="px-2">
|
|
503
|
-
<v-toolbar-title class="text-subtitle-1 font-weight-bold">
|
|
504
|
-
Visitor QR Code
|
|
505
|
-
</v-toolbar-title>
|
|
506
|
-
<v-btn icon="mdi-close" variant="text" title="Close" @click="closeHidQrPreview" />
|
|
507
|
-
</v-toolbar>
|
|
508
|
-
|
|
509
|
-
<v-card-text class="px-8 pb-6 text-center">
|
|
510
|
-
<div class="text-h6 font-weight-bold">
|
|
511
|
-
{{ hidQrTemplate.header || "Visitor Access Pass" }}
|
|
512
|
-
</div>
|
|
513
|
-
<div class="text-body-2 text-medium-emphasis mt-1">
|
|
514
|
-
{{ hidQrTemplate.subtext || "Scan at the HID reader for entry" }}
|
|
515
|
-
</div>
|
|
516
|
-
|
|
517
|
-
<div class="hid-qr-image-shell my-5">
|
|
518
|
-
<v-img :src="hidQrPreview.imageUrl" width="280" height="280" alt="Visitor access QR code" />
|
|
519
|
-
</div>
|
|
520
|
-
|
|
521
|
-
<div class="text-h6 font-weight-bold">{{ visitor.name }}</div>
|
|
522
|
-
<div v-if="hidQrPreview.expiresAt" class="text-caption text-medium-emphasis mt-1">
|
|
523
|
-
Valid until {{ formatHidQrExpiry(hidQrPreview.expiresAt) }}
|
|
524
|
-
</div>
|
|
525
|
-
|
|
526
|
-
<v-alert v-if="hidQrPreviewError" type="error" variant="tonal" density="compact" class="mt-4 text-left">
|
|
527
|
-
{{ hidQrPreviewError }}
|
|
528
|
-
</v-alert>
|
|
529
|
-
</v-card-text>
|
|
530
|
-
|
|
531
|
-
<v-divider />
|
|
532
|
-
<v-card-actions class="pa-0">
|
|
533
|
-
<v-btn
|
|
534
|
-
variant="text"
|
|
535
|
-
height="52"
|
|
536
|
-
class="flex-grow-1 text-none"
|
|
537
|
-
prepend-icon="mdi-download"
|
|
538
|
-
@click="downloadHidQrCode"
|
|
539
|
-
>
|
|
540
|
-
Download
|
|
541
|
-
</v-btn>
|
|
542
|
-
<v-btn
|
|
543
|
-
v-if="hasHidQrPrinterConfig"
|
|
544
|
-
variant="flat"
|
|
545
|
-
color="primary-button"
|
|
546
|
-
height="52"
|
|
547
|
-
class="flex-grow-1 text-none"
|
|
548
|
-
prepend-icon="mdi-printer"
|
|
549
|
-
:loading="hidQrPrinting"
|
|
550
|
-
@click="printHidQrCodePass"
|
|
551
|
-
>
|
|
552
|
-
Print
|
|
553
|
-
</v-btn>
|
|
554
|
-
<v-btn
|
|
555
|
-
v-else
|
|
556
|
-
variant="flat"
|
|
557
|
-
color="primary-button"
|
|
558
|
-
height="52"
|
|
559
|
-
class="flex-grow-1 text-none"
|
|
560
|
-
@click="closeHidQrPreview"
|
|
561
|
-
>
|
|
562
|
-
Close
|
|
563
|
-
</v-btn>
|
|
564
|
-
</v-card-actions>
|
|
565
|
-
</v-card>
|
|
566
|
-
</v-dialog>
|
|
567
499
|
</v-card>
|
|
568
500
|
</template>
|
|
569
501
|
|
|
570
502
|
<script setup lang="ts">
|
|
571
|
-
import QRCode from "qrcode";
|
|
572
503
|
import { propsFactory } from "vuetify/lib/util/propsFactory.mjs";
|
|
573
504
|
import useBuildingUnit from "../composables/useBuildingUnit";
|
|
574
505
|
import useWebUsb from "../composables/useWebUsb";
|
|
@@ -580,7 +511,6 @@ import useVisitor from "../composables/useVisitor";
|
|
|
580
511
|
import useSiteEntryPassSettings from "../composables/useSiteEntryPassSettings";
|
|
581
512
|
import useAccessManagement from "../composables/useAccessManagement";
|
|
582
513
|
import usePeople from "../composables/usePeople";
|
|
583
|
-
import useHidAmico from "../composables/useHidAmico";
|
|
584
514
|
|
|
585
515
|
const prop = defineProps({
|
|
586
516
|
type: {
|
|
@@ -632,7 +562,6 @@ const {
|
|
|
632
562
|
const { getBySiteId: getEntryPassSettingsBySiteId } =
|
|
633
563
|
useSiteEntryPassSettings();
|
|
634
564
|
const { createVisitorPass, signQr } = useAccessManagement();
|
|
635
|
-
const { issueVisitorQr } = useHidAmico();
|
|
636
565
|
const { testConnection } = useWebUsb();
|
|
637
566
|
const {
|
|
638
567
|
findPersonByNRIC,
|
|
@@ -681,15 +610,6 @@ const hidQrPrinted = ref(false);
|
|
|
681
610
|
const isPhoneFieldFocused = ref(false);
|
|
682
611
|
const isPlateNumberFieldFocused = ref(false);
|
|
683
612
|
const isNricFieldFocused = ref(false);
|
|
684
|
-
const hidQrPrinting = ref(false);
|
|
685
|
-
const hidQrPreviewDialog = ref(false);
|
|
686
|
-
const hidQrPreviewError = ref("");
|
|
687
|
-
const hidQrPendingCompletion = ref<"done" | "done:more" | null>(null);
|
|
688
|
-
const hidQrPreview = reactive({
|
|
689
|
-
imageUrl: "",
|
|
690
|
-
issuedAt: "",
|
|
691
|
-
expiresAt: "",
|
|
692
|
-
});
|
|
693
613
|
|
|
694
614
|
const registeredUnitCompanyName = ref("N/A");
|
|
695
615
|
|
|
@@ -790,8 +710,7 @@ const requireNRIC = computed(() => {
|
|
|
790
710
|
});
|
|
791
711
|
|
|
792
712
|
const hidQrCodePassConfig = computed(() => {
|
|
793
|
-
|
|
794
|
-
return site?.metadata?.hidQrCodePass;
|
|
713
|
+
return (siteData.value as any)?.metadata?.hidQrCodePass || {};
|
|
795
714
|
});
|
|
796
715
|
|
|
797
716
|
const hidQrCodePassEnabled = computed(() => {
|
|
@@ -817,12 +736,6 @@ const hidQrValidityMinutes = computed(() => {
|
|
|
817
736
|
return Number.isFinite(value) && value > 0 ? value : 60;
|
|
818
737
|
});
|
|
819
738
|
|
|
820
|
-
const hidQrReaderId = computed(() => String(hidQrCodePassConfig.value?.readerId || ""));
|
|
821
|
-
const hidQrFormat = computed<"0" | "1" | "2">(() => {
|
|
822
|
-
const value = String(hidQrCodePassConfig.value?.qrFormat || "0");
|
|
823
|
-
return value === "1" || value === "2" ? value : "0";
|
|
824
|
-
});
|
|
825
|
-
|
|
826
739
|
const hidQrCodePassAllowed = computed(() => {
|
|
827
740
|
return (
|
|
828
741
|
hidQrCodePassEnabled.value &&
|
|
@@ -1797,7 +1710,7 @@ async function submit(options: { skipNonCheckedOutCheck?: boolean } = {}) {
|
|
|
1797
1710
|
return;
|
|
1798
1711
|
}
|
|
1799
1712
|
|
|
1800
|
-
if (passType.value === "HID_QR" && !
|
|
1713
|
+
if (passType.value === "HID_QR" && !validateHidQrPrinterConfig()) return;
|
|
1801
1714
|
if (
|
|
1802
1715
|
prop.mode === "add" &&
|
|
1803
1716
|
!options.skipNonCheckedOutCheck &&
|
|
@@ -1887,10 +1800,11 @@ async function submit(options: { skipNonCheckedOutCheck?: boolean } = {}) {
|
|
|
1887
1800
|
|
|
1888
1801
|
if (res) {
|
|
1889
1802
|
if (passType.value === "HID_QR") {
|
|
1890
|
-
const
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1803
|
+
const printed = await printHidQrCodePass(
|
|
1804
|
+
getVisitorIdFromResponse(res),
|
|
1805
|
+
false
|
|
1806
|
+
);
|
|
1807
|
+
if (!printed) return;
|
|
1894
1808
|
}
|
|
1895
1809
|
|
|
1896
1810
|
if (
|
|
@@ -1923,9 +1837,12 @@ async function submit(options: { skipNonCheckedOutCheck?: boolean } = {}) {
|
|
|
1923
1837
|
});
|
|
1924
1838
|
|
|
1925
1839
|
if (passType.value === "QR") {
|
|
1840
|
+
console.log("[QR Print] passRes:", passRes);
|
|
1926
1841
|
const accessCards: any[] =
|
|
1927
1842
|
(passRes as any)?.data?.accessCards ?? [];
|
|
1843
|
+
console.log("[QR Print] accessCards:", accessCards);
|
|
1928
1844
|
const printer = entryPassSettings.value?.data?.settings?.printer;
|
|
1845
|
+
console.log("[QR Print] printer config:", printer);
|
|
1929
1846
|
if (!printer?.vendorId || !printer?.productId) {
|
|
1930
1847
|
console.warn("[QR Print] Aborted: no printer configured.");
|
|
1931
1848
|
} else if (!accessCards.length) {
|
|
@@ -1946,14 +1863,31 @@ async function submit(options: { skipNonCheckedOutCheck?: boolean } = {}) {
|
|
|
1946
1863
|
const address = [blockLabel, levelLabel, unitLabel]
|
|
1947
1864
|
.filter(Boolean)
|
|
1948
1865
|
.join("/");
|
|
1866
|
+
console.log(
|
|
1867
|
+
"[QR Print] Printing",
|
|
1868
|
+
accessCards.length,
|
|
1869
|
+
"card(s). vendorId:",
|
|
1870
|
+
vendorId,
|
|
1871
|
+
"productId:",
|
|
1872
|
+
productId,
|
|
1873
|
+
"address:",
|
|
1874
|
+
address
|
|
1875
|
+
);
|
|
1949
1876
|
|
|
1950
1877
|
await nextTick();
|
|
1951
1878
|
for (const qrCode of accessCards) {
|
|
1879
|
+
console.log("[QR Print] Signing QR for card:", qrCode._id);
|
|
1952
1880
|
const signed = await signQr({
|
|
1953
1881
|
cardId: qrCode._id,
|
|
1954
1882
|
purpose: "vms",
|
|
1955
1883
|
});
|
|
1956
1884
|
const qrData = signed?.data;
|
|
1885
|
+
console.log(
|
|
1886
|
+
"[QR Print] signQr result:",
|
|
1887
|
+
signed,
|
|
1888
|
+
"| qrData:",
|
|
1889
|
+
qrData
|
|
1890
|
+
);
|
|
1957
1891
|
if (!qrData) {
|
|
1958
1892
|
console.warn(
|
|
1959
1893
|
"[QR Print] Skipping card",
|
|
@@ -2013,23 +1947,17 @@ function parseUsbId(value: unknown) {
|
|
|
2013
1947
|
: parseInt(text, 10);
|
|
2014
1948
|
}
|
|
2015
1949
|
|
|
2016
|
-
function getVisitorIdFromResponse(response:
|
|
1950
|
+
function getVisitorIdFromResponse(response: any) {
|
|
2017
1951
|
if (typeof response === "string") return response;
|
|
2018
|
-
if (!response || typeof response !== "object") return "";
|
|
2019
|
-
|
|
2020
|
-
const result = response as Record<string, unknown>;
|
|
2021
|
-
const nested = result.data && typeof result.data === "object"
|
|
2022
|
-
? result.data as Record<string, unknown>
|
|
2023
|
-
: {};
|
|
2024
1952
|
return (
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
1953
|
+
response?._id ||
|
|
1954
|
+
response?.id ||
|
|
1955
|
+
response?.data?._id ||
|
|
1956
|
+
response?.data?.id ||
|
|
1957
|
+
response?.insertedId ||
|
|
1958
|
+
response?.data?.insertedId ||
|
|
2031
1959
|
""
|
|
2032
|
-
)
|
|
1960
|
+
);
|
|
2033
1961
|
}
|
|
2034
1962
|
|
|
2035
1963
|
function getHidQrPrinterUsbIds() {
|
|
@@ -2038,19 +1966,7 @@ function getHidQrPrinterUsbIds() {
|
|
|
2038
1966
|
return { vendorId, productId };
|
|
2039
1967
|
}
|
|
2040
1968
|
|
|
2041
|
-
function validateHidQrReaderConfig() {
|
|
2042
|
-
if (!hidQrReaderId.value) {
|
|
2043
|
-
errorMessage.value =
|
|
2044
|
-
"No HID reader is configured for QR Code Pass. Please select a reader in Settings.";
|
|
2045
|
-
return false;
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
return true;
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
1969
|
function validateHidQrPrinterConfig() {
|
|
2052
|
-
if (!validateHidQrReaderConfig()) return false;
|
|
2053
|
-
|
|
2054
1970
|
if (!hasHidQrPrinterConfig.value) {
|
|
2055
1971
|
errorMessage.value =
|
|
2056
1972
|
"No printer is configured on HID settings. Please configure the HID QR Code printer before continuing.";
|
|
@@ -2068,6 +1984,42 @@ function validateHidQrPrinterConfig() {
|
|
|
2068
1984
|
return true;
|
|
2069
1985
|
}
|
|
2070
1986
|
|
|
1987
|
+
function buildHidQrCodePayload(visitorId = "") {
|
|
1988
|
+
if (hidQrPrintPayload.value) return hidQrPrintPayload.value;
|
|
1989
|
+
|
|
1990
|
+
const issuedAt = new Date();
|
|
1991
|
+
const expiresAt = new Date(
|
|
1992
|
+
issuedAt.getTime() + hidQrValidityMinutes.value * 60 * 1000
|
|
1993
|
+
);
|
|
1994
|
+
const nonce =
|
|
1995
|
+
typeof crypto !== "undefined" && "randomUUID" in crypto
|
|
1996
|
+
? crypto.randomUUID()
|
|
1997
|
+
: `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
1998
|
+
|
|
1999
|
+
hidQrPrintPayload.value = JSON.stringify({
|
|
2000
|
+
purpose: "hid-amico-visitor-access",
|
|
2001
|
+
visitorId,
|
|
2002
|
+
site: prop.site,
|
|
2003
|
+
org: prop.org,
|
|
2004
|
+
visitorType: prop.type,
|
|
2005
|
+
name: visitor.name || "",
|
|
2006
|
+
company: visitor.company || "",
|
|
2007
|
+
contact: visitor.contact || "",
|
|
2008
|
+
nric: visitor.nric || "",
|
|
2009
|
+
plateNumber: visitor.plateNumber || "",
|
|
2010
|
+
block: visitor.block || "",
|
|
2011
|
+
level: visitor.level || "",
|
|
2012
|
+
unit: visitor.unit || "",
|
|
2013
|
+
unitName: visitor.unitName || "",
|
|
2014
|
+
address: buildVisitorAddress(),
|
|
2015
|
+
issuedAt: issuedAt.toISOString(),
|
|
2016
|
+
expiresAt: expiresAt.toISOString(),
|
|
2017
|
+
nonce,
|
|
2018
|
+
});
|
|
2019
|
+
|
|
2020
|
+
return hidQrPrintPayload.value;
|
|
2021
|
+
}
|
|
2022
|
+
|
|
2071
2023
|
function buildVisitorAddress() {
|
|
2072
2024
|
const blockLabel =
|
|
2073
2025
|
blocksArray.value.find((b: any) => b.value === visitor.block)?.title || "";
|
|
@@ -2080,58 +2032,20 @@ function buildVisitorAddress() {
|
|
|
2080
2032
|
return [blockLabel, levelLabel, unitLabel].filter(Boolean).join("/");
|
|
2081
2033
|
}
|
|
2082
2034
|
|
|
2083
|
-
async function
|
|
2084
|
-
hidQrPreviewError.value = "";
|
|
2085
|
-
if (!visitorId) {
|
|
2086
|
-
errorMessage.value = "Unable to identify the visitor for HID QR Code enrollment.";
|
|
2087
|
-
return false;
|
|
2088
|
-
}
|
|
2089
|
-
|
|
2090
|
-
try {
|
|
2091
|
-
const response = await issueVisitorQr(hidQrReaderId.value, {
|
|
2092
|
-
visitorId,
|
|
2093
|
-
validityMinutes: hidQrValidityMinutes.value,
|
|
2094
|
-
qrFormat: hidQrFormat.value,
|
|
2095
|
-
});
|
|
2096
|
-
const credential = "data" in response ? response.data : response;
|
|
2097
|
-
hidQrPrintPayload.value = credential.qrValue;
|
|
2098
|
-
if (!hidQrPrintPayload.value) {
|
|
2099
|
-
errorMessage.value = "HID QR Code enrollment did not return a QR value.";
|
|
2100
|
-
return false;
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
|
-
hidQrPreview.imageUrl = await QRCode.toDataURL(hidQrPrintPayload.value, {
|
|
2104
|
-
width: 360,
|
|
2105
|
-
margin: 2,
|
|
2106
|
-
errorCorrectionLevel: "M",
|
|
2107
|
-
});
|
|
2108
|
-
hidQrPreview.issuedAt = credential.issuedAt || "";
|
|
2109
|
-
hidQrPreview.expiresAt = credential.expiresAt || "";
|
|
2110
|
-
hidQrPreviewDialog.value = true;
|
|
2111
|
-
return true;
|
|
2112
|
-
} catch (error: unknown) {
|
|
2113
|
-
errorMessage.value = errorConverter(error) || "Unable to generate HID QR Code.";
|
|
2114
|
-
return false;
|
|
2115
|
-
}
|
|
2116
|
-
}
|
|
2117
|
-
|
|
2118
|
-
async function printHidQrCodePass() {
|
|
2035
|
+
async function printHidQrCodePass(visitorId = "", manageProcessing = true) {
|
|
2119
2036
|
if (hidQrPrinted.value) return true;
|
|
2120
2037
|
|
|
2121
2038
|
if (!validateHidQrPrinterConfig()) return false;
|
|
2122
2039
|
|
|
2123
2040
|
const { vendorId, productId } = getHidQrPrinterUsbIds();
|
|
2124
2041
|
|
|
2125
|
-
|
|
2126
|
-
hidQrPreviewError.value = "";
|
|
2042
|
+
if (manageProcessing) processing.value = true;
|
|
2127
2043
|
try {
|
|
2128
|
-
if (!hidQrPrintPayload.value) return false;
|
|
2129
|
-
|
|
2130
2044
|
const printResult = await testConnection(
|
|
2131
2045
|
vendorId,
|
|
2132
2046
|
productId,
|
|
2133
2047
|
true,
|
|
2134
|
-
|
|
2048
|
+
buildHidQrCodePayload(visitorId),
|
|
2135
2049
|
null,
|
|
2136
2050
|
null,
|
|
2137
2051
|
visitor.company || visitor.name || "",
|
|
@@ -2141,7 +2055,7 @@ async function printHidQrCodePass() {
|
|
|
2141
2055
|
);
|
|
2142
2056
|
|
|
2143
2057
|
if (!printResult?.success || printResult?.printTest?.success === false) {
|
|
2144
|
-
|
|
2058
|
+
errorMessage.value =
|
|
2145
2059
|
printResult?.printTest?.error ||
|
|
2146
2060
|
printResult?.error ||
|
|
2147
2061
|
"Unable to print HID QR Code. Please check the printer connection.";
|
|
@@ -2150,40 +2064,14 @@ async function printHidQrCodePass() {
|
|
|
2150
2064
|
|
|
2151
2065
|
hidQrPrinted.value = true;
|
|
2152
2066
|
return true;
|
|
2153
|
-
} catch (error:
|
|
2154
|
-
|
|
2067
|
+
} catch (error: any) {
|
|
2068
|
+
errorMessage.value = error?.message || "Unable to print HID QR Code.";
|
|
2155
2069
|
return false;
|
|
2156
2070
|
} finally {
|
|
2157
|
-
|
|
2071
|
+
if (manageProcessing) processing.value = false;
|
|
2158
2072
|
}
|
|
2159
2073
|
}
|
|
2160
2074
|
|
|
2161
|
-
function formatHidQrExpiry(value: string) {
|
|
2162
|
-
const date = new Date(value);
|
|
2163
|
-
return Number.isNaN(date.getTime()) ? value : date.toLocaleString();
|
|
2164
|
-
}
|
|
2165
|
-
|
|
2166
|
-
function downloadHidQrCode() {
|
|
2167
|
-
if (!hidQrPreview.imageUrl) return;
|
|
2168
|
-
const safeName = String(visitor.name || "visitor")
|
|
2169
|
-
.trim()
|
|
2170
|
-
.replace(/[^a-z0-9]+/gi, "-")
|
|
2171
|
-
.replace(/^-|-$/g, "")
|
|
2172
|
-
.toLowerCase();
|
|
2173
|
-
const link = document.createElement("a");
|
|
2174
|
-
link.href = hidQrPreview.imageUrl;
|
|
2175
|
-
link.download = `hid-visitor-qr-${safeName || "visitor"}.png`;
|
|
2176
|
-
link.click();
|
|
2177
|
-
}
|
|
2178
|
-
|
|
2179
|
-
function closeHidQrPreview() {
|
|
2180
|
-
hidQrPreviewDialog.value = false;
|
|
2181
|
-
const completion = hidQrPendingCompletion.value;
|
|
2182
|
-
hidQrPendingCompletion.value = null;
|
|
2183
|
-
if (completion === "done:more") emit("done:more");
|
|
2184
|
-
else if (completion === "done") emit("done");
|
|
2185
|
-
}
|
|
2186
|
-
|
|
2187
2075
|
watch(
|
|
2188
2076
|
() => visitor.plateNumber,
|
|
2189
2077
|
(newVal) => {
|
|
@@ -2324,15 +2212,4 @@ onMounted(() => {
|
|
|
2324
2212
|
font-size: 0.95rem;
|
|
2325
2213
|
opacity: 1;
|
|
2326
2214
|
}
|
|
2327
|
-
|
|
2328
|
-
.hid-qr-preview-card {
|
|
2329
|
-
border-radius: 6px;
|
|
2330
|
-
}
|
|
2331
|
-
|
|
2332
|
-
.hid-qr-image-shell {
|
|
2333
|
-
display: inline-flex;
|
|
2334
|
-
padding: 12px;
|
|
2335
|
-
border: 1px solid #dce3e8;
|
|
2336
|
-
background: #ffffff;
|
|
2337
|
-
}
|
|
2338
2215
|
</style>
|
|
@@ -1601,6 +1601,7 @@ const {
|
|
|
1601
1601
|
watch(getVisitorReq, (newData: any) => {
|
|
1602
1602
|
if (newData) {
|
|
1603
1603
|
items.value = newData.items ?? [];
|
|
1604
|
+
// items.value = [{ _id: "testid", name: "John Doe", type: "Contractor", company: "ABC Corp", location: "Block A, Level 1, Unit 101", contact: "12345678", plateNumber: "SGX1234A", checkIn: new Date().toISOString(), checkOut: null, status: "registered", visitorPass: [{ _id: "1", keyId: "pass1", prefixAndName: "VIP Pass" }, { _id: "2", keyId: "pass2", prefixAndName: "VIP Pass 2" }], passKeys: [{ _id: "1", keyId: "key2", prefixAndName: "Master Key" }] }];
|
|
1604
1605
|
pages.value = newData.pages ?? 0;
|
|
1605
1606
|
pageRange.value = newData?.pageRange ?? "-- - -- of --";
|
|
1606
1607
|
}
|
|
@@ -1707,7 +1708,7 @@ async function handleVisitorDataFromScannedQRCodeCheckInOut(
|
|
|
1707
1708
|
site: visitorData.site ?? siteId,
|
|
1708
1709
|
},
|
|
1709
1710
|
update: {
|
|
1710
|
-
updatedBy: currentUser.value
|
|
1711
|
+
updatedBy: currentUser.value._id,
|
|
1711
1712
|
visitorPass: visitorPass.find(
|
|
1712
1713
|
(i: Record<string, any>) => i.keyId === null
|
|
1713
1714
|
)
|
|
@@ -89,13 +89,13 @@ export default function useComment() {
|
|
|
89
89
|
|
|
90
90
|
comments.value = _comments.items
|
|
91
91
|
.map((comment: any) => {
|
|
92
|
-
if (comment.createdBy === currentUser.value
|
|
92
|
+
if (comment.createdBy === currentUser.value._id) {
|
|
93
93
|
comment.justify = "end";
|
|
94
94
|
} else {
|
|
95
95
|
comment.justify = "start";
|
|
96
96
|
if (
|
|
97
97
|
((Array.isArray(comment.seenBy) &&
|
|
98
|
-
!comment.seenBy.includes(currentUser.value
|
|
98
|
+
!comment.seenBy.includes(currentUser.value._id)) ||
|
|
99
99
|
!comment?.seenBy) &&
|
|
100
100
|
comment?._id
|
|
101
101
|
) {
|
|
@@ -111,7 +111,7 @@ export default function useComment() {
|
|
|
111
111
|
);
|
|
112
112
|
|
|
113
113
|
if (Array.isArray(updateSeenIds) && updateSeenIds.length > 0) {
|
|
114
|
-
const seenBy = await updateSeenBy(updateSeenIds, currentUser.value
|
|
114
|
+
const seenBy = await updateSeenBy(updateSeenIds, currentUser.value._id);
|
|
115
115
|
}
|
|
116
116
|
} catch (error) {
|
|
117
117
|
console.log("error :", error);
|
|
@@ -28,46 +28,6 @@ type HidIdentityPayload = {
|
|
|
28
28
|
metadata?: Record<string, any>;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
type HidVisitorQrData = {
|
|
32
|
-
visitorId: string;
|
|
33
|
-
readerId: string;
|
|
34
|
-
hidUserId: number;
|
|
35
|
-
qrValue: string;
|
|
36
|
-
qrFormat: "0" | "1" | "2";
|
|
37
|
-
issuedAt: string;
|
|
38
|
-
expiresAt: string;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
type HidVisitorQrResponse = HidVisitorQrData | { data: HidVisitorQrData };
|
|
42
|
-
|
|
43
|
-
type HidPermissionListResponse = {
|
|
44
|
-
items: THidPermissionCandidate[];
|
|
45
|
-
page: number;
|
|
46
|
-
pages: number;
|
|
47
|
-
pageRange: string;
|
|
48
|
-
limit: number;
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
type HidFacialEnrollmentResult = {
|
|
52
|
-
user_id?: number;
|
|
53
|
-
success?: boolean;
|
|
54
|
-
scores?: Record<string, unknown>;
|
|
55
|
-
errors?: Array<{ code?: number; message?: string }>;
|
|
56
|
-
};
|
|
57
|
-
|
|
58
|
-
export type HidSipAccountData = {
|
|
59
|
-
orgId: string;
|
|
60
|
-
site: string;
|
|
61
|
-
userId: string;
|
|
62
|
-
extension: string;
|
|
63
|
-
username: string;
|
|
64
|
-
password: string;
|
|
65
|
-
sipAddress: string;
|
|
66
|
-
websocketUrl: string;
|
|
67
|
-
status: "active";
|
|
68
|
-
provisionedAt: string;
|
|
69
|
-
};
|
|
70
|
-
|
|
71
31
|
export default function useHidAmico() {
|
|
72
32
|
const basePath = "/api/access-management/hid";
|
|
73
33
|
|
|
@@ -118,16 +78,6 @@ export default function useHidAmico() {
|
|
|
118
78
|
});
|
|
119
79
|
}
|
|
120
80
|
|
|
121
|
-
function issueVisitorQr(
|
|
122
|
-
readerId: string,
|
|
123
|
-
payload: { visitorId: string; validityMinutes: number; qrFormat: "0" | "1" | "2" },
|
|
124
|
-
) {
|
|
125
|
-
return useNuxtApp().$api<HidVisitorQrResponse>(`${basePath}/readers/${readerId}/visitor-qr`, {
|
|
126
|
-
method: "POST",
|
|
127
|
-
body: payload,
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
|
|
131
81
|
function getReaderConfiguration(readerId: string, payload: Record<string, string[]> = {}) {
|
|
132
82
|
return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/configuration`, {
|
|
133
83
|
method: "POST",
|
|
@@ -162,33 +112,6 @@ export default function useHidAmico() {
|
|
|
162
112
|
});
|
|
163
113
|
}
|
|
164
114
|
|
|
165
|
-
function setUserImage(
|
|
166
|
-
readerId: string,
|
|
167
|
-
hidUserId: string | number,
|
|
168
|
-
image: Blob,
|
|
169
|
-
options: { timestamp?: number; match?: boolean } = {},
|
|
170
|
-
) {
|
|
171
|
-
return useNuxtApp().$api<{ data: HidFacialEnrollmentResult }>(
|
|
172
|
-
`${basePath}/readers/${readerId}/users/${hidUserId}/image`,
|
|
173
|
-
{
|
|
174
|
-
method: "PUT",
|
|
175
|
-
query: {
|
|
176
|
-
...(options.timestamp ? { timestamp: options.timestamp } : {}),
|
|
177
|
-
match: options.match ?? true,
|
|
178
|
-
},
|
|
179
|
-
headers: { "Content-Type": "application/octet-stream" },
|
|
180
|
-
body: image,
|
|
181
|
-
},
|
|
182
|
-
);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
function deleteUserImage(readerId: string, hidUserId: string | number) {
|
|
186
|
-
return useNuxtApp().$api<Record<string, unknown>>(
|
|
187
|
-
`${basePath}/readers/${readerId}/users/${hidUserId}/image`,
|
|
188
|
-
{ method: "DELETE" },
|
|
189
|
-
);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
115
|
function createIdentity(readerId: string, payload: HidIdentityPayload) {
|
|
193
116
|
return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/identities`, {
|
|
194
117
|
method: "POST",
|
|
@@ -228,49 +151,6 @@ export default function useHidAmico() {
|
|
|
228
151
|
});
|
|
229
152
|
}
|
|
230
153
|
|
|
231
|
-
function getSitePermissions(siteId: string, orgId: string, readerId: string) {
|
|
232
|
-
return useNuxtApp().$api<{ data: THidSitePermissions }>(
|
|
233
|
-
`${basePath}/sites/${siteId}/permissions`,
|
|
234
|
-
{ method: "GET", query: { orgId, readerId } },
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
function ensureSipAccount(siteId: string, orgId: string) {
|
|
239
|
-
return useNuxtApp().$api<{ data: HidSipAccountData }>(
|
|
240
|
-
`${basePath}/sites/${siteId}/intercom/sip-account`,
|
|
241
|
-
{ method: "POST", body: { orgId } },
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function getPermissionCandidates(
|
|
246
|
-
siteId: string,
|
|
247
|
-
query: {
|
|
248
|
-
orgId: string;
|
|
249
|
-
readerId: string;
|
|
250
|
-
category: THidPermissionCategory | "intercom";
|
|
251
|
-
search?: string;
|
|
252
|
-
page?: number;
|
|
253
|
-
limit?: number;
|
|
254
|
-
},
|
|
255
|
-
) {
|
|
256
|
-
return useNuxtApp().$api<HidPermissionListResponse>(
|
|
257
|
-
`${basePath}/sites/${siteId}/permission-candidates`,
|
|
258
|
-
{ method: "GET", query },
|
|
259
|
-
);
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
function updateSitePermissions(
|
|
263
|
-
siteId: string,
|
|
264
|
-
orgId: string,
|
|
265
|
-
readerId: string,
|
|
266
|
-
assignments: Array<Pick<THidPermissionAssignment, "subjectId" | "category" | "intercom">>,
|
|
267
|
-
) {
|
|
268
|
-
return useNuxtApp().$api<{ data: THidSitePermissions; message: string }>(
|
|
269
|
-
`${basePath}/sites/${siteId}/permissions`,
|
|
270
|
-
{ method: "PUT", body: { orgId, readerId, assignments } },
|
|
271
|
-
);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
154
|
return {
|
|
275
155
|
getReaders,
|
|
276
156
|
createReader,
|
|
@@ -279,13 +159,10 @@ export default function useHidAmico() {
|
|
|
279
159
|
testReader,
|
|
280
160
|
syncReader,
|
|
281
161
|
runObjectOperation,
|
|
282
|
-
issueVisitorQr,
|
|
283
162
|
getReaderConfiguration,
|
|
284
163
|
setReaderConfiguration,
|
|
285
164
|
getLogs,
|
|
286
165
|
getUserImage,
|
|
287
|
-
setUserImage,
|
|
288
|
-
deleteUserImage,
|
|
289
166
|
getIdentities,
|
|
290
167
|
createIdentity,
|
|
291
168
|
updateIdentity,
|
|
@@ -293,9 +170,5 @@ export default function useHidAmico() {
|
|
|
293
170
|
getIntercomStatus,
|
|
294
171
|
makeIntercomCall,
|
|
295
172
|
finalizeIntercomCall,
|
|
296
|
-
ensureSipAccount,
|
|
297
|
-
getSitePermissions,
|
|
298
|
-
getPermissionCandidates,
|
|
299
|
-
updateSitePermissions,
|
|
300
173
|
};
|
|
301
174
|
}
|
|
@@ -41,6 +41,13 @@ export default function useHidNavigation() {
|
|
|
41
41
|
params: { org, site },
|
|
42
42
|
},
|
|
43
43
|
},
|
|
44
|
+
{
|
|
45
|
+
title: "Identity Mapping",
|
|
46
|
+
route: {
|
|
47
|
+
name: "org-site-access-mgmt-identity-mapping",
|
|
48
|
+
params: { org, site },
|
|
49
|
+
},
|
|
50
|
+
},
|
|
44
51
|
{
|
|
45
52
|
title: "Access Logs",
|
|
46
53
|
route: {
|
|
@@ -20,8 +20,6 @@ export type FormBlock =
|
|
|
20
20
|
| { type: "checkbox-group"; title?: string; single?: boolean; items: CheckboxItemDef[] }
|
|
21
21
|
| { type: "paragraph"; text: string }
|
|
22
22
|
| { type: "section-header"; text: string }
|
|
23
|
-
| { type: "ordered-list"; title?: string; items: string[] }
|
|
24
|
-
| { type: "unordered-list"; title?: string; items: string[] }
|
|
25
23
|
| { type: "divider" }
|
|
26
24
|
| { type: "signature-row"; label: string; key: string }
|
|
27
25
|
| { type: "acknowledgement"; text: string }
|