@7365admin1/layer-common 4.97.1-staging.500 → 4.97.1-staging.502

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.
@@ -142,11 +142,20 @@ export function applySelection(
142
142
 
143
143
  /** Per-category totals for the dialog header, counted over the whole reader. */
144
144
  export function countByCategory(state: HidAssignmentState): Record<THidPermissionCategory, number> {
145
- const counts = { resident: 0, property_management: 0, service_provider: 0 } as Record<
146
- THidPermissionCategory,
147
- number
148
- >;
149
- for (const value of state.values()) counts[value.category] += 1;
145
+ /*
146
+ * Every category seeded at zero, and the increment guarded.
147
+ * `counts[category] += 1` on a key this object does not hold yields NaN, so a
148
+ * category added to the union but missed here would print "NaN" beside a tab
149
+ * rather than a number. That is exactly what happened when
150
+ * `service_provider_member` was added.
151
+ */
152
+ const counts = HID_PERMISSION_CATEGORIES.reduce(
153
+ (seeded, category) => ({ ...seeded, [category]: 0 }),
154
+ {} as Record<THidPermissionCategory, number>,
155
+ );
156
+ for (const value of state.values()) {
157
+ counts[value.category] = (counts[value.category] ?? 0) + 1;
158
+ }
150
159
  return counts;
151
160
  }
152
161
 
@@ -196,3 +205,55 @@ export function readCandidatePage(response: unknown): {
196
205
  pageRange,
197
206
  };
198
207
  }
208
+
209
+ /* ── THE SUBJECT AN IDENTITY NAMES ─────────────────────────────────────── */
210
+
211
+ /**
212
+ * WHICH GRANT AN ENROLLED PERSON'S ACCESS IS FILED UNDER.
213
+ *
214
+ * Mirrors `permissionSubjectOfIdentity` in core, which is the authority. This
215
+ * repository cannot import it - layer-common does not depend on the core
216
+ * package - so there is a copy, and the point of putting it HERE is that there
217
+ * is only one.
218
+ *
219
+ * There used to be two: a local `subjectOf` in `HidAccessPermissions.vue` and
220
+ * the same ternary inline in `HidUserEnrollment.vue`. The first was stale. It
221
+ * read every `member` link as `property_management`, so granting a contractor
222
+ * from the permissions screen wrote a staff assignment - and the reconcile then
223
+ * derived `staff` from that category and rewrote the identity's `type`, moving
224
+ * the person out of the service-provider tab and into the member one. The
225
+ * screen for managing contractors reclassified them by being used.
226
+ *
227
+ * A `member` link is how BOTH a property-management member and a service
228
+ * provider's own employee are stored - the person's membership at this site -
229
+ * so the identity's `type` is the discriminator. `"contractor"` is written by
230
+ * the enrolment form and re-confirmed by `permissionIdentityType` on every
231
+ * reconcile, so the two agree instead of one overwriting the other.
232
+ *
233
+ * Returns null for a visitor and for an identity linked to nobody. Visitor
234
+ * access comes from `user_access_rules` rather than a group, so there is nothing
235
+ * to switch; an identity holding only an account link names no subject to
236
+ * assign. Both are still DRAWN by the permissions screen, disabled, with a
237
+ * reason - dropping them silently is what once made the pager say "1-9 of 9"
238
+ * over seven rows.
239
+ */
240
+ export function subjectOfIdentity(
241
+ identity: Record<string, unknown> | null | undefined,
242
+ ): { subjectId: string; category: THidPermissionCategory } | null {
243
+ if (!identity) return null;
244
+ if (identity.person) {
245
+ return { subjectId: String(identity.person), category: "resident" };
246
+ }
247
+ if (identity.member) {
248
+ return {
249
+ subjectId: String(identity.member),
250
+ category: String(identity.type ?? "") === "contractor"
251
+ ? "service_provider_member"
252
+ : "property_management",
253
+ };
254
+ }
255
+ if (identity.serviceProvider) {
256
+ return { subjectId: String(identity.serviceProvider), category: "service_provider" };
257
+ }
258
+ return null;
259
+ }