@7365admin1/layer-common 4.135.0 → 4.137.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/assets/css/primitives.css +54 -11
- package/assets/css/screens.css +3 -3
- package/assets/css/tokens.css +17 -12
- package/components/AnprCameraBadge.vue +1 -1
- package/components/CameraWall.vue +7 -7
- package/components/CameraWallTile.vue +8 -8
- package/components/Chat/ListCard.vue +1 -1
- package/components/CleaningScheduleMain.vue +56 -20
- package/components/ClientMain.vue +1 -1
- package/components/ClientSubscriptionForm.vue +1 -1
- package/components/DashboardMain.vue +13 -13
- package/components/HidAccessLogDashboard.vue +3 -3
- package/components/HidIntercomManagement.vue +14 -14
- package/components/HidQrCodeConfiguration.vue +3 -3
- package/components/HidReaderManagement.vue +1 -1
- package/components/InventoryPhotoThumbs.vue +1 -1
- package/components/Layout/Header.vue +2 -2
- package/components/Layout/NavigationDrawer.vue +1 -1
- package/components/Nfc/NFCRouteScheduleTimes.vue +1 -1
- package/components/PlateNumberDisplay.vue +1 -1
- package/components/RolePermissionFormCreate.vue +6 -2
- package/components/RolePermissionFormPreviewUpdate.vue +20 -4
- package/components/ScheduleAreaMain.vue +6 -9
- package/components/ScheduleGuide.vue +101 -0
- package/components/ScheduleTaskForm.vue +88 -7
- package/components/ScheduleTaskMain.vue +163 -56
- package/components/StatusChip.vue +1 -1
- package/components/VisitorsReportPreview.vue +2 -2
- 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/breadcrumb.ts +7 -0
- package/utils/nav-sections.ts +2 -0
- package/utils/org-modules.ts +3 -0
- package/utils/permission-labels.ts +6 -0
- package/utils/schedule-task.ts +106 -23
- package/utils/security-catalogue.ts +1 -1
- package/utils/trade-catalogue.ts +43 -0
|
@@ -5,10 +5,52 @@
|
|
|
5
5
|
:org-id="orgId"
|
|
6
6
|
:site="site"
|
|
7
7
|
:service-type="serviceType"
|
|
8
|
-
text="An extra visit adds one more round to
|
|
9
|
-
|
|
8
|
+
text="An extra visit adds one more round to the Daily Checklist at a time you choose - once, or on a repeat."
|
|
9
|
+
how-title="How extra visits work"
|
|
10
|
+
how-key="extra-visits"
|
|
11
|
+
>
|
|
12
|
+
<ol class="sg-steps">
|
|
13
|
+
<li>
|
|
14
|
+
Every night the app makes the <strong>Daily Checklist</strong>
|
|
15
|
+
for every area on this site. You do not need to do anything for
|
|
16
|
+
that.
|
|
17
|
+
</li>
|
|
18
|
+
<li>
|
|
19
|
+
An <strong>extra visit</strong> adds one more round to that
|
|
20
|
+
checklist, in the areas you pick, at the time you set - once, or
|
|
21
|
+
repeating (every day, weekdays, chosen days, or monthly).
|
|
22
|
+
</li>
|
|
23
|
+
<li>
|
|
24
|
+
At that time, staff see the extra round in their mobile app's
|
|
25
|
+
daily checklist (with a push notification) and tick each unit as
|
|
26
|
+
they finish it.
|
|
27
|
+
</li>
|
|
28
|
+
<li>
|
|
29
|
+
Here and in the Daily Checklist you see how it went:
|
|
30
|
+
<strong>Done</strong>, <strong>In progress</strong>,
|
|
31
|
+
<strong>Needs redo</strong> or <strong>Missed</strong>.
|
|
32
|
+
</li>
|
|
33
|
+
</ol>
|
|
34
|
+
<div class="sg-example">
|
|
35
|
+
<strong>Example:</strong> the lobby toilets get busy at lunch. Add
|
|
36
|
+
an extra visit called "Lunch toilet check", pick the area "Lobby
|
|
37
|
+
Toilets", set 2:00 pm, every weekday. From the next weekday, staff
|
|
38
|
+
get one more toilet round at 2:00 pm, on top of their normal ones.
|
|
39
|
+
</div>
|
|
40
|
+
<div class="sg-h">What the words mean</div>
|
|
41
|
+
<dl class="sg-legend">
|
|
42
|
+
<template v-for="h in STATUS_HELP" :key="h.word">
|
|
43
|
+
<dt><StatusChip :status="h.word" :label="h.word" /></dt>
|
|
44
|
+
<dd>{{ h.meaning }}</dd>
|
|
45
|
+
</template>
|
|
46
|
+
<template v-for="h in RESULT_HELP" :key="'r' + h.word">
|
|
47
|
+
<dt><StatusChip :status="h.word" :label="h.word" /></dt>
|
|
48
|
+
<dd>Last visit: {{ h.meaning }}</dd>
|
|
49
|
+
</template>
|
|
50
|
+
</dl>
|
|
51
|
+
</ScheduleGuide>
|
|
10
52
|
<TableMain
|
|
11
|
-
:title="'Extra Visits
|
|
53
|
+
:title="'Extra Visits'"
|
|
12
54
|
:items="items"
|
|
13
55
|
:headers="headers"
|
|
14
56
|
:loading="loading"
|
|
@@ -26,9 +68,14 @@
|
|
|
26
68
|
<div class="st-empty">
|
|
27
69
|
<strong>No extra visits yet.</strong>
|
|
28
70
|
<span>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
71
|
+
Your Daily Checklist already covers the normal rounds. Add an
|
|
72
|
+
extra visit when an area needs one more round at a set time -
|
|
73
|
+
for example "Lunch toilet check, Lobby Toilets, 2:00 pm every
|
|
74
|
+
weekday".
|
|
75
|
+
</span>
|
|
76
|
+
<span v-if="canCreateScheduleTask">
|
|
77
|
+
Press <strong>Add Extra Visit</strong> above to start. Your
|
|
78
|
+
Areas and Units must be set up first.
|
|
32
79
|
</span>
|
|
33
80
|
</div>
|
|
34
81
|
</template>
|
|
@@ -61,27 +108,6 @@
|
|
|
61
108
|
</v-row>
|
|
62
109
|
</template>
|
|
63
110
|
|
|
64
|
-
<template #item.areas="{ value }">
|
|
65
|
-
<div v-if="value && value.length > 0">
|
|
66
|
-
<StatusChip
|
|
67
|
-
v-for="(area, idx) in value.slice(0, 2)"
|
|
68
|
-
:key="idx"
|
|
69
|
-
class="mr-1 mb-1"
|
|
70
|
-
:dot="false"
|
|
71
|
-
tone="neutral"
|
|
72
|
-
:label="area.name"
|
|
73
|
-
/>
|
|
74
|
-
<StatusChip
|
|
75
|
-
v-if="value.length > 2"
|
|
76
|
-
class="mb-1"
|
|
77
|
-
:dot="false"
|
|
78
|
-
tone="neutral"
|
|
79
|
-
:label="`+${value.length - 2} more`"
|
|
80
|
-
/>
|
|
81
|
-
</div>
|
|
82
|
-
<span v-else>N/A</span>
|
|
83
|
-
</template>
|
|
84
|
-
|
|
85
111
|
<template #item.when="{ item }">
|
|
86
112
|
<span class="st-when">{{ item.whenText }}</span>
|
|
87
113
|
</template>
|
|
@@ -91,24 +117,34 @@
|
|
|
91
117
|
</template>
|
|
92
118
|
|
|
93
119
|
<template #item.lastRun="{ item }">
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
v-else
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
120
|
+
<div class="st-cell">
|
|
121
|
+
<StatusChip
|
|
122
|
+
v-if="item.lastRun && item.lastRunWord"
|
|
123
|
+
:status="item.lastRunWord"
|
|
124
|
+
:label="item.lastRunText"
|
|
125
|
+
/>
|
|
126
|
+
<span v-else>{{ item.lastRunText }}</span>
|
|
127
|
+
<a
|
|
128
|
+
v-if="item.lastRun"
|
|
129
|
+
href="#"
|
|
130
|
+
class="st-link"
|
|
131
|
+
@click.prevent.stop="openChecklist(item)"
|
|
132
|
+
>
|
|
133
|
+
See that day's checklist
|
|
134
|
+
</a>
|
|
135
|
+
</div>
|
|
105
136
|
</template>
|
|
106
137
|
|
|
107
138
|
<template #item.status="{ item }">
|
|
108
|
-
<
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
139
|
+
<div class="st-cell">
|
|
140
|
+
<StatusChip
|
|
141
|
+
:status="item.stateLabel"
|
|
142
|
+
:label="item.stateLabel"
|
|
143
|
+
/>
|
|
144
|
+
<span v-if="item.stateKey === 'ended'" class="st-note">
|
|
145
|
+
{{ item.finishedText }}
|
|
146
|
+
</span>
|
|
147
|
+
</div>
|
|
112
148
|
</template>
|
|
113
149
|
|
|
114
150
|
<template #item.actions="{ item }">
|
|
@@ -123,6 +159,17 @@
|
|
|
123
159
|
>
|
|
124
160
|
Edit
|
|
125
161
|
</v-btn>
|
|
162
|
+
<v-btn
|
|
163
|
+
v-if="canUpdateScheduleTask && item.stateKey === 'ended'"
|
|
164
|
+
size="small"
|
|
165
|
+
variant="tonal"
|
|
166
|
+
color="primary"
|
|
167
|
+
class="text-none"
|
|
168
|
+
prepend-icon="mdi-calendar-refresh"
|
|
169
|
+
@click.stop="onExtendRow(item)"
|
|
170
|
+
>
|
|
171
|
+
Extend or repeat
|
|
172
|
+
</v-btn>
|
|
126
173
|
<v-btn
|
|
127
174
|
v-if="canUpdateScheduleTask && item.stateKey !== 'ended'"
|
|
128
175
|
size="small"
|
|
@@ -155,6 +202,7 @@
|
|
|
155
202
|
v-model:show="showForm"
|
|
156
203
|
:mode="formMode"
|
|
157
204
|
:taskData="selectedTask"
|
|
205
|
+
:start-step="formStartStep"
|
|
158
206
|
:site="props.site"
|
|
159
207
|
:serviceType="props.serviceType"
|
|
160
208
|
@refresh="getTaskRefresh"
|
|
@@ -194,7 +242,7 @@
|
|
|
194
242
|
|
|
195
243
|
<v-row no-gutters class="mb-2">
|
|
196
244
|
<v-col cols="5" class="text-subtitle-2 font-weight-bold">
|
|
197
|
-
Next
|
|
245
|
+
Next visit:
|
|
198
246
|
</v-col>
|
|
199
247
|
<v-col cols="7" class="text-subtitle-2">
|
|
200
248
|
{{ selectedNext || "-" }}
|
|
@@ -203,10 +251,18 @@
|
|
|
203
251
|
|
|
204
252
|
<v-row no-gutters class="mb-2">
|
|
205
253
|
<v-col cols="5" class="text-subtitle-2 font-weight-bold">
|
|
206
|
-
Last
|
|
254
|
+
Last visit:
|
|
207
255
|
</v-col>
|
|
208
256
|
<v-col cols="7" class="text-subtitle-2">
|
|
209
257
|
{{ lastRunLabel(selectedTask) }}
|
|
258
|
+
<a
|
|
259
|
+
v-if="selectedTask.lastRun"
|
|
260
|
+
href="#"
|
|
261
|
+
class="st-link d-block"
|
|
262
|
+
@click.prevent="openChecklist(selectedTask)"
|
|
263
|
+
>
|
|
264
|
+
See that day's checklist
|
|
265
|
+
</a>
|
|
210
266
|
</v-col>
|
|
211
267
|
</v-row>
|
|
212
268
|
|
|
@@ -319,7 +375,7 @@
|
|
|
319
375
|
</div>
|
|
320
376
|
<span v-else class="text-caption text-disabled"
|
|
321
377
|
>No units in this area, so it is skipped when
|
|
322
|
-
this visit
|
|
378
|
+
this visit is added.</span
|
|
323
379
|
>
|
|
324
380
|
</v-card-text>
|
|
325
381
|
</div>
|
|
@@ -348,6 +404,12 @@
|
|
|
348
404
|
:status="stateOf(selectedTask).label"
|
|
349
405
|
:label="stateOf(selectedTask).label"
|
|
350
406
|
/>
|
|
407
|
+
<div
|
|
408
|
+
v-if="stateOf(selectedTask).key === 'ended'"
|
|
409
|
+
class="st-note mt-1"
|
|
410
|
+
>
|
|
411
|
+
{{ finishedReason(selectedTask as any) }}
|
|
412
|
+
</div>
|
|
351
413
|
</v-col>
|
|
352
414
|
</v-row>
|
|
353
415
|
|
|
@@ -383,8 +445,8 @@
|
|
|
383
445
|
</v-card-title>
|
|
384
446
|
|
|
385
447
|
<v-card-text class="screen-modal__body">
|
|
386
|
-
This stops all future
|
|
387
|
-
happened
|
|
448
|
+
This stops all future extra visits from it. Visits that already
|
|
449
|
+
happened stay on their Daily Checklists. Delete it?
|
|
388
450
|
</v-card-text>
|
|
389
451
|
|
|
390
452
|
<div class="screen-modal__footer">
|
|
@@ -422,8 +484,12 @@ import useScheduleTask from "../composables/useScheduleTask";
|
|
|
422
484
|
import useUtils from "../composables/useUtils";
|
|
423
485
|
import useAreas from "../composables/useAreas";
|
|
424
486
|
import {
|
|
487
|
+
RESULT_HELP,
|
|
488
|
+
STATUS_HELP,
|
|
425
489
|
STATUS_LABELS,
|
|
490
|
+
describeRow,
|
|
426
491
|
describeWhen,
|
|
492
|
+
finishedReason,
|
|
427
493
|
fmtMoment,
|
|
428
494
|
lastRunLabel,
|
|
429
495
|
scheduleNameFor,
|
|
@@ -458,7 +524,16 @@ const canViewScheduleTaskDetails = computed(
|
|
|
458
524
|
() => props.canViewScheduleTaskDetails
|
|
459
525
|
);
|
|
460
526
|
|
|
461
|
-
const
|
|
527
|
+
const checklistRoute = scheduleNameFor(props.serviceType).route;
|
|
528
|
+
|
|
529
|
+
/** Opens the Daily Checklist filtered to the day this extra visit was last added. */
|
|
530
|
+
function openChecklist(row: Record<string, any>) {
|
|
531
|
+
const day = String(row?.lastRun || "").slice(0, 10);
|
|
532
|
+
navigateTo({
|
|
533
|
+
path: `/${props.orgId}/${props.site}/${checklistRoute}`,
|
|
534
|
+
query: /^\d{4}-\d{2}-\d{2}$/.test(day) ? { date: day } : {},
|
|
535
|
+
});
|
|
536
|
+
}
|
|
462
537
|
|
|
463
538
|
const page = ref(1);
|
|
464
539
|
const pages = ref(0);
|
|
@@ -468,6 +543,7 @@ const searchInput = ref("");
|
|
|
468
543
|
const showForm = ref(false);
|
|
469
544
|
const selectedTask = ref<Record<string, any>>({});
|
|
470
545
|
const formMode = ref<"add" | "edit">("add");
|
|
546
|
+
const formStartStep = ref(1);
|
|
471
547
|
const submitting = ref(false);
|
|
472
548
|
const dialogShowMoreActions = ref(false);
|
|
473
549
|
const dialogDeleteTask = ref(false);
|
|
@@ -517,11 +593,10 @@ watch(dialogShowMoreActions, async (isOpen) => {
|
|
|
517
593
|
const items = ref<any[]>([]);
|
|
518
594
|
|
|
519
595
|
const headers = [
|
|
520
|
-
{ title: "
|
|
521
|
-
{ title: "
|
|
522
|
-
{ title: "
|
|
523
|
-
{ title: "
|
|
524
|
-
{ title: "Last run", value: "lastRun", sortable: false },
|
|
596
|
+
{ title: "Extra visit", value: "title" },
|
|
597
|
+
{ title: "What will happen", value: "when", sortable: false },
|
|
598
|
+
{ title: "Next visit", value: "nextRun", sortable: false },
|
|
599
|
+
{ title: "Last visit", value: "lastRun", sortable: false },
|
|
525
600
|
{ title: "Status", value: "status", sortable: false },
|
|
526
601
|
{ title: "", value: "actions", sortable: false, align: "end" as const },
|
|
527
602
|
];
|
|
@@ -566,7 +641,9 @@ watchEffect(() => {
|
|
|
566
641
|
const st = stateOf(row);
|
|
567
642
|
return {
|
|
568
643
|
...row,
|
|
569
|
-
whenText:
|
|
644
|
+
whenText: describeRow(row),
|
|
645
|
+
finishedText: finishedReason(row),
|
|
646
|
+
lastRunWord: ({ done: "Done", missed: "Missed", pending: "In progress" } as Record<string, string>)[row.lastRunStatus] || "",
|
|
570
647
|
nextRunText: fmtMoment(row.nextRun),
|
|
571
648
|
lastRunText: lastRunLabel(row),
|
|
572
649
|
stateKey: st.key,
|
|
@@ -590,6 +667,7 @@ watch(
|
|
|
590
667
|
|
|
591
668
|
function openFormDialog() {
|
|
592
669
|
formMode.value = "add";
|
|
670
|
+
formStartStep.value = 1;
|
|
593
671
|
selectedTask.value = {};
|
|
594
672
|
showForm.value = true;
|
|
595
673
|
}
|
|
@@ -597,6 +675,15 @@ function openFormDialog() {
|
|
|
597
675
|
function onEditRow(item: Record<string, any>) {
|
|
598
676
|
selectedTask.value = item;
|
|
599
677
|
formMode.value = "edit";
|
|
678
|
+
formStartStep.value = 1;
|
|
679
|
+
showForm.value = true;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
// A finished extra visit: open its "When" step so a new end date or repeat is one change away.
|
|
683
|
+
function onExtendRow(item: Record<string, any>) {
|
|
684
|
+
selectedTask.value = item;
|
|
685
|
+
formMode.value = "edit";
|
|
686
|
+
formStartStep.value = 3;
|
|
600
687
|
showForm.value = true;
|
|
601
688
|
}
|
|
602
689
|
|
|
@@ -612,8 +699,8 @@ async function togglePause(item: Record<string, any>) {
|
|
|
612
699
|
await setScheduleTaskPaused(item._id, !resuming);
|
|
613
700
|
showMessage(
|
|
614
701
|
resuming
|
|
615
|
-
? "Resumed.
|
|
616
|
-
: "Paused.
|
|
702
|
+
? "Resumed. Extra visits are added again from its next date."
|
|
703
|
+
: "Paused. No extra visits are added until you resume it.",
|
|
617
704
|
"success",
|
|
618
705
|
);
|
|
619
706
|
await getTaskRefresh();
|
|
@@ -649,6 +736,7 @@ async function onRowClick(data: any) {
|
|
|
649
736
|
const onEditFromMoreAction = async () => {
|
|
650
737
|
dialogShowMoreActions.value = false;
|
|
651
738
|
formMode.value = "edit";
|
|
739
|
+
formStartStep.value = stateOf(selectedTask.value).key === "ended" ? 3 : 1;
|
|
652
740
|
showForm.value = true;
|
|
653
741
|
};
|
|
654
742
|
|
|
@@ -693,8 +781,27 @@ async function _deleteScheduleTask() {
|
|
|
693
781
|
color: var(--text2);
|
|
694
782
|
}
|
|
695
783
|
.st-when {
|
|
784
|
+
display: inline-block;
|
|
785
|
+
min-width: 220px;
|
|
696
786
|
white-space: normal;
|
|
697
787
|
}
|
|
788
|
+
.st-cell {
|
|
789
|
+
display: flex;
|
|
790
|
+
flex-direction: column;
|
|
791
|
+
align-items: flex-start;
|
|
792
|
+
gap: 4px;
|
|
793
|
+
}
|
|
794
|
+
.st-note {
|
|
795
|
+
font-size: var(--fs-chip-lg);
|
|
796
|
+
color: var(--text2);
|
|
797
|
+
white-space: normal;
|
|
798
|
+
}
|
|
799
|
+
.st-link {
|
|
800
|
+
font-size: var(--fs-chip-lg);
|
|
801
|
+
font-weight: 600;
|
|
802
|
+
color: rgb(var(--v-theme-primary));
|
|
803
|
+
text-decoration: underline;
|
|
804
|
+
}
|
|
698
805
|
.st-actions {
|
|
699
806
|
display: flex;
|
|
700
807
|
justify-content: flex-end;
|
|
@@ -104,7 +104,7 @@ const toneClass = computed(() =>
|
|
|
104
104
|
font-size: var(--fs-chip);
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
/* The role tag: `padding:3px 10px;border-radius:999px;font-size:
|
|
107
|
+
/* The role tag: `padding:3px 10px;border-radius:999px;font-size:12.5px`. */
|
|
108
108
|
.status-chip--pill {
|
|
109
109
|
padding: 3px 10px;
|
|
110
110
|
font-size: var(--fs-chip);
|
|
@@ -397,12 +397,12 @@ onMounted(() => {
|
|
|
397
397
|
|
|
398
398
|
:deep(.v-data-table thead tr th) {
|
|
399
399
|
padding: 6px !important;
|
|
400
|
-
font-size:
|
|
400
|
+
font-size: 12px !important;
|
|
401
401
|
}
|
|
402
402
|
|
|
403
403
|
:deep(.v-data-table tbody tr td) {
|
|
404
404
|
padding: 4px !important;
|
|
405
|
-
font-size:
|
|
405
|
+
font-size: 12px !important;
|
|
406
406
|
}
|
|
407
407
|
|
|
408
408
|
:deep(.v-img) {
|
|
@@ -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.137.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/breadcrumb.ts
CHANGED
|
@@ -25,6 +25,13 @@ const ACRONYMS: Record<string, string> = {
|
|
|
25
25
|
const RENAMED: [string, string][] = [
|
|
26
26
|
["Building Mgmt", "Occupancy Mgmt"],
|
|
27
27
|
["Event Mgmt", "Task Calendar"],
|
|
28
|
+
// Schedule screens (owner 21 Oct): the same names the menu and the mobile apps use.
|
|
29
|
+
["Schedule Task", "Extra Visits"],
|
|
30
|
+
...["Cleaning", "Technician", "Pest", "Pool", "Landscape"].flatMap((t): [string, string][] => [
|
|
31
|
+
[`${t} Schedule Area`, "Area"],
|
|
32
|
+
[`${t} Schedule Unit`, "Unit"],
|
|
33
|
+
[`${t} Schedule`, "Daily Checklist"],
|
|
34
|
+
]),
|
|
28
35
|
];
|
|
29
36
|
|
|
30
37
|
export function moduleNameFromRoute(route: {
|
package/utils/nav-sections.ts
CHANGED
|
@@ -50,6 +50,8 @@ export const NAV_SECTIONS: Record<string, string> = {
|
|
|
50
50
|
Area: "SERVICE DELIVERY",
|
|
51
51
|
Unit: "SERVICE DELIVERY",
|
|
52
52
|
"Schedule Task": "SERVICE DELIVERY",
|
|
53
|
+
"Daily Checklist": "SERVICE DELIVERY",
|
|
54
|
+
"Extra Visits": "SERVICE DELIVERY",
|
|
53
55
|
|
|
54
56
|
// EQUIPMENT
|
|
55
57
|
"Equipment Management": "EQUIPMENT",
|
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
|
/**
|