@7365admin1/layer-common 3.2.2-staging.153 → 3.2.2-staging.155

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.
@@ -357,6 +357,29 @@
357
357
  </h2>
358
358
  </div>
359
359
 
360
+ <!--
361
+ THE TWO SERIES COLOURS STAY LITERAL, AND HERE IS WHY.
362
+
363
+ Everything else in this chart already follows the theme - the gridlines,
364
+ the axis labels and the ring around each dot are all Vuetify theme vars.
365
+ The two series are not, and they are the only chart colours in the file
366
+ that are still hexes.
367
+
368
+ They are left that way deliberately. The handoff names no chart palette
369
+ at all (searched: no "chart", no "legend", no "graph", and neither hex
370
+ appears anywhere in it), so the fallback rule is "use the token the
371
+ literal was already imitating". `#e53935` is plainly `--err`. `#ff9b9b`
372
+ is a second, softer red, and the palette has no second red - the only
373
+ other red token is `--err-bg`, a 10%-alpha FILL that would be invisible
374
+ as a 3px line. Re-pointing just one of them would collapse the pair in
375
+ dark mode, where `--err` is `rgb(229,115,115)` and would sit almost on
376
+ top of `#ff9b9b`'s `rgb(255,155,155)` - two lines the reader could no
377
+ longer tell apart. That is the same "do not merge two series into one
378
+ colour" trap as the Work Order Status bars below.
379
+
380
+ So: a two-series chart palette is missing from the design system, and
381
+ that is a designer question, not something to invent here.
382
+ -->
360
383
  <div v-if="hasChartData" class="mt-4" ref="chartContainerRef">
361
384
  <svg
362
385
  :viewBox="`0 0 ${chartWidth} 260`"
@@ -2473,6 +2496,29 @@ const todayAttentions = computed(() =>
2473
2496
  normalizeListItems(["todayAttentions", "attentions"])
2474
2497
  );
2475
2498
 
2499
+ /**
2500
+ * THE WORK ORDER STATUS BARS.
2501
+ *
2502
+ * Was three hand-picked hexes - `#2fb84d` green, `#f12d2d` red, `#ff8900`
2503
+ * orange - and blind to the theme: the SAME three bars in light and in dark,
2504
+ * because a literal cannot follow a theme. Each is re-pointed at the token
2505
+ * whose hue it was already imitating, which is the same treatment the KPI
2506
+ * tiles got, and which keeps all three bars distinguishable.
2507
+ *
2508
+ * ONE OF THEM DOES NOT AGREE WITH `utils/status.ts`, ON PURPOSE.
2509
+ * That file maps `completed -> ok` and `pending -> warn` (both honoured here),
2510
+ * but it also maps `in progress -> warn` - the SAME tone as `pending`. Applying
2511
+ * it literally would paint two of these three bars the same amber and make the
2512
+ * chart unreadable, which is a regression, not a fix. So "In Progress" keeps
2513
+ * the tone it is drawn in today (`--err`, the token `#f12d2d` was imitating)
2514
+ * and this stays a colour change with no behaviour change.
2515
+ *
2516
+ * That leaves a real inconsistency to settle: on this very page the "In
2517
+ * Progress" feedback CHIP is amber (`warn`) while this bar is red. Resolving it
2518
+ * means either giving `in progress` its own tone in `utils/status.ts` - which
2519
+ * is shared by every chip in the estate - or ruling that a chart series is not
2520
+ * a status chip. Both are design decisions, so neither is taken here.
2521
+ */
2476
2522
  const workOrderStatus = computed(() => {
2477
2523
  const summary = finalDashboardData.value?.workOrderStatusSummary;
2478
2524
  const metric = getMetric(["openWorkOrder", "workOrders", "workOrder"]);
@@ -2493,19 +2539,19 @@ const workOrderStatus = computed(() => {
2493
2539
  label: "Completed",
2494
2540
  value: completed,
2495
2541
  percent: (completed / max) * 100,
2496
- color: "#2fb84d",
2542
+ color: "var(--ok)",
2497
2543
  },
2498
2544
  {
2499
2545
  label: "In Progress",
2500
2546
  value: inProgress,
2501
2547
  percent: (inProgress / max) * 100,
2502
- color: "#f12d2d",
2548
+ color: "var(--err)",
2503
2549
  },
2504
2550
  {
2505
2551
  label: "Pending",
2506
2552
  value: pending,
2507
2553
  percent: (pending / max) * 100,
2508
- color: "#ff8900",
2554
+ color: "var(--warn)",
2509
2555
  },
2510
2556
  ];
2511
2557
  });
@@ -3945,32 +3991,53 @@ function formatDashboardTime(value?: string) {
3945
3991
  text-transform: capitalize;
3946
3992
  }
3947
3993
 
