@7365admin1/layer-common 4.50.0 → 4.51.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/components/MemberMain.vue +61 -3
- package/composables/useMember.ts +3 -0
- package/package.json +1 -1
- package/utils/role.ts +13 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 4.51.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- ac247e2: Members: a banner "N members here have a role from another site. Assign each one a role for this site." with a one-click filter to list them (server `roleState=other-site`); disappears at 0. `useMember().getAll` accepts `roleState`.
|
|
8
|
+
|
|
3
9
|
## 4.50.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -1539,11 +1545,11 @@
|
|
|
1539
1545
|
`utils/console-tier.ts` + `composables/useConsoleTier.ts` mirror the server's
|
|
1540
1546
|
own rule from two endpoints the console already calls, unprojected:
|
|
1541
1547
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1548
|
+
GET /api/members/user/:user/app/admin the Seven365 staff membership
|
|
1549
|
+
GET /api/roles/id/:role that membership's role document
|
|
1544
1550
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1551
|
+
owner = member.type === "admin" && role.type === "admin" && role.default === true
|
|
1552
|
+
staff = member.type === "admin" && role.type === "admin"
|
|
1547
1553
|
|
|
1548
1554
|
`role.default` is the marker because it is the only property of a platform
|
|
1549
1555
|
staff role no API caller can set - `role.controller.ts` validates create and
|
|
@@ -27,6 +27,28 @@
|
|
|
27
27
|
</template>
|
|
28
28
|
</PageHeader>
|
|
29
29
|
|
|
30
|
+
<!-- Owner decision 2026-09-25: members holding ANOTHER site's role keep
|
|
31
|
+
their access; the company assigns each one a role for this site.
|
|
32
|
+
Shown while any remain (server count), gone at 0. -->
|
|
33
|
+
<v-alert
|
|
34
|
+
v-if="props.assignRole && otherSiteBanner"
|
|
35
|
+
type="warning"
|
|
36
|
+
variant="tonal"
|
|
37
|
+
density="compact"
|
|
38
|
+
class="mb-3"
|
|
39
|
+
data-test="member-other-site-banner"
|
|
40
|
+
>
|
|
41
|
+
<div class="d-flex align-center flex-wrap ga-2">
|
|
42
|
+
<span class="flex-grow-1">{{ otherSiteBanner }}</span>
|
|
43
|
+
<AppButton
|
|
44
|
+
data-test="member-other-site-filter"
|
|
45
|
+
@click="toggleOtherSite()"
|
|
46
|
+
>
|
|
47
|
+
{{ onlyOtherSite ? "Show all members" : "Show them" }}
|
|
48
|
+
</AppButton>
|
|
49
|
+
</div>
|
|
50
|
+
</v-alert>
|
|
51
|
+
|
|
30
52
|
<AppCard class="table-card">
|
|
31
53
|
<CardToolbar :count="pageRange">
|
|
32
54
|
<template #tabs>
|
|
@@ -278,7 +300,7 @@
|
|
|
278
300
|
</template>
|
|
279
301
|
|
|
280
302
|
<script setup lang="ts">
|
|
281
|
-
import { memberRoleChip, withCurrentRole } from "../utils/role";
|
|
303
|
+
import { memberRoleChip, otherSiteBannerText, withCurrentRole } from "../utils/role";
|
|
282
304
|
|
|
283
305
|
const props = defineProps({
|
|
284
306
|
/** The design's page title. Every app that mounts this screen calls it
|
|
@@ -372,6 +394,7 @@ const props = defineProps({
|
|
|
372
394
|
});
|
|
373
395
|
|
|
374
396
|
const items = ref<Array<Record<string, any>>>([]);
|
|
397
|
+
const onlyOtherSite = ref(false);
|
|
375
398
|
const page = ref(1);
|
|
376
399
|
const pages = ref(10);
|
|
377
400
|
const pageRange = ref("-- - -- of --");
|
|
@@ -444,12 +467,47 @@ const {
|
|
|
444
467
|
page: page.value,
|
|
445
468
|
type: props.type,
|
|
446
469
|
siteId: props.siteId,
|
|
470
|
+
roleState: onlyOtherSite.value ? "other-site" : "",
|
|
447
471
|
}),
|
|
448
472
|
{
|
|
449
|
-
watch: [() => props.status, () => props.orgId, () => props.siteId, headerSearch],
|
|
473
|
+
watch: [() => props.status, () => props.orgId, () => props.siteId, headerSearch, onlyOtherSite],
|
|
450
474
|
}
|
|
451
475
|
);
|
|
452
476
|
|
|
477
|
+
/*
|
|
478
|
+
* "N members here have a role from another site" — the count is the server's
|
|
479
|
+
* (`roleState=other-site`, `total`), for this org / site / app, so it is right
|
|
480
|
+
* however many pages the list has. Refreshed with the list (after an Assign
|
|
481
|
+
* Role the list refreshes, and so does this).
|
|
482
|
+
*/
|
|
483
|
+
const { data: otherSiteReq, refresh: refreshOtherSite } = useAsyncData(
|
|
484
|
+
`members-other-site-${props.type}-${props.orgId}-${props.siteId}-${props.status}`,
|
|
485
|
+
() =>
|
|
486
|
+
props.assignRole && props.orgId
|
|
487
|
+
? _getAll({
|
|
488
|
+
status: props.status,
|
|
489
|
+
org: props.orgId,
|
|
490
|
+
page: 1,
|
|
491
|
+
limit: 1,
|
|
492
|
+
type: props.type,
|
|
493
|
+
siteId: props.siteId,
|
|
494
|
+
roleState: "other-site",
|
|
495
|
+
}).catch(() => null)
|
|
496
|
+
: Promise.resolve(null),
|
|
497
|
+
{ watch: [() => props.status, () => props.orgId, () => props.siteId] }
|
|
498
|
+
);
|
|
499
|
+
const otherSiteBanner = computed(() =>
|
|
500
|
+
otherSiteBannerText(otherSiteReq.value?.total)
|
|
501
|
+
);
|
|
502
|
+
watch(otherSiteBanner, (text) => {
|
|
503
|
+
// the last one fixed: back to the full list
|
|
504
|
+
if (!text && onlyOtherSite.value) onlyOtherSite.value = false;
|
|
505
|
+
});
|
|
506
|
+
function toggleOtherSite() {
|
|
507
|
+
page.value = 1;
|
|
508
|
+
onlyOtherSite.value = !onlyOtherSite.value;
|
|
509
|
+
}
|
|
510
|
+
|
|
453
511
|
const loading = computed(() => getAllReqStatus.value === "pending");
|
|
454
512
|
|
|
455
513
|
watch(headerSearch, () => {
|
|
@@ -567,7 +625,7 @@ async function updateMemberRole() {
|
|
|
567
625
|
props.orgId
|
|
568
626
|
);
|
|
569
627
|
await setMember({ mode: "assign-role" });
|
|
570
|
-
await getAll();
|
|
628
|
+
await Promise.all([getAll(), refreshOtherSite()]);
|
|
571
629
|
} catch (error: any) {
|
|
572
630
|
// `message.value` alone sets the snackbar's TEXT and never opens it, so a
|
|
573
631
|
// refused role change said nothing at all - the dialog just closed and the
|
package/composables/useMember.ts
CHANGED
|
@@ -69,10 +69,13 @@ export default function useMember() {
|
|
|
69
69
|
type = "",
|
|
70
70
|
status = "active",
|
|
71
71
|
siteId = "",
|
|
72
|
+
roleState = "",
|
|
72
73
|
} = {}) {
|
|
73
74
|
const query: Record<string, any> = { page, limit, type, status };
|
|
74
75
|
|
|
75
76
|
if (search) query.search = search;
|
|
77
|
+
// "other-site": only members holding a role pinned to another site.
|
|
78
|
+
if (roleState) query.roleState = roleState;
|
|
76
79
|
if (user) query.user = user;
|
|
77
80
|
if (org) query.org = org;
|
|
78
81
|
if (siteId) query.siteId = siteId;
|
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.51.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.",
|
package/utils/role.ts
CHANGED
|
@@ -60,3 +60,16 @@ export function memberRoleChip(item: {
|
|
|
60
60
|
return name ? { text: name, tone: "info" } : null;
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The Members banner (owner decision 2026-09-25: keep the warning, companies
|
|
66
|
+
* re-assign a role for the right site, nobody's access is cut). `null` at 0 so
|
|
67
|
+
* the banner disappears once every member here has this site's role.
|
|
68
|
+
*/
|
|
69
|
+
export function otherSiteBannerText(count: unknown): string | null {
|
|
70
|
+
const n = typeof count === "number" && Number.isFinite(count) ? Math.floor(count) : 0;
|
|
71
|
+
if (n <= 0) return null;
|
|
72
|
+
return n === 1
|
|
73
|
+
? "1 member here has a role from another site. Assign them a role for this site."
|
|
74
|
+
: `${n} members here have a role from another site. Assign each one a role for this site.`;
|
|
75
|
+
}
|