@7365admin1/layer-common 4.35.1-staging.385 → 4.37.0
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 +16 -4
- package/assets/css/primitives.css +0 -43
- package/components/AccessCardAddForm.vue +4 -5
- package/components/AccessCardAssignToUnitForm.vue +2 -3
- package/components/AccessCardPreviewDialog.vue +3 -4
- package/components/AppSelect.vue +3 -77
- package/components/CameraForm.vue +6 -20
- package/components/DashboardMain.vue +60 -66
- package/components/Dialog/UpdateMoreAction.vue +1 -11
- package/components/DocumentForm.vue +112 -407
- package/components/DocumentManagement.vue +12 -213
- package/components/EntryPass/QrTemplatePreview.vue +25 -21
- package/components/EntryPassInformation.vue +24 -119
- package/components/Facility/BookingSetup.vue +0 -63
- package/components/HidAccessLogDashboard.vue +17 -74
- package/components/HidIntercomManagement.vue +14 -180
- package/components/HidQrCodeConfiguration.vue +195 -17
- package/components/HidReaderManagement.vue +0 -398
- package/components/HidReaderUserRoster.vue +3 -44
- package/components/HidUserEnrollment.vue +155 -845
- package/components/InvitationClientForm.vue +1 -19
- package/components/Layout/NavigationDrawer.vue +1 -4
- package/components/NotificationBell.vue +7 -59
- package/components/RolePermissionFormCreate.vue +3 -4
- package/components/RolePermissionFormPreviewUpdate.vue +5 -12
- package/components/RolePermissionMain.vue +0 -1
- package/components/ServiceProviderInvitationPrompt.vue +155 -0
- package/components/ServiceProviderMain.vue +30 -178
- package/components/VisitorForm.vue +0 -12
- package/components/VisitorManagement.vue +10 -285
- package/components/VisitorSocketPopUp.vue +1 -2
- package/composables/useANPRSettings.ts +15 -45
- package/composables/useAccessManagement.ts +0 -5
- package/composables/useCleaningPermission.ts +0 -2
- package/composables/useCommonPermission.ts +0 -32
- package/composables/useDocument.ts +0 -7
- package/composables/useFacility.ts +0 -11
- package/composables/useHidAmico.ts +0 -159
- package/composables/useHidNavigation.ts +1 -25
- package/composables/usePeople.ts +0 -19
- package/composables/usePropertyPermission.ts +0 -19
- package/composables/useSecurityPermission.ts +0 -19
- package/composables/useServiceProvider.ts +0 -3
- package/composables/useServiceProviderInvitation.ts +3 -101
- package/composables/useSiteEntryPassSettings.ts +0 -26
- package/composables/useWebUsb.ts +11 -14
- package/package.json +2 -2
- package/pages/[org]/[site]/access-mgmt/administrator/index.vue +1 -1
- package/pages/[org]/[site]/access-mgmt/hid-cards/index.vue +1 -1
- package/pages/[org]/[site]/access-mgmt/hid-readers/index.vue +1 -1
- package/plugins/API.ts +0 -11
- package/types/document.d.ts +0 -14
- package/types/facility.d.ts +0 -4
- package/types/people.d.ts +1 -28
- package/utils/module-routes.ts +1 -8
- package/utils/nav-sections.ts +2 -8
- package/utils/notification-target.ts +8 -277
- package/utils/permission-labels.ts +2 -20
- package/components/ClientInvitations.vue +0 -291
- package/components/HidAccessPermissions.vue +0 -424
- package/components/HidProfileQrCode.vue +0 -332
- package/composables/useHidReaderSelection.ts +0 -50
- package/composables/useSiteCategory.ts +0 -45
- package/pages/[org]/[site]/access-mgmt/hid-qr-code/index.vue +0 -23
- package/utils/dashboard-widget-prefs.ts +0 -54
- package/utils/hid-permission-assignments.ts +0 -190
- package/utils/hid-reader-selection.ts +0 -52
- package/utils/occupancy-role.ts +0 -109
- package/utils/sp-inviter-row.ts +0 -97
|
@@ -28,22 +28,6 @@ export type HidReaderPayload = {
|
|
|
28
28
|
lastCallbackType?: string;
|
|
29
29
|
};
|
|
30
30
|
|
|
31
|
-
export type HidProfileQr = {
|
|
32
|
-
readerId: string;
|
|
33
|
-
userId: string;
|
|
34
|
-
memberId: string;
|
|
35
|
-
hidUserId: string;
|
|
36
|
-
registration: string;
|
|
37
|
-
readerName: string;
|
|
38
|
-
portalName: string;
|
|
39
|
-
qrEnabled: boolean;
|
|
40
|
-
issued: boolean;
|
|
41
|
-
qrValue: string;
|
|
42
|
-
issuedAt: string;
|
|
43
|
-
/** null when the permission lookup itself failed, so "unknown" stays distinct from "no". */
|
|
44
|
-
assigned: boolean | null;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
31
|
type HidIdentityPayload = {
|
|
48
32
|
site?: string;
|
|
49
33
|
hidUserId?: string | number;
|
|
@@ -155,28 +139,6 @@ export type HidSipAccountData = {
|
|
|
155
139
|
provisionedAt: string;
|
|
156
140
|
};
|
|
157
141
|
|
|
158
|
-
/** One row of the site's SIP account list - who owns the extension, never its password. */
|
|
159
|
-
export type HidSipAccountListItem = {
|
|
160
|
-
_id: string;
|
|
161
|
-
userId: string;
|
|
162
|
-
name: string;
|
|
163
|
-
email: string;
|
|
164
|
-
/**
|
|
165
|
-
* What the owner is called after their name on the contact list: their
|
|
166
|
-
* "Blk 1/Level 1/Unit 1" when they live here, and their membership type -
|
|
167
|
-
* "Security Agency" - when they do not. "" only when the site knows neither.
|
|
168
|
-
*
|
|
169
|
-
* Optional because an API still on the `@7365admin1/core` release that
|
|
170
|
-
* predates it answers without the field; the contact picker falls back to the
|
|
171
|
-
* bare name rather than showing a dangling separator.
|
|
172
|
-
*/
|
|
173
|
-
unitLabel?: string;
|
|
174
|
-
extension: string;
|
|
175
|
-
username: string;
|
|
176
|
-
status: string;
|
|
177
|
-
provisionedAt: string;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
142
|
/**
|
|
181
143
|
* True only for the specific validation error an API that predates core #1878
|
|
182
144
|
* returns when the discover body carries `site`. Deliberately narrow: after
|
|
@@ -204,36 +166,6 @@ export default function useHidAmico() {
|
|
|
204
166
|
});
|
|
205
167
|
}
|
|
206
168
|
|
|
207
|
-
/**
|
|
208
|
-
* The signed-in member's own QR code on one reader.
|
|
209
|
-
*
|
|
210
|
-
* No user id in the path: the server takes the caller from the session, so
|
|
211
|
-
* these cannot be pointed at somebody else. `assigned` in the response says
|
|
212
|
-
* whether the HID permissions screen actually admits this member to the
|
|
213
|
-
* reader - issuing a code does not grant access, and the screen has to be
|
|
214
|
-
* able to say so.
|
|
215
|
-
*/
|
|
216
|
-
function getProfileQr(readerId: string) {
|
|
217
|
-
return useNuxtApp().$api<{ data: HidProfileQr }>(
|
|
218
|
-
`${basePath}/readers/${readerId}/profile-qr`,
|
|
219
|
-
{ method: "GET" },
|
|
220
|
-
);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
function issueProfileQr(readerId: string) {
|
|
224
|
-
return useNuxtApp().$api<{ data: HidProfileQr }>(
|
|
225
|
-
`${basePath}/readers/${readerId}/profile-qr`,
|
|
226
|
-
{ method: "POST" },
|
|
227
|
-
);
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
function deleteProfileQr(readerId: string) {
|
|
231
|
-
return useNuxtApp().$api<{ data: HidProfileQr }>(
|
|
232
|
-
`${basePath}/readers/${readerId}/profile-qr`,
|
|
233
|
-
{ method: "DELETE" },
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
169
|
function createReader(payload: HidReaderPayload) {
|
|
238
170
|
return useNuxtApp().$api<HidApiRecord>(`${basePath}/readers`, {
|
|
239
171
|
method: "POST",
|
|
@@ -325,36 +257,6 @@ export default function useHidAmico() {
|
|
|
325
257
|
});
|
|
326
258
|
}
|
|
327
259
|
|
|
328
|
-
/**
|
|
329
|
-
* Which credentials a reader accepts at the door.
|
|
330
|
-
*
|
|
331
|
-
* Separate from `setReaderConfiguration` on purpose. That one is the generic
|
|
332
|
-
* configuration endpoint, gated by an allowlist of writable objects; this is
|
|
333
|
-
* a dedicated route that validates the four methods against the reader's own
|
|
334
|
-
* `capabilities` and confirms the write by reading the device back, so a
|
|
335
|
-
* refusal here names the credential the hardware would not take.
|
|
336
|
-
*/
|
|
337
|
-
function setReaderIdentificationMethods(readerId: string, payload: HidApiRecord) {
|
|
338
|
-
return useNuxtApp().$api<HidApiRecord>(
|
|
339
|
-
`${basePath}/readers/${readerId}/identification-methods`,
|
|
340
|
-
{ method: "PUT", body: payload },
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/**
|
|
345
|
-
* Put a reader back to the settings iService holds for it.
|
|
346
|
-
*
|
|
347
|
-
* A POST rather than a GET because it WRITES: reading the reader's
|
|
348
|
-
* configuration is only the first half, and anything that drifted is
|
|
349
|
-
* re-applied before this returns.
|
|
350
|
-
*/
|
|
351
|
-
function reconcileReaderIdentificationMethods(readerId: string) {
|
|
352
|
-
return useNuxtApp().$api<HidApiRecord>(
|
|
353
|
-
`${basePath}/readers/${readerId}/identification-methods/reconcile`,
|
|
354
|
-
{ method: "POST" },
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
|
|
358
260
|
function getIdentities(readerId: string, query: HidApiRecord = {}) {
|
|
359
261
|
return useNuxtApp().$api<HidApiRecord>(`${basePath}/readers/${readerId}/identities`, {
|
|
360
262
|
method: "GET",
|
|
@@ -512,27 +414,6 @@ export default function useHidAmico() {
|
|
|
512
414
|
);
|
|
513
415
|
}
|
|
514
416
|
|
|
515
|
-
function getUserPasswordStatus(readerId: string, hidUserId: string | number) {
|
|
516
|
-
return useNuxtApp().$api<HidApiRecord>(
|
|
517
|
-
`${basePath}/readers/${readerId}/users/${hidUserId}/password`,
|
|
518
|
-
{ method: "GET" },
|
|
519
|
-
);
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function setUserPassword(readerId: string, hidUserId: string | number, password: string) {
|
|
523
|
-
return useNuxtApp().$api<HidApiRecord>(
|
|
524
|
-
`${basePath}/readers/${readerId}/users/${hidUserId}/password`,
|
|
525
|
-
{ method: "PUT", body: { password } },
|
|
526
|
-
);
|
|
527
|
-
}
|
|
528
|
-
|
|
529
|
-
function deleteUserPassword(readerId: string, hidUserId: string | number) {
|
|
530
|
-
return useNuxtApp().$api<HidApiRecord>(
|
|
531
|
-
`${basePath}/readers/${readerId}/users/${hidUserId}/password`,
|
|
532
|
-
{ method: "DELETE" },
|
|
533
|
-
);
|
|
534
|
-
}
|
|
535
|
-
|
|
536
417
|
function deleteUserPin(readerId: string, hidUserId: string | number) {
|
|
537
418
|
return useNuxtApp().$api<{ data: { pinEnrolled: boolean } }>(
|
|
538
419
|
`${basePath}/readers/${readerId}/users/${hidUserId}/pin`,
|
|
@@ -593,36 +474,6 @@ export default function useHidAmico() {
|
|
|
593
474
|
);
|
|
594
475
|
}
|
|
595
476
|
|
|
596
|
-
function getSipAccounts(siteId: string, orgId: string) {
|
|
597
|
-
return useNuxtApp().$api<{ data: HidSipAccountListItem[] }>(
|
|
598
|
-
`${basePath}/sites/${siteId}/intercom/sip-accounts`,
|
|
599
|
-
{ method: "GET", query: { orgId } },
|
|
600
|
-
);
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/**
|
|
604
|
-
* Wake the resident's phone for a call the web phone has just placed.
|
|
605
|
-
*
|
|
606
|
-
* The SIP INVITE is the call and travels over the WebSocket, not through the
|
|
607
|
-
* API; this only rings the resident's mobile so somebody is there to answer
|
|
608
|
-
* it. It is therefore fired after the INVITE and never awaited in a way that
|
|
609
|
-
* could fail the call — see `startWebCall` in `HidIntercomManagement.vue`.
|
|
610
|
-
*
|
|
611
|
-
* `notified: false` with a reason is a normal response, not an error: staff
|
|
612
|
-
* extensions and external SIP addresses have no resident behind them.
|
|
613
|
-
*/
|
|
614
|
-
function notifyIntercomCall(
|
|
615
|
-
siteId: string,
|
|
616
|
-
payload: { orgId: string; destination: string; callId: string; readerId?: string },
|
|
617
|
-
) {
|
|
618
|
-
return useNuxtApp().$api<{
|
|
619
|
-
data: { notified: boolean; delivered?: number; reason?: string };
|
|
620
|
-
}>(
|
|
621
|
-
`${basePath}/sites/${siteId}/intercom/notify-call`,
|
|
622
|
-
{ method: "POST", body: payload },
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
|
|
626
477
|
function getPermissionCandidates(
|
|
627
478
|
siteId: string,
|
|
628
479
|
query: {
|
|
@@ -654,9 +505,6 @@ export default function useHidAmico() {
|
|
|
654
505
|
|
|
655
506
|
return {
|
|
656
507
|
getReaders,
|
|
657
|
-
getProfileQr,
|
|
658
|
-
issueProfileQr,
|
|
659
|
-
deleteProfileQr,
|
|
660
508
|
discoverReader,
|
|
661
509
|
createReader,
|
|
662
510
|
updateReader,
|
|
@@ -671,8 +519,6 @@ export default function useHidAmico() {
|
|
|
671
519
|
issueVisitorQr,
|
|
672
520
|
getReaderConfiguration,
|
|
673
521
|
setReaderConfiguration,
|
|
674
|
-
setReaderIdentificationMethods,
|
|
675
|
-
reconcileReaderIdentificationMethods,
|
|
676
522
|
getLogs,
|
|
677
523
|
setUserImage,
|
|
678
524
|
deleteUserImage,
|
|
@@ -684,9 +530,6 @@ export default function useHidAmico() {
|
|
|
684
530
|
getUserPinStatus,
|
|
685
531
|
setUserPin,
|
|
686
532
|
deleteUserPin,
|
|
687
|
-
getUserPasswordStatus,
|
|
688
|
-
setUserPassword,
|
|
689
|
-
deleteUserPassword,
|
|
690
533
|
getIdentities,
|
|
691
534
|
getReaderUsers,
|
|
692
535
|
getReaderAccessLogs,
|
|
@@ -697,8 +540,6 @@ export default function useHidAmico() {
|
|
|
697
540
|
makeIntercomCall,
|
|
698
541
|
finalizeIntercomCall,
|
|
699
542
|
ensureSipAccount,
|
|
700
|
-
getSipAccounts,
|
|
701
|
-
notifyIntercomCall,
|
|
702
543
|
getSitePermissions,
|
|
703
544
|
getPermissionCandidates,
|
|
704
545
|
updateSitePermissions,
|
|
@@ -46,33 +46,9 @@ export default function useHidNavigation() {
|
|
|
46
46
|
},
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
|
-
// "HID Reader Users" now points at `access-mgmt/administrator`, not at
|
|
50
|
-
// `access-mgmt/hid-users`.
|
|
51
|
-
//
|
|
52
|
-
// Both routes mount a roster of the reader's users, but only this one
|
|
53
|
-
// can act on them. `administrator` renders HidUserEnrollment WITHOUT
|
|
54
|
-
// `card-management`, and that prop is what gates the Enroll action:
|
|
55
|
-
// <template v-if="!props.cardManagement" #actions>
|
|
56
|
-
// `hid-users` renders HidReaderUserRoster, which is read-only apart
|
|
57
|
-
// from PIN management. So the menu entry with this name used to lead
|
|
58
|
-
// to the screen that cannot enrol, while the screen that can was
|
|
59
|
-
// reachable only by typing the URL.
|
|
60
|
-
//
|
|
61
|
-
// `hid-users` is left in place and simply unlisted - the same state
|
|
62
|
-
// `administrator` was in - so any existing link to it still resolves.
|
|
63
49
|
title: "HID Reader Users",
|
|
64
50
|
route: {
|
|
65
|
-
name: "org-site-access-mgmt-
|
|
66
|
-
params: { org, site },
|
|
67
|
-
},
|
|
68
|
-
},
|
|
69
|
-
{
|
|
70
|
-
// The signed-in member's own pass, not somebody else's. Every other
|
|
71
|
-
// entry in this group acts on a resident or a visitor; this one has
|
|
72
|
-
// no subject but the caller.
|
|
73
|
-
title: "HID QR Code",
|
|
74
|
-
route: {
|
|
75
|
-
name: "org-site-access-mgmt-hid-qr-code",
|
|
51
|
+
name: "org-site-access-mgmt-hid-users",
|
|
76
52
|
params: { org, site },
|
|
77
53
|
},
|
|
78
54
|
},
|
package/composables/usePeople.ts
CHANGED
|
@@ -115,24 +115,6 @@ export default function () {
|
|
|
115
115
|
});
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
-
/**
|
|
119
|
-
* The blocks, levels and units at a site that actually have an occupant.
|
|
120
|
-
* The other half of the unit picker: this narrows the cascade, then
|
|
121
|
-
* `getPeopleByUnit` answers "which person in this unit?".
|
|
122
|
-
*/
|
|
123
|
-
async function getOccupiedStructure(
|
|
124
|
-
site: string,
|
|
125
|
-
{ type = "", status = "active", block = "", level = "" } = {}
|
|
126
|
-
) {
|
|
127
|
-
return await useNuxtApp().$api<Record<string, any>>(
|
|
128
|
-
`/api/people/site/${site}/occupied-structure`,
|
|
129
|
-
{
|
|
130
|
-
method: "GET",
|
|
131
|
-
query: { type, status, block, level },
|
|
132
|
-
}
|
|
133
|
-
);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
118
|
async function getPeopleByUnit(
|
|
137
119
|
_id: string,
|
|
138
120
|
{ status = "active", type = "" } = {}
|
|
@@ -166,7 +148,6 @@ export default function () {
|
|
|
166
148
|
findPersonByPlateNumberMultipleResult,
|
|
167
149
|
findPersonByContact,
|
|
168
150
|
getPeopleByUnit,
|
|
169
|
-
getOccupiedStructure,
|
|
170
151
|
searchCompanyList,
|
|
171
152
|
findUsersByPlateNumber,
|
|
172
153
|
reviewResidentPerson,
|
|
@@ -42,7 +42,6 @@ export default function usePropertyPermission() {
|
|
|
42
42
|
siteSettingsPermissions,
|
|
43
43
|
|
|
44
44
|
inventoryPermissions,
|
|
45
|
-
robotPermissions,
|
|
46
45
|
} = useCommonPermissions();
|
|
47
46
|
|
|
48
47
|
|
|
@@ -580,23 +579,6 @@ export default function usePropertyPermission() {
|
|
|
580
579
|
description:
|
|
581
580
|
"Allows the user to view the list of all passes and keys on mobile. Checked by the iMA365 mobile app.",
|
|
582
581
|
},
|
|
583
|
-
// DV-0396: the same three writes Security offers, enforced by API-core on
|
|
584
|
-
// /api/keys and /api/qr-code-templates for every app that calls them.
|
|
585
|
-
"add-passes-and-keys": {
|
|
586
|
-
check: true,
|
|
587
|
-
description:
|
|
588
|
-
"Allows the user to add pass templates and to create or generate passes and keys.",
|
|
589
|
-
},
|
|
590
|
-
"update-passes-and-keys": {
|
|
591
|
-
check: true,
|
|
592
|
-
description:
|
|
593
|
-
"Allows the user to edit pass templates, passes and keys, restore them, and change a pass or key back to Available.",
|
|
594
|
-
},
|
|
595
|
-
"delete-passes-and-keys": {
|
|
596
|
-
check: true,
|
|
597
|
-
description:
|
|
598
|
-
"Allows the user to delete pass templates, passes and keys.",
|
|
599
|
-
},
|
|
600
582
|
},
|
|
601
583
|
"online-forms": {
|
|
602
584
|
"see-all-online-forms": {
|
|
@@ -619,7 +601,6 @@ export default function usePropertyPermission() {
|
|
|
619
601
|
"Allows the user to view the list of all resident transactions on mobile. Checked by the iMA365 mobile app.",
|
|
620
602
|
},
|
|
621
603
|
},
|
|
622
|
-
"robot-mgmt": robotPermissions,
|
|
623
604
|
"site-settings": siteSettingsPermissions
|
|
624
605
|
};
|
|
625
606
|
|
|
@@ -669,31 +669,12 @@ export default function useSecurityPermission() {
|
|
|
669
669
|
},
|
|
670
670
|
// Property's descriptions minus its "Checked by the iMA365 mobile app."
|
|
671
671
|
// sentence, which is not true of this application.
|
|
672
|
-
// Add / update / delete (DV-0396), also offered by Property Management and
|
|
673
|
-
// enforced by API-core on /api/keys and /api/qr-code-templates. The security web app's
|
|
674
|
-
// Templates and Passes & Keys pages and the guard app's pass & key screen
|
|
675
|
-
// read them. See All is also what shows those two web pages.
|
|
676
672
|
"passes-and-keys-mgmt": {
|
|
677
673
|
"see-all-passes-and-keys": {
|
|
678
674
|
check: true,
|
|
679
675
|
description:
|
|
680
676
|
"Allows the user to view the list of all passes and keys on mobile.",
|
|
681
677
|
},
|
|
682
|
-
"add-passes-and-keys": {
|
|
683
|
-
check: true,
|
|
684
|
-
description:
|
|
685
|
-
"Allows the user to add pass templates and to create or generate passes and keys.",
|
|
686
|
-
},
|
|
687
|
-
"update-passes-and-keys": {
|
|
688
|
-
check: true,
|
|
689
|
-
description:
|
|
690
|
-
"Allows the user to edit pass templates, passes and keys, restore them, and change a pass or key back to Available.",
|
|
691
|
-
},
|
|
692
|
-
"delete-passes-and-keys": {
|
|
693
|
-
check: true,
|
|
694
|
-
description:
|
|
695
|
-
"Allows the user to delete pass templates, passes and keys.",
|
|
696
|
-
},
|
|
697
678
|
},
|
|
698
679
|
"resident-transactions": {
|
|
699
680
|
"see-all-resident-transactions": {
|
|
@@ -58,8 +58,6 @@ export default function useServiceProvider() {
|
|
|
58
58
|
orgId = "",
|
|
59
59
|
siteId = "",
|
|
60
60
|
status = "",
|
|
61
|
-
/** "engagements": also providers engaged through an accepted invitation. */
|
|
62
|
-
include = "",
|
|
63
61
|
} = {}) {
|
|
64
62
|
return useNuxtApp().$api<Record<string, any>>("/api/service-providers", {
|
|
65
63
|
method: "GET",
|
|
@@ -71,7 +69,6 @@ export default function useServiceProvider() {
|
|
|
71
69
|
orgId,
|
|
72
70
|
siteId,
|
|
73
71
|
...(status ? { status } : {}),
|
|
74
|
-
...(include ? { include } : {}),
|
|
75
72
|
},
|
|
76
73
|
});
|
|
77
74
|
}
|
|
@@ -18,21 +18,6 @@ export type TServiceProviderInvitation = {
|
|
|
18
18
|
expireAt?: string;
|
|
19
19
|
deletedAt?: string | null;
|
|
20
20
|
metadata?: Record<string, any>;
|
|
21
|
-
/** A lapsed "pending" reads "expired"; `status` stays the stored value. */
|
|
22
|
-
viewStatus?: string;
|
|
23
|
-
/** The company that accepted it, or the new company's name (sign-up). */
|
|
24
|
-
companyName?: string;
|
|
25
|
-
providerOrgId?: string;
|
|
26
|
-
service?: string;
|
|
27
|
-
invitedByName?: string;
|
|
28
|
-
invitedAt?: string | null;
|
|
29
|
-
/** When it was accepted, declined, cancelled or expired. */
|
|
30
|
-
answeredAt?: string | null;
|
|
31
|
-
/** Shown only once accepted. */
|
|
32
|
-
contactEmail?: string;
|
|
33
|
-
contactPhone?: string;
|
|
34
|
-
/** Accepted and still engaged on the site. */
|
|
35
|
-
onSite?: boolean;
|
|
36
21
|
history?: Array<{
|
|
37
22
|
action: string;
|
|
38
23
|
at: string;
|
|
@@ -41,32 +26,6 @@ export type TServiceProviderInvitation = {
|
|
|
41
26
|
}>;
|
|
42
27
|
};
|
|
43
28
|
|
|
44
|
-
/** One card of "Client invitations" (the service provider's side). */
|
|
45
|
-
export type TReceivedServiceProviderInvitation = {
|
|
46
|
-
_id: string;
|
|
47
|
-
/** The client organisation that sent it. */
|
|
48
|
-
orgName: string;
|
|
49
|
-
siteName: string;
|
|
50
|
-
siteId?: string;
|
|
51
|
-
/** The service asked for, when the sender recorded one (e.g. "security"). */
|
|
52
|
-
service?: string;
|
|
53
|
-
invitedByName?: string;
|
|
54
|
-
createdAt?: string;
|
|
55
|
-
expireAt?: string;
|
|
56
|
-
/** When it was accepted, declined or withdrawn. */
|
|
57
|
-
answeredAt?: string | null;
|
|
58
|
-
/** pending | complete | declined | expired | cancelled */
|
|
59
|
-
status: string;
|
|
60
|
-
statusLabel: string;
|
|
61
|
-
/** The company that accepted it (whose Sites list gained the site). */
|
|
62
|
-
providerOrgId?: string;
|
|
63
|
-
providerOrgName?: string;
|
|
64
|
-
/** Accepted AND on the Sites list of the company on screen. */
|
|
65
|
-
onThisOrg?: boolean;
|
|
66
|
-
/** Accepted, but the site is on no company's Sites list: offer to add it. */
|
|
67
|
-
needsSiteLink?: boolean;
|
|
68
|
-
};
|
|
69
|
-
|
|
70
29
|
/** The words on screen. Kept beside the server's own list on purpose — a
|
|
71
30
|
* status the server invents must never render as a raw stored value. */
|
|
72
31
|
export const SERVICE_PROVIDER_INVITE_LABELS: Record<string, string> = {
|
|
@@ -88,9 +47,7 @@ export default function useServiceProviderInvitation() {
|
|
|
88
47
|
|
|
89
48
|
function getInvitations({
|
|
90
49
|
org = "",
|
|
91
|
-
siteId = "",
|
|
92
50
|
status = "",
|
|
93
|
-
search = "",
|
|
94
51
|
page = 1,
|
|
95
52
|
limit = 20,
|
|
96
53
|
} = {}): Promise<{
|
|
@@ -100,14 +57,7 @@ export default function useServiceProviderInvitation() {
|
|
|
100
57
|
}> {
|
|
101
58
|
return useNuxtApp().$api(base, {
|
|
102
59
|
method: "GET",
|
|
103
|
-
query: {
|
|
104
|
-
org,
|
|
105
|
-
status,
|
|
106
|
-
page,
|
|
107
|
-
limit,
|
|
108
|
-
...(siteId ? { siteId } : {}),
|
|
109
|
-
...(search ? { search } : {}),
|
|
110
|
-
},
|
|
60
|
+
query: { org, status, page, limit },
|
|
111
61
|
});
|
|
112
62
|
}
|
|
113
63
|
|
|
@@ -170,14 +120,8 @@ export default function useServiceProviderInvitation() {
|
|
|
170
120
|
|
|
171
121
|
// ---- the invited service provider ----------------------------------------
|
|
172
122
|
|
|
173
|
-
function getInvitation(
|
|
174
|
-
id:
|
|
175
|
-
org?: string,
|
|
176
|
-
): Promise<TReceivedServiceProviderInvitation & { tab?: string | null }> {
|
|
177
|
-
return useNuxtApp().$api(`${base}/${id}`, {
|
|
178
|
-
method: "GET",
|
|
179
|
-
query: org ? { org } : {},
|
|
180
|
-
});
|
|
123
|
+
function getInvitation(id: string): Promise<TServiceProviderInvitation> {
|
|
124
|
+
return useNuxtApp().$api(`${base}/${id}`, { method: "GET" });
|
|
181
125
|
}
|
|
182
126
|
|
|
183
127
|
function declineInvitation(id: string) {
|
|
@@ -186,45 +130,6 @@ export default function useServiceProviderInvitation() {
|
|
|
186
130
|
});
|
|
187
131
|
}
|
|
188
132
|
|
|
189
|
-
/**
|
|
190
|
-
* Adds the site to `org`'s Sites list - the company on screen, which the
|
|
191
|
-
* server checks the invited account belongs to.
|
|
192
|
-
*/
|
|
193
|
-
function acceptInvitation(id: string, org?: string) {
|
|
194
|
-
return useNuxtApp().$api<{ message: string }>(`${base}/${id}/accept`, {
|
|
195
|
-
method: "PUT",
|
|
196
|
-
body: org ? { org } : {},
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
/**
|
|
201
|
-
* An accepted invitation whose site never reached this company's Sites
|
|
202
|
-
* list: add it now. Safe to repeat - the server adds it once.
|
|
203
|
-
*/
|
|
204
|
-
function linkInvitationSite(id: string, org: string) {
|
|
205
|
-
return useNuxtApp().$api<{ message: string }>(`${base}/${id}/link-site`, {
|
|
206
|
-
method: "PUT",
|
|
207
|
-
body: { org },
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* "Client invitations": one tab of the invitations sent to the signed-in
|
|
213
|
-
* account, plus the count in every tab. `org` is the company on screen.
|
|
214
|
-
*/
|
|
215
|
-
function getReceivedInvitations({
|
|
216
|
-
org,
|
|
217
|
-
status = "pending",
|
|
218
|
-
}: { org?: string; status?: string } = {}): Promise<{
|
|
219
|
-
items: TReceivedServiceProviderInvitation[];
|
|
220
|
-
counts?: Record<string, number>;
|
|
221
|
-
}> {
|
|
222
|
-
return useNuxtApp().$api(`${base}/received`, {
|
|
223
|
-
method: "GET",
|
|
224
|
-
query: { status, ...(org ? { org } : {}) },
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
|
|
228
133
|
return {
|
|
229
134
|
getInvitations,
|
|
230
135
|
cancelInvitation,
|
|
@@ -236,8 +141,5 @@ export default function useServiceProviderInvitation() {
|
|
|
236
141
|
rejectInvitation,
|
|
237
142
|
getInvitation,
|
|
238
143
|
declineInvitation,
|
|
239
|
-
acceptInvitation,
|
|
240
|
-
linkInvitationSite,
|
|
241
|
-
getReceivedInvitations,
|
|
242
144
|
};
|
|
243
145
|
}
|
|
@@ -27,35 +27,9 @@ export default function useSiteEntryPassSettings() {
|
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
-
/**
|
|
31
|
-
* The encrypted ACM URL the access-card screens hand to API-core, which
|
|
32
|
-
* decrypts it and sends the ACM commands to that host.
|
|
33
|
-
*
|
|
34
|
-
* Resolves the site's own "Enter entrypass URL" setting first, the way
|
|
35
|
-
* `VisitorForm` does for contractor passes. The access-card forms used to
|
|
36
|
-
* call `/api/encrypt-acm-url` with no `url`, so every site's door/lift
|
|
37
|
-
* levels and access groups came from the route's hardcoded default host
|
|
38
|
-
* whatever the site had configured. A site with no URL set, or whose
|
|
39
|
-
* settings cannot be read, still gets that default.
|
|
40
|
-
*/
|
|
41
|
-
async function getEncryptedAcmUrl(siteId: string): Promise<string> {
|
|
42
|
-
let rawUrl = "";
|
|
43
|
-
try {
|
|
44
|
-
const settings: any = await getBySiteId(siteId);
|
|
45
|
-
rawUrl = String(settings?.data?.settings?.url ?? "").trim();
|
|
46
|
-
} catch {
|
|
47
|
-
// No entry pass settings for this site yet - the default host applies.
|
|
48
|
-
}
|
|
49
|
-
const { encrypted } = await $fetch<{ encrypted: string }>(
|
|
50
|
-
`/api/encrypt-acm-url${rawUrl ? `?url=${encodeURIComponent(rawUrl)}` : ""}`,
|
|
51
|
-
);
|
|
52
|
-
return encrypted;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
30
|
return {
|
|
56
31
|
getBySiteId,
|
|
57
32
|
getById,
|
|
58
33
|
save,
|
|
59
|
-
getEncryptedAcmUrl,
|
|
60
34
|
};
|
|
61
35
|
}
|
package/composables/useWebUsb.ts
CHANGED
|
@@ -345,19 +345,16 @@ export default function useWebUsb() {
|
|
|
345
345
|
70,
|
|
346
346
|
);
|
|
347
347
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
// Generated at the size it is drawn at: the old corner copies were
|
|
353
|
-
// downscaled from the 250px canvas, which softened their modules.
|
|
354
|
-
const qrSize = 350;
|
|
355
|
-
const qrCanvas = await QRCode.toCanvas(qrUrlImage, {
|
|
356
|
-
width: qrSize,
|
|
357
|
-
margin: 1,
|
|
358
|
-
});
|
|
348
|
+
const qrSmall = 140;
|
|
349
|
+
const qrCenter = 250;
|
|
350
|
+
const margin = 20;
|
|
351
|
+
const qrCanvas = await QRCode.toCanvas(qrUrlImage, { width: qrCenter });
|
|
359
352
|
|
|
360
|
-
ctx.drawImage(qrCanvas,
|
|
353
|
+
ctx.drawImage(qrCanvas, margin, 90, qrSmall, qrSmall);
|
|
354
|
+
ctx.drawImage(qrCanvas, canvas.width - qrSmall - margin, 90, qrSmall, qrSmall);
|
|
355
|
+
ctx.drawImage(qrCanvas, (canvas.width - qrCenter) / 2, 110, qrCenter, qrCenter);
|
|
356
|
+
ctx.drawImage(qrCanvas, margin, 300, qrSmall, qrSmall);
|
|
357
|
+
ctx.drawImage(qrCanvas, canvas.width - qrSmall - margin, 300, qrSmall, qrSmall);
|
|
361
358
|
|
|
362
359
|
ctx.font = "25px Arial";
|
|
363
360
|
ctx.fillText(companyName || "", canvas.width / 2, 470);
|
|
@@ -404,8 +401,8 @@ export default function useWebUsb() {
|
|
|
404
401
|
/**
|
|
405
402
|
* The self-service onboarding QR: one large QR code, a "SCAN QR CODE"-style
|
|
406
403
|
* subtext, and a dark site-name banner along the bottom - a single-purpose
|
|
407
|
-
* gate sign, not the visitor-pass receipt `createReceiptLayout`
|
|
408
|
-
* (that one
|
|
404
|
+
* gate sign, not the multi-copy visitor-pass receipt `createReceiptLayout`
|
|
405
|
+
* produces (that one prints several QRs to be cut apart and handed out).
|
|
409
406
|
*/
|
|
410
407
|
async function createOnboardingQrLayout(
|
|
411
408
|
qrUrlImage: string,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@7365admin1/layer-common",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "4.
|
|
5
|
+
"version": "4.37.0",
|
|
6
6
|
"author": "7365admin1",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
8
8
|
"//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"build": "nuxt build .playground",
|
|
37
37
|
"generate": "nuxt generate .playground",
|
|
38
38
|
"preview": "nuxt preview .playground",
|
|
39
|
-
"test": "esbuild composables/
|
|
39
|
+
"test": "esbuild composables/useVisitorSocket.ts --format=esm --outfile=test/.build/useVisitorSocket.mjs --log-level=error && esbuild composables/usePlateToastWindow.ts --format=esm --outfile=test/.build/usePlateToastWindow.mjs --log-level=error && esbuild utils/client-invite-modules.ts --bundle --platform=node --format=esm --outfile=test/.build/client-invite-modules.mjs --log-level=error && esbuild utils/robot-display.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-display.mjs --log-level=error && esbuild utils/building-map.ts --bundle --platform=node --format=esm --outfile=test/.build/building-map.mjs --log-level=error && esbuild utils/robot-building.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-building.mjs --log-level=error && esbuild utils/robot-fleet-core.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-fleet-core.mjs --log-level=error && node --experimental-strip-types --test \"test/*.test.mjs\" && yarn test:units",
|
|
40
40
|
"test:units": "node --experimental-strip-types --test \"utils/*.test.ts\"",
|
|
41
41
|
"release": "yarn run build && changeset publish"
|
|
42
42
|
},
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
:org="orgId"
|
|
6
6
|
message="Enable HID as a service for this site before managing physical HID cards."
|
|
7
7
|
>
|
|
8
|
-
<HidUserEnrollment :site="siteId"
|
|
8
|
+
<HidUserEnrollment :site="siteId" card-management />
|
|
9
9
|
</HidEnabledGate>
|
|
10
10
|
</v-container>
|
|
11
11
|
</template>
|