@7365admin1/layer-common 3.0.8 → 3.0.9

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.
Files changed (31) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components/DashboardMain.vue +1894 -844
  3. package/components/EntryPassInformation.vue +60 -0
  4. package/components/HidAccessLogDashboard.vue +1691 -0
  5. package/components/HidEnabledGate.vue +51 -0
  6. package/components/HidIdentityMapping.vue +975 -0
  7. package/components/HidQrCodeConfiguration.vue +618 -0
  8. package/components/HidReaderForm.vue +246 -0
  9. package/components/HidReaderManagement.vue +1233 -0
  10. package/components/HidServiceSettingsPanel.vue +150 -0
  11. package/components/HidUserEnrollment.vue +1274 -0
  12. package/components/Layout/NavigationDrawer.vue +31 -1
  13. package/components/NavigationItem.vue +11 -4
  14. package/components/SiteSettings.vue +24 -1
  15. package/components/VisitorForm.vue +195 -4
  16. package/components/VisitorFormSelection.vue +12 -2
  17. package/composables/useDashboard.ts +29 -2
  18. package/composables/useHidAmico.ts +152 -0
  19. package/composables/useHidNavigation.ts +106 -0
  20. package/composables/useMember.ts +7 -1
  21. package/composables/useOptionalServices.ts +118 -0
  22. package/composables/useSiteSettings.ts +1 -1
  23. package/composables/useUser.ts +1 -2
  24. package/nuxt.config.ts +1 -1
  25. package/package.json +2 -1
  26. package/pages/[org]/[site]/access-mgmt/access-logs/index.vue +21 -0
  27. package/pages/[org]/[site]/access-mgmt/administrator/index.vue +21 -0
  28. package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +21 -0
  29. package/pages/[org]/[site]/access-mgmt/hid-users/index.vue +21 -0
  30. package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +21 -0
  31. package/types/local.d.ts +2 -1
@@ -12,7 +12,7 @@
12
12
 
13
13
  <!-- NAVIGATION ITEMS -->
14
14
  <v-tooltip
15
- v-for="(item, index) in props.navigationItems"
15
+ v-for="(item, index) in navigationItems"
16
16
  :key="`${item.title}-${index}`"
17
17
  location="right"
18
18
  :disabled="!item.disabled"
@@ -52,6 +52,36 @@ const emit = defineEmits(["navigate"]);
52
52
 
53
53
  const { drawer } = useLocal();
54
54
  const { APP_NAME } = useRuntimeConfig().public;
55
+ const route = useRoute();
56
+ const orgId = computed(() => String(route.params.org ?? ""));
57
+ const siteId = computed(() => String(route.params.site ?? ""));
58
+ const { useHidFaceReaderMenu } = useHidNavigation();
59
+ const { menuItem: hidFaceReaderMenu } = useHidFaceReaderMenu({
60
+ org: orgId,
61
+ site: siteId,
62
+ cacheKey: "layout-navigation-hid-site",
63
+ });
64
+
65
+ const navigationItems = computed(() => {
66
+ const items = [...(props.navigationItems as TNavigationItem[])];
67
+
68
+ if (
69
+ hidFaceReaderMenu.value &&
70
+ !items.some((item) => item.title === hidFaceReaderMenu.value?.title)
71
+ ) {
72
+ const insertAfterIndex = items.findIndex(
73
+ (item) => item.title === "Pass & Key Mgmt",
74
+ );
75
+
76
+ if (insertAfterIndex >= 0) {
77
+ items.splice(insertAfterIndex + 1, 0, hidFaceReaderMenu.value);
78
+ } else {
79
+ items.push(hidFaceReaderMenu.value);
80
+ }
81
+ }
82
+
83
+ return items;
84
+ });
55
85
 
