@7365admin1/layer-common 4.97.1-staging.581 → 4.97.1-staging.583

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.
Files changed (39) hide show
  1. package/assets/css/primitives.css +54 -11
  2. package/assets/css/screens.css +3 -3
  3. package/assets/css/tokens.css +17 -12
  4. package/components/AnprCameraBadge.vue +1 -1
  5. package/components/AreaMain.vue +11 -5
  6. package/components/BillingMain.vue +12 -53
  7. package/components/BulletinBoardManagement.vue +9 -13
  8. package/components/CameraWall.vue +7 -7
  9. package/components/CameraWallTile.vue +8 -8
  10. package/components/Chat/ListCard.vue +1 -1
  11. package/components/CleaningScheduleMain.vue +56 -20
  12. package/components/ClientMain.vue +1 -1
  13. package/components/ClientSubscriptionForm.vue +1 -1
  14. package/components/DashboardMain.vue +13 -13
  15. package/components/HidAccessLogDashboard.vue +3 -3
  16. package/components/HidIntercomManagement.vue +14 -14
  17. package/components/HidQrCodeConfiguration.vue +3 -3
  18. package/components/HidReaderManagement.vue +1 -1
  19. package/components/InventoryPhotoThumbs.vue +1 -1
  20. package/components/InvitationMain.vue +8 -0
  21. package/components/Layout/Header.vue +2 -2
  22. package/components/Layout/NavigationDrawer.vue +1 -1
  23. package/components/MemberMain.vue +8 -0
  24. package/components/Nfc/NFCRouteScheduleTimes.vue +1 -1
  25. package/components/PlateNumberDisplay.vue +1 -1
  26. package/components/RolePermissionMain.vue +13 -0
  27. package/components/ScheduleAreaMain.vue +6 -9
  28. package/components/ScheduleGuide.vue +101 -0
  29. package/components/ScheduleTaskForm.vue +88 -7
  30. package/components/ScheduleTaskMain.vue +163 -56
  31. package/components/SiteSettings.vue +5 -5
  32. package/components/StatusChip.vue +1 -1
  33. package/components/VisitorsReportPreview.vue +2 -2
  34. package/composables/useKey.ts +20 -16
  35. package/package.json +1 -1
  36. package/pages/unauthorized.vue +4 -3
  37. package/utils/breadcrumb.ts +7 -0
  38. package/utils/nav-sections.ts +2 -0
  39. package/utils/schedule-task.ts +106 -23
@@ -195,7 +195,7 @@
195
195
  align-items: center;
196
196
  gap: 5px;
197
197
  margin-top: 4px;
198
- font-size: 12.5px;
198
+ font-size: 13.5px;
199
199
  font-weight: 600;
200
200
  color: var(--muted);
201
201
  }
@@ -268,7 +268,7 @@
268
268
  }
269
269
 
270
270
  .app-toolbar__count {
271
- font-size: 12.5px;
271
+ font-size: 13.5px;
272
272
  font-weight: 600;
273
273
  color: var(--muted);
274
274
  font-variant-numeric: tabular-nums;
@@ -336,7 +336,7 @@
336
336
  align-items: center;
337
337
  padding: var(--cellpad) var(--cellpad-x);
338
338
  border-top: 1px solid var(--border);
339
- font-size: 13px;
339
+ font-size: 14px;
340
340
  font-weight: 600;
341
341
  color: var(--text);
342
342
  }
@@ -371,7 +371,7 @@
371
371
  gap: 8px;
372
372
  padding: 52px var(--cellpad-x);
373
373
  border-top: 1px solid var(--border);
374
- font-size: 13px;
374
+ font-size: 14px;
375
375
  font-weight: 600;
376
376
  color: var(--muted);
377
377
  text-align: center;
@@ -605,7 +605,7 @@ button.app-btn--err {
605
605
  }
606
606
 
