@7365admin1/layer-common 4.97.1-staging.595 → 4.97.1-staging.596
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.
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
:org-id="orgId"
|
|
6
6
|
:site="site"
|
|
7
7
|
:service-type="serviceType"
|
|
8
|
-
:text="FEATURE_SUBTITLE"
|
|
9
|
-
how-title="How extra checks work"
|
|
8
|
+
:text="embedded ? '' : FEATURE_SUBTITLE"
|
|
9
|
+
:how-title="embedded ? '' : 'How extra checks work'"
|
|
10
10
|
how-key="extra-visits"
|
|
11
11
|
>
|
|
12
12
|
<ol class="sg-steps">
|
|
@@ -509,6 +509,8 @@ const props = defineProps({
|
|
|
509
509
|
canUpdateScheduleTask: { type: Boolean, default: false },
|
|
510
510
|
canDeleteScheduleTask: { type: Boolean, default: false },
|
|
511
511
|
canViewScheduleTaskDetails: { type: Boolean, default: false },
|
|
512
|
+
/** Inside the Task Calendar page: its one help box replaces this page's own. */
|
|
513
|
+
embedded: { type: Boolean, default: false },
|
|
512
514
|
});
|
|
513
515
|
|
|
514
516
|
const { formatDate, debounce } = useUtils();
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
TASK CALENDAR for the five trade apps (owner 24 Oct 2026): one page for every
|
|
3
|
+
scheduled job at the site, in two tabs.
|
|
4
|
+
|
|
5
|
+
"From managing agent" - tasks the managing agent gave this company
|
|
6
|
+
(TaskCalendarReadOnly, view only).
|
|
7
|
+
"My extra checks" - the company's own extra checks
|
|
8
|
+
(ScheduleTaskMain, add / edit / pause / delete).
|
|
9
|
+
|
|
10
|
+
Each tab shows only when the viewer's role already allows it (the same keys
|
|
11
|
+
the two separate menu items used), so nobody gains or loses access. The tab
|
|
12
|
+
is kept in the address (?tab=agent | extra) so a link opens the right one.
|
|
13
|
+
-->
|
|
14
|
+
<template>
|
|
15
|
+
<v-row no-gutters>
|
|
16
|
+
<v-col cols="12">
|
|
17
|
+
<PageHeader title="Task Calendar" />
|
|
18
|
+
|
|
19
|
+
<v-expansion-panels
|
|
20
|
+
v-model="helpPanel"
|
|
21
|
+
class="tc-help mb-4"
|
|
22
|
+
variant="accordion"
|
|
23
|
+
flat
|
|
24
|
+
>
|
|
25
|
+
<v-expansion-panel value="how">
|
|
26
|
+
<v-expansion-panel-title class="tc-help__title">
|
|
27
|
+
<v-icon size="20" class="mr-2">mdi-help-circle-outline</v-icon>
|
|
28
|
+
How the Task Calendar works
|
|
29
|
+
</v-expansion-panel-title>
|
|
30
|
+
<v-expansion-panel-text class="tc-help__body">
|
|
31
|
+
<ul class="tc-help__list">
|
|
32
|
+
<li v-if="showAgent">
|
|
33
|
+
<strong>From managing agent</strong> - jobs the building's
|
|
34
|
+
managing agent gave your company, for example a monthly pump
|
|
35
|
+
service. You can only look at them here. The managing agent
|
|
36
|
+
marks them done.
|
|
37
|
+
</li>
|
|
38
|
+
<li v-if="showExtra">
|
|
39
|
+
<strong>My extra checks</strong> - checks your company adds on
|
|
40
|
+
top of the normal daily rounds, for example "Lunch toilet
|
|
41
|
+
check, Lobby Toilets, 2:00 pm every weekday". The managing
|
|
42
|
+
agent can see them too, but cannot change them.
|
|
43
|
+
</li>
|
|
44
|
+
<li v-if="showExtra">
|
|
45
|
+
At the set time, the extra check appears in staff's
|
|
46
|
+
<strong>Daily Checklist</strong> on their phone and they get a
|
|
47
|
+
notification. They tick each unit there.
|
|
48
|
+
</li>
|
|
49
|
+
</ul>
|
|
50
|
+
<div class="tc-help__h">What the words mean</div>
|
|
51
|
+
<dl class="tc-help__legend">
|
|
52
|
+
<template v-for="h in words" :key="h.word">
|
|
53
|
+
<dt><StatusChip :status="h.word" :label="h.word" :tone="h.tone" /></dt>
|
|
54
|
+
<dd>{{ h.meaning }}</dd>
|
|
55
|
+
</template>
|
|
56
|
+
</dl>
|
|
57
|
+
</v-expansion-panel-text>
|
|
58
|
+
</v-expansion-panel>
|
|
59
|
+
</v-expansion-panels>
|
|
60
|
+
|
|
61
|
+
<div v-if="tabs.length > 1" class="app-tabs mb-4" role="tablist">
|
|
62
|
+
<button
|
|
63
|
+
v-for="t in tabs"
|
|
64
|
+
:key="t.key"
|
|
65
|
+
type="button"
|
|
66
|
+
role="tab"
|
|
67
|
+
class="app-tab"
|
|
68
|
+
:class="{ 'app-tab--active': tab === t.key }"
|
|
69
|
+
:aria-selected="tab === t.key"
|
|
70
|
+
:data-test="`task-calendar-tab-${t.key}`"
|
|
71
|
+
@click="chooseTab(t.key)"
|
|
72
|
+
>
|
|
73
|
+
{{ t.name }}
|
|
74
|
+
</button>
|
|
75
|
+
</div>
|
|
76
|
+
|
|
77
|
+
<TaskCalendarReadOnly
|
|
78
|
+
v-if="tab === 'agent'"
|
|
79
|
+
:site-id="siteId"
|
|
80
|
+
embedded
|
|
81
|
+
/>
|
|
82
|
+
<ScheduleTaskMain
|
|
83
|
+
v-else-if="tab === 'extra'"
|
|
84
|
+
:org-id="orgId"
|
|
85
|
+
:site="siteId"
|
|
86
|
+
:service-type="serviceType"
|
|
87
|
+
:can-view-schedule-tasks="canViewScheduleTasks"
|
|
88
|
+
:can-create-schedule-task="canCreateScheduleTask"
|
|
89
|
+
:can-update-schedule-task="canUpdateScheduleTask"
|
|
90
|
+
:can-delete-schedule-task="canDeleteScheduleTask"
|
|
91
|
+
:can-view-schedule-task-details="canViewScheduleTaskDetails"
|
|
92
|
+
embedded
|
|
93
|
+
/>
|
|
94
|
+
<v-alert v-else type="info" variant="tonal" class="ma-4">
|
|
95
|
+
Your role does not include the Task Calendar.
|
|
96
|
+
</v-alert>
|
|
97
|
+
</v-col>
|
|
98
|
+
</v-row>
|
|
99
|
+
</template>
|
|
100
|
+
|
|
101
|
+
<script setup lang="ts">
|
|
102
|
+
import {
|
|
103
|
+
pickTaskCalendarTab,
|
|
104
|
+
taskCalendarWords,
|
|
105
|
+
type TTaskCalendarTab,
|
|
106
|
+
} from "../utils/task-calendar-tabs";
|
|
107
|
+
|
|
108
|
+
const props = defineProps({
|
|
109
|
+
orgId: { type: String, required: true },
|
|
110
|
+
siteId: { type: String, required: true },
|
|
111
|
+
serviceType: { type: String, default: "" },
|
|
112
|
+
/** Opened from an old Extra Checks link: start on "My extra checks". */
|
|
113
|
+
initialTab: { type: String, default: "" },
|
|
114
|
+
canViewTaskCalendar: { type: Boolean, default: false },
|
|
115
|
+
canViewScheduleTasks: { type: Boolean, default: false },
|
|
116
|
+
canCreateScheduleTask: { type: Boolean, default: false },
|
|
117
|
+
canUpdateScheduleTask: { type: Boolean, default: false },
|
|
118
|
+
canDeleteScheduleTask: { type: Boolean, default: false },
|
|
119
|
+
canViewScheduleTaskDetails: { type: Boolean, default: false },
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const route = useRoute();
|
|
123
|
+
const router = useRouter();
|
|
124
|
+
|
|
125
|
+
const showAgent = computed(() => props.canViewTaskCalendar);
|
|
126
|
+
const showExtra = computed(() => props.canViewScheduleTasks);
|
|
127
|
+
|
|
128
|
+
const words = computed(() =>
|
|
129
|
+
taskCalendarWords({ agent: showAgent.value, extra: showExtra.value }),
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
const tabs = computed(() => [
|
|
133
|
+
...(showAgent.value ? [{ key: "agent" as const, name: "From managing agent" }] : []),
|
|
134
|
+
...(showExtra.value ? [{ key: "extra" as const, name: "My extra checks" }] : []),
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
const tab = computed<TTaskCalendarTab | "">(() =>
|
|
138
|
+
pickTaskCalendarTab(route.query.tab, props.initialTab, {
|
|
139
|
+
agent: showAgent.value,
|
|
140
|
+
extra: showExtra.value,
|
|
141
|
+
}),
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
function chooseTab(key: TTaskCalendarTab) {
|
|
145
|
+
if (key === tab.value) return;
|
|
146
|
+
router.replace({ query: { ...route.query, tab: key } });
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Open until the viewer closes it; remembered on this browser only.
|
|
150
|
+
const HELP_STORE = "task-calendar-help";
|
|
151
|
+
const helpPanel = ref<string | undefined>("how");
|
|
152
|
+
onMounted(() => {
|
|
153
|
+
try {
|
|
154
|
+
if (localStorage.getItem(HELP_STORE) === "closed") helpPanel.value = undefined;
|
|
155
|
+
} catch {
|
|
156
|
+
// storage blocked: stay open
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
watch(helpPanel, (v) => {
|
|
160
|
+
try {
|
|
161
|
+
localStorage.setItem(HELP_STORE, v ? "open" : "closed");
|
|
162
|
+
} catch {
|
|
163
|
+
// storage blocked: nothing to remember
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
</script>
|
|
167
|
+
|
|
168
|
+
<style scoped>
|
|
169
|
+
.tc-help {
|
|
170
|
+
border: 1px solid var(--border);
|
|
171
|
+
border-radius: 8px;
|
|
172
|
+
}
|
|
173
|
+
.tc-help__title {
|
|
174
|
+
font-weight: 700;
|
|
175
|
+
}
|
|
176
|
+
.tc-help__body {
|
|
177
|
+
font-size: var(--fs-cell);
|
|
178
|
+
color: var(--text);
|
|
179
|
+
}
|
|
180
|
+
.tc-help__list {
|
|
181
|
+
margin: 0 0 12px 18px;
|
|
182
|
+
display: flex;
|
|
183
|
+
flex-direction: column;
|
|
184
|
+
gap: 6px;
|
|
185
|
+
}
|
|
186
|
+
.tc-help__h {
|
|
187
|
+
font-weight: 700;
|
|
188
|
+
margin-bottom: 6px;
|
|
189
|
+
}
|
|
190
|
+
.tc-help__legend {
|
|
191
|
+
display: grid;
|
|
192
|
+
grid-template-columns: max-content 1fr;
|
|
193
|
+
gap: 6px 12px;
|
|
194
|
+
align-items: center;
|
|
195
|
+
margin: 0;
|
|
196
|
+
}
|
|
197
|
+
.tc-help__legend dd {
|
|
198
|
+
margin: 0;
|
|
199
|
+
}
|
|
200
|
+
</style>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<v-row no-gutters>
|
|
3
|
-
<v-col cols="12">
|
|
3
|
+
<v-col v-if="!embedded" cols="12">
|
|
4
4
|
<PageHeader title="Task Calendar"
|
|
5
5
|
subtitle="Tasks the managing agent assigned to your company at this site. View only - the managing agent marks them done." />
|
|
6
6
|
</v-col>
|
|
@@ -54,6 +54,8 @@ import { readListPage } from '../utils/list-page';
|
|
|
54
54
|
|
|
55
55
|
const props = defineProps({
|
|
56
56
|
siteId: { type: String, required: true },
|
|
57
|
+
/** Inside the Task Calendar page (TaskCalendarMain), which has the title and help. */
|
|
58
|
+
embedded: { type: Boolean, default: false },
|
|
57
59
|
});
|
|
58
60
|
|
|
59
61
|
const LIMIT = 20;
|
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.596",
|
|
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
|
@@ -26,8 +26,9 @@ const RENAMED: [string, string][] = [
|
|
|
26
26
|
["Building Mgmt", "Occupancy Mgmt"],
|
|
27
27
|
["Event Mgmt", "Task Calendar"],
|
|
28
28
|
// Schedule screens (owner 21 Oct): the same names the menu and the mobile apps use.
|
|
29
|
-
|
|
30
|
-
["
|
|
29
|
+
// Extra Checks live inside the Task Calendar page since 24 Oct 2026.
|
|
30
|
+
["Schedule Task", "Task Calendar"],
|
|
31
|
+
["Extra Visits", "Task Calendar"],
|
|
31
32
|
...["Cleaning", "Technician", "Pest", "Pool", "Landscape"].flatMap((t): [string, string][] => [
|
|
32
33
|
[`${t} Schedule Area`, "Area"],
|
|
33
34
|
[`${t} Schedule Unit`, "Unit"],
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trade apps' Task Calendar (owner 24 Oct 2026): which tab to show, and the
|
|
3
|
+
* words of its one help box. Pure: no Vue, unit-tested with `node --test`.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
export type TTaskCalendarTab = "agent" | "extra";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* The tab to open: the address (?tab=) when the viewer may see it, else the
|
|
10
|
+
* page's own start tab (an old Extra Checks link starts on "extra"), else the
|
|
11
|
+
* first tab the viewer may see. "" = no tab at all (role has neither).
|
|
12
|
+
*/
|
|
13
|
+
export function pickTaskCalendarTab(
|
|
14
|
+
query: unknown,
|
|
15
|
+
initial: unknown,
|
|
16
|
+
allowed: { agent: boolean; extra: boolean },
|
|
17
|
+
): TTaskCalendarTab | "" {
|
|
18
|
+
const ok = (t: unknown): t is TTaskCalendarTab =>
|
|
19
|
+
(t === "agent" && allowed.agent) || (t === "extra" && allowed.extra);
|
|
20
|
+
const asked = Array.isArray(query) ? query[0] : query;
|
|
21
|
+
if (ok(asked)) return asked;
|
|
22
|
+
if (ok(initial)) return initial;
|
|
23
|
+
if (allowed.agent) return "agent";
|
|
24
|
+
if (allowed.extra) return "extra";
|
|
25
|
+
return "";
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** One line per status word, only for the tabs the viewer sees. */
|
|
29
|
+
export function taskCalendarWords(show: { agent: boolean; extra: boolean }): { word: string; meaning: string; tone?: "info" | "err" }[] {
|
|
30
|
+
const words: { word: string; meaning: string; tone?: "info" | "err" }[] = [];
|
|
31
|
+
if (show.agent) {
|
|
32
|
+
words.push(
|
|
33
|
+
{ word: "Upcoming", meaning: "Managing agent job, not due yet.", tone: "info" },
|
|
34
|
+
{ word: "Overdue", meaning: "Managing agent job, due date passed and not marked done.", tone: "err" },
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
if (show.extra) {
|
|
38
|
+
words.push(
|
|
39
|
+
{ word: "Active", meaning: "Extra check: it will be added at its next date and time." },
|
|
40
|
+
{ word: "Paused", meaning: "Extra check: stopped for now. Press Resume to start it again." },
|
|
41
|
+
{ word: "Finished", meaning: "Extra check: its last date has passed. Press \"Extend or repeat\" to keep it going." },
|
|
42
|
+
{ word: "In progress", meaning: "Last check: some units are ticked, the rest can still be done today." },
|
|
43
|
+
{ word: "Missed", meaning: "Last check: the day ended with units not done." },
|
|
44
|
+
{ word: "Needs redo", meaning: "Last check: a supervisor sent a unit back to be done again." },
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const done = [
|
|
48
|
+
show.agent ? "the managing agent marked the job done" : "",
|
|
49
|
+
show.extra ? "staff ticked every unit of the extra check" : "",
|
|
50
|
+
].filter(Boolean);
|
|
51
|
+
if (done.length) {
|
|
52
|
+
const text = done.join("; or ");
|
|
53
|
+
words.push({ word: "Done", meaning: `${text.charAt(0).toUpperCase()}${text.slice(1)}.` });
|
|
54
|
+
}
|
|
55
|
+
return words;
|
|
56
|
+
}
|