56
86
  const handleClick = (item: any) => {
57
87
  if (item.disabled) return;
@@ -22,9 +22,9 @@
22
22
  </v-list-group>
23
23
 
24
24
  <v-list-item
25
- v-else-if="props.route && props.route.name"
25
+ v-else-if="routeTo"
26
26
  :prepend-icon="icon"
27
- :to="props.route"
27
+ :to="routeTo"
28
28
  class="text-subtitle-2"
29
29
  >
30
30
  {{ title }}
@@ -76,8 +76,15 @@ const props = defineProps({
76
76
  });
77
77
 
78
78
  function onParentClick() {
79
- if (props.route && props.route.name) {
80
- navigateTo(props.route);
79
+ if (routeTo.value) {
80
+ navigateTo(routeTo.value);
81
81
  }
82
82
  }
83
+
84
+ const routeTo = computed(() => {
85
+ if (!props.route) return null;
86
+ if (props.route.name) return props.route;
87
+ if (props.route.path) return props.route;
88
+ return null;
89
+ });
83
90
  </script>
@@ -150,7 +150,28 @@
150
150
  </v-expansion-panel-text>
151
151
  </v-expansion-panel>
152
152
 
153
- <slot name="panels" :can-manage="canManageSiteSettings" :site-id="siteId" />
153
+ <v-expansion-panel color="primary">
154
+ <v-expansion-panel-title>
155
+ <v-icon class="mr-2">mdi-face-recognition</v-icon>
156
+ HID Face Reader
157
+ </v-expansion-panel-title>
158
+ <v-expansion-panel-text>
159
+ <HidServiceSettingsPanel
160
+ :site="siteId"
161
+ :org="orgId"
162
+ :site-data="siteData"
163
+ @refresh="refreshSiteData"
164
+ />
165
+ <v-divider class="my-4" />
166
+ <HidQrCodeConfiguration :site="siteId" />
167
+ </v-expansion-panel-text>
168
+ </v-expansion-panel>
169
+
170
+ <slot
171
+ name="panels"
172
+ :can-manage="canManageSiteSettings"
173
+ :site-id="siteId"
174
+ />
154
175
  </v-expansion-panels>
155
176
 
156
177
  <Snackbar v-model="toast.show" :text="toast.message" :color="toast.color" />
@@ -165,6 +186,7 @@ import useANPRSettings from "../composables/useANPRSettings";
165
186
 
166
187
  const props = defineProps({
167
188
  siteId: { type: String, required: true },
189
+ orgId: { type: String, default: "" },
168
190
  createdBy: { type: String },
169
191
  canManageSiteSettings: { type: Boolean, default: false },
170
192
  showAnprCamera: { type: Boolean, default: false },
@@ -227,6 +249,7 @@ const { data: siteData, refresh: refreshSiteData } = await useLazyAsyncData(
227
249
  `site-${props.siteId}`,
228
250
  () => getSiteById(props.siteId)
229
251
  );
252
+ const orgId = computed(() => props.orgId || String((siteData.value as any)?.orgId ?? ""));
230
253
 
231
254
  watch(
232
255
  siteData,
@@ -357,14 +357,17 @@
357
357
  :passKeys="visitor.passKeys"
358
358
  />
359
359
  <EntryPassInformation
360
- v-if="entryPassSettings?.data?.settings?.nfcPass"
360
+ v-if="showEntryPassInformation"
361
361
  v-model="passType"
362
362
  v-model:quantity="passQuantity"
363
363
  v-model:cards="passCards"
364
364
  :settings="entryPassSettings"
365
- :loading="entryPassSettingsPending"
365
+ :loading="entryPassSettingsPending || siteDataPending"
366
366
  :site-id="prop.site"
367
367
  :unit-id="visitor.unit || null"
368
+ :visitor-type="prop.type"
369
+ :hid-qr-code-enabled="hidQrCodePassAllowed"
370
+ :hid-qr-printer-configured="hasHidQrPrinterConfig"
368
371
  />
369
372
  </v-col>
370
373
 
@@ -619,6 +622,8 @@ const visitor = reactive<Partial<TVisitorPayload>>({
619
622
  const passType = ref<string | null>(null);
620
623
  const passQuantity = ref<number | null>(1);
621
624
  const passCards = ref<{ _id: string; cardNo: string }[]>([]);
625
+ const hidQrPrintPayload = ref<string | null>(null);
626
+ const hidQrPrinted = ref(false);
622
627
 
623
628
  const registeredUnitCompanyName = ref("N/A");
624
629
 
@@ -685,13 +690,46 @@ const showNextButton = computed(() => {
685
690
  });
686
691
 
687
692
  const showShowKeysField = computed(() => {
688
- return prop.type === "contractor";
693
+ return ["guest", "contractor", "walk-in"].includes(prop.type);
689
694
  });
690
695
 
691
696
  const requireNRIC = computed(() => {
692
697
  return prop.type !== "walk-in" && prop.type !== "guest";
693
698
  });
694
699
 
700
+ const hidQrCodePassConfig = computed(() => {
701
+ return (siteData.value as any)?.metadata?.hidQrCodePass || {};
702
+ });
703
+
704
+ const hidQrCodePassEnabled = computed(() => {
705
+ return Boolean(hidQrCodePassConfig.value?.enabled);
706
+ });
707
+
708
+ const hidQrPrinterConfig = computed(() => {
709
+ return hidQrCodePassConfig.value?.printer || {};
710
+ });
711
+
712
+ const hasHidQrPrinterConfig = computed(() => {
713
+ return Boolean(hidQrPrinterConfig.value?.vendorId && hidQrPrinterConfig.value?.productId);
714
+ });
715
+
716
+ const hidQrTemplate = computed(() => {
717
+ return hidQrCodePassConfig.value?.template || {};
718
+ });
719
+
720
+ const hidQrValidityMinutes = computed(() => {
721
+ const value = Number(hidQrCodePassConfig.value?.validityMinutes);
722
+ return Number.isFinite(value) && value > 0 ? value : 60;
723
+ });
724
+
725
+ const hidQrCodePassAllowed = computed(() => {
726
+ return hidQrCodePassEnabled.value && ["guest", "contractor", "walk-in"].includes(prop.type);
727
+ });
728
+
729
+ const showEntryPassInformation = computed(() => {
730
+ return Boolean(entryPassSettings.value?.data?.settings?.nfcPass || hidQrCodePassAllowed.value);
731
+ });
732
+
695
733
  const companyNames = ref<string[]>([]);
696
734
  const companyAutofillDataArray = ref<
697
735
  {
@@ -1189,6 +1227,13 @@ async function handleNextPage() {
1189
1227
  async function submit() {
1190
1228
  formRef.value!.validate();
1191
1229
  errorMessage.value = "";
1230
+ if (!validForm.value) {
1231
+ errorMessage.value = "Please complete all required fields *";
1232
+ return;
1233
+ }
1234
+
1235
+ if (passType.value === "HID_QR" && !validateHidQrPrinterConfig()) return;
1236
+
1192
1237
  processing.value = true;
1193
1238
 
1194
1239
  let payload: Partial<TVisitorPayload> = {
@@ -1251,7 +1296,12 @@ async function submit() {
1251
1296
  }
1252
1297
 
1253
1298
  if (res) {
1254
- if (prop.type === "contractor" && passType.value) {
1299
+ if (passType.value === "HID_QR") {
1300
+ const printed = await printHidQrCodePass(getVisitorIdFromResponse(res), false);
1301
+ if (!printed) return;
1302
+ }
1303
+
1304
+ if (prop.type === "contractor" && (passType.value === "QR" || passType.value === "NFC")) {
1255
1305
  const visitorId = res as unknown as string;
1256
1306
  const rawUrl = entryPassSettings.value?.data?.settings?.url;
1257
1307
  const { encrypted: acmUrl } = await $fetch<{ encrypted: string }>(
@@ -1373,6 +1423,131 @@ async function submit() {
1373
1423
  }
1374
1424
  }
1375
1425
 
1426
+ function parseUsbId(value: unknown) {
1427
+ if (typeof value === "number") return value;
1428
+ const text = String(value || "").trim();
1429
+ if (!text) return NaN;
1430
+ return text.toLowerCase().startsWith("0x")
1431
+ ? parseInt(text, 16)
1432
+ : parseInt(text, 10);
1433
+ }
1434
+
1435
+ function getVisitorIdFromResponse(response: any) {
1436
+ if (typeof response === "string") return response;
1437
+ return (
1438
+ response?._id ||
1439
+ response?.id ||
1440
+ response?.data?._id ||
1441
+ response?.data?.id ||
1442
+ response?.insertedId ||
1443
+ response?.data?.insertedId ||
1444
+ ""
1445
+ );
1446
+ }
1447
+
1448
+ function getHidQrPrinterUsbIds() {
1449
+ const vendorId = parseUsbId(hidQrPrinterConfig.value.vendorId);
1450
+ const productId = parseUsbId(hidQrPrinterConfig.value.productId);
1451
+ return { vendorId, productId };
1452
+ }
1453
+
1454
+ function validateHidQrPrinterConfig() {
1455
+ if (!hasHidQrPrinterConfig.value) {
1456
+ errorMessage.value = "No printer is configured on HID settings. Please configure the HID QR Code printer before continuing.";
1457
+ return false;
1458
+ }
1459
+
1460
+ const { vendorId, productId } = getHidQrPrinterUsbIds();
1461
+
1462
+ if (!Number.isFinite(vendorId) || !Number.isFinite(productId)) {
1463
+ errorMessage.value = "Invalid HID QR Code printer configuration. Please check Vendor and Product ID in Settings.";
1464
+ return false;
1465
+ }
1466
+
1467
+ return true;
1468
+ }
1469
+
1470
+ function buildHidQrCodePayload(visitorId = "") {
1471
+ if (hidQrPrintPayload.value) return hidQrPrintPayload.value;
1472
+
1473
+ const issuedAt = new Date();
1474
+ const expiresAt = new Date(issuedAt.getTime() + hidQrValidityMinutes.value * 60 * 1000);
1475
+ const nonce =
1476
+ typeof crypto !== "undefined" && "randomUUID" in crypto
1477
+ ? crypto.randomUUID()
1478
+ : `${Date.now()}-${Math.random().toString(36).slice(2)}`;
1479
+
1480
+ hidQrPrintPayload.value = JSON.stringify({
1481
+ purpose: "hid-amico-visitor-access",
1482
+ visitorId,
1483
+ site: prop.site,
1484
+ org: prop.org,
1485
+ visitorType: prop.type,
1486
+ name: visitor.name || "",
1487
+ company: visitor.company || "",
1488
+ contact: visitor.contact || "",
1489
+ nric: visitor.nric || "",
1490
+ plateNumber: visitor.plateNumber || "",
1491
+ block: visitor.block || "",
1492
+ level: visitor.level || "",
1493
+ unit: visitor.unit || "",
1494
+ unitName: visitor.unitName || "",
1495
+ address: buildVisitorAddress(),
1496
+ issuedAt: issuedAt.toISOString(),
1497
+ expiresAt: expiresAt.toISOString(),
1498
+ nonce,
1499
+ });
1500
+
1501
+ return hidQrPrintPayload.value;
1502
+ }
1503
+
1504
+ function buildVisitorAddress() {
1505
+ const blockLabel = blocksArray.value.find((b: any) => b.value === visitor.block)?.title || "";
1506
+ const levelLabel = levelsArray.value.find((l: any) => l.value === visitor.level)?.title || "";
1507
+ const unitLabel = unitsArray.value.find((u: any) => u.value === visitor.unit)?.title || visitor.unitName || "";
1508
+ return [blockLabel, levelLabel, unitLabel].filter(Boolean).join("/");
1509
+ }
1510
+
1511
+ async function printHidQrCodePass(visitorId = "", manageProcessing = true) {
1512
+ if (hidQrPrinted.value) return true;
1513
+
1514
+ if (!validateHidQrPrinterConfig()) return false;
1515
+
1516
+ const { vendorId, productId } = getHidQrPrinterUsbIds();
1517
+
1518
+ if (manageProcessing) processing.value = true;
1519
+ try {
1520
+ const printResult = await testConnection(
1521
+ vendorId,
1522
+ productId,
1523
+ true,
1524
+ buildHidQrCodePayload(visitorId),
1525
+ null,
1526
+ null,
1527
+ visitor.company || visitor.name || "",
1528
+ buildVisitorAddress(),
1529
+ hidQrTemplate.value?.header || "HID QR Code",
1530
+ hidQrTemplate.value?.subtext || "Scan QR code for access",
1531
+ );
1532
+
1533
+ if (!printResult?.success || printResult?.printTest?.success === false) {
1534
+ errorMessage.value =
1535
+ printResult?.printTest?.error ||
1536
+ printResult?.error ||
1537
+ "Unable to print HID QR Code. Please check the printer connection.";
1538
+ return false;
1539
+ }
1540
+
1541
+ hidQrPrinted.value = true;
1542
+ return true;
1543
+ } catch (error: any) {
1544
+ errorMessage.value = error?.message || "Unable to print HID QR Code.";
1545
+ return false;
1546
+ } finally {
1547
+ if (manageProcessing) processing.value = false;
1548
+ }
1549
+ }
1550
+
1376
1551
  watch(
1377
1552
  () => visitor.plateNumber,
1378
1553
  (newVal) => {
@@ -1380,6 +1555,22 @@ watch(
1380
1555
  }
1381
1556
  );
1382
1557
 
1558
+ watch(
1559
+ [
1560
+ () => passType.value,
1561
+ () => visitor.name,
1562
+ () => visitor.company,
1563
+ () => visitor.block,
1564
+ () => visitor.level,
1565
+ () => visitor.unit,
1566
+ () => visitor.unitName,
1567
+ ],
1568
+ () => {
1569
+ hidQrPrintPayload.value = null;
1570
+ hidQrPrinted.value = false;
1571
+ }
1572
+ );
1573
+
1383
1574
  const membersFieldIncomplete = computed(() => {
1384
1575
  if (!visitor.members || visitor.members.length === 0) return false;
1385
1576
 
@@ -11,7 +11,17 @@
11
11
  <v-card-text style="max-height: 100vh; overflow-y: auto" class="pa-5 my-5">
12
12
  <span class="text-subtitle-2 w-100 font-weight-medium d-flex justify-center mb-3">Please Select Visitor Type</span>
13
13
  <template v-for="item in selection" :key="item.value">
14
- <v-btn v-if="showSelection(item)" color="primary-button" block variant="flat" rounded="md" size="48" :text="item.label" @click="select(item.value)" class="my-2 text-capitalize text-subtitle-2" />
14
+ <v-btn
15
+ v-if="showSelection(item)"
16
+ color="primary-button"
17
+ block
18
+ variant="flat"
19
+ rounded="md"
20
+ size="48"
21
+ :text="item.label"
22
+ @click="select(item.value)"
23
+ class="my-2 text-capitalize text-subtitle-2"
24
+ />
15
25
  </template>
16
26
  </v-card-text>
17
27
 
@@ -39,7 +49,7 @@ const prop = defineProps({
39
49
  mode: {
40
50
  type: String as PropType<"add" | "edit" | "register">,
41
51
  default: "add",
42
- }
52
+ },
43
53
  });
44
54
 
45
55
  const emit = defineEmits(['cancel', 'select']);
@@ -1,4 +1,22 @@
1
1
  export default function () {
2
+ function getDashboard(
3
+ site = "",
4
+ serviceType: TServiceType,
5
+ period: TDashboardValues = "today",
6
+ query: Record<string, any> = {}
7
+ ) {
8
+ return useNuxtApp().$api<Record<string, any>>(
9
+ `/api/dashboard/serviceType/${serviceType}/site/${site}`,
10
+ {
11
+ method: "GET",
12
+ query: {
13
+ period,
14
+ ...query,
15
+ },
16
+ }
17
+ );
18
+ }
19
+
2
20
  function getDashboardHygiene(
3
21
  site = "",
4
22
  period: TDashboardValues = "today",
@@ -34,6 +52,7 @@ export default function () {
34
52
  function getDashboardHygieneTaskSchedule(
35
53
  site = "",
36
54
  serviceType: TServiceType,
55
+ period: TDashboardValues = "today",
37
56
  page = 1,
38
57
  limit = 4
39
58
  ) {
@@ -42,6 +61,7 @@ export default function () {
42
61
  >(`/api/dashboard/serviceType/${serviceType}/site/${site}/task-schedule`, {
43
62
  method: "GET",
44
63
  query: {
64
+ period,
45
65
  page,
46
66
  limit,
47
67
  },
@@ -51,6 +71,7 @@ export default function () {
51
71
  function getDashboardHygieneAttendance(
52
72
  site = "",
53
73
  serviceType: TServiceType,
74
+ period: TDashboardValues = "today",
54
75
  page = 1,
55
76
  limit = 4
56
77
  ) {
@@ -59,6 +80,7 @@ export default function () {
59
80
  {
60
81
  method: "GET",
61
82
  query: {
83
+ period,
62
84
  page,
63
85
  limit,
64
86
  },
@@ -69,6 +91,7 @@ export default function () {
69
91
  function getDashboardHygieneFeedbacks(
70
92
  site = "",
71
93
  serviceType: TServiceType,
94
+ period: TDashboardValues = "today",
72
95
  page = 1,
73
96
  limit = 4
74
97
  ) {
@@ -77,6 +100,7 @@ export default function () {
77
100
  {
78
101
  method: "GET",
79
102
  query: {
103
+ period,
80
104
  page,
81
105
  limit,
82
106
  },
@@ -86,12 +110,13 @@ export default function () {
86
110
 
87
111
  function getDashboardPropertyManagement(
88
112
  site = "",
113
+ serviceType: TServiceType = "property_management_agency",
89
114
  period: TDashboardValues = "today",
90
115
  type = "",
91
116
  facility = ""
92
117
  ) {
93
118
  return useNuxtApp().$api<Record<string, any>>(
94
- `/api/dashboard/serviceType/property_management_agency/site/${site}`,
119
+ `/api/dashboard/serviceType/${serviceType}/site/${site}`,
95
120
  {
96
121
  method: "GET",
97
122
  query: {
@@ -105,11 +130,12 @@ export default function () {
105
130
 
106
131
  function getDashboardSecurity(
107
132
  site = "",
133
+ serviceType: TServiceType = "security_agency",
108
134
  period: TDashboardValues = "today",
109
135
  type = ""
110
136
  ) {
111
137
  return useNuxtApp().$api<Record<string, any>>(
112
- `/api/dashboard/serviceType/security_agency/site/${site}`,
138
+ `/api/dashboard/serviceType/${serviceType}/site/${site}`,
113
139
  {
114
140
  method: "GET",
115
141
  query: {
@@ -121,6 +147,7 @@ export default function () {
121
147
  }
122
148
 
123
149
  return {
150
+ getDashboard,
124
151
  getDashboardHygiene,
125
152
  getDashboardHygieneWeeklyActivity,
126
153
  getDashboardHygieneTaskSchedule,
@@ -0,0 +1,152 @@
1
+ type HidReaderPayload = {
2
+ site?: string;
3
+ name?: string;
4
+ location?: string;
5
+ baseUrl?: string;
6
+ username?: string;
7
+ password?: string;
8
+ deviceId?: string;
9
+ monitorPath?: string;
10
+ enabled?: boolean;
11
+ status?: string;
12
+ lastSyncAt?: string;
13
+ lastSyncStatus?: "idle" | "running" | "completed" | "failed";
14
+ lastSyncMessage?: string;
15
+ };
16
+
17
+ type HidIdentityPayload = {
18
+ site?: string;
19
+ hidUserId?: string | number;
20
+ registration?: string;
21
+ cardNo?: string;
22
+ person?: string;
23
+ user?: string;
24
+ member?: string;
25
+ visitor?: string;
26
+ type?: "resident" | "staff" | "contractor" | "visitor" | "unknown";
27
+ status?: "active" | "inactive" | "deleted";
28
+ metadata?: Record<string, any>;
29
+ };
30
+
31
+ export default function useHidAmico() {
32
+ const basePath = "/api/access-management/hid";
33
+
34
+ function getReaders(query: Record<string, any> = {}) {
35
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers`, {
36
+ method: "GET",
37
+ query,
38
+ });
39
+ }
40
+
41
+ function createReader(payload: HidReaderPayload) {
42
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers`, {
43
+ method: "POST",
44
+ body: payload,
45
+ });
46
+ }
47
+
48
+ function updateReader(readerId: string, payload: HidReaderPayload) {
49
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}`, {
50
+ method: "PATCH",
51
+ body: payload,
52
+ });
53
+ }
54
+
55
+ function deleteReader(readerId: string) {
56
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}`, {
57
+ method: "DELETE",
58
+ });
59
+ }
60
+
61
+ function testReader(readerId: string) {
62
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/test`, {
63
+ method: "POST",
64
+ });
65
+ }
66
+
67
+ function syncReader(readerId: string, payload: Record<string, any> = {}) {
68
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/sync`, {
69
+ method: "POST",
70
+ body: payload,
71
+ });
72
+ }
73
+
74
+ function runObjectOperation(readerId: string, payload: Record<string, any>) {
75
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/objects`, {
76
+ method: "POST",
77
+ body: payload,
78
+ });
79
+ }
80
+
81
+ function getReaderConfiguration(readerId: string, payload: Record<string, string[]> = {}) {
82
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/configuration`, {
83
+ method: "POST",
84
+ body: payload,
85
+ });
86
+ }
87
+
88
+ function setReaderConfiguration(readerId: string, payload: Record<string, any>) {
89
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/configuration`, {
90
+ method: "PUT",
91
+ body: payload,
92
+ });
93
+ }
94
+
95
+ function getIdentities(readerId: string, query: Record<string, any> = {}) {
96
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/identities`, {
97
+ method: "GET",
98
+ query,
99
+ });
100
+ }
101
+
102
+ function getLogs(readerId: string, query: Record<string, any> = {}) {
103
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/logs`, {
104
+ method: "GET",
105
+ query,
106
+ });
107
+ }
108
+
109
+ function getUserImage(readerId: string, hidUserId: string | number) {
110
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/users/${hidUserId}/image`, {
111
+ method: "GET",
112
+ });
113
+ }
114
+
115
+ function createIdentity(readerId: string, payload: HidIdentityPayload) {
116
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/readers/${readerId}/identities`, {
117
+ method: "POST",
118
+ body: payload,
119
+ });
120
+ }
121
+
122
+ function updateIdentity(identityId: string, payload: HidIdentityPayload) {
123
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/identities/${identityId}`, {
124
+ method: "PATCH",
125
+ body: payload,
126
+ });
127
+ }
128
+
129
+ function deleteIdentity(identityId: string) {
130
+ return useNuxtApp().$api<Record<string, any>>(`${basePath}/identities/${identityId}`, {
131
+ method: "DELETE",
132
+ });
133
+ }
134
+
135
+ return {
136
+ getReaders,
137
+ createReader,
138
+ updateReader,
139
+ deleteReader,
140
+ testReader,
141
+ syncReader,
142
+ runObjectOperation,
143
+ getReaderConfiguration,
144
+ setReaderConfiguration,
145
+ getLogs,
146
+ getUserImage,
147
+ getIdentities,
148
+ createIdentity,
149
+ updateIdentity,
150
+ deleteIdentity,
151
+ };
152
+ }