@7365admin1/layer-common 4.147.2 → 4.148.1

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,42 @@
1
+ /**
2
+ * What the screen says after a visitor is added, registered or checked in
3
+ * (owner, 2026-10-23): the action, then whether the ANPR barrier opened -
4
+ * "Opening Barrier Failed." in red. Mirrors isecure365's `visitor-barrier.ts`.
5
+ * The server reports `barrier: "opened" | "failed"`, absent when no barrier
6
+ * was asked to open, and `barrierCamera`: the camera's type and gate, "Entry
7
+ * Gate 1" - absent from older servers and for a camera with no gate saved.
8
+ */
9
+ export type TBarrierOutcome = "opened" | "failed";
10
+
11
+ export const VISITOR_ACTION_SUCCESS = {
12
+ add: "Add Visitor Success.",
13
+ register: "Register Visitor Success.",
14
+ checkIn: "Check In Visitor Success.",
15
+ } as const;
16
+
17
+ export const BARRIER_MESSAGES: Record<TBarrierOutcome, string> = {
18
+ opened: "Opening Barrier Success.",
19
+ failed: "Opening Barrier Failed.",
20
+ };
21
+
22
+ /**
23
+ * The barrier line: "Opening Barrier Success at Entry Gate 1." - or the plain
24
+ * "Opening Barrier Success." when the camera is not named.
25
+ */
26
+ export function barrierMessage(outcome: TBarrierOutcome, camera?: string): string {
27
+ const plain = BARRIER_MESSAGES[outcome];
28
+ const name = String(camera ?? "").trim();
29
+ return name ? `${plain.replace(/\.$/, "")} at ${name}.` : plain;
30
+ }
31
+
32
+ /** The camera named in a server reply ("Entry Gate 1"), if it has one. */
33
+ export function barrierCameraOf(data: unknown): string | undefined {
34
+ const value = (data as { barrierCamera?: unknown } | null | undefined)?.barrierCamera;
35
+ return typeof value === "string" && value.trim() ? value.trim() : undefined;
36
+ }
37
+
38
+ /** The barrier outcome in a server reply, if it has one. */
39
+ export function barrierOf(data: unknown): TBarrierOutcome | undefined {
40
+ const value = (data as { barrier?: unknown } | null | undefined)?.barrier;
41
+ return value === "opened" || value === "failed" ? value : undefined;
42
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * The ANPR camera a visit is checked at for a blocked plate and whose barrier
3
+ * opens for it (owner, 2026-10-23). Mirrors isecure365's
4
+ * `visitor-camera.helpers.ts`.
5
+ *
6
+ * Only an ACTIVE camera of type Entry, Both Entry and Exit or Visitors can do
7
+ * that. When a site has more than one, the user may pick a default from the
8
+ * visitor list (a camera icon beside the pagination); it is saved per site on
9
+ * their user as `defaultCameraGate: [{ site, cameraGateNumber }]` and sent as
10
+ * `anprGate` with add, register and guest check-in. None picked, or a gate no
11
+ * longer there: the server uses the site's first camera.
12
+ */
13
+ export const VISITOR_CAMERA_DIRECTIONS = ["entry", "both", "visitors"];
14
+
15
+ const DIRECTION_LABELS: Record<string, string> = {
16
+ entry: "Entry",
17
+ both: "Both Entry and Exit",
18
+ visitors: "Visitors",
19
+ };
20
+
21
+ export type TVisitorCameraChoice = { value: number; title: string };
22
+
23
+ type TCameraLike = { _id?: unknown; direction?: unknown; host?: unknown; gate?: unknown; status?: unknown };
24
+
25
+ /** The port in a camera's address; the protocol's default when none is written. */
26
+ export function cameraPort(host: unknown): string {
27
+ const text = String(host ?? "").trim();
28
+ if (!text) return "";
29
+ const withoutScheme = text.replace(/^[a-z][a-z0-9+.-]*:\/\//i, "");
30
+ const hostPart = withoutScheme.split(/[/?#]/)[0] ?? "";
31
+ const port = hostPart.match(/:(\d+)$/)?.[1];
32
+ if (port) return port;
33
+ return /^https:\/\//i.test(text) ? "443" : "80";
34
+ }
35
+
36
+ function gateOf(camera: { gate?: unknown } | null | undefined): number | null {
37
+ const gate = Number(camera?.gate);
38
+ return Number.isInteger(gate) && gate >= 1 ? gate : null;
39
+ }
40
+
41
+ /** "Entry Gate 1"; a camera with no gate saved yet: "Entry:8001". */
42
+ export function visitorCameraLabel(camera: TCameraLike): string {
43
+ const direction = String(camera?.direction ?? "").toLowerCase();
44
+ const type = DIRECTION_LABELS[direction] ?? direction;
45
+ const gate = gateOf(camera);
46
+ if (gate) return `${type} Gate ${gate}`;
47
+ const port = cameraPort(camera?.host);
48
+ return port ? `${type}:${port}` : type;
49
+ }
50
+
51
+ /** The site's active Entry, Both and Visitors ANPR cameras. */
52
+ export function visitorCameras(cameras: unknown): TCameraLike[] {
53
+ const list = Array.isArray(cameras) ? (cameras as TCameraLike[]) : [];
54
+ return list.filter(
55
+ (camera) =>
56
+ Boolean(camera?._id) &&
57
+ String(camera?.status ?? "active") === "active" &&
58
+ VISITOR_CAMERA_DIRECTIONS.includes(String(camera?.direction ?? "").toLowerCase()),
59
+ );
60
+ }
61
+
62
+ /** The gates that can be picked as the default ("Entry Gate 1"); a camera with no gate cannot. */
63
+ export function visitorGateChoices(cameras: unknown): TVisitorCameraChoice[] {
64
+ return visitorCameras(cameras)
65
+ .filter((camera) => gateOf(camera) !== null)
66
+ .map((camera) => ({ value: gateOf(camera) as number, title: visitorCameraLabel(camera) }))
67
+ .sort((a, b) => a.value - b.value);
68
+ }
69
+
70
+ /** The user's default gate at this site, or undefined. */
71
+ export function defaultGateForSite(saved: unknown, siteId: unknown): number | undefined {
72
+ if (!siteId || !Array.isArray(saved)) return undefined;
73
+ const entry = (saved as Array<{ site?: unknown; cameraGateNumber?: unknown }>).find(
74
+ (item) => String(item?.site ?? "") === String(siteId),
75
+ );
76
+ const gate = Number(entry?.cameraGateNumber);
77
+ return Number.isInteger(gate) && gate >= 1 ? gate : undefined;
78
+ }
79
+
80
+ export const VISITOR_CAMERA_LABEL = "Default ANPR Camera";