@7365admin1/layer-common 4.134.0 → 4.136.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 +16 -4
- 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/robot-fleet-ops.ts +6 -0
- package/utils/security-catalogue.ts +1 -1
- package/utils/trade-catalogue.ts +43 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 4.136.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 32c6516: Trade roles (hygiene, M&E, pest control, pool, landscape): new "Task Calendar -> See calendar" box (`event-mgmt:see-task-calendar`) in the five trade catalogues. The role editor stamps a hidden `catalogue:trade-v2` marker when it saves a trade role; roles never saved keep the calendar (the box is pre-ticked when an unmarked role is opened for edit). Helper `utils/trade-catalogue` (`canSeeTaskCalendar`, `withTradeMarker`, `tradeV2PreTicks`, `isTradeCatalogue`) for the apps' gates. Markers are no longer counted in permission totals. Needs core with the same key.
|
|
8
|
+
|
|
9
|
+
## 4.135.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 04a54e7: Robots, Super Admin site setup, Fleet operations: new switch "Carry on an interrupted patrol after charging" (OFF by default) for OneWay security robots.
|
|
14
|
+
|
|
3
15
|
## 4.134.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
|
@@ -3359,11 +3371,11 @@ Monthly report: a month that had patrols opens the list of that month's patrol r
|
|
|
3359
3371
|
`utils/console-tier.ts` + `composables/useConsoleTier.ts` mirror the server's
|
|
3360
3372
|
own rule from two endpoints the console already calls, unprojected:
|
|
3361
3373
|
|
|
3362
|
-
|
|
3363
|
-
|
|
3374
|
+
GET /api/members/user/:user/app/admin the Seven365 staff membership
|
|
3375
|
+
GET /api/roles/id/:role that membership's role document
|
|
3364
3376
|
|
|
3365
|
-
|
|
3366
|
-
|
|
3377
|
+
owner = member.type === "admin" && role.type === "admin" && role.default === true
|
|
3378
|
+
staff = member.type === "admin" && role.type === "admin"
|
|
3367
3379
|
|
|
3368
3380
|
`role.default` is the marker because it is the only property of a platform
|
|
3369
3381
|
staff role no API caller can set - `role.controller.ts` validates create and
|
|
@@ -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 = {
|
|
@@ -298,6 +299,8 @@ export default function useCleaningPermission() {
|
|
|
298
299
|
},
|
|
299
300
|
},
|
|
300
301
|
"robot-mgmt": robotPermissions,
|
|
302
|
+
// Task Calendar -> See calendar (trade catalogue v2, 2026-10-21).
|
|
303
|
+
"event-mgmt": taskCalendarPermissions,
|
|
301
304
|
};
|
|
302
305
|
|
|
303
306
|
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
|
|
|
@@ -61,6 +62,8 @@ export default function useMechanicalElectricalPermission() {
|
|
|
61
62
|
// same seven actions Cleaning offers (decision 3A) -- taken from it, like
|
|
62
63
|
// the keys above, so the two cannot drift. Catalogue-only: grants nothing.
|
|
63
64
|
"emergency-contact": schedulePermissions["emergency-contact"],
|
|
65
|
+
// Task Calendar -> See calendar (trade catalogue v2, 2026-10-21).
|
|
66
|
+
"event-mgmt": taskCalendarPermissions,
|
|
64
67
|
|
|
65
68
|
};
|
|
66
69
|
|
|
@@ -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",
|
|
@@ -394,7 +396,7 @@ export const PERMISSIONS = {
|
|
|
394
396
|
// Security catalogue v2 (2026-10-20). `catalogue:security-v2` is the marker the
|
|
395
397
|
// role editor stamps on save; it grants nothing (see utils/security-catalogue.ts).
|
|
396
398
|
"offline-data": ["see-offline-data"],
|
|
397
|
-
catalogue: ["security-v2"],
|
|
399
|
+
catalogue: ["security-v2", "trade-v2"],
|
|
398
400
|
"visitor-mgmt": [
|
|
399
401
|
"invite-visitor",
|
|
400
402
|
"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.
|
|
5
|
+
"version": "4.136.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/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
|
/**
|
package/utils/robot-fleet-ops.ts
CHANGED
|
@@ -35,6 +35,12 @@ export const FLEET_OPS_SWITCHES = [
|
|
|
35
35
|
help: "An idle robot at or below the low-battery mark is driven to its charger (never during an emergency). The alert is sent either way.",
|
|
36
36
|
moves: true,
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
key: "resumePatrolAfterCharge",
|
|
40
|
+
label: "Carry on an interrupted patrol after charging",
|
|
41
|
+
help: "OneWay robots do not resume a patrol after charging. With this on, a patrol interrupted by charging restarts from the next point once the robot is charged and standing by. Never after a cancel or emergency stop, and not once the last point was reached. This DRIVES the robot - switch on only after the on-site test.",
|
|
42
|
+
moves: true,
|
|
43
|
+
},
|
|
38
44
|
] as const;
|
|
39
45
|
|
|
40
46
|
export type TFleetOpsKey = (typeof FLEET_OPS_SWITCHES)[number]["key"];
|
|
@@ -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
|
+
}
|