@7365admin1/layer-common 4.97.1-staging.580 → 4.97.1-staging.581
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/components/RolePermissionFormCreate.vue +6 -2
- package/components/RolePermissionFormPreviewUpdate.vue +20 -4
- package/composables/useCleaningPermission.ts +3 -0
- package/composables/useCommonPermission.ts +12 -0
- package/composables/useLandscapingPermission.ts +2 -0
- package/composables/useMechanicalElectricalPermission.ts +3 -0
- package/composables/usePestControlPermission.ts +2 -0
- package/composables/usePoolMaintenancePermission.ts +2 -0
- package/constants/permissions.ts +3 -1
- package/package.json +1 -1
- package/utils/org-modules.ts +3 -0
- package/utils/permission-labels.ts +6 -0
- package/utils/security-catalogue.ts +1 -1
- package/utils/trade-catalogue.ts +43 -0
|
@@ -284,6 +284,7 @@ import useUtils from "../composables/useUtils";
|
|
|
284
284
|
import useSite from "../composables/useSite";
|
|
285
285
|
import { readApiError } from "../utils/subscription-form";
|
|
286
286
|
import { withSecurityMarker } from "../utils/security-catalogue";
|
|
287
|
+
import { isTradeCatalogue, withTradeMarker } from "../utils/trade-catalogue";
|
|
287
288
|
import { applyRolePreset, ROLE_PRESETS, type TRolePreset } from "../utils/inventory-presets";
|
|
288
289
|
|
|
289
290
|
/** Shown after "Couldn't save:" when the server gave no reason (network, timeout). */
|
|
@@ -534,10 +535,13 @@ async function submit() {
|
|
|
534
535
|
try {
|
|
535
536
|
await createRole({
|
|
536
537
|
name: name.value,
|
|
537
|
-
// Security catalogue
|
|
538
|
+
// Security catalogue (it carries `offline-data`) / trade catalogue (it
|
|
539
|
+
// offers See calendar): stamp that catalogue's v2 marker.
|
|
538
540
|
permissions: "offline-data" in (props.permissions || {})
|
|
539
541
|
? withSecurityMarker(permState.value.selected)
|
|
540
|
-
:
|
|
542
|
+
: isTradeCatalogue(props.permissions)
|
|
543
|
+
? withTradeMarker(permState.value.selected)
|
|
544
|
+
: permState.value.selected,
|
|
541
545
|
type: props.type,
|
|
542
546
|
org: props.org,
|
|
543
547
|
site: props.siteId,
|
|
@@ -277,6 +277,11 @@ import {
|
|
|
277
277
|
securityV2PreTicks,
|
|
278
278
|
withSecurityMarker,
|
|
279
279
|
} from "../utils/security-catalogue";
|
|
280
|
+
import {
|
|
281
|
+
isTradeCatalogue as isTradeCatalogueOf,
|
|
282
|
+
tradeV2PreTicks,
|
|
283
|
+
withTradeMarker,
|
|
284
|
+
} from "../utils/trade-catalogue";
|
|
280
285
|
import {
|
|
281
286
|
holdsPermission,
|
|
282
287
|
withoutPermission,
|
|
@@ -651,12 +656,21 @@ const isSecurityCatalogue = computed(
|
|
|
651
656
|
() => "offline-data" in (props.permissions as Record<string, unknown>)
|
|
652
657
|
);
|
|
653
658
|
|
|
654
|
-
|
|
659
|
+
/** Trade catalogue only: the five trade trees offer Task Calendar -> See calendar. */
|
|
660
|
+
const isTradeCatalogue = computed(() =>
|
|
661
|
+
isTradeCatalogueOf(props.permissions as Record<string, any>)
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
// Opening an UNMARKED security (or trade) role for edit: pre-tick the new boxes the role
|
|
655
665
|
// already effectively has (legacy fallback), so an unrelated save never strips
|
|
656
666
|
// access. Cancel restores the original list.
|
|
657
667
|
watch(edit, (on) => {
|
|
658
|
-
if (!on
|
|
659
|
-
const add =
|
|
668
|
+
if (!on) return;
|
|
669
|
+
const add = isSecurityCatalogue.value
|
|
670
|
+
? securityV2PreTicks(definedModel.value ?? [])
|
|
671
|
+
: isTradeCatalogue.value
|
|
672
|
+
? tradeV2PreTicks(definedModel.value ?? [])
|
|
673
|
+
: [];
|
|
660
674
|
if (add.length) definedModel.value = [...definedModel.value, ...add];
|
|
661
675
|
});
|
|
662
676
|
|
|
@@ -735,7 +749,9 @@ async function submit() {
|
|
|
735
749
|
props.id,
|
|
736
750
|
isSecurityCatalogue.value
|
|
737
751
|
? withSecurityMarker(definedModel.value)
|
|
738
|
-
:
|
|
752
|
+
: isTradeCatalogue.value
|
|
753
|
+
? withTradeMarker(definedModel.value)
|
|
754
|
+
: definedModel.value
|
|
739
755
|
);
|
|
740
756
|
emit("success");
|
|
741
757
|
} catch (error: any) {
|
|
@@ -13,6 +13,7 @@ export default function useCleaningPermission() {
|
|
|
13
13
|
bulletinBoardPermissions,
|
|
14
14
|
|
|
15
15
|
inventoryPermissions,
|
|
16
|
+
taskCalendarPermissions,
|
|
16
17
|
robotPermissions,
|
|
17
18
|
} = useCommonPermissions();
|
|
18
19
|
const permissions: TPermissions = {
|
|
@@ -309,6 +310,8 @@ export default function useCleaningPermission() {
|
|
|
309
310
|
},
|
|
310
311
|
},
|
|
311
312
|
"robot-mgmt": robotPermissions,
|
|
313
|
+
// Task Calendar -> See calendar (trade catalogue v2, 2026-10-21).
|
|
314
|
+
"event-mgmt": taskCalendarPermissions,
|
|
312
315
|
};
|
|
313
316
|
|
|
314
317
|
return {
|
|
@@ -391,7 +391,19 @@ export function useCommonPermissions() {
|
|
|
391
391
|
},
|
|
392
392
|
};
|
|
393
393
|
|
|
394
|
+
// Trade catalogue v2 (owner 2026-10-21): Task Calendar per role. Grants
|
|
395
|
+
// nothing alone; a role without `catalogue:trade-v2` keeps the calendar
|
|
396
|
+
// (utils/trade-catalogue.ts).
|
|
397
|
+
const taskCalendarPermissions: Record<string, TPermission> = {
|
|
398
|
+
"see-task-calendar": {
|
|
399
|
+
check: true,
|
|
400
|
+
label: "See calendar",
|
|
401
|
+
description: "Allows the user to open the Task Calendar.",
|
|
402
|
+
},
|
|
403
|
+
};
|
|
404
|
+
|
|
394
405
|
return {
|
|
406
|
+
taskCalendarPermissions,
|
|
395
407
|
robotPermissions,
|
|
396
408
|
inventoryPermissions,
|
|
397
409
|
invitationPermissions,
|
|
@@ -31,9 +31,11 @@ export default function useLandscapingPermission() {
|
|
|
31
31
|
serviceProviderPermissions,
|
|
32
32
|
|
|
33
33
|
inventoryPermissions,
|
|
34
|
+
taskCalendarPermissions,
|
|
34
35
|
} = useCommonPermissions();
|
|
35
36
|
const permissions: TPermissions = {
|
|
36
37
|
inventory: inventoryPermissions,
|
|
38
|
+
"event-mgmt": taskCalendarPermissions,
|
|
37
39
|
members: memberPermissions,
|
|
38
40
|
roles: rolePermissions,
|
|
39
41
|
invitations: invitationPermissions,
|
|
@@ -31,6 +31,7 @@ export default function useMechanicalElectricalPermission() {
|
|
|
31
31
|
bulletinBoardPermissions,
|
|
32
32
|
|
|
33
33
|
inventoryPermissions,
|
|
34
|
+
taskCalendarPermissions,
|
|
34
35
|
} = useCommonPermissions();
|
|
35
36
|
const { permissions: schedulePermissions } = useCleaningPermission();
|
|
36
37
|
|
|
@@ -66,6 +67,8 @@ export default function useMechanicalElectricalPermission() {
|
|
|
66
67
|
// same seven actions Cleaning offers (decision 3A) -- taken from it, like
|
|
67
68
|
// the keys above, so the two cannot drift. Catalogue-only: grants nothing.
|
|
68
69
|
"emergency-contact": schedulePermissions["emergency-contact"],
|
|
70
|
+
// Task Calendar -> See calendar (trade catalogue v2, 2026-10-21).
|
|
71
|
+
"event-mgmt": taskCalendarPermissions,
|
|
69
72
|
|
|
70
73
|
};
|
|
71
74
|
|
|
@@ -31,9 +31,11 @@ export default function usePestControlPermission() {
|
|
|
31
31
|
serviceProviderPermissions,
|
|
32
32
|
|
|
33
33
|
inventoryPermissions,
|
|
34
|
+
taskCalendarPermissions,
|
|
34
35
|
} = useCommonPermissions();
|
|
35
36
|
const permissions: TPermissions = {
|
|
36
37
|
inventory: inventoryPermissions,
|
|
38
|
+
"event-mgmt": taskCalendarPermissions,
|
|
37
39
|
members: memberPermissions,
|
|
38
40
|
roles: rolePermissions,
|
|
39
41
|
invitations: invitationPermissions,
|
|
@@ -31,9 +31,11 @@ export default function usePoolMaintenancePermission() {
|
|
|
31
31
|
serviceProviderPermissions,
|
|
32
32
|
|
|
33
33
|
inventoryPermissions,
|
|
34
|
+
taskCalendarPermissions,
|
|
34
35
|
} = useCommonPermissions();
|
|
35
36
|
const permissions: TPermissions = {
|
|
36
37
|
inventory: inventoryPermissions,
|
|
38
|
+
"event-mgmt": taskCalendarPermissions,
|
|
37
39
|
members: memberPermissions,
|
|
38
40
|
roles: rolePermissions,
|
|
39
41
|
invitations: invitationPermissions,
|
package/constants/permissions.ts
CHANGED
|
@@ -304,6 +304,8 @@ export const PERMISSIONS = {
|
|
|
304
304
|
"delete-emergency-contact-category",
|
|
305
305
|
],
|
|
306
306
|
"event-mgmt": [
|
|
307
|
+
// Trade catalogue v2 (2026-10-21): Task Calendar -> See calendar.
|
|
308
|
+
"see-task-calendar",
|
|
307
309
|
"see-all-event",
|
|
308
310
|
"add-event",
|
|
309
311
|
"update-event",
|
|
@@ -416,7 +418,7 @@ export const PERMISSIONS = {
|
|
|
416
418
|
// Security catalogue v2 (2026-10-20). `catalogue:security-v2` is the marker the
|
|
417
419
|
// role editor stamps on save; it grants nothing (see utils/security-catalogue.ts).
|
|
418
420
|
"offline-data": ["see-offline-data"],
|
|
419
|
-
catalogue: ["security-v2"],
|
|
421
|
+
catalogue: ["security-v2", "trade-v2"],
|
|
420
422
|
"visitor-mgmt": [
|
|
421
423
|
"invite-visitor",
|
|
422
424
|
"see-all-visitor",
|
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.97.1-staging.
|
|
5
|
+
"version": "4.97.1-staging.581",
|
|
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/org-modules.ts
CHANGED
|
@@ -259,7 +259,10 @@ const GRANT_COVERS: Readonly<Record<string, readonly string[]>> = {
|
|
|
259
259
|
"visitor:create": ["visitor-mgmt:invite-visitor"],
|
|
260
260
|
};
|
|
261
261
|
|
|
262
|
+
// Trade v2 See calendar: open to every trade role before the box, so no owner
|
|
263
|
+
// grant names it (core `boundToOwnerGrant` never refuses it either).
|
|
262
264
|
const grantHolds = (grant: readonly string[], key: string) =>
|
|
265
|
+
key === "event-mgmt:see-task-calendar" ||
|
|
263
266
|
grant.includes(key) || grant.some((g) => GRANT_COVERS[g]?.includes(key));
|
|
264
267
|
|
|
265
268
|
/**
|
|
@@ -68,6 +68,12 @@ export const RESOURCE_LABELS: Record<string, string> = {
|
|
|
68
68
|
export const RESOURCE_LABELS_BY_TYPE: Record<string, Record<string, string>> = {
|
|
69
69
|
property_management_agency: { "virtual-patrol": "CCTV" },
|
|
70
70
|
real_estate_developer: { "virtual-patrol": "CCTV" },
|
|
71
|
+
// Trade catalogue v2: the trade apps call `event-mgmt` the Task Calendar.
|
|
72
|
+
cleaning_services: { "event-mgmt": "Task Calendar" },
|
|
73
|
+
mechanical_electrical_services: { "event-mgmt": "Task Calendar" },
|
|
74
|
+
pest_control_services: { "event-mgmt": "Task Calendar" },
|
|
75
|
+
pool_maintenance_services: { "event-mgmt": "Task Calendar" },
|
|
76
|
+
landscaping_services: { "event-mgmt": "Task Calendar" },
|
|
71
77
|
};
|
|
72
78
|
|
|
73
79
|
/**
|
|
@@ -25,7 +25,7 @@ export const isSecurityV2Marked = (perms?: readonly string[] | null) =>
|
|
|
25
25
|
|
|
26
26
|
/** Real keys = anything that is not the marker or the wildcard. */
|
|
27
27
|
export const countRealPermissions = (perms?: readonly string[] | null) =>
|
|
28
|
-
(perms ?? []).filter((p) => p
|
|
28
|
+
(perms ?? []).filter((p) => !p.startsWith("catalogue:")).length; // every catalogue marker (security-v2, trade-v2)
|
|
29
29
|
|
|
30
30
|
/**
|
|
31
31
|
* Effective grant for a v2 key. Empty list / "*" = allowed (same as
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { holdsPermission } from "./permission-spellings.ts";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Trade catalogue v2 (hygiene, M&E, pest control, pool, landscape), owner
|
|
5
|
+
* 2026-10-21: "Task Calendar -> See calendar" per role, and "keep access until
|
|
6
|
+
* changed". The role editor stamps the marker when it saves a trade role. A role
|
|
7
|
+
* without it was never saved with the box and keeps the calendar (it was open
|
|
8
|
+
* to every role). The marker grants nothing. Mirrors core
|
|
9
|
+
* `trade-catalogue.util.ts`; keep the two in step.
|
|
10
|
+
*/
|
|
11
|
+
export const TRADE_CATALOGUE_MARKER = "catalogue:trade-v2";
|
|
12
|
+
export const TASK_CALENDAR_KEY = "event-mgmt:see-task-calendar";
|
|
13
|
+
|
|
14
|
+
export const isTradeV2Marked = (perms?: readonly string[] | null) =>
|
|
15
|
+
!!perms?.includes(TRADE_CATALOGUE_MARKER);
|
|
16
|
+
|
|
17
|
+
/** Is this editor catalogue a trade one (it offers the See calendar box)? */
|
|
18
|
+
export const isTradeCatalogue = (catalogue?: Record<string, any> | null) =>
|
|
19
|
+
!!catalogue?.["event-mgmt"]?.["see-task-calendar"];
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* May this role open the Task Calendar? No role = no. Empty list / "*" = yes
|
|
23
|
+
* (empty means all). Unmarked = yes (keeps today's access). Marked = the box.
|
|
24
|
+
*/
|
|
25
|
+
export function canSeeTaskCalendar(perms: readonly string[] | null | undefined): boolean {
|
|
26
|
+
if (!perms) return false;
|
|
27
|
+
if (!perms.length || perms.includes("*")) return true;
|
|
28
|
+
if (!isTradeV2Marked(perms)) return true;
|
|
29
|
+
return holdsPermission(perms, TASK_CALENDAR_KEY);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Marker to append on save: only when the list has a real key and no "*". */
|
|
33
|
+
export function withTradeMarker(perms: readonly string[]): string[] {
|
|
34
|
+
const keys = perms.filter((p) => p !== TRADE_CATALOGUE_MARKER);
|
|
35
|
+
if (!keys.some((p) => !p.startsWith("catalogue:")) || keys.includes("*")) return keys;
|
|
36
|
+
return [...keys, TRADE_CATALOGUE_MARKER];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Box to pre-tick when opening an UNMARKED trade role in the editor. */
|
|
40
|
+
export function tradeV2PreTicks(perms: readonly string[]): string[] {
|
|
41
|
+
if (!perms.length || perms.includes("*") || isTradeV2Marked(perms)) return [];
|
|
42
|
+
return perms.includes(TASK_CALENDAR_KEY) ? [] : [TASK_CALENDAR_KEY];
|
|
43
|
+
}
|