3948
- /* Was five pairs of literal pastels - the same pale green on the dark page,
3949
- and none of them the tones the rest of the product uses. */
3994
+ /* THESE FIVE ARE LITERAL ON PURPOSE - THE PRINT VIEW IS PAPER.
3995
+ *
3996
+ * `.print-dashboard-view` above pins its surface to `#ffffff` and its ink to
3997
+ * `#1e293b` in BOTH themes, because a printed page is white however the app is
3998
+ * themed. Putting the status tokens on these badges therefore did the opposite
3999
+ * of what tokenising usually does: it let the SCREEN theme reach a surface that
4000
+ * does not follow it. Measured on the print branch at 1440:
4001
+ *
4002
+ * light In Progress rgb(59,111,212) on rgb(235,241,251) 4.20:1
4003
+ * light Completed rgb(15,138,98) on rgb(231,243,239) 3.82:1
4004
+ * dark In Progress rgb(125,166,236) on rgb(239,244,253) 2.23:1
4005
+ * dark Completed rgb(65,199,149) on rgb(232,248,242) 1.93:1
4006
+ *
4007
+ * The dark rows are the defect: the dark-theme INK composited over the white
4008
+ * paper the print view forces, so anyone who exports the dashboard while the
4009
+ * app is in dark mode gets washed-out badges - 1.93:1, the lowest reading taken
4010
+ * on this component. So these go back to fixed pastels, and specifically to the
4011
+ * SAME five pairs the Word export writes into its own stylesheet further up, so
4012
+ * the on-page print view and the exported document finally match.
4013
+ *
4014
+ * Same rule as the signature pad: a surface that does not follow the theme must
4015
+ * not be painted with tokens that do.
4016
+ */
3950
4017
  .badge-success {
3951
- background-color: var(--ok-bg);
3952
- color: var(--ok);
4018
+ background-color: #d1fae5;
4019
+ color: #065f46;
3953
4020
  }
3954
4021
 
3955
4022
  .badge-info {
3956
- background-color: var(--info-bg);
3957
- color: var(--info);
4023
+ background-color: #e0f2fe;
4024
+ color: #075985;
3958
4025
  }
3959
4026
 
3960
4027
  .badge-danger,
3961
4028
  .badge-error {
3962
- background-color: var(--err-bg);
3963
- color: var(--err);
4029
+ background-color: #fee2e2;
4030
+ color: #991b1b;
3964
4031
  }
3965
4032
 
3966
4033
  .badge-warning {
3967
- background-color: var(--warn-bg);
3968
- color: var(--warn);
4034
+ background-color: #fef3c7;
4035
+ color: #92400e;
3969
4036
  }
3970
4037
 
3971
4038
  .badge-neutral {
3972
- background-color: var(--hover);
3973
- color: var(--text2);
4039
+ background-color: #f1f5f9;
4040
+ color: #475569;
3974
4041
  }
3975
4042
 
3976
4043
  /* ------------------------------------------------------------------ */
@@ -1,14 +1,31 @@
1
+ <!--
2
+ The gate that stands in for a whole screen when HID is not enabled for a site.
3
+
4
+ What it was: a raw `v-alert type="info" variant="tonal"` — Vuetify's own tonal
5
+ slab, at Vuetify's 4px radius, in Vuetify's font, on a Vuetify blue the theme
6
+ does not control. It is a full-screen state, not an inline warning.
7
+
8
+ DESIGN GAP, STATED: the handoff draws no "service not enabled" page. So this
9
+ is assembled from the established primitive rather than invented, exactly as
10
+ `PlaceholderComponent` was — `.screen-empty` (`assets/css/screens.css:149`) is
11
+ the design's centred, muted, 52px-padded block for "there is nothing to show
12
+ here", and the heading takes the `--fs-card-title` / `--fw-card-title` type
13
+ the design uses for every panel heading.
14
+
15
+ `.tone-info` sets `--tone-bg`; the icon's soft disc is the only thing that
16
+ paints with it, so the block keeps the informational colour without becoming
17
+ a coloured slab. Icons are translated centrally — no hand-mapping here.
18
+ -->
1
19
  <template>
2
20
  <slot v-if="hidEnabled" />
3
21
 
4
- <v-alert
5
- v-else
6
- type="info"
7
- variant="tonal"
8
- class="mt-6"
9
- title="HID service is not enabled"
10
- :text="message"
11
- />
22
+ <div v-else class="screen-empty hid-gate">
23
+ <span class="hid-gate__icon tone-info">
24
+ <v-icon icon="mdi-information-outline" size="24" />
25
+ </span>
26
+ <h2 class="hid-gate__title">HID service is not enabled</h2>
27
+ <p class="hid-gate__body">{{ message }}</p>
28
+ </div>
12
29
  </template>
13
30
 
14
31
  <script setup lang="ts">
@@ -49,3 +66,40 @@ watchEffect(() => {
49
66
  );
50
67
  });
