@7365admin1/layer-common 4.35.1-staging.384 → 4.36.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 +10 -4
- package/assets/css/primitives.css +0 -43
- package/assets/css/robot-hero.css +2 -2
- 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/Robot/BuildingMap.vue +9 -16
- package/components/Robot/LiftQueue.vue +4 -9
- package/components/Robot/MapEditor.vue +8 -1
- 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/useRobotBuilding.ts +3 -22
- package/composables/useRobots.ts +0 -13
- 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/robot-fleet-core.ts +0 -57
- package/utils/sp-inviter-row.ts +0 -86
|
@@ -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
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { computed,
|
|
2
|
-
import { hasFleetRobots, isFleetBuilding } from "../utils/robot-fleet-core";
|
|
1
|
+
import { computed, ref, watch, type Ref } from "vue";
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* One site's building for the HERO building map and lift queue: the robots
|
|
@@ -8,10 +7,6 @@ import { hasFleetRobots, isFleetBuilding } from "../utils/robot-fleet-core";
|
|
|
8
7
|
* SITE (`/building-maps?site=`), whichever robot type it was saved from: the
|
|
9
8
|
* building belongs to the site, so a map drawn in Hygiene shows in Security.
|
|
10
9
|
*
|
|
11
|
-
* A site with HERO Fleet Core robots draws the FLEET building instead (read
|
|
12
|
-
* through from the fleet server, in the same plan frame as those robots,
|
|
13
|
-
* read-only), refreshed every 5 s so doors and lifts move.
|
|
14
|
-
*
|
|
15
10
|
* ponytail: the first 50 robots at the site; the map read is one call.
|
|
16
11
|
*/
|
|
17
12
|
export default function useRobotBuilding(
|
|
@@ -19,7 +14,7 @@ export default function useRobotBuilding(
|
|
|
19
14
|
site: Ref<string>,
|
|
20
15
|
type: Ref<"cleaning" | "security">,
|
|
21
16
|
) {
|
|
22
|
-
const { getSiteFleet, getSiteSummary, listSiteBuildingMaps, getBuildingMap
|
|
17
|
+
const { getSiteFleet, getSiteSummary, listSiteBuildingMaps, getBuildingMap } = useRobots();
|
|
23
18
|
|
|
24
19
|
/** Every robot at the site this app may see, both types where the app shows both. */
|
|
25
20
|
const all = ref<Array<Record<string, any>>>([]);
|
|
@@ -32,24 +27,10 @@ export default function useRobotBuilding(
|
|
|
32
27
|
const rows = computed(() => all.value.filter((r) => r.type === type.value));
|
|
33
28
|
|
|
34
29
|
async function loadMap() {
|
|
35
|
-
if (hasFleetRobots(all.value)) {
|
|
36
|
-
const building = await getFleetCoreBuilding(site.value).catch(() => null);
|
|
37
|
-
if (building) return building;
|
|
38
|
-
}
|
|
39
30
|
const newest = (await listSiteBuildingMaps(site.value, { limit: 1 }))?.items?.[0];
|
|
40
31
|
return newest ? await getBuildingMap(String(newest._id)) : null;
|
|
41
32
|
}
|
|
42
33
|
|
|
43
|
-
/** Live doors and lifts of the fleet building; a failed refresh keeps the last reading. */
|
|
44
|
-
const fleet = computed(() => isFleetBuilding(map.value));
|
|
45
|
-
const timer = setInterval(async () => {
|
|
46
|
-
if (!fleet.value || (typeof document !== "undefined" && document.visibilityState === "hidden")) return;
|
|
47
|
-
const mine = run;
|
|
48
|
-
const next = await getFleetCoreBuilding(site.value).catch(() => null);
|
|
49
|
-
if (next && mine === run) map.value = next;
|
|
50
|
-
}, 5000);
|
|
51
|
-
onBeforeUnmount(() => clearInterval(timer));
|
|
52
|
-
|
|
53
34
|
let run = 0;
|
|
54
35
|
async function load() {
|
|
55
36
|
const mine = ++run;
|
|
@@ -78,5 +59,5 @@ export default function useRobotBuilding(
|
|
|
78
59
|
|
|
79
60
|
watch([organization, site], load, { immediate: true });
|
|
80
61
|
|
|
81
|
-
return { all, rows, map,
|
|
62
|
+
return { all, rows, map, names, loading, error, reload: load };
|
|
82
63
|
}
|
package/composables/useRobots.ts
CHANGED
|
@@ -293,18 +293,6 @@ export default function useRobots() {
|
|
|
293
293
|
});
|
|
294
294
|
}
|
|
295
295
|
|
|
296
|
-
/**
|
|
297
|
-
* The HERO Fleet Core building at a site: levels, walls, doors, lifts (in the
|
|
298
|
-
* HERO plan frame) with live door and lift states, read through from the
|
|
299
|
-
* fleet server - nothing stored. 404 where the server has no fleet.
|
|
300
|
-
*/
|
|
301
|
-
function getFleetCoreBuilding(site: string) {
|
|
302
|
-
return useNuxtApp().$api<Record<string, any>>("/api/robots/fleet-core/building", {
|
|
303
|
-
method: "GET",
|
|
304
|
-
query: { site, appSlug },
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
|
|
308
296
|
/* ------------------------------------------ alerts + activity log */
|
|
309
297
|
|
|
310
298
|
/**
|
|
@@ -406,7 +394,6 @@ export default function useRobots() {
|
|
|
406
394
|
return {
|
|
407
395
|
listBuildingMaps,
|
|
408
396
|
listSiteBuildingMaps,
|
|
409
|
-
getFleetCoreBuilding,
|
|
410
397
|
getSiteFaults,
|
|
411
398
|
getSiteActivity,
|
|
412
399
|
getNetworkActivity,
|
|
@@ -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
|
}
|