@7365admin1/layer-common 4.35.1-staging.450 → 4.35.1-staging.451

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.
@@ -75,8 +75,8 @@
75
75
  </v-toolbar>
76
76
  <v-card-text>
77
77
  <p class="text-caption mb-3">
78
- Only active {{ adding === "members" ? "members" : "service providers" }} that are not assigned to a unit
79
- yet are listed.
78
+ Only active {{ adding === "members" ? "members" : "service provider people" }} that are not assigned to a
79
+ unit yet are listed.
80
80
  </p>
81
81
  <v-autocomplete v-model="selected" v-model:search="candidateSearch" :items="candidates"
82
82
  :loading="candidatesLoading" item-title="name" item-value="_id" multiple chips closable-chips
@@ -103,12 +103,22 @@
103
103
  <script setup lang="ts">
104
104
  /**
105
105
  * A building unit's Management tab: the management members and the service
106
- * providers attached to this unit. Each can be on one unit at a time, and
107
- * only while active - the server detaches them the moment they are suspended,
108
- * set inactive or deleted, so they simply drop off this list.
106
+ * provider people (Members > Service Providers, not the provider companies)
107
+ * attached to this unit. Each can be on one unit at a time, and only while
108
+ * active - the server detaches them the moment they are suspended or deleted,
109
+ * so they simply drop off this list.
109
110
  */
110
111
  type TSection = "members" | "providers";
111
- type TRow = { _id: string; name: string; email?: string; roleName?: string; type?: string };
112
+ type TRow = {
113
+ _id: string;
114
+ name: string;
115
+ email?: string;
116
+ roleName?: string;
117
+ type?: string;
118
+ company?: string;
119
+ role?: string;
120
+ status?: string;
121
+ };
112
122
 
113
123
  const props = defineProps({
114
124
  unitId: {
@@ -136,7 +146,7 @@ const org = computed(() => (route.params.org as string) ?? "");
136
146
  const site = computed(() => props.siteId || ((route.params.site as string) ?? ""));
137
147
 
138
148
  const { getAll: getMembers, updateMemberUnit } = useMember();
139
- const { getAll: getProviders, updateUnit: updateProviderUnit } = useServiceProvider();
149
+ const { getSiteMembers: getProviderPeople, updateSiteMemberUnit: updateProviderUnit } = useServiceProvider();
140
150
  const { debounce } = useUtils();
141
151
 
142
152
  const panels = ref<TSection[]>(["members", "providers"]);
@@ -147,8 +157,17 @@ function membersQuery(unit: string, search = "") {
147
157
  return getMembers({ org: org.value, siteId: site.value, type: props.managementType, status: "active", unit, search, limit: 50 });
148
158
  }
149
159
 
150
- function providersQuery(unit: string, search = "") {
151
- return getProviders({ siteId: site.value, status: "active", unit, search, limit: 50 });
160
+ /**
161
+ * The site's provider people on `unit`, active only. The endpoint returns the
162
+ * whole list (no paging or search), so `search` is matched here.
163
+ */
164
+ async function providersQuery(unit: string, search = "") {
165
+ const res = await getProviderPeople(site.value, unit);
166
+ const q = search.toLowerCase();
167
+ const items = (res?.items ?? []).filter(
168
+ (p) => p.status === "active" && (!q || [p.name, p.email, p.company].some((v) => v?.toLowerCase().includes(q))),
169
+ );
170
+ return { items };
152
171
  }
153
172
 
154
173
  const { data: memberRes, pending: membersLoading, refresh: refreshMembers } = useLazyAsyncData(
@@ -161,7 +180,9 @@ const { data: providerRes, pending: providersLoading, refresh: refreshProviders
161
180
  );
162
181
 
163
182
  function subtitleOf(row: TRow) {
164
- return [row.roleName || row.type?.replace(/_/g, " "), row.email].filter(Boolean).join(" · ");
183
+ return [row.company, row.roleName || row.role || row.type?.replace(/_/g, " "), row.email]
184
+ .filter(Boolean)
185
+ .join(" · ");
165
186
  }
166
187
 
167
188
  const sections = computed(() => [
@@ -15,6 +15,9 @@ export type TSiteProviderMember = {
15
15
  lastLoginAt?: string | null;
16
16
  /** a subcontractor: the provider that brought its company onto the site */
17
17
  invitedVia?: string;
18
+ /** The building unit this person is attached to (a unit's Management tab). */
19
+ unit?: string | null;
20
+ unitName?: string;
18
21
  };
19
22
 
20
23
  export type TSiteProviderCompany = {
@@ -307,17 +310,29 @@ export default function useServiceProvider() {
307
310
  * Members > Service Providers: the provider people with access to this
308
311
  * site, and one entry per provider company with its head count. The
309
312
  * site's own organisation only - the server refuses anyone else.
313
+ * `unit`: a building unit id, or "none" (people on no unit yet).
310
314
  */
311
- function getSiteMembers(siteId: string): Promise<{
315
+ function getSiteMembers(siteId: string, unit = ""): Promise<{
312
316
  items: TSiteProviderMember[];
313
317
  companies: TSiteProviderCompany[];
314
318
  }> {
315
319
  return useNuxtApp().$api("/api/service-providers/site-members", {
316
320
  method: "GET",
317
- query: { siteId },
321
+ query: { siteId, ...(unit ? { unit } : {}) },
318
322
  });
319
323
  }
320
324
 
325
+ /** Attach one of those people to a building unit, or detach them (`unit` ""). */
326
+ function updateSiteMemberUnit(id: string, unit: string) {
327
+ return useNuxtApp().$api<Record<string, any>>(
328
+ `/api/service-providers/site-members/${id}/unit`,
329
+ {
330
+ method: "PATCH",
331
+ body: { unit },
332
+ }
333
+ );
334
+ }
335
+
321
336
  /** The same list as an Excel file, under the same filters. */
322
337
  function exportSiteMembers(
323
338
  siteId: string,
@@ -334,6 +349,7 @@ export default function useServiceProvider() {
334
349
 
335
350
  return {
336
351
  getSiteMembers,
352
+ updateSiteMemberUnit,
337
353
  exportSiteMembers,
338
354
  serviceProviders,
339
355
  serviceProvider,
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.35.1-staging.450",
5
+ "version": "4.35.1-staging.451",
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.",