@7365admin1/layer-common 3.1.4-staging.49 → 3.1.4-staging.51

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.
@@ -0,0 +1,236 @@
1
+ import { computed, onScopeDispose, readonly, ref, shallowRef } from "vue";
2
+ import type { SimpleUser, SimpleUserDelegate, SimpleUserOptions } from "sip.js/lib/platform/web";
3
+
4
+ export type SipWebPhoneConfig = {
5
+ webSocketServer: string;
6
+ aor: string;
7
+ authorizationUsername: string;
8
+ authorizationPassword: string;
9
+ displayName?: string;
10
+ video?: boolean;
11
+ };
12
+
13
+ export type SipWebPhoneMedia = {
14
+ localVideo?: HTMLVideoElement | null;
15
+ remoteAudio?: HTMLAudioElement | null;
16
+ remoteVideo?: HTMLVideoElement | null;
17
+ };
18
+
19
+ type ConnectionState = "disconnected" | "connecting" | "registered" | "error";
20
+ type CallState = "idle" | "calling" | "incoming" | "active" | "ending";
21
+
22
+ export default function useSipWebPhone() {
23
+ const simpleUser = shallowRef<SimpleUser>();
24
+ const connectionState = ref<ConnectionState>("disconnected");
25
+ const callState = ref<CallState>("idle");
26
+ const errorMessage = ref("");
27
+ const muted = ref(false);
28
+ const currentTarget = ref("");
29
+ const activeConfig = shallowRef<SipWebPhoneConfig>();
30
+
31
+ const isRegistered = computed(() => connectionState.value === "registered");
32
+ const hasCall = computed(() => callState.value !== "idle");
33
+
34
+ async function connect(config: SipWebPhoneConfig, media: SipWebPhoneMedia) {
35
+ validateConfig(config);
36
+ await disconnect();
37
+
38
+ connectionState.value = "connecting";
39
+ errorMessage.value = "";
40
+ activeConfig.value = { ...config };
41
+
42
+ try {
43
+ const { SimpleUser: SimpleUserConstructor } = await import("sip.js/lib/platform/web");
44
+ const delegate: SimpleUserDelegate = {
45
+ onCallCreated: () => {
46
+ if (callState.value !== "incoming") callState.value = "calling";
47
+ },
48
+ onCallReceived: () => {
49
+ callState.value = "incoming";
50
+ currentTarget.value = "Incoming call";
51
+ },
52
+ onCallAnswered: () => {
53
+ callState.value = "active";
54
+ },
55
+ onCallHangup: () => resetCall(),
56
+ onRegistered: () => {
57
+ connectionState.value = "registered";
58
+ },
59
+ onUnregistered: () => {
60
+ connectionState.value = "disconnected";
61
+ },
62
+ onServerDisconnect: (error) => {
63
+ connectionState.value = error ? "error" : "disconnected";
64
+ if (error) errorMessage.value = error.message;
65
+ resetCall();
66
+ },
67
+ };
68
+ const options: SimpleUserOptions = {
69
+ aor: normalizeSipUri(config.aor),
70
+ delegate,
71
+ media: {
72
+ constraints: { audio: true, video: Boolean(config.video) },
73
+ local: { video: media.localVideo || undefined },
74
+ remote: {
75
+ audio: media.remoteAudio || undefined,
76
+ video: media.remoteVideo || undefined,
77
+ },
78
+ },
79
+ userAgentOptions: {
80
+ authorizationUsername: config.authorizationUsername,
81
+ authorizationPassword: config.authorizationPassword,
82
+ displayName: config.displayName || config.authorizationUsername,
83
+ },
84
+ };
85
+
86
+ const user = new SimpleUserConstructor(config.webSocketServer.trim(), options);
87
+ simpleUser.value = user;
88
+ await user.connect();
89
+ await user.register();
90
+ } catch (error) {
91
+ await disconnect(false);
92
+ connectionState.value = "error";
93
+ errorMessage.value = getErrorMessage(error);
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ async function disconnect(clearError = true) {
99
+ const user = simpleUser.value;
100
+ simpleUser.value = undefined;
101
+
102
+ if (user) {
103
+ try {
104
+ if (hasCall.value) await user.hangup();
105
+ } catch {
106
+ // The session may already have ended remotely.
107
+ }
108
+ try {
109
+ await user.unregister();
110
+ } catch {
111
+ // Registration may not have completed.
112
+ }
113
+ try {
114
+ await user.disconnect();
115
+ } catch {
116
+ // The WebSocket may already be closed.
117
+ }
118
+ }
119
+
120
+ connectionState.value = "disconnected";
121
+ activeConfig.value = undefined;
122
+ resetCall();
123
+ if (clearError) errorMessage.value = "";
124
+ }
125
+
126
+ async function call(target: string) {
127
+ const user = requireRegisteredUser();
128
+ const destination = normalizeDestination(target, activeConfig.value?.aor || "");
129
+ errorMessage.value = "";
130
+ currentTarget.value = destination;
131
+ callState.value = "calling";
132
+ try {
133
+ await user.call(destination);
134
+ } catch (error) {
135
+ resetCall();
136
+ errorMessage.value = getErrorMessage(error);
137
+ throw error;
138
+ }
139
+ }
140
+
141
+ async function answer() {
142
+ const user = requireRegisteredUser();
143
+ await user.answer();
144
+ }
145
+
146
+ async function decline() {
147
+ const user = requireUser();
148
+ callState.value = "ending";
149
+ await user.decline();
150
+ resetCall();
151
+ }
152
+
153
+ async function hangup() {
154
+ const user = requireUser();
155
+ callState.value = "ending";
156
+ await user.hangup();
157
+ resetCall();
158
+ }
159
+
160
+ function toggleMute() {
161
+ const user = requireUser();
162
+ if (muted.value) {
163
+ user.unmute();
164
+ } else {
165
+ user.mute();
166
+ }
167
+ muted.value = user.isMuted();
168
+ }
169
+
170
+ function resetCall() {
171
+ callState.value = "idle";
172
+ currentTarget.value = "";
173
+ muted.value = false;
174
+ }
175
+
176
+ function requireRegisteredUser() {
177
+ const user = requireUser();
178
+ if (!isRegistered.value) throw new Error("Connect the web phone before making a call.");
179
+ return user;
180
+ }
181
+
182
+ function requireUser() {
183
+ if (!simpleUser.value) throw new Error("Web phone is not connected.");
184
+ return simpleUser.value;
185
+ }
186
+
187
+ onScopeDispose(() => {
188
+ void disconnect();
189
+ });
190
+
191
+ return {
192
+ connectionState: readonly(connectionState),
193
+ callState: readonly(callState),
194
+ errorMessage: readonly(errorMessage),
195
+ muted: readonly(muted),
196
+ currentTarget: readonly(currentTarget),
197
+ isRegistered,
198
+ hasCall,
199
+ connect,
200
+ disconnect,
201
+ call,
202
+ answer,
203
+ decline,
204
+ hangup,
205
+ toggleMute,
206
+ };
207
+ }
208
+
209
+ function validateConfig(config: SipWebPhoneConfig) {
210
+ if (!/^wss:\/\//i.test(config.webSocketServer.trim())) {
211
+ throw new Error("SIP WebSocket URL must use WSS.");
212
+ }
213
+ if (!config.aor.trim() || !config.authorizationUsername.trim() || !config.authorizationPassword) {
214
+ throw new Error("SIP address, username, and password are required.");
215
+ }
216
+ }
217
+
218
+ function normalizeSipUri(value: string) {
219
+ const trimmed = value.trim();
220
+ return trimmed.toLowerCase().startsWith("sip:") ? trimmed : `sip:${trimmed}`;
221
+ }
222
+
223
+ function normalizeDestination(target: string, aor: string) {
224
+ const trimmed = target.trim();
225
+ if (!trimmed) throw new Error("Enter an extension or SIP address to call.");
226
+ if (trimmed.toLowerCase().startsWith("sip:")) return trimmed;
227
+ if (trimmed.includes("@")) return `sip:${trimmed}`;
228
+
229
+ const domain = normalizeSipUri(aor).split("@")[1];
230
+ if (!domain) throw new Error("Enter a full SIP address for the destination.");
231
+ return `sip:${trimmed}@${domain}`;
232
+ }
233
+
234
+ function getErrorMessage(error: unknown) {
235
+ return error instanceof Error ? error.message : "SIP connection failed.";
236
+ }
@@ -11,6 +11,10 @@ interface USBDevice {
11
11
  close(): Promise<void>;
12
12
  selectConfiguration(configurationValue: number): Promise<void>;
13
13
  claimInterface(interfaceNumber: number): Promise<void>;
14
+ selectAlternateInterface(
15
+ interfaceNumber: number,
16
+ alternateSetting: number,
17
+ ): Promise<void>;
14
18
  releaseInterface(interfaceNumber: number): Promise<void>;
15
19
  transferOut(
16
20
  endpointNumber: number,
@@ -19,14 +23,27 @@ interface USBDevice {
19
23
  configuration: {
20
24
  interfaces: Array<{
21
25
  interfaceNumber: number;
26
+ alternate: USBAlternateInterface;
22
27
  alternates: Array<{
28
+ alternateSetting: number;
29
+ interfaceClass: number;
23
30
  endpoints: Array<{
24
31
  endpointNumber: number;
25
32
  direction: "in" | "out";
26
33
  }>;
27
34
  }>;
28
35
  }>;
29
- };
36
+ } | null;
37
+ configurations: Array<{ configurationValue: number }>;
38
+ }
39
+
40
+ interface USBAlternateInterface {
41
+ alternateSetting: number;
42
+ interfaceClass: number;
43
+ endpoints: Array<{
44
+ endpointNumber: number;
45
+ direction: "in" | "out";
46
+ }>;
30
47
  }
31
48
 
32
49
  interface USBOutTransferResult {
@@ -39,13 +56,86 @@ declare global {
39
56
  usb: {
40
57
  getDevices(): Promise<USBDevice[]>;
41
58
  requestDevice(options: {
42
- filters: Array<{ vendorId?: number; productId?: number }>;
59
+ filters: Array<{
60
+ vendorId?: number;
61
+ productId?: number;
62
+ classCode?: number;
63
+ }>;
43
64
  }): Promise<USBDevice>;
44
65
  };
45
66
  }
46
67
  }
47
68
 
48
69
  export default function useWebUsb() {
70
+ const printerConnections = new WeakMap<
71
+ USBDevice,
72
+ { interfaceNumber: number; endpointNumber: number }
73
+ >();
74
+
75
+ const protectedInterfaceClasses = new Set([0x01, 0x03, 0x08, 0x0b, 0x0e, 0x10, 0xe0]);
76
+
77
+ function getErrorMessage(error: unknown, fallback: string) {
78
+ return error instanceof Error && error.message ? error.message : fallback;
79
+ }
80
+
81
+ function findPrinterInterface(device: USBDevice) {
82
+ const candidates = (device.configuration?.interfaces || []).flatMap((usbInterface) =>
83
+ usbInterface.alternates.flatMap((alternate) => {
84
+ const outputEndpoint = alternate.endpoints.find((endpoint) => endpoint.direction === "out");
85
+ if (!outputEndpoint || protectedInterfaceClasses.has(alternate.interfaceClass)) return [];
86
+
87
+ return [{
88
+ interfaceNumber: usbInterface.interfaceNumber,
89
+ alternateSetting: alternate.alternateSetting,
90
+ activeAlternateSetting: usbInterface.alternate.alternateSetting,
91
+ interfaceClass: alternate.interfaceClass,
92
+ endpointNumber: outputEndpoint.endpointNumber,
93
+ }];
94
+ }),
95
+ );
96
+
97
+ candidates.sort((left, right) => {
98
+ const priority = (interfaceClass: number) => interfaceClass === 0x07 ? 0 : interfaceClass === 0xff ? 1 : 2;
99
+ return priority(left.interfaceClass) - priority(right.interfaceClass);
100
+ });
101
+
102
+ return candidates[0] || null;
103
+ }
104
+
105
+ async function preparePrinterDevice(device: USBDevice) {
106
+ await device.open();
107
+ if (!device.configuration) {
108
+ await device.selectConfiguration(device.configurations[0]?.configurationValue || 1);
109
+ }
110
+
111
+ const printerInterface = findPrinterInterface(device);
112
+ if (!printerInterface) {
113
+ throw new Error(
114
+ "The selected USB device is not a compatible printer. Select the receipt printer instead of a Bluetooth, camera, keyboard, or other system device.",
115
+ );
116
+ }
117
+
118
+ await device.claimInterface(printerInterface.interfaceNumber);
119
+ if (printerInterface.alternateSetting !== printerInterface.activeAlternateSetting) {
120
+ await device.selectAlternateInterface(
121
+ printerInterface.interfaceNumber,
122
+ printerInterface.alternateSetting,
123
+ );
124
+ }
125
+
126
+ printerConnections.set(device, {
127
+ interfaceNumber: printerInterface.interfaceNumber,
128
+ endpointNumber: printerInterface.endpointNumber,
129
+ });
130
+ return device;
131
+ }
132
+
133
+ function getPrinterConnection(device: USBDevice) {
134
+ const connection = printerConnections.get(device);
135
+ if (!connection) throw new Error("Printer interface is not connected");
136
+ return connection;
137
+ }
138
+
49
139
  const isWebUsbSupported = computed(() => {
50
140
  return typeof navigator !== "undefined" && "usb" in navigator;
51
141
  });
@@ -75,7 +165,9 @@ export default function useWebUsb() {
75
165
  throw new Error("Web USB is not supported in this browser");
76
166
  }
77
167
  try {
78
- const device = await navigator.usb.requestDevice({ filters: [] });
168
+ const device = await navigator.usb.requestDevice({
169
+ filters: [{ classCode: 0x07 }, { classCode: 0xff }],
170
+ });
79
171
  return {
80
172
  vendorId: device.vendorId,
81
173
  productId: device.productId,
@@ -84,12 +176,12 @@ export default function useWebUsb() {
84
176
  serialNumber: device.serialNumber || "Unknown",
85
177
  deviceId: `${device.vendorId}:${device.productId}`,
86
178
  };
87
- } catch (error: any) {
88
- if (error.name === "NotFoundError") {
179
+ } catch (error: unknown) {
180
+ if (error instanceof DOMException && error.name === "NotFoundError") {
89
181
  throw new Error("No device selected");
90
182
  }
91
183
  console.error("Error requesting USB device:", error);
92
- throw new Error("Failed to access USB device");
184
+ throw new Error(getErrorMessage(error, "Failed to access USB printer"));
93
185
  }
94
186
  };
95
187
 
@@ -110,14 +202,17 @@ export default function useWebUsb() {
110
202
  });
111
203
  }
112
204
  console.log("[WebUSB] Opening device:", device.vendorId, device.productId);
113
- await device.open();
114
- await device.selectConfiguration(1);
115
- await device.claimInterface(0);
205
+ await preparePrinterDevice(device);
116
206
  console.log("[WebUSB] Device opened and interface claimed.");
117
207
  return device;
118
- } catch (error) {
208
+ } catch (error: unknown) {
119
209
  console.error("Error connecting to USB device:", error);
120
- throw new Error("Failed to connect to USB device");
210
+ try {
211
+ await device?.close();
212
+ } catch (closeError) {
213
+ console.error("Error closing rejected USB device:", closeError);
214
+ }
215
+ throw new Error(getErrorMessage(error, "Failed to connect to USB printer"));
121
216
  }
122
217
  };
123
218
 
@@ -173,15 +268,20 @@ export default function useWebUsb() {
173
268
  ? "Connection and print test successful"
174
269
  : "Connection successful but print test failed",
175
270
  };
176
- } catch (error: any) {
271
+ } catch (error: unknown) {
177
272
  return {
178
273
  success: false,
179
- error: error.message,
274
+ error: getErrorMessage(error, "Failed to connect to USB printer"),
180
275
  message: "Connection failed",
181
276
  };
182
277
  } finally {
183
278
  if (device) {
184
279
  try {
280
+ const connection = printerConnections.get(device);
281
+ if (connection) {
282
+ await device.releaseInterface(connection.interfaceNumber);
283
+ printerConnections.delete(device);
284
+ }
185
285
  await device.close();
186
286
  } catch (closeError) {
187
287
  console.error("Error closing device:", closeError);
@@ -205,21 +305,16 @@ export default function useWebUsb() {
205
305
  ];
206
306
 
207
307
  const testData = new Uint8Array(testCommands);
208
- const usbInterface = device.configuration.interfaces[0];
209
- const alternate = usbInterface.alternates[0];
210
- const outputEndpoint = alternate.endpoints.find(
211
- (ep: any) => ep.direction === "out",
212
- );
213
-
214
- if (!outputEndpoint) {
215
- throw new Error("No output endpoint found");
216
- }
217
-
218
- await device.transferOut(outputEndpoint.endpointNumber, testData);
308
+ const { endpointNumber } = getPrinterConnection(device);
309
+ await device.transferOut(endpointNumber, testData);
219
310
  return { success: true, message: "Test print sent successfully" };
220
- } catch (error: any) {
311
+ } catch (error: unknown) {
221
312
  console.error("Test print error:", error);
222
- return { success: false, error: error.message, message: "Test print failed" };
313
+ return {
314
+ success: false,
315
+ error: getErrorMessage(error, "Test print failed"),
316
+ message: "Test print failed",
317
+ };
223
318
  }
224
319
  };
225
320
 
@@ -354,13 +449,8 @@ export default function useWebUsb() {
354
449
  ) => {
355
450
  try {
356
451
  console.log("[WebUSB] printQrCode start — doorLevel:", doorLevel, "liftLevel:", liftLevel, "company:", companyName, "address:", address);
357
- const usbInterface = device.configuration.interfaces[0];
358
- const alternate = usbInterface.alternates[0];
359
- const outputEndpoint = alternate.endpoints.find(
360
- (ep: any) => ep.direction === "out",
361
- );
362
- if (!outputEndpoint) throw new Error("No output endpoint found");
363
- console.log("[WebUSB] Using endpoint:", outputEndpoint.endpointNumber);
452
+ const { endpointNumber } = getPrinterConnection(device);
453
+ console.log("[WebUSB] Using endpoint:", endpointNumber);
364
454
 
365
455
  const canvas = await createReceiptLayout(
366
456
  urlImage,
@@ -375,17 +465,17 @@ export default function useWebUsb() {
375
465
  const rasterData = canvasToRaster(canvas);
376
466
  console.log("[WebUSB] Raster data size:", rasterData.byteLength, "bytes");
377
467
 
378
- await device.transferOut(outputEndpoint.endpointNumber, rasterData.buffer);
468
+ await device.transferOut(endpointNumber, rasterData.buffer);
379
469
  console.log("[WebUSB] Raster data sent.");
380
470
 
381
471
  const CUT = new Uint8Array([0x1d, 0x56, 0x41, 0x10]);
382
- await device.transferOut(outputEndpoint.endpointNumber, CUT.buffer);
472
+ await device.transferOut(endpointNumber, CUT.buffer);
383
473
  console.log("[WebUSB] Cut command sent.");
384
474
 
385
475
  return { success: true, message: "QR print sent successfully" };
386
- } catch (error: any) {
476
+ } catch (error: unknown) {
387
477
  console.error("[WebUSB] printQrCode error:", error);
388
- return { success: false, error: error.message };
478
+ return { success: false, error: getErrorMessage(error, "Unable to print QR code") };
389
479
  }
390
480
  };
391
481
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.1.4-staging.49",
5
+ "version": "3.1.4-staging.51",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -40,6 +40,7 @@
40
40
  "qrcode": "^1.5.4",
41
41
  "qrcode.vue": "^3.4.1",
42
42
  "sass": "^1.80.6",
43
+ "sip.js": "0.21.2",
43
44
  "socket.io-client": "^4.8.3",
44
45
  "vue-draggable-next": "^2.3.0",
45
46
  "vue3-signature": "^0.2.4",
@@ -12,75 +12,40 @@ export default defineNuxtPlugin(() => {
12
12
 
13
13
  const { userAppRole, id, orgNature } = useLocalSetup();
14
14
 
15
- router.afterEach(async (to) => {
15
+ router.beforeEach(async (to) => {
16
16
  const isMember = to.meta?.memberOnly;
17
17
 
18
18
  if (!isMember) return;
19
19
 
20
20
  const APP = useRuntimeConfig().public.APP;
21
- const org = computed(
22
- () =>
23
- (to.params.org as string) || (to.params.organization as string) || ""
24
- );
21
+ const org =
22
+ (to.params.org as string) || (to.params.organization as string) || "";
25
23
 
26
- console.log('[secure-member-plugin', 'org', org.value)
27
-
28
- if (!hexSchema.safeParse(org.value).success) {
29
- return router.replace({ name: "require-organization-membership" });
24
+ if (!hexSchema.safeParse(org).success) {
25
+ return { name: "require-organization-membership" };
30
26
  }
31
27
 
32
- const userId = computed(() => useCookie("user").value ?? "");
33
-
34
- const { data: userMemberData, error: userMemberError } =
35
- await useLazyAsyncData(
36
- "plugin-get-member-by-id-" + userId.value + "-" + APP + "-" + org.value,
37
- () => getByUserType(userId.value, APP, org.value),
38
- { watch: [userId] }
39
- );
40
-
41
- watchEffect(() => {
42
- if (userMemberError.value) {
43
- console.log('running-secure-member-redirect-plugin')
44
- navigateTo(
45
- {
46
- name: "index",
47
- },
48
- { replace: true }
49
- );
50
- }
51
- });
52
-
53
- const roleId = ref("roleId");
28
+ const userId = useCookie("user").value ?? "";
29
+ if (!userId) return { name: "index" };
54
30
 
55
- watchEffect(() => {
56
- if (userMemberData.value) {
57
- id.value = userMemberData.value.org ?? "";
58
- roleId.value = userMemberData.value.role ?? "roleId";
59
- }
60
- });
31
+ try {
32
+ const userMemberData = await getByUserType(userId, APP, org);
33
+ id.value = userMemberData.org ?? "";
61
34
 
62
- const { data: getOrgByIdReq } = await useLazyAsyncData(
63
- "plugin-get-org-by-id-" + org.value,
64
- () => getById(org.value),
65
- { watch: [org] }
66
- );
35
+ const [orgResult, roleResult] = await Promise.allSettled([
36
+ getById(org),
37
+ userMemberData.role ? getRoleById(userMemberData.role) : null,
38
+ ]);
67
39
 
68
- watchEffect(() => {
69
- if (getOrgByIdReq.value) {
70
- orgNature.value = getOrgByIdReq.value.nature ?? "";
40
+ if (orgResult.status === "fulfilled" && orgResult.value) {
41
+ orgNature.value = orgResult.value.nature ?? "";
71
42
  }
72
- });
73
43
 
74
- const { data: getRoleByIdReq } = await useLazyAsyncData(
75
- "plugin-get-role-by-id-" + roleId.value,
76
- () => getRoleById(roleId.value),
77
- { watch: [roleId] }
78
- );
79
-
80
- watchEffect(() => {
81
- if (getRoleByIdReq.value) {
82
- userAppRole.value = getRoleByIdReq.value;
44
+ if (roleResult.status === "fulfilled" && roleResult.value) {
45
+ userAppRole.value = roleResult.value;
83
46
  }
84
- });
47
+ } catch (error) {
48
+ return { name: "index" };
49
+ }
85
50
  });
86
51
  });
package/types/site.d.ts CHANGED
@@ -1,5 +1,70 @@
1
1
  declare type TSiteCreate = Pick<TSite, "name" | "description" | "orgId">;
2
2
 
3
+ declare type THidQrCodeFormat = "0" | "1" | "2";
4
+
5
+ declare type THidPermissionCategory =
6
+ | "resident"
7
+ | "property_management"
8
+ | "service_provider";
9
+
10
+ declare type THidPermissionAssignment = {
11
+ subjectId: string;
12
+ category: THidPermissionCategory;
13
+ name: string;
14
+ subtitle?: string;
15
+ intercom: boolean;
16
+ };
17
+
18
+ declare type THidSitePermissions = {
19
+ site: string;
20
+ assignments: THidPermissionAssignment[];
21
+ counts: {
22
+ resident: number;
23
+ propertyManagement: number;
24
+ serviceProvider: number;
25
+ intercom: number;
26
+ };
27
+ };
28
+
29
+ declare type THidPermissionCandidate = THidPermissionAssignment & {
30
+ selected: boolean;
31
+ };
32
+
33
+ declare type THidQrCodePassConfig = {
34
+ enabled: boolean;
35
+ onlineMode?: boolean;
36
+ readerId: string;
37
+ qrFormat: THidQrCodeFormat;
38
+ identificationMethods?: {
39
+ facial: boolean;
40
+ card: boolean;
41
+ qrCode: boolean;
42
+ idPassword: boolean;
43
+ pin: boolean;
44
+ bluetooth: boolean;
45
+ };
46
+ printer: {
47
+ vendorId: string;
48
+ productId: string;
49
+ };
50
+ template: {
51
+ header: string;
52
+ subtext: string;
53
+ };
54
+ validityMinutes: number | null;
55
+ updatedAt?: string;
56
+ };
57
+
58
+ declare type TSiteMetadata = {
59
+ block?: number;
60
+ guardPosts?: number;
61
+ gracePeriod?: number;
62
+ incidentCounter?: number;
63
+ incidentLogo?: string;
64
+ services?: Record<string, unknown>[];
65
+ hidQrCodePass?: THidQrCodePassConfig;
66
+ };
67
+
3
68
 
4
69
  declare type TSite = {
5
70
  _id?: string;