607
607
  .app-field__label {
608
- font-size: 12px;
608
+ font-size: 13px;
609
609
  font-weight: 600;
610
610
  color: var(--muted);
611
611
  white-space: nowrap;
@@ -928,7 +928,7 @@ select option {
928
928
  background: transparent;
929
929
  color: var(--muted);
930
930
  font-family: inherit;
931
- font-size: 12.5px;
931
+ font-size: 13.5px;
932
932
  font-weight: 700;
933
933
  line-height: 1.2;
934
934
  white-space: nowrap;
@@ -1055,7 +1055,7 @@ select option {
1055
1055
  padding-inline-start: 12px;
1056
1056
  color: var(--text2);
1057
1057
  font-family: var(--font-sans);
1058
- font-size: 13.5px;
1058
+ font-size: 14.5px;
1059
1059
  font-weight: 700;
1060
1060
  opacity: 1;
1061
1061
  }
@@ -1136,7 +1136,7 @@ select option {
1136
1136
  align-items: center;
1137
1137
  padding: 3px 8px;
1138
1138
  border-radius: 999px;
1139
- font-size: 11.5px;
1139
+ font-size: 13px;
1140
1140
  font-weight: 800;
1141
1141
  line-height: 1.2;
1142
1142
  background: var(--ok-bg);
@@ -1150,7 +1150,7 @@ select option {
1150
1150
 
1151
1151
  .app-kpi__sub {
1152
1152
  margin-top: 6px;
1153
- font-size: 12.5px;
1153
+ font-size: 13.5px;
1154
1154
  font-weight: 600;
1155
1155
  color: var(--muted);
1156
1156
  }
@@ -1208,7 +1208,7 @@ select option {
1208
1208
 
1209
1209
  .v-data-table__td-title {
1210
1210
  color: var(--muted);
1211
- font-size: 12px;
1211
+ font-size: 13px;
1212
1212
  }
1213
1213
 
1214
1214
  .v-data-table__td-value {
@@ -1241,7 +1241,7 @@ select option {
1241
1241
  /* ------------------------------------------------------------------ */
1242
1242
  .app-input-label {
1243
1243
  font-family: var(--font-sans);
1244
- font-size: 12.5px;
1244
+ font-size: 13.5px;
1245
1245
  font-weight: 700;
1246
1246
  letter-spacing: 0;
1247
1247
  color: var(--text);
@@ -1251,3 +1251,46 @@ select option {
1251
1251
  .app-input-label__req {
1252
1252
  color: var(--err);
1253
1253
  }
1254
+
1255
+ /* ------------------------------------------------------------------ */
1256
+ /* READABLE TEXT FLOOR (owner 21 Oct 2026: "the font size is too small"). */
1257
+ /* ------------------------------------------------------------------ */
1258
+ /*
1259
+ * Vuetify's stock small sizes, raised once here so every screen in every app
1260
+ * follows without per-page edits: captions, field hints/errors, small chips
1261
+ * and buttons, table footers and list subtitles. Body/table text is 15px via
1262
+ * the tokens; secondary text is at least 13px; nothing is under 12px.
1263
+ * `!important` only where Vuetify's own rule is a utility (`.text-caption`)
1264
+ * or outranks a single class.
1265
+ */
1266
+ .text-caption {
1267
+ font-size: 13px !important;
1268
+ line-height: 1.4 !important;
1269
+ }
1270
+ .text-overline {
1271
+ font-size: 12px !important;
1272
+ }
1273
+ .v-messages,
1274
+ .v-messages__message {
1275
+ font-size: 13px;
1276
+ line-height: 1.35;
1277
+ }
1278
+ .v-chip.v-chip--size-small {
1279
+ font-size: 13px;
1280
+ }
1281
+ .v-chip.v-chip--size-x-small {
1282
+ font-size: 12px;
1283
+ }
1284
+ .v-btn.v-btn--size-small {
1285
+ font-size: 13px;
1286
+ }
1287
+ .v-btn.v-btn--size-x-small {
1288
+ font-size: 12px;
1289
+ }
1290
+ .v-data-table-footer,
1291
+ .v-data-table-footer .v-field__input {
1292
+ font-size: 13.5px;
1293
+ }
1294
+ .v-list-item-subtitle {
1295
+ font-size: 14px;
1296
+ }
@@ -656,7 +656,7 @@
656
656
  }
657
657
 
658
658
  .table-card__range {
659
- font-size: 12px;
659
+ font-size: 13px;
660
660
  font-weight: 600;
661
661
  color: var(--muted);
662
662
  font-variant-numeric: tabular-nums;
@@ -1233,7 +1233,7 @@
1233
1233
  justify-content: center;
1234
1234
  pointer-events: none;
1235
1235
  font-family: var(--font-sans);
1236
- font-size: 12.5px;
1236
+ font-size: 13.5px;
1237
1237
  font-weight: 600;
1238
1238
  letter-spacing: 0;
1239
1239
  color: rgb(139, 143, 152); /* light `--muted` - the pad is paper */
@@ -1241,7 +1241,7 @@
1241
1241
 
1242
1242
  .screen-signpad__label {
1243
1243
  font-family: var(--font-sans);
1244
- font-size: 12.5px;
1244
+ font-size: 13.5px;
1245
1245
  font-weight: 600;
1246
1246
  letter-spacing: 0;
1247
1247
  color: var(--muted);
@@ -122,41 +122,46 @@
122
122
  --font-sans: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
123
123
  "Helvetica Neue", Arial, sans-serif;
124
124
 
125
- /* Type scale - size / weight / letter-spacing, straight from the contract. */
126
- --fs-h1: 22px;
125
+ /*
126
+ * Type scale. Raised 21 Oct 2026 (owner: "most of the pages the font size is
127
+ * too small"): body and table text 15px, secondary text at least 13px,
128
+ * nothing under 12px. Was h1 22 / card 15.5 / head 11 / cell 13.5 / dense 13 /
129
+ * nav 13 / section 10.5 / btn 13.5 / chip 11.5 / chip-lg 12 / crumb 12.5.
130
+ */
131
+ --fs-h1: 24px;
127
132
  --fw-h1: 800;
128
133
  --ls-h1: -0.01em;
129
134
 
130
- --fs-card-title: 15.5px;
135
+ --fs-card-title: 16.5px;
131
136
  --fw-card-title: 800;
132
137
 
133
138
  --fs-kpi: 30px;
134
139
  --fw-kpi: 800;
135
140
  --ls-kpi: -0.02em;
136
141
 
137
- --fs-table-head: 11px;
142
+ --fs-table-head: 12.5px;
138
143
  --fw-table-head: 800;
139
144
  --ls-table-head: 0.07em;
140
145
 
141
- --fs-cell: 13.5px;
146
+ --fs-cell: 15px;
142
147
  --fw-cell: 600;
143
- --fs-cell-dense: 13px;
148
+ --fs-cell-dense: 14px;
144
149
 
145
- --fs-nav: 13px;
150
+ --fs-nav: 14px;
146
151
 
147
- --fs-section-label: 10.5px;
152
+ --fs-section-label: 12px;
148
153
  --fw-section-label: 800;
149
154
  --ls-section-label: 0.09em;
150
155
 
151
- --fs-btn: 13.5px;
156
+ --fs-btn: 14px;
152
157
  --fw-btn: 700;
153
158
  --fw-btn-strong: 800;
154
159
 
155
- --fs-chip: 11.5px;
156
- --fs-chip-lg: 12px;
160
+ --fs-chip: 13px;
161
+ --fs-chip-lg: 13px;
157
162
  --fw-chip: 700;
158
163
 
159
- --fs-breadcrumb: 12.5px;
164
+ --fs-breadcrumb: 13.5px;
160
165
 
161
166
  /* Shape */
162
167
  --r-card: 14px;
@@ -179,7 +179,7 @@ const isBlocked = computed(() => props.cameras?.some((c) => c.list === "blocked"
179
179
  border: 1px solid var(--border);
180
180
  background: var(--card);
181
181
  color: var(--text);
182
- font-size: 11px;
182
+ font-size: 12px;
183
183
  line-height: 18px;
184
184
  cursor: pointer;
185
185
  }
@@ -759,14 +759,20 @@ const onShowQRAction = () => {
759
759
  const id =
760
760
  (selectedItem.value as any)?._id || (selectedItem.value as any)?.id;
761
761
  const runtimeConfig = useRuntimeConfig ? useRuntimeConfig() : undefined;
762
- const origin =
763
- runtimeConfig?.public?.APP_HYGIENE ||
764
- (typeof window !== "undefined" && window.location?.origin
762
+ // The QR opens this app's own schedule page (M&E, pest, pool, landscape
763
+ // pass their route); only Hygiene uses the configured Hygiene host.
764
+ const scheduleSegment = props.scheduleRoute || "cleaning-schedule";
765
+ const currentOrigin =
766
+ typeof window !== "undefined" && window.location?.origin
765
767
  ? window.location.origin
766
- : "https://hygiene.app.iservice365.org");
768
+ : "";
769
+ const origin =
770
+ (scheduleSegment === "cleaning-schedule" && runtimeConfig?.public?.APP_HYGIENE) ||
771
+ currentOrigin ||
772
+ "https://hygiene.app.iservice365.org";
767
773
  qrPath.value = `${origin}/${props.orgId}/${
768
774
  props.site
769
- }/cleaning-schedule?qrcode=true&id=${encodeURIComponent(
775
+ }/${scheduleSegment}?qrcode=true&id=${encodeURIComponent(
770
776
  id
771
777
  )}&site=${encodeURIComponent(props.site)}`;
772
778
  };
@@ -1,62 +1,21 @@
1
1
  <template>
2
2
  <div>
3
3
  <!--
4
- THE DESIGN'S PAGE SCAFFOLD (same shape as Service Providers).
5
-
6
- Placement only: the same refresh control, the same range text, the same
7
- pager bound to the same `page`, and the table, its columns and its cell
8
- templates are untouched. The screen simply gains the page title it has
9
- never had, and its card + toolbar are the design's rather than Vuetify's.
4
+ Service-provider billing is not built yet. This screen used to draw an
5
+ empty table with a refresh button that did nothing, which read as
6
+ "there are no bills". It now says plainly what is (not) here.
10
7
  -->
11
8
  <PageHeader title="Billing" />
12
9
 
13
- <AppCard class="table-card">
14
- <CardToolbar :count="pageRange">
15
- <template #left>
16
- <AppButton variant="icon" icon="mdi-refresh" aria-label="Refresh" />
17
- </template>
18
-
19
- <template #right>
20
- <local-pagination v-model="page" :length="pages" />
21
- </template>
22
- </CardToolbar>
23
-
24
- <v-data-table
25
- :headers="headers"
26
- :items="items"
27
- item-value="_id"
28
- items-per-page="20"
29
- fixed-header
30
- hide-default-footer
31
- hide-default-header
32
- style="max-height: calc(100vh - (180px))"
33
- >
34
- <template #item.permissions="{ value }">
35
- <span class="text-caption font-weight-bold text-capitalize">
36
- permissions
37
- </span>
38
- <StatusChip :dot="false" tone="neutral" :label="`${value.length}`" />
39
- </template>
40
-
41
- <template #item.nature="{ item }">
42
- <span class="text-capitalize">
43
- {{ item.nature }}
44
- </span>
45
- </template>
46
- </v-data-table>
10
+ <AppCard class="pa-6">
11
+ <p class="text-body-1 mb-2">
12
+ Service provider billing is not available yet.
13
+ </p>
14
+ <p class="text-body-2 text-medium-emphasis mb-0">
15
+ Bills from your service providers will appear here once this feature is
16
+ switched on. For billing questions today, please contact your service
17
+ provider directly.
18
+ </p>
47
19
  </AppCard>
48
20
  </div>
49
21
  </template>
50
-
51
- <script setup lang="ts">
52
- const page = ref(1);
53
- const pages = ref(0);
54
- const pageRange = ref("-- - -- of --");
55
-
56
- const items = ref<Array<Record<string, any>>>([]);
57
-
58
- const headers = [
59
- { title: "Name", value: "name" },
60
- { title: "Nature", value: "nature" },
61
- ];
62
- </script>
@@ -13,7 +13,7 @@
13
13
 
14
14
  <TableMain :headers="headers" :items="items" v-model:search="searchInput"
15
15
  :loading="getAnnouncementPending" :page="page" :pages="pages" :pageRange="pageRange"
16
- @refresh="getAnnouncementsRefresh" :show-header="APP_CONSTANTS.RESIDENT === props.recipients" @update:page="handleUpdatePage" @row-click="handleRowClick">
16
+ @refresh="getAnnouncementsRefresh" show-header @update:page="handleUpdatePage" @row-click="handleRowClick">
17
17
  <!--
18
18
  The tabs move ONTO the toolbar row (the design draws them there)
19
19
  instead of a stacked strip above the table. Same options, same
@@ -83,7 +83,6 @@
83
83
  </template>
84
84
  <script setup lang="ts">
85
85
  import useBulletin from '../composables/useBulletin';
86
- import { APP_CONSTANTS } from '../constants/app';
87
86
  // Imported by relative path, not left to Nuxt's auto-import: this layer's
88
87
  // components always import their utils explicitly, and a missing import here
89
88
  // is a ReferenceError that blanks the screen in every consuming app.
@@ -141,18 +140,15 @@ const { debounce } = useUtils()
141
140
 
142
141
 
143
142
  const headers = computed(() => {
144
- const arr = [
143
+ // Every app shows when an announcement was posted and when it runs/ends.
144
+ // These used to appear only for recipients="resident", which no app passes,
145
+ // so staff saw bare titles with no dates.
146
+ return [
145
147
  { title: "Title", value: "title", align: "start" },
146
- ]
147
-
148
- if (props.recipients === APP_CONSTANTS.RESIDENT) {
149
- arr.push({ title: "Date Created", value: "createdAt", align: "start" });
150
- arr.push({ title: "Start Date/End Date", value: "duration", align: "start" });
151
- arr.push({ title: "No Expiration", value: "noExpiration", align: "start" });
152
- arr.push({ title: "", value: "actions", align: "center" });
153
- }
154
-
155
- return arr;
148
+ { title: "Date Created", value: "createdAt", align: "start" },
149
+ { title: "Start Date/End Date", value: "duration", align: "start" },
150
+ { title: "No Expiration", value: "noExpiration", align: "start" },
151
+ ];
156
152
 
157
153
  });
158
154
  // THE SERVER PAGES THIS LIST. `getAll` is called with `page`, so `items` is
@@ -983,7 +983,7 @@ async function toggleFullscreen() {
983
983
  justify-content: space-between;
984
984
  gap: 6px;
985
985
  margin: 6px 0 0;
986
- font-size: 11px;
986
+ font-size: 12px;
987
987
  }
988
988
  .vms__picker-range {
989
989
  color: var(--vms-text-dim);
@@ -999,7 +999,7 @@ async function toggleFullscreen() {
999
999
  align-items: center;
1000
1000
  justify-content: space-between;
1001
1001
  margin: 0 0 6px;
1002
- font-size: 11px;
1002
+ font-size: 12px;
1003
1003
  letter-spacing: 0.06em;
1004
1004
  text-transform: uppercase;
1005
1005
  color: var(--vms-text-dim);
@@ -1008,7 +1008,7 @@ async function toggleFullscreen() {
1008
1008
  .vms__picker-empty,
1009
1009
  .vms__picker-note {
1010
1010
  margin: 6px 0 0;
1011
- font-size: 11px;
1011
+ font-size: 12px;
1012
1012
  line-height: 15px;
1013
1013
  color: var(--vms-text-dim);
1014
1014
  }
@@ -1017,7 +1017,7 @@ async function toggleFullscreen() {
1017
1017
  background: none;
1018
1018
  border: none;
1019
1019
  color: var(--vms-accent);
1020
- font-size: 11px;
1020
+ font-size: 12px;
1021
1021
  cursor: pointer;
1022
1022
  padding: 0;
1023
1023
  text-transform: none;
@@ -1186,7 +1186,7 @@ async function toggleFullscreen() {
1186
1186
  */
1187
1187
  .vms__caps-head {
1188
1188
  margin: 0 0 6px;
1189
- font-size: 11px;
1189
+ font-size: 12px;
1190
1190
  letter-spacing: 0.06em;
1191
1191
  text-transform: uppercase;
1192
1192
  color: var(--vms-text-dim);
@@ -1238,7 +1238,7 @@ async function toggleFullscreen() {
1238
1238
  }
1239
1239
 
1240
1240
  .vms__cap-tag {
1241
- font-size: 11px;
1241
+ font-size: 12px;
1242
1242
  font-weight: 400;
1243
1243
  padding: 1px 6px;
1244
1244
  border-radius: var(--vms-radius);
@@ -1266,7 +1266,7 @@ async function toggleFullscreen() {
1266
1266
  margin: 6px 0 0;
1267
1267
  padding-top: 6px;
1268
1268
  border-top: 1px solid var(--vms-line);
1269
- font-size: 11px;
1269
+ font-size: 12px;
1270
1270
  line-height: 16px;
1271
1271
  color: var(--vms-text-dim);
1272
1272
  }
@@ -606,7 +606,7 @@ async function toggleFullscreen() {
606
606
  }
607
607
 
608
608
  .vms-tile--dense .vms-tile__reason {
609
- font-size: 11px;
609
+ font-size: 12px;
610
610
  line-height: 15px;
611
611
  }
612
612
 
@@ -659,7 +659,7 @@ async function toggleFullscreen() {
659
659
 
660
660
  .vms-tile__state {
661
661
  color: var(--vms-text, #e8ecf1);
662
- font-size: 11px;
662
+ font-size: 12px;
663
663
  line-height: 14px;
664
664
  white-space: nowrap;
665
665
  }
@@ -673,7 +673,7 @@ async function toggleFullscreen() {
673
673
  }
674
674
 
675
675
  .vms-tile--dense .vms-tile__state {
676
- font-size: 10px;
676
+ font-size: 12px;
677
677
  line-height: 13px;
678
678
  }
679
679
 
@@ -715,7 +715,7 @@ async function toggleFullscreen() {
715
715
  border-radius: var(--vms-radius, 3px);
716
716
  background: var(--vms-scrim, rgba(7, 9, 12, 0.62));
717
717
  color: var(--vms-text-dim, #94a0b0);
718
- font-size: 11px;
718
+ font-size: 12px;
719
719
  line-height: 15px;
720
720
  white-space: nowrap;
721
721
  overflow: hidden;
@@ -723,7 +723,7 @@ async function toggleFullscreen() {
723
723
  }
724
724
 
725
725
  .vms-tile--dense .vms-tile__health {
726
- font-size: 10px;
726
+ font-size: 12px;
727
727
  line-height: 14px;
728
728
  }
729
729
 
@@ -752,7 +752,7 @@ async function toggleFullscreen() {
752
752
  padding: 1px 6px;
753
753
  border-radius: var(--vms-radius, 3px);
754
754
  background: var(--vms-scrim, rgba(7, 9, 12, 0.62));
755
- font-size: 11px;
755
+ font-size: 12px;
756
756
  line-height: 15px;
757
757
  /* The row is click-through; this one control is not. */
758
758
  pointer-events: auto;
@@ -768,7 +768,7 @@ async function toggleFullscreen() {
768
768
  border: none;
769
769
  padding: 2px 2px;
770
770
  color: var(--vms-accent, #5b9be0);
771
- font-size: 11px;
771
+ font-size: 12px;
772
772
  line-height: 15px;
773
773
  cursor: pointer;
774
774
  }
@@ -791,7 +791,7 @@ async function toggleFullscreen() {
791
791
  }
792
792
 
793
793
  .vms-tile--dense .vms-tile__name {
794
- font-size: 11px;
794
+ font-size: 12px;
795
795
  line-height: 15px;
796
796
  }
797
797
 
@@ -84,7 +84,7 @@ defineProps({
84
84
  }
85
85
 
86
86
  .chat-listcard__initials {
87
- font-size: 9px;
87
+ font-size: 12px;
88
88
  font-weight: 700;
89
89
  letter-spacing: 0;
90
90
  }
@@ -7,8 +7,42 @@
7
7
  :site="site"
8
8
  :service-type="serviceType"
9
9
  :schedule-route="scheduleRoute"
10
- text="A new checklist is created for every area automatically each midnight. Staff tick off each unit on the mobile app."
11
- />
10
+ text="The app makes a new checklist for every area automatically each midnight. Staff tick off each unit on their mobile app; you follow progress here."
11
+ how-title="How the Daily Checklist works"
12
+ how-key="daily-checklist"
13
+ >
14
+ <ol class="sg-steps">
15
+ <li>
16
+ Every midnight a new row appears here for the day. It holds every
17
+ area, with as many visits as the area's "visits per day", plus
18
+ any <strong>Extra Visits</strong> set for that day.
19
+ </li>
20
+ <li>
21
+ Staff open the same list in their mobile app and tick each unit
22
+ when it is done (a photo is needed to finish a visit).
23
+ </li>
24
+ <li>
25
+ <strong>Closes in</strong> counts down to midnight. When it
26
+ reaches zero the day's list closes: it becomes view-only, and any
27
+ unit not ticked counts as <strong>Missed</strong>.
28
+ </li>
29
+ <li>
30
+ Click a row to see its areas, then an area to see each unit,
31
+ send a unit back (<strong>Needs redo</strong>) or add a remark.
32
+ </li>
33
+ <li>
34
+ <strong>Download PDF</strong> saves that day's report - every
35
+ area, unit, who ticked it and when - to send to your client.
36
+ </li>
37
+ </ol>
38
+ <div class="sg-h">What the status words mean</div>
39
+ <dl class="sg-legend">
40
+ <template v-for="h in DAILY_STATUS_HELP" :key="h.word">
41
+ <dt><StatusChip :status="h.word" :label="h.word" /></dt>
42
+ <dd>{{ h.meaning }}</dd>
43
+ </template>
44
+ </dl>
45
+ </ScheduleGuide>
12
46
  <TableMain
13
47
  v-if="canViewSchedules"
14
48
  :title="pageTitle"
@@ -25,10 +59,11 @@
25
59
  >
26
60
  <template #no-data>
27
61
  <div class="cs-empty">
28
- <strong>No daily lists yet.</strong>
62
+ <strong>No daily checklists here yet.</strong>
29
63
  <span>
30
- A list is created automatically every midnight, once your Areas
31
- and Units are set up.
64
+ A checklist is made automatically every midnight once your Areas
65
+ and Units are set up. If you filtered by date or status, clear
66
+ the filter to see every day.
32
67
  </span>
33
68
  </div>
34
69
  </template>
@@ -125,7 +160,7 @@
125
160
  @click.stop="downloadItem(item)"
126
161
  >
127
162
  <v-icon left>mdi-download</v-icon>
128
- Download
163
+ Download PDF
129
164
  </v-btn>
130
165
  </template>
131
166
  </TableMain>
@@ -136,7 +171,8 @@
136
171
  -->
137
172
  <AppCard class="pa-4">
138
173
  <div class="cs-no-permission">
139
- You do not have permission to view cleaning schedules.
174
+ You do not have permission to view the Daily Checklist. Ask your
175
+ administrator to give your role access to it.
140
176
  </div>
141
177
  </AppCard>
142
178
  </div>
@@ -152,9 +188,10 @@
152
188
  <script lang="ts" setup>
153
189
  import useCleaningSchedules from "../composables/useCleaningSchedules";
154
190
  import {
191
+ DAILY_STATUS_HELP,
192
+ dailyChecklistTitle,
155
193
  dailyStatusLabel,
156
194
  hasListForToday,
157
- scheduleNameFor,
158
195
  } from "../utils/schedule-task";
159
196
  import useUtils from "../composables/useUtils";
160
197
 
@@ -171,20 +208,19 @@ const props = defineProps({
171
208
  canGenerateChecklist: { type: Boolean, default: false },
172
209
  });
173
210
 
174
- // "Daily Schedule - Cleaning" / "- Landscape" / "- Pest" / "- Pool" / "- Technician"
175
- const pageTitle = `Daily Schedule – ${scheduleNameFor(props.serviceType).title.replace(
176
- / Schedule$/,
177
- "",
178
- )}`;
211
+ // "Daily Checklist – Cleaning" / "– Landscape" / "– Pest" / "– Pool" / "– Technician"
212
+ const pageTitle = dailyChecklistTitle(props.serviceType);
179
213
 
180
- const startDate = ref("");
181
- const endDate = ref("");
214
+ // `?date=YYYY-MM-DD` (from an Extra Visit's "See that day's checklist") opens on that one day.
215
+ const linkedDay = String(useRoute().query.date ?? "");
216
+ const startDate = ref(/^\d{4}-\d{2}-\d{2}$/.test(linkedDay) ? linkedDay : "");
217
+ const endDate = ref(startDate.value);
182
218
  const status = ref<TScheduleAreaStatus>("all");
183
219
  const statusOptions = [
184
220
  { title: "All", value: "all" },
185
- { title: "Open", value: "open" },
221
+ { title: "Not started", value: "open" },
186
222
  { title: "In progress", value: "ongoing" },
187
- { title: "Completed", value: "completed" },
223
+ { title: "Done", value: "completed" },
188
224
  { title: "Closed", value: "closed" },
189
225
  ] as const;
190
226
 
@@ -194,10 +230,10 @@ const pages = ref(0);
194
230
  const pageRange = ref("-- - -- of --");
195
231
 
196
232
  const headers = [
197
- { title: "Created Date", value: "createdAt" },
198
- { title: "Close in", value: "closeIn" },
233
+ { title: "Day", value: "createdAt" },
234
+ { title: "Closes in", value: "closeIn" },
199
235
  { title: "Status", value: "status" },
200
- { title: "Completion Date", value: "completedAt" },
236
+ { title: "Finished on", value: "completedAt" },
201
237
  { title: "", value: "download" },
202
238
  ];
203
239
  const remainingTime = ref({} as Record<string, string>);
@@ -899,7 +899,7 @@ onUnmounted(() => {
899
899
  position: absolute;
900
900
  top: -8px;
901
901
  left: 9px;
902
- font-size: 10px;
902
+ font-size: 12px;
903
903
  color: var(--muted);
904
904
  background: var(--card);
905
905
  padding: 0 3px;
@@ -463,7 +463,7 @@ onMounted(load)
463
463
  }
464
464
  .sub-free-badge {
465
465
  display: block;
466
- font-size: 11px;
466
+ font-size: 12px;
467
467
  font-weight: 700;
468
468
  color: var(--text2);
469
469
  margin-top: 2px;