@7365admin1/layer-common 4.136.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.
@@ -1447,7 +1447,7 @@ function buildPdfHtml() {
1447
1447
  background: #ffffff;
1448
1448
  color: #0a2638;
1449
1449
  font-family: Arial, Helvetica, sans-serif;
1450
- font-size: 9px;
1450
+ font-size: 12px;
1451
1451
  }
1452
1452
 
1453
1453
  .pdf-summary {
@@ -1482,7 +1482,7 @@ function buildPdfHtml() {
1482
1482
  }
1483
1483
 
1484
1484
  .pdf-value {
1485
- font-size: 10px;
1485
+ font-size: 12px;
1486
1486
  line-height: 1.35;
1487
1487
  }
1488
1488
 
@@ -1693,7 +1693,7 @@ watch(selectedReaderId, (value) => {
1693
1693
  .history-table-card :deep(th) {
1694
1694
  background: var(--thead);
1695
1695
  color: var(--muted);
1696
- font-size: 11px;
1696
+ font-size: 12px;
1697
1697
  font-weight: 800;
1698
1698
  text-transform: uppercase;
1699
1699
  letter-spacing: 0.04em;
@@ -2246,7 +2246,7 @@ export default {
2246
2246
  .field-label {
2247
2247
  margin: 12px 0 7px;
2248
2248
  color: var(--text2);
2249
- font-size: 10px;
2249
+ font-size: 12px;
2250
2250
  font-weight: 500;
2251
2251
  }
2252
2252
 
@@ -2257,7 +2257,7 @@ export default {
2257
2257
  .field-hint {
2258
2258
  margin: 6px 0 0;
2259
2259
  color: var(--text2);
2260
- font-size: 11px;
2260
+ font-size: 12px;
2261
2261
  line-height: 1.4;
2262
2262
  }
2263
2263
 
@@ -2282,7 +2282,7 @@ export default {
2282
2282
  .sip-label {
2283
2283
  margin: 12px 0 6px;
2284
2284
  color: var(--text);
2285
- font-size: 10px;
2285
+ font-size: 12px;
2286
2286
  font-weight: 500;
2287
2287
  line-height: 1.2;
2288
2288
  }
@@ -2320,7 +2320,7 @@ export default {
2320
2320
 
2321
2321
  .sip-section-switch small {
2322
2322
  color: var(--muted);
2323
- font-size: 10px;
2323
+ font-size: 12px;
2324
2324
  line-height: 1.4;
2325
2325
  }
2326
2326
 
@@ -2462,7 +2462,7 @@ export default {
2462
2462
  .call-label {
2463
2463
  margin: 8px 0 6px;
2464
2464
  color: var(--text);
2465
- font-size: 10px;
2465
+ font-size: 12px;
2466
2466
  font-weight: 500;
2467
2467
  letter-spacing: 0;
2468
2468
  }
@@ -2485,7 +2485,7 @@ export default {
2485
2485
 
2486
2486
  .call-radio-list :deep(.v-label) {
2487
2487
  color: var(--text2);
2488
- font-size: 11px;
2488
+ font-size: 12px;
2489
2489
  letter-spacing: 0;
2490
2490
  opacity: 1;
2491
2491
  }
@@ -2501,7 +2501,7 @@ export default {
2501
2501
  border-radius: 3px;
2502
2502
  background: var(--card);
2503
2503
  color: var(--text);
2504
- font-size: 11px;
2504
+ font-size: 12px;
2505
2505
  letter-spacing: 0;
2506
2506
  }
2507
2507
 
@@ -2657,7 +2657,7 @@ export default {
2657
2657
  .web-phone-status {
2658
2658
  min-width: 92px;
2659
2659
  justify-content: center;
2660
- font-size: 10px;
2660
+ font-size: 12px;
2661
2661
  }
2662
2662
 
2663
2663
  .web-phone-status.ready {
@@ -2749,7 +2749,7 @@ export default {
2749
2749
 
2750
2750
  .sip-account-label {
2751
2751
  color: var(--muted);
2752
- font-size: 11px;
2752
+ font-size: 12px;
2753
2753
  }
2754
2754
 
2755
2755
  .sip-account-extension {
@@ -2762,7 +2762,7 @@ export default {
2762
2762
  max-width: 170px;
2763
2763
  overflow: hidden;
2764
2764
  color: var(--text2);
2765
- font-size: 11px;
2765
+ font-size: 12px;
2766
2766
  text-align: right;
2767
2767
  text-overflow: ellipsis;
2768
2768
  white-space: nowrap;
@@ -2838,7 +2838,7 @@ export default {
2838
2838
  max-width: 100%;
2839
2839
  overflow: hidden;
2840
2840
  color: var(--border);
2841
- font-size: 11px;
2841
+ font-size: 12px;
2842
2842
  text-overflow: ellipsis;
2843
2843
  white-space: nowrap;
2844
2844
  }
@@ -2895,7 +2895,7 @@ export default {
2895
2895
 
2896
2896
  .web-phone-error {
2897
2897
  margin-top: 14px;
2898
- font-size: 11px;
2898
+ font-size: 12px;
2899
2899
  }
2900
2900
 
2901
2901
  .call-two-col {
@@ -2922,7 +2922,7 @@ export default {
2922
2922
  min-height: 38px;
2923
2923
  border: 1px dashed var(--border);
2924
2924
  color: var(--text2);
2925
- font-size: 11px;
2925
+ font-size: 12px;
2926
2926
  letter-spacing: 0;
2927
2927
  }
2928
2928
 
@@ -2952,7 +2952,7 @@ export default {
2952
2952
  min-height: 32px;
2953
2953
  margin: 0 0 10px;
2954
2954
  color: var(--text2);
2955
- font-size: 11px;
2955
+ font-size: 12px;
2956
2956
  }
2957
2957
 
2958
2958
  .call-file-name {
@@ -644,7 +644,7 @@ function showMessage(text: string, color: string) {
644
644
  .pass-subtext {
645
645
  min-height: 18px;
646
646
  color: #6b7280;
647
- font-size: 11px;
647
+ font-size: 12px;
648
648
  }
649
649
 
650
650
  .qr-grid {
@@ -667,7 +667,7 @@ function showMessage(text: string, color: string) {
667
667
  background-size: 10px 10px;
668
668
  background-position: 0 0, 0 5px, 5px -5px, -5px 0;
669
669
  color: #6b7280;
670
- font-size: 9px;
670
+ font-size: 12px;
671
671
  }
672
672
 
673
673
  .qr-placeholder.small {
@@ -685,7 +685,7 @@ function showMessage(text: string, color: string) {
685
685
  display: grid;
686
686
  gap: 4px;
687
687
  color: #4b5563;
688
- font-size: 10px;
688
+ font-size: 12px;
689
689
  }
690
690
 
691
691
  .pass-footer strong {
@@ -1303,7 +1303,7 @@ function formatCallback(reader?: HidRecord | null) {
1303
1303
  nobody opens. */
1304
1304
  .identification-copy small {
1305
1305
  color: var(--muted);
1306
- font-size: 11px;
1306
+ font-size: 12px;
1307
1307
  }
1308
1308
 
1309
1309
  .identification-applied {
@@ -57,7 +57,7 @@ const list = computed(() => [
57
57
  position: absolute;
58
58
  left: 2px;
59
59
  bottom: 2px;
60
- font-size: 9px;
60
+ font-size: 12px;
61
61
  line-height: 1;
62
62
  padding: 1px 3px;
63
63
  border-radius: 3px;
@@ -307,7 +307,7 @@ const { getNameInitials } = useUtils();
307
307
  }
308
308
 
309
309
  .app-profile-menu__name {
310
- font-size: 13.5px;
310
+ font-size: 14.5px;
311
311
  font-weight: 800;
312
312
  line-height: 1.3;
313
313
  color: var(--text);
@@ -317,7 +317,7 @@ const { getNameInitials } = useUtils();
317
317
  }
318
318
 
319
319
  .app-profile-menu__site {
320
- font-size: 11.5px;
320
+ font-size: 13px;
321
321
  font-weight: 600;
322
322
  line-height: 1.3;
323
323
  color: var(--muted);
@@ -297,7 +297,7 @@ const handleClick = (item: any) => {
297
297
  }
298
298
 
299
299
  .brand-drawer__suite {
300
- font-size: 11.5px;
300
+ font-size: 13px;
301
301
  font-weight: 600;
302
302
  line-height: 1.3;
303
303
  color: var(--muted);
@@ -70,7 +70,7 @@ const hidden = computed(() => (props.limit ? props.windows.slice(props.limit) :
70
70
  }
71
71
 
72
72
  .route-times__next {
73
- font-size: 10px;
73
+ font-size: 12px;
74
74
  font-weight: 700;
75
75
  color: var(--warn);
76
76
  }
@@ -144,7 +144,7 @@ const extraCount = computed(() => {
144
144
  width: fit-content;
145
145
  padding: 0 6px;
146
146
  border-radius: 999px;
147
- font-size: 11px;
147
+ font-size: 12px;
148
148
  font-weight: 600;
149
149
  line-height: 18px;
150
150
  }
@@ -6,7 +6,7 @@
6
6
  :site="site"
7
7
  :service-type="serviceType"
8
8
  :schedule-route="scheduleRoute"
9
- text="These are the areas on this day's checklist. Open an area to see its units and visits."
9
+ text="These are the areas on this day's checklist. Click an area to see each unit, tick it as done, or send it back to be redone."
10
10
  />
11
11
  <TableMain
12
12
  :title="pageTitle"
@@ -142,7 +142,7 @@
142
142
 
143
143
  <script setup lang="ts">
144
144
  import useCleaningSchedules from "../composables/useCleaningSchedules";
145
- import { dailyStatusLabel, scheduleNameFor } from "../utils/schedule-task";
145
+ import { dailyChecklistTitle, dailyStatusLabel } from "../utils/schedule-task";
146
146
  import useUtils from "../composables/useUtils";
147
147
 
148
148
  const props = defineProps({
@@ -157,11 +157,8 @@ const props = defineProps({
157
157
  canManageScheduleTasks: { type: Boolean, default: false },
158
158
  });
159
159
 
160
- // "Daily Schedule - Cleaning" / "- Landscape" / ... (same title as the day list)
161
- const pageTitle = `Daily Schedule – ${scheduleNameFor(props.serviceType).title.replace(
162
- / Schedule$/,
163
- "",
164
- )}`;
160
+ // "Daily Checklist – Cleaning" / "– Landscape" / ... (same title as the day list)
161
+ const pageTitle = dailyChecklistTitle(props.serviceType);
165
162
 
166
163
  const submitting = ref(false);
167
164
  const message = ref("");
@@ -198,9 +195,9 @@ const items = ref<Array<Record<string, any>>>([]);
198
195
  const statusFilter = ref<TScheduleAreaStatus>("all");
199
196
  const statusOptions = [
200
197
  { title: "All", value: "all" },
201
- { title: "Open", value: "open" },
198
+ { title: "Not started", value: "open" },
202
199
  { title: "In progress", value: "ongoing" },
203
- { title: "Completed", value: "completed" },
200
+ { title: "Done", value: "completed" },
204
201
  ];
205
202
  const areaTypeFilter = ref<TAreaType>("all");
206
203
  const typeOptions = [
@@ -9,6 +9,29 @@
9
9
  <template>
10
10
  <div class="schedule-guide">
11
11
  <p v-if="text" class="schedule-guide__text">{{ text }}</p>
12
+ <!--
13
+ "How it works": the page's own short manual (owner 21 Oct - a supervisor
14
+ who was never trained must understand the screen from the screen). Open
15
+ by default until the viewer closes it; the choice is remembered on this
16
+ browser only.
17
+ -->
18
+ <v-expansion-panels
19
+ v-if="$slots.default && howTitle"
20
+ v-model="howPanel"
21
+ class="schedule-guide__how"
22
+ variant="accordion"
23
+ flat
24
+ >
25
+ <v-expansion-panel value="how">
26
+ <v-expansion-panel-title class="schedule-guide__how-title">
27
+ <v-icon size="20" class="mr-2">mdi-help-circle-outline</v-icon>
28
+ {{ howTitle }}
29
+ </v-expansion-panel-title>
30
+ <v-expansion-panel-text class="schedule-guide__how-body">
31
+ <slot />
32
+ </v-expansion-panel-text>
33
+ </v-expansion-panel>
34
+ </v-expansion-panels>
12
35
  <v-alert
13
36
  v-if="problems.length"
14
37
  type="warning"
@@ -44,6 +67,27 @@ const props = defineProps({
44
67
  serviceType: { type: String, default: "" },
45
68
  scheduleRoute: { type: String, default: "" },
46
69
  text: { type: String, default: "" },
70
+ /** Heading of the "How it works" panel; the panel shows only with this and a default slot. */
71
+ howTitle: { type: String, default: "" },
72
+ /** Remembers open/closed per panel on this browser. */
73
+ howKey: { type: String, default: "" },
74
+ });
75
+
76
+ const HOW_STORE = () => `schedule-guide-how:${props.howKey || props.howTitle}`;
77
+ const howPanel = ref<string | undefined>("how");
78
+ onMounted(() => {
79
+ try {
80
+ if (localStorage.getItem(HOW_STORE()) === "closed") howPanel.value = undefined;
81
+ } catch {
82
+ // storage blocked: stay open
83
+ }
84
+ });
85
+ watch(howPanel, (v) => {
86
+ try {
87
+ localStorage.setItem(HOW_STORE(), v ? "open" : "closed");
88
+ } catch {
89
+ // storage blocked: nothing to remember
90
+ }
47
91
  });
48
92
 
49
93
  const { getAreas, getAreaById } = useAreas();
@@ -125,8 +169,65 @@ watch(() => [props.site, props.serviceType], check);
125
169
  font-size: var(--fs-cell);
126
170
  color: var(--text2);
127
171
  }
172
+ .schedule-guide__how {
173
+ margin-bottom: 12px;
174
+ border: 1px solid var(--border);
175
+ border-radius: var(--r-inner);
176
+ overflow: hidden;
177
+ }
178
+ .schedule-guide__how-title {
179
+ font-family: var(--font-sans);
180
+ font-size: var(--fs-card-title);
181
+ font-weight: 700;
182
+ }
183
+ .schedule-guide__how-body {
184
+ font-family: var(--font-sans);
185
+ font-size: var(--fs-cell);
186
+ line-height: 1.55;
187
+ }
128
188
  .schedule-guide__list {
129
189
  margin: 0;
130
190
  padding-left: 18px;
131
191
  }
132
192
  </style>
193
+
194
+ <!-- Shared by the "How it works" content the schedule screens put in the slot. -->
195
+ <style>
196
+ .sg-steps {
197
+ margin: 0 0 12px;
198
+ padding-left: 22px;
199
+ }
200
+ .sg-steps li {
201
+ margin-bottom: 6px;
202
+ }
203
+ .sg-example {
204
+ margin: 0 0 12px;
205
+ padding: 12px 14px;
206
+ border-left: 3px solid rgb(var(--v-theme-primary));
207
+ border-radius: var(--r-inner-sm);
208
+ background: var(--hover);
209
+ }
210
+ .sg-legend {
211
+ display: grid;
212
+ grid-template-columns: max-content 1fr;
213
+ gap: 8px 12px;
214
+ align-items: center;
215
+ margin: 0;
216
+ }
217
+ .sg-legend dd {
218
+ margin: 0;
219
+ }
220
+ .sg-h {
221
+ margin: 4px 0 8px;
222
+ font-weight: 700;
223
+ }
224
+ @media (max-width: 599.98px) {
225
+ .sg-legend {
226
+ grid-template-columns: 1fr;
227
+ gap: 2px 0;
228
+ }
229
+ .sg-legend dd {
230
+ margin-bottom: 8px;
231
+ }
232
+ }
233
+ </style>
@@ -59,8 +59,10 @@
59
59
  variant="outlined"
60
60
  density="comfortable"
61
61
  class="filter-field mb-4"
62
- placeholder="For example: Midday toilet round"
62
+ placeholder="For example: Lunch toilet check"
63
63
  :error-messages="fieldError('title')"
64
+ hint="Staff see this name on their phone, on the extra round."
65
+ persistent-hint
64
66
  hide-details="auto"
65
67
  />
66
68
  <InputLabel for="description" title="Instructions" />
@@ -72,6 +74,8 @@
72
74
  class="filter-field"
73
75
  rows="3"
74
76
  placeholder="Optional. Anything staff should know for this visit."
77
+ hint="Optional. For example: refill soap and tissue as well."
78
+ persistent-hint
75
79
  hide-details="auto"
76
80
  />
77
81
  </div>
@@ -81,8 +85,9 @@
81
85
  <v-stepper-window-item :value="2">
82
86
  <div class="st-step">
83
87
  <p class="st-step__help">
84
- Pick the areas that get the extra visit. Every unit in an
85
- area is added to the checklist.
88
+ Pick the areas that get the extra visit. Every unit in a
89
+ picked area gets one more tick box on the Daily Checklist
90
+ for this visit.
86
91
  </p>
87
92
  <InputLabel for="selected-areas" title="Areas" required />
88
93
  <v-select
@@ -165,7 +170,8 @@
165
170
  <div class="st-step">
166
171
  <p class="st-step__help">
167
172
  Choose the time, and whether it happens once or repeats.
168
- Times are Singapore time.
173
+ Times are Singapore time. The extra round appears on staff's
174
+ checklist at this time.
169
175
  </p>
170
176
  <v-row dense>
171
177
  <v-col cols="12" sm="6">
@@ -193,6 +199,8 @@
193
199
  variant="outlined"
194
200
  density="comfortable"
195
201
  class="filter-field"
202
+ :hint="REPEAT_HINTS[repeat]"
203
+ persistent-hint
196
204
  hide-details="auto"
197
205
  />
198
206
  </v-col>
@@ -200,7 +208,7 @@
200
208
 
201
209
  <!-- Once: pick the day(s) -->
202
210
  <div v-if="repeat === 'none'" class="mt-4">
203
- <InputLabel title="Day" required />
211
+ <InputLabel title="Day(s)" required />
204
212
  <div class="st-panel">
205
213
  <v-date-picker
206
214
  v-model="pickerDates"
@@ -213,7 +221,8 @@
213
221
  class="ma-0"
214
222
  />
215
223
  <div class="st-step__help mt-2">
216
- {{ selectedDates.length }} day(s) selected.
224
+ {{ selectedDates.length }} day(s) selected. Click a day
225
+ again to remove it.
217
226
  </div>
218
227
  </div>
219
228
  <div v-if="fieldError('dates').length" class="st-error">
@@ -304,6 +313,32 @@
304
313
  <div class="st-summary__label">This will:</div>
305
314
  <div class="st-summary__text">{{ summary }}</div>
306
315
  </div>
316
+
317
+ <!-- What staff will see: the same card shape as the mobile checklist. -->
318
+ <div class="st-summary__label mt-4">
319
+ What staff will see on their phone:
320
+ </div>
321
+ <div class="st-preview">
322
+ <div class="st-preview__head">
323
+ <v-icon size="18" class="mr-1">mdi-clock-outline</v-icon>
324
+ {{ previewTime }} · Extra visit
325
+ </div>
326
+ <div class="st-preview__title">
327
+ {{ subject.trim() || "(your title)" }}
328
+ </div>
329
+ <div v-if="description.trim()" class="st-preview__desc">
330
+ {{ description.trim() }}
331
+ </div>
332
+ <div
333
+ v-for="area in selectedAreas"
334
+ :key="areaKey(area)"
335
+ class="st-preview__row"
336
+ >
337
+ <v-icon size="18" class="mr-1">mdi-checkbox-blank-outline</v-icon>
338
+ {{ area.name }} -
339
+ {{ unitCountOf(area) }} unit{{ unitCountOf(area) === 1 ? "" : "s" }} to tick
340
+ </div>
341
+ </div>
307
342
  <div v-if="fieldError('never').length" class="st-error mt-2">
308
343
  {{ fieldError("never")[0] }}
309
344
  </div>
@@ -372,6 +407,7 @@ import {
372
407
  REPEAT_OPTIONS,
373
408
  WEEK_DAYS,
374
409
  buildSummary,
410
+ fmtTime,
375
411
  sgtToday,
376
412
  validateSchedule,
377
413
  type TRepeat,
@@ -395,6 +431,11 @@ const props = defineProps({
395
431
  type: String,
396
432
  default: "",
397
433
  },
434
+ /** The step the dialog opens on (3 = When, used by "Extend or repeat"). */
435
+ startStep: {
436
+ type: Number,
437
+ default: 1,
438
+ },
398
439
  });
399
440
 
400
441
  const show = defineModel("show", { type: Boolean, default: false });
@@ -476,6 +517,15 @@ const formState = computed(() => ({
476
517
  const errors = computed<TScheduleErrors>(() => validateSchedule(formState.value));
477
518
  const summary = computed(() => buildSummary(formState.value, totalUnits.value));
478
519
 
520
+ const REPEAT_HINTS: Record<TRepeat, string> = {
521
+ none: "Only on the day(s) you pick below.",
522
+ daily: "Every day, weekends included.",
523
+ weekdays: "Monday to Friday.",
524
+ weekly: "On the weekdays you pick below.",
525
+ monthly: "Once a month, on the start date's day.",
526
+ };
527
+ const previewTime = computed(() => fmtTime(scheduleTime.value) || "--");
528
+
479
529
  const STEP_FIELDS: Record<number, (keyof TScheduleErrors)[]> = {
480
530
  1: ["title"],
481
531
  2: ["areas"],
@@ -694,6 +744,7 @@ const close = () => {
694
744
  watch(
695
745
  [show, () => props.mode, () => props.taskData],
696
746
  async ([isOpen, mode]) => {
747
+ if (isOpen) step.value = props.startStep || 1;
697
748
  if (isOpen && mode === "edit") {
698
749
  await loadTaskData();
699
750
  } else if (!isOpen) {
@@ -710,7 +761,7 @@ watch(
710
761
  }
711
762
  .st-step__help {
712
763
  margin: 0 0 12px;
713
- font-size: var(--fs-chip-lg);
764
+ font-size: var(--fs-cell);
714
765
  font-weight: 600;
715
766
  color: var(--text2);
716
767
  }
@@ -748,6 +799,36 @@ watch(
748
799
  font-size: 15px;
749
800
  font-weight: 600;
750
801
  }
802
+ .st-preview {
803
+ margin-top: 6px;
804
+ padding: 14px 16px;
805
+ border: 1px solid var(--border);
806
+ border-left: 4px solid rgb(var(--v-theme-primary));
807
+ border-radius: var(--r-inner);
808
+ background: var(--card);
809
+ font-size: var(--fs-cell);
810
+ }
811
+ .st-preview__head {
812
+ display: flex;
813
+ align-items: center;
814
+ font-size: var(--fs-chip-lg);
815
+ font-weight: 700;
816
+ color: var(--text2);
817
+ }
818
+ .st-preview__title {
819
+ margin: 4px 0;
820
+ font-size: var(--fs-card-title);
821
+ font-weight: 800;
822
+ }
823
+ .st-preview__desc {
824
+ margin-bottom: 6px;
825
+ color: var(--text2);
826
+ }
827
+ .st-preview__row {
828
+ display: flex;
829
+ align-items: center;
830
+ padding: 4px 0;
831
+ }
751
832
  .st-stepper,
752
833
  .st-stepper__header {
753
834
  background: transparent;