@7365admin1/layer-common 4.1.2-staging.244 → 4.1.2-staging.246
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.
|
@@ -35,9 +35,15 @@ const dateFormattedReadOnly = ref<string | null>(null)
|
|
|
35
35
|
|
|
36
36
|
const dateInput = ref<HTMLInputElement | null>(null)
|
|
37
37
|
const datePickerRef = ref<HTMLInputElement | null>(null)
|
|
38
|
+
let nativeInput: HTMLInputElement | null = null
|
|
38
39
|
|
|
39
40
|
const isInitialLoad = ref(true)
|
|
40
41
|
|
|
42
|
+
function handleNativeInputClick(e: MouseEvent) {
|
|
43
|
+
e.stopPropagation()
|
|
44
|
+
openDatePicker()
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
function clearDate() {
|
|
42
48
|
date.value = null
|
|
43
49
|
dateFormattedReadOnly.value = null
|
|
@@ -88,15 +94,17 @@ watch(date, () => {
|
|
|
88
94
|
onMounted(async () => {
|
|
89
95
|
await nextTick()
|
|
90
96
|
isInitialLoad.value = false
|
|
91
|
-
|
|
97
|
+
nativeInput = (datePickerRef.value as any)?.$el?.querySelector('input')
|
|
92
98
|
if (nativeInput) {
|
|
93
|
-
nativeInput.addEventListener('click',
|
|
94
|
-
e.stopPropagation()
|
|
95
|
-
openDatePicker()
|
|
96
|
-
})
|
|
99
|
+
nativeInput.addEventListener('click', handleNativeInputClick)
|
|
97
100
|
}
|
|
98
101
|
})
|
|
99
102
|
|
|
103
|
+
onBeforeUnmount(() => {
|
|
104
|
+
nativeInput?.removeEventListener('click', handleNativeInputClick)
|
|
105
|
+
nativeInput = null
|
|
106
|
+
})
|
|
107
|
+
|
|
100
108
|
defineExpose({
|
|
101
109
|
validate
|
|
102
110
|
})
|
|
@@ -70,9 +70,15 @@ const inputPlaceholder = computed(
|
|
|
70
70
|
|
|
71
71
|
const dateInput = ref<HTMLInputElement | null>(null);
|
|
72
72
|
const dateTimePickerRef = ref<HTMLInputElement | null>(null);
|
|
73
|
+
let nativeInput: HTMLInputElement | null = null;
|
|
73
74
|
|
|
74
75
|
const isInitialLoad = ref(true);
|
|
75
76
|
|
|
77
|
+
function handleNativeInputClick(e: MouseEvent) {
|
|
78
|
+
e.stopPropagation();
|
|
79
|
+
openDatePicker();
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
function openDatePicker() {
|
|
77
83
|
setTimeout(() => {
|
|
78
84
|
dateInput.value?.showPicker?.();
|
|
@@ -177,17 +183,19 @@ onMounted(async () => {
|
|
|
177
183
|
await nextTick();
|
|
178
184
|
isInitialLoad.value = false;
|
|
179
185
|
// Wait until Vuetify renders its internal input
|
|
180
|
-
|
|
186
|
+
nativeInput = (dateTimePickerRef.value as any)?.$el?.querySelector(
|
|
181
187
|
"input"
|
|
182
188
|
);
|
|
183
189
|
if (nativeInput) {
|
|
184
|
-
nativeInput.addEventListener("click",
|
|
185
|
-
e.stopPropagation();
|
|
186
|
-
openDatePicker();
|
|
187
|
-
});
|
|
190
|
+
nativeInput.addEventListener("click", handleNativeInputClick);
|
|
188
191
|
}
|
|
189
192
|
});
|
|
190
193
|
|
|
194
|
+
onBeforeUnmount(() => {
|
|
195
|
+
nativeInput?.removeEventListener("click", handleNativeInputClick);
|
|
196
|
+
nativeInput = null;
|
|
197
|
+
});
|
|
198
|
+
|
|
191
199
|
defineExpose({
|
|
192
200
|
validate,
|
|
193
201
|
});
|
|
@@ -7,40 +7,88 @@ export function useBulletinBoardPermission() {
|
|
|
7
7
|
// flat `{action: ...}` map. Unwrapped, `catalogue["bulletin-board"]` was
|
|
8
8
|
// undefined and all five gates were reachable only through `"*"`.
|
|
9
9
|
const { bulletinBoardPermissions } = useCommonPermissions();
|
|
10
|
-
|
|
10
|
+
|
|
11
|
+
// This layer shipped asking for `bulletin-board:<action>` only. Measured
|
|
12
|
+
// read-only against the staging `roles` collection on 2026-08-26 (428 roles,
|
|
13
|
+
// 71 of them holding `*`), that resource is held by 8-9 roles, while the
|
|
14
|
+
// `-mgmt` spelling of the same five actions is held by 80-82:
|
|
15
|
+
//
|
|
16
|
+
// bulletin-board:add-bulletin-board 8 -mgmt: 82
|
|
17
|
+
// bulletin-board:see-all-bulletin-boards 9 -mgmt: 3
|
|
18
|
+
// -mgmt (singular): 80
|
|
19
|
+
// bulletin-board:see-bulletin-board-details 8 -mgmt: 82
|
|
20
|
+
// bulletin-board:update-bulletin-board 8 -mgmt: 81
|
|
21
|
+
// bulletin-board:delete-bulletin-board 8 -mgmt: 81
|
|
22
|
+
//
|
|
23
|
+
// `-hygiene`, `-security` and `-pest-control` leave
|
|
24
|
+
// `canViewBulletinBoardDetails` unbound on `BulletinBoardManagement`, so this
|
|
25
|
+
// composable is what gates their sidebar-linked bulletin board page, and the
|
|
26
|
+
// ~74 roles holding the right under `bulletin-board-mgmt` were refused.
|
|
27
|
+
//
|
|
28
|
+
// Both resources are accepted rather than swapped, so the gate is correct
|
|
29
|
+
// before AND after any role migration and needs none to work. No permission
|
|
30
|
+
// key is renamed or invented and no role document changes -- renaming a key
|
|
31
|
+
// without migrating the roles that store it is what left the pest-control
|
|
32
|
+
// work-order gate stranding 92 roles. Every string below is canonical or a
|
|
33
|
+
// recorded entry in `LEGACY_PERMISSION_ALIASES`.
|
|
34
|
+
const permissions: TPermissions = {
|
|
35
|
+
"bulletin-board": bulletinBoardPermissions,
|
|
36
|
+
"bulletin-board-mgmt": {
|
|
37
|
+
...bulletinBoardPermissions,
|
|
38
|
+
// `-property-management` and the security mobile app say "board"
|
|
39
|
+
// singular; that is the spelling 80 roles were actually granted.
|
|
40
|
+
"see-all-bulletin-board": bulletinBoardPermissions["see-all-bulletin-boards"],
|
|
41
|
+
},
|
|
42
|
+
};
|
|
11
43
|
|
|
12
44
|
const { userAppRole } = useLocalSetup();
|
|
13
45
|
|
|
46
|
+
// Every gate fails closed while the role is still resolving: `userAppRole` is
|
|
47
|
+
// null on the first tick and a null role must never read as "allowed".
|
|
14
48
|
const canViewBulletinBoard = computed(() => {
|
|
15
49
|
if (!userAppRole.value) return false;
|
|
16
50
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
17
|
-
return
|
|
51
|
+
return (
|
|
52
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board", "see-all-bulletin-boards") ||
|
|
53
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board-mgmt", "see-all-bulletin-boards") ||
|
|
54
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board-mgmt", "see-all-bulletin-board")
|
|
55
|
+
);
|
|
18
56
|
});
|
|
19
57
|
|
|
20
58
|
const canCreateBulletinBoard = computed(() => {
|
|
21
59
|
if (!userAppRole.value) return false;
|
|
22
60
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
23
|
-
return
|
|
61
|
+
return (
|
|
62
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board", "add-bulletin-board") ||
|
|
63
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board-mgmt", "add-bulletin-board")
|
|
64
|
+
);
|
|
24
65
|
});
|
|
25
66
|
|
|
26
67
|
const canUpdateBulletinBoard = computed(() => {
|
|
27
68
|
if (!userAppRole.value) return false;
|
|
28
69
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
29
|
-
return
|
|
70
|
+
return (
|
|
71
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board", "update-bulletin-board") ||
|
|
72
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board-mgmt", "update-bulletin-board")
|
|
73
|
+
);
|
|
30
74
|
});
|
|
31
75
|
|
|
32
76
|
const canViewBulletinBoardDetails = computed(() => {
|
|
33
77
|
if (!userAppRole.value) return false;
|
|
34
78
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
35
|
-
return
|
|
79
|
+
return (
|
|
80
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board", "see-bulletin-board-details") ||
|
|
81
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board-mgmt", "see-bulletin-board-details")
|
|
82
|
+
);
|
|
36
83
|
});
|
|
37
84
|
|
|
38
|
-
|
|
39
|
-
|
|
40
85
|
const canDeleteBulletinBoard = computed(() => {
|
|
41
86
|
if (!userAppRole.value) return false;
|
|
42
87
|
if (userAppRole.value.permissions.includes("*")) return true;
|
|
43
|
-
return
|
|
88
|
+
return (
|
|
89
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board", "delete-bulletin-board") ||
|
|
90
|
+
hasPermission(userAppRole.value, permissions, "bulletin-board-mgmt", "delete-bulletin-board")
|
|
91
|
+
);
|
|
44
92
|
});
|
|
45
93
|
|
|
46
94
|
return {
|
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.1.2-staging.
|
|
5
|
+
"version": "4.1.2-staging.246",
|
|
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.",
|