51
68
  </script>
69
+
70
+ <style scoped>
71
+ /* It replaces a screen's whole body, so it holds the viewport rather than
72
+ collapsing to the height of three lines. Same reason as PlaceholderComponent. */
73
+ .hid-gate {
74
+ min-height: 40vh;
75
+ }
76
+
77
+ .hid-gate__icon {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ justify-content: center;
81
+ width: 44px;
82
+ height: 44px;
83
+ border-radius: 50%;
84
+ background: var(--tone-bg);
85
+ }
86
+
87
+ /* `.screen-empty .v-icon` paints `--muted`; inside the tone scope the icon is
88
+ the informational colour, so it has to win that one-class rule. */
89
+ .hid-gate .hid-gate__icon .v-icon {
90
+ color: var(--info);
91
+ }
92
+
93
+ .hid-gate__title {
94
+ margin: 0;
95
+ font-size: var(--fs-card-title);
96
+ font-weight: var(--fw-card-title);
97
+ color: var(--text);
98
+ }
99
+
100
+ .hid-gate__body {
101
+ max-width: 46ch;
102
+ margin: 0;
103
+ font-weight: var(--fw-cell);
104
+ }
105
+ </style>
@@ -51,9 +51,9 @@ export default function useFeedback() {
51
51
 
52
52
  const feedback = useState("feedback", () => new MFeedback());
53
53
  const feedbacks = useState("feedbacks", (): TFeedback[] => []);
54
- const page = useState("page", () => 1);
55
- const pages = useState("pages", () => 0);
56
- const pageRange = useState("pageRange", () => "-- - -- of --");
54
+ const page = useState("feedbackPage", () => 1);
55
+ const pages = useState("feedbackPages", () => 0);
56
+ const pageRange = useState("feedbackPageRange", () => "-- - -- of --");
57
57
 
58
58
  function setFeedback(data?: TFeedback) {
59
59
  feedback.value = data || new MFeedback();
@@ -1,8 +1,8 @@
1
1
  export default function useMember() {
2
2
  const members = useState<Array<TMember>>("members", () => []);
3
- const page = useState("page", () => 1);
4
- const pages = useState("pages", () => 0);
5
- const pageRange = useState("pageRange", () => "-- - -- of --");
3
+ const page = useState("memberPage", () => 1);
4
+ const pages = useState("memberPages", () => 0);
5
+ const pageRange = useState("memberPageRange", () => "-- - -- of --");
6
6
 
7
7
  const member = useState<TMember>("member", () => ({
8
8
  _id: "",
@@ -8,9 +8,9 @@ export default function useServiceProvider() {
8
8
  () => []
9
9
  );
10
10
 
11
- const page = useState("page", () => 1);
12
- const pages = useState("pages", () => 0);
13
- const pageRange = useState("pageRange", () => "-- - -- of --");
11
+ const page = useState("serviceProviderPage", () => 1);
12
+ const pages = useState("serviceProviderPages", () => 0);
13
+ const pageRange = useState("serviceProviderPageRange", () => "-- - -- of --");
14
14
 
15
15
  const serviceProvider = useState(
16
16
  "serviceProvider",
@@ -57,9 +57,23 @@ export default function useWorkOrder() {
57
57
 
58
58
  const workOrder = useState("workOrder", () => new MWorkOrder());
59
59
  const workOrders = useState<Array<TWorkOrder>>("workOrders", () => []);
60
- const page = useState("page", () => 1);
61
- const pages = useState("pages", () => 0);
62
- const pageRange = useState("pageRange", () => "-- - -- of --");
60
+ /*
61
+ * Namespaced, like every other composable in this folder (`facilityPage`,
62
+ * `commentPage`, `unit-billPage`). These three were registered on the bare
63
+ * keys "page" / "pages" / "pageRange", which `useState` treats as ONE
64
+ * app-global cell per key - so `useFeedback`, `useMember` and
65
+ * `useServiceProvider`, which register the same bare keys, were handed the
66
+ * very same refs. Two of those screens open at once and they share one page
67
+ * counter.
68
+ *
69
+ * Nothing depended on the collision: these three are never assigned anywhere
70
+ * in this composable, and `WorkOrder/ListView.vue` is the only file in the
71
+ * layer or in any of the 11 web apps that destructures them - every real
72
+ * work-order page keeps its own local `ref(1)`.
73
+ */
74
+ const page = useState("workOrderPage", () => 1);
75
+ const pages = useState("workOrderPages", () => 0);
76
+ const pageRange = useState("workOrderPageRange", () => "-- - -- of --");
63
77
 
64
78
  function setWorkOrder(data?: TWorkOrder) {
65
79
  workOrder.value = data || new MWorkOrder();
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "3.2.2-staging.153",
5
+ "version": "3.2.2-staging.155",
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.",