@7365admin1/layer-common 4.73.1-staging.462 → 4.73.1-staging.463
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.
|
@@ -47,14 +47,22 @@
|
|
|
47
47
|
<div>
|
|
48
48
|
<div class="card-details__label">User</div>
|
|
49
49
|
<div class="card-details__value">
|
|
50
|
-
|
|
51
|
-
|
|
50
|
+
<template v-if="holderState === 'named'">
|
|
51
|
+
{{ holderName }}
|
|
52
|
+
<template v-if="details.user?.email">({{ details.user.email }})</template>
|
|
53
|
+
</template>
|
|
54
|
+
<!-- Assigned, but the server did not send the name back. Saying
|
|
55
|
+
so beats "N/A", which reads as "nobody holds this card". -->
|
|
56
|
+
<template v-else-if="holderState === 'assigned'">
|
|
57
|
+
<span class="text-medium-emphasis">Assigned — name unavailable</span>
|
|
58
|
+
</template>
|
|
59
|
+
<template v-else>Unassigned</template>
|
|
52
60
|
</div>
|
|
53
61
|
</div>
|
|
54
62
|
|
|
55
|
-
<div v-if="
|
|
63
|
+
<div v-if="holderType">
|
|
56
64
|
<div class="card-details__label">User Type</div>
|
|
57
|
-
<div class="card-details__value">{{
|
|
65
|
+
<div class="card-details__value">{{ holderType }}</div>
|
|
58
66
|
</div>
|
|
59
67
|
|
|
60
68
|
<!-- Visitor / Resident Credentials -->
|
|
@@ -216,6 +224,51 @@ const sortedHistory = computed(() =>
|
|
|
216
224
|
)
|
|
217
225
|
);
|
|
218
226
|
|
|
227
|
+
/**
|
|
228
|
+
* WHO HOLDS THIS CARD.
|
|
229
|
+
*
|
|
230
|
+
* `userCred` is written by the visitor-pass flow and carries the person's
|
|
231
|
+
* details on the card itself. `user` is the resolved account - the card only
|
|
232
|
+
* stores `userId`, so the server has to join it.
|
|
233
|
+
*
|
|
234
|
+
* `card-details` does not do that join today (the lookup is commented out in
|
|
235
|
+
* core's `getCardDetailsRepo`), so this is empty for every card assigned
|
|
236
|
+
* through Assign to Person and the field falls through to `holderState`.
|
|
237
|
+
* Nothing here needs to change when the join is restored.
|
|
238
|
+
*/
|
|
239
|
+
const holderName = computed(
|
|
240
|
+
() => details.value?.userCred?.name ?? details.value?.user?.name ?? "",
|
|
241
|
+
);
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* A card with a `userId` IS assigned, whether or not the name came back.
|
|
245
|
+
*
|
|
246
|
+
* Printing "N/A" for both cases is the actual defect here: it reads as "nobody
|
|
247
|
+
* holds this card", which is the opposite of the truth for an assigned one and
|
|
248
|
+
* could have someone hand out a card that is already in use.
|
|
249
|
+
*/
|
|
250
|
+
const holderState = computed<"named" | "assigned" | "unassigned">(() => {
|
|
251
|
+
if (holderName.value) return "named";
|
|
252
|
+
return details.value?.userId ? "assigned" : "unassigned";
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* WHICH KIND OF PERSON HOLDS THIS CARD — Visitor/Resident, Management or
|
|
257
|
+
* Service Provider, matching the groups Assign to Person offers.
|
|
258
|
+
*
|
|
259
|
+
* `holderType` is what the server should send once it records the category the
|
|
260
|
+
* card was assigned under. `userType` is NOT that: it is the card's own pool,
|
|
261
|
+
* and `getCardDetailsRepo` filters on `EAccessCardUserTypes.DEFAULT`, so every
|
|
262
|
+
* card this dialog can load has the literal value "Visitor/Resident" whoever
|
|
263
|
+
* actually holds it. Preferring `holderType` means a card given to Management
|
|
264
|
+
* stops describing itself as a resident's the day the field arrives.
|
|
265
|
+
*
|
|
266
|
+
* Falls back to `userType` so nothing disappears in the meantime.
|
|
267
|
+
*/
|
|
268
|
+
const holderType = computed(
|
|
269
|
+
() => details.value?.holderType ?? details.value?.userType ?? "",
|
|
270
|
+
);
|
|
271
|
+
|
|
219
272
|
watch(
|
|
220
273
|
() => props.modelValue,
|
|
221
274
|
async (val) => {
|
|
@@ -453,7 +453,7 @@ const emit = defineEmits<{
|
|
|
453
453
|
const { getResidents, assignUser } = useAccessManagement();
|
|
454
454
|
const { getEncryptedAcmUrl } = useSiteEntryPassSettings();
|
|
455
455
|
const { getAll: getMembers } = useMember();
|
|
456
|
-
const {
|
|
456
|
+
const { getSiteMembers } = useServiceProvider();
|
|
457
457
|
|
|
458
458
|
const historyDialog = ref(false);
|
|
459
459
|
const showAssignForm = ref(false);
|
|
@@ -552,14 +552,12 @@ const personTypeLabel = (person: Record<string, any>) => {
|
|
|
552
552
|
const assignNoDataText = computed(() => {
|
|
553
553
|
if (peopleLoading.value) return "Loading...";
|
|
554
554
|
if (assignCategory.value === "management") return "No management members assigned to this unit";
|
|
555
|
-
if (assignCategory.value === "providers") return "No service
|
|
555
|
+
if (assignCategory.value === "providers") return "No service provider members assigned to this unit";
|
|
556
556
|
return "No residents or tenants found for this unit";
|
|
557
557
|
});
|
|
558
558
|
|
|
559
|
-
const assignUnassignableText = computed(
|
|
560
|
-
|
|
561
|
-
? "A card is held by a person, and this is the provider company. Assigning to service providers needs the server to say which account the card belongs to."
|
|
562
|
-
: "This person has no user account, so a card cannot be assigned to them.",
|
|
559
|
+
const assignUnassignableText = computed(
|
|
560
|
+
() => "This person has no account on record, so a card cannot be assigned to them.",
|
|
563
561
|
);
|
|
564
562
|
|
|
565
563
|
/** Residents and tenants of THIS UNIT - the list the dialog has always shown. */
|
|
@@ -619,41 +617,48 @@ async function loadManagementRows(): Promise<AssignRow[]> {
|
|
|
619
617
|
}
|
|
620
618
|
|
|
621
619
|
/**
|
|
622
|
-
* Service Providers
|
|
620
|
+
* Service Providers - the provider PEOPLE attached to this unit.
|
|
623
621
|
*
|
|
624
|
-
* `GET /api/service-providers?siteId&
|
|
625
|
-
*
|
|
626
|
-
* providers
|
|
622
|
+
* `GET /api/service-providers/site-members?siteId&unit=<id>`. A card is held by
|
|
623
|
+
* a person, so this lists provider staff rather than the companies
|
|
624
|
+
* `GET /api/service-providers` returns, and the `unit` filter keeps it to the
|
|
625
|
+
* people that unit actually has - the same scoping Management uses.
|
|
627
626
|
*
|
|
628
|
-
*
|
|
629
|
-
* service `type`, and there is no user account behind a company. See
|
|
630
|
-
* `assigneeId` below.
|
|
627
|
+
* The response also carries a `companies` summary; only `items` is people.
|
|
631
628
|
*/
|
|
632
629
|
async function loadProviderRows(): Promise<AssignRow[]> {
|
|
633
630
|
if (!props.siteId || !props.unit?._id) return [];
|
|
634
|
-
const res
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
631
|
+
const res = await getSiteMembers(props.siteId, props.unit._id);
|
|
632
|
+
const rows = res?.items ?? [];
|
|
633
|
+
return rows.map((member: Record<string, any>) => ({
|
|
634
|
+
/*
|
|
635
|
+
* `user` when the endpoint returns it, otherwise the MEMBERSHIP `_id`.
|
|
636
|
+
*
|
|
637
|
+
* `site-members` does not carry `user` today (the repository projects it,
|
|
638
|
+
* the service layer drops it), and the product decision is to assign on
|
|
639
|
+
* `_id` rather than wait. Worth knowing what that means: the card's
|
|
640
|
+
* `userId` then holds a `service-provider-members` id, so the card list's
|
|
641
|
+
* `userId -> users` lookup finds nothing and the holder column reads blank
|
|
642
|
+
* for these cards. The door access itself is unaffected - the ACM command
|
|
643
|
+
* is built from the CARD's id, not the assignee's.
|
|
644
|
+
*
|
|
645
|
+
* `user` is preferred so that the day it starts arriving, these cards
|
|
646
|
+
* begin resolving to real people with no change here.
|
|
647
|
+
*/
|
|
648
|
+
assigneeId: String(member.user ?? member._id ?? ""),
|
|
649
|
+
name: member.name || member.email || "",
|
|
650
|
+
subtitle: member.email ?? "",
|
|
643
651
|
/*
|
|
644
|
-
*
|
|
645
|
-
*
|
|
646
|
-
*
|
|
647
|
-
*
|
|
648
|
-
* that has not been made. Sending one anyway would write an id the server
|
|
649
|
-
* stores without checking and that matches no user.
|
|
652
|
+
* The service the company provides, plus this person's role - two people
|
|
653
|
+
* from the same company differ only by role ("VMS Access" vs "Security
|
|
654
|
+
* Access"). `typeLabel` is computed by the endpoint; the local converter is
|
|
655
|
+
* the fallback for a row that predates it.
|
|
650
656
|
*/
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
raw: provider,
|
|
657
|
+
meta:
|
|
658
|
+
[member.typeLabel || serviceTypeLabel(member.type), member.role]
|
|
659
|
+
.filter(Boolean)
|
|
660
|
+
.join(" · "),
|
|
661
|
+
raw: member,
|
|
657
662
|
}));
|
|
658
663
|
}
|
|
659
664
|
|
|
@@ -18,6 +18,13 @@ export type TSiteProviderMember = {
|
|
|
18
18
|
/** The building unit this person is attached to (a unit's Management tab). */
|
|
19
19
|
unit?: string | null;
|
|
20
20
|
unitName?: string;
|
|
21
|
+
/**
|
|
22
|
+
* The person's user account. NOT yet returned by the endpoint — the
|
|
23
|
+
* repository projects it but the service layer drops it. Anything acting on
|
|
24
|
+
* the PERSON (assigning an access card writes it onto the card as `userId`)
|
|
25
|
+
* needs this rather than `_id`, which identifies the membership.
|
|
26
|
+
*/
|
|
27
|
+
user?: string | null;
|
|
21
28
|
};
|
|
22
29
|
|
|
23
30
|
export type TSiteProviderCompany = {
|
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.73.1-staging.
|
|
5
|
+
"version": "4.73.1-staging.463",
|
|
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.",
|