@7365admin1/layer-common 3.2.1 → 3.2.2-staging.101

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 (126) hide show
  1. package/.changeset/camera-wall-gated-endpoint.md +47 -0
  2. package/.changeset/camera-wall-plain-english.md +60 -0
  3. package/.changeset/camera-wall-selection-and-guidance.md +65 -0
  4. package/.changeset/dark-primary-contrast.md +39 -0
  5. package/.changeset/dashboard-dark-theme-contrast.md +15 -0
  6. package/.changeset/notification-preferences.md +17 -0
  7. package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +27 -0
  8. package/.changeset/service-provider-invitation-actions.md +28 -0
  9. package/assets/css/primitives.css +680 -0
  10. package/assets/css/screens.css +541 -0
  11. package/assets/css/shell.css +258 -0
  12. package/assets/css/tokens.css +220 -0
  13. package/components/AccessCardQrTagging.vue +1 -1
  14. package/components/AccessManagement.vue +53 -72
  15. package/components/AppButton.vue +52 -0
  16. package/components/AppCard.vue +20 -0
  17. package/components/AppDateField.vue +35 -0
  18. package/components/AppField.vue +51 -0
  19. package/components/AppKpiTile.vue +65 -0
  20. package/components/AppSegmented.vue +38 -0
  21. package/components/AppSelect.vue +203 -0
  22. package/components/AreaChecklistHistoryLogs.vue +51 -44
  23. package/components/AreaChecklistHistoryMain.vue +6 -2
  24. package/components/AreaMain.vue +9 -17
  25. package/components/AttendanceMain.vue +4 -3
  26. package/components/Avatar/Main.vue +54 -2
  27. package/components/BillingMain.vue +26 -30
  28. package/components/BuildingManagement/buildings.vue +27 -38
  29. package/components/BuildingManagement/units.vue +26 -42
  30. package/components/BulletinBoardManagement.vue +23 -10
  31. package/components/CameraWall.vue +1034 -0
  32. package/components/CameraWallTile.vue +818 -0
  33. package/components/CardToolbar.vue +37 -0
  34. package/components/CleaningScheduleMain.vue +29 -46
  35. package/components/ClientMain.vue +90 -78
  36. package/components/DashboardEmptyState.vue +11 -1
  37. package/components/DashboardMain.vue +349 -275
  38. package/components/DashboardPanel.vue +1 -1
  39. package/components/DocumentManagement.vue +129 -93
  40. package/components/EntryPassInformation.vue +4 -4
  41. package/components/EntryPassMain.vue +49 -27
  42. package/components/EquipmentItemMain.vue +4 -6
  43. package/components/EquipmentManagementMain.vue +9 -7
  44. package/components/FeedbackMain.vue +42 -56
  45. package/components/FormDialog.vue +60 -21
  46. package/components/HidAccessLogDashboard.vue +119 -44
  47. package/components/HidCameraCaptureDialog.vue +199 -0
  48. package/components/HidIntercomManagement.vue +825 -202
  49. package/components/HidQrCodeConfiguration.vue +927 -93
  50. package/components/HidServiceSettingsPanel.vue +20 -1
  51. package/components/HidUserEnrollment.vue +454 -106
  52. package/components/InvitationMain.vue +46 -61
  53. package/components/Layout/Header.vue +289 -37
  54. package/components/Layout/NavigationDrawer.vue +340 -30
  55. package/components/ManageChecklistMain.vue +17 -17
  56. package/components/MemberMain.vue +79 -60
  57. package/components/MyAttendanceMain.vue +3 -5
  58. package/components/NavigationItem.vue +9 -0
  59. package/components/Nfc/NFCPatrolRouteMain.vue +116 -103
  60. package/components/NotificationSettings.vue +361 -0
  61. package/components/OnlineFormConfigurationForm.vue +620 -224
  62. package/components/OnlineFormFill.vue +36 -8
  63. package/components/OnlineFormsConfiguration.vue +7 -3
  64. package/components/PageHeader.vue +43 -0
  65. package/components/PassInformation.vue +104 -45
  66. package/components/QrTemplate/CreditCardLandscape.vue +19 -12
  67. package/components/QrTemplate/PrintDialog.vue +209 -196
  68. package/components/RolePermissionMain.vue +184 -91
  69. package/components/ScanVisitorQRCode.vue +35 -7
  70. package/components/ScheduleAreaMain.vue +15 -49
  71. package/components/ScheduleTaskMain.vue +23 -26
  72. package/components/ScheduleTastTicketMain.vue +18 -43
  73. package/components/ServiceProviderInvitationPrompt.vue +150 -0
  74. package/components/ServiceProviderMain.vue +346 -157
  75. package/components/SiteSettings.vue +25 -10
  76. package/components/SiteSettingsWorkOrder.vue +61 -30
  77. package/components/SpecificAttr.vue +10 -2
  78. package/components/StatusChip.vue +93 -0
  79. package/components/StockCard.vue +17 -12
  80. package/components/SwitchContext.vue +33 -1
  81. package/components/TableHygiene.vue +29 -46
  82. package/components/TableList.vue +1 -1
  83. package/components/TableListSecondary.vue +1 -1
  84. package/components/TableMain.vue +161 -77
  85. package/components/TableV3.vue +61 -66
  86. package/components/UnitMain.vue +9 -17
  87. package/components/VehicleManagement.vue +20 -9
  88. package/components/VisitorForm.vue +294 -78
  89. package/components/VisitorManagement.vue +221 -71
  90. package/components/VisitorSocketPopUp.vue +56 -2
  91. package/components/WorkOrder/Main.vue +42 -51
  92. package/composables/useComment.ts +3 -3
  93. package/composables/useHidAmico.ts +151 -0
  94. package/composables/useHidNavigation.ts +0 -7
  95. package/composables/useLocalAuth.ts +9 -36
  96. package/composables/useNotificationPreference.ts +87 -0
  97. package/composables/useOnlineFormPages.ts +2 -0
  98. package/composables/useOrg.ts +1 -1
  99. package/composables/useRole.ts +22 -0
  100. package/composables/useServiceProviderInvitation.ts +145 -0
  101. package/composables/useSipWebPhone.ts +336 -0
  102. package/composables/useSiteSettings.ts +50 -0
  103. package/composables/useThemePreference.ts +63 -0
  104. package/composables/useVisitor.ts +6 -1
  105. package/composables/useVisitorSocket.ts +26 -0
  106. package/composables/useWebUsb.ts +127 -37
  107. package/nuxt.config.ts +31 -0
  108. package/package.json +4 -1
  109. package/pages/[org]/[site]/access-mgmt/intercom/index.vue +1 -1
  110. package/pages/notification-settings.vue +19 -0
  111. package/plugins/secure-member.client.ts +21 -56
  112. package/plugins/vuetify.ts +68 -9
  113. package/test/visitor-socket.test.mjs +36 -0
  114. package/types/org.d.ts +2 -0
  115. package/types/site.d.ts +71 -0
  116. package/utils/camera-wall.test.ts +571 -0
  117. package/utils/camera-wall.ts +926 -0
  118. package/utils/nav-sections.test.ts +190 -0
  119. package/utils/nav-sections.ts +151 -0
  120. package/utils/status.test.ts +77 -0
  121. package/utils/status.ts +90 -0
  122. package/utils/theme.test.ts +534 -0
  123. package/utils/theme.ts +294 -0
  124. package/components/HidIdentityMapping.vue +0 -975
  125. package/middleware/member.ts +0 -4
  126. package/pages/[org]/[site]/access-mgmt/identity-mapping/index.vue +0 -23
package/utils/theme.ts ADDED
@@ -0,0 +1,294 @@
1
+ /**
2
+ * THE PLATFORM PALETTE, AND THE BRIDGE BETWEEN TWO COLOUR SYSTEMS.
3
+ *
4
+ * The redesign is specified in raw CSS custom properties (`--bg`, `--card`,
5
+ * `--muted`, `--ok`...). This product is built in Vuetify, which paints from
6
+ * its own `--v-theme-*` RGB triples. Stacking the two would leave every
7
+ * `v-card`, `v-btn`, `v-chip` and `v-data-table` in eleven applications on its
8
+ * factory colours underneath a correct-looking token file.
9
+ *
10
+ * So there is ONE source of truth - `PALETTE` below - and it flows one way:
11
+ *
12
+ * PALETTE -> Vuetify theme colours -> `--v-theme-*` -> `--bg` etc.
13
+ * (LIGHT_THEME/DARK_THEME) (Vuetify emits) (assets/css/tokens.css)
14
+ *
15
+ * `assets/css/tokens.css` does not restate a single colour; it aliases each
16
+ * design token to the Vuetify variable that already holds it. That matters for
17
+ * more than tidiness: Vuetify puts `.v-theme--light` / `.v-theme--dark` on
18
+ * every `v-theme-provider` subtree, so a token read inside the `plain-dark`
19
+ * layout or inside `FormDialog` resolves to that subtree's theme instead of the
20
+ * page's. A `body.dark` class - which is how the prototype does it - cannot do
21
+ * that, and would have gone wrong in exactly those two places.
22
+ *
23
+ * Every ratio quoted here is WCAG 2.1, alpha composited against the surface the
24
+ * value actually sits on, and `utils/theme.test.ts` re-measures all of them on
25
+ * every run. This file has already been got wrong once by moving a colour for
26
+ * one use and forgetting the other.
27
+ */
28
+
29
+ /**
30
+ * WHAT MOVED FROM THE DESIGN, AND WHY - the owner's decision, 2026-08-13:
31
+ * "minimal darkening, keep the look".
32
+ *
33
+ * Measured against the design as handed over, the LIGHT theme failed AA on 27
34
+ * text pairs and the DARK theme on 6. Six light values move, none by enough to
35
+ * read as a different colour, and the dark theme's own palette is untouched:
36
+ *
37
+ * --muted #8b8f98 -> #6b7079 (table headers, breadcrumbs, sub-lines)
38
+ * --ok #0f8a62 -> #0e7c58 (-10%)
39
+ * --warn #a97a14 -> #8e6611 (-16%)
40
+ * --err #cf4b4b -> #bc4444 (-9%)
41
+ * --info #3b6fd4 -> #3869c9 (-5%)
42
+ * --accent-text #4a6eb6 -> #476aaf (-4%)
43
+ *
44
+ * The soft chip BACKGROUNDS are left exactly as designed. The design states
45
+ * them literally rather than deriving them from the foreground, so darkening a
46
+ * label does not drag its chip with it - a Pending chip is the same colour it
47
+ * was, its text is simply readable on it.
48
+ *
49
+ * `--accent-text` is `color-mix(in oklab, var(--accent) 75%, var(--text))` in
50
+ * the design. It is resolved to a plain hex here because Vuetify needs a
51
+ * concrete colour and because a test cannot measure a `color-mix()`.
52
+ */
53
+
54
+ /**
55
+ * WHITE ON THE ACCENT: A DARKER ACCENT FOR FILLS, NOT A BIGGER FONT.
56
+ *
57
+ * White on `--accent` (#5b8def) is 3.23:1 in BOTH themes - the fill is
58
+ * theme-independent, so the failure is too. It is the primary button's label
59
+ * and the active segment of the Today/Week/Month control. There were two ways
60
+ * out and only one of them keeps the design:
61
+ *
62
+ * - The type route. 3.23:1 is legal for LARGE text, which starts at 18.66px
63
+ * bold. The design's buttons are 13.5px/700-800 in a 40px pill. Reaching
64
+ * 18.66px would make the label taller than the button was drawn for and
65
+ * change every button in the product. Rejected.
66
+ * - `--accent-strong`, an 18%-darker accent used ONLY where a white label
67
+ * sits on an accent fill. White reads 4.57:1 on it, and the fill still
68
+ * holds its own edge (4.57:1 against a light card, 3.64:1 against a dark
69
+ * one). Chosen.
70
+ *
71
+ * The point is what it does NOT touch. `--accent` itself is unchanged, so the
72
+ * links, the active sidebar item, the tab underline, the toggle in its on
73
+ * state, the chart lines, the focus rings, the logo tile and every
74
+ * `--accent-soft` background are the exact colour the design specifies. Only
75
+ * the two filled surfaces that carry white lettering are darker.
76
+ */
77
+
78
+ /**
79
+ * `primary` IS BOTH A FILL AND A FOREGROUND, AND ONE VALUE CANNOT SERVE BOTH.
80
+ *
81
+ * Vuetify emits `.bg-primary` and `.text-primary` from the same token, so the
82
+ * colour that fills a button is also the colour of every `class="text-primary"`
83
+ * sentence, every `color="primary"` icon, spinner and text button, and the
84
+ * focused outline on every input. On a dark page those pull in opposite
85
+ * directions: a fill that carries a white label wants to be dark, a foreground
86
+ * that is readable on a dark card wants to be light. There is no overlap.
87
+ *
88
+ * That is why the split established on 2026-08-12 survives this rewrite, just
89
+ * pointed at the new palette:
90
+ *
91
+ * primary = --accent-text (a FOREGROUND: links, icons, text)
92
+ * primary-button = --accent-strong (a FILL that carries a white label)
93
+ * brand-surface = --sidebar (the navigation drawer's own fill)
94
+ *
95
+ * Re-pointing rather than renaming is deliberate: `text-primary` and
96
+ * `primary-button` are written in roughly eighty places across this layer and
97
+ * the applications, and none of them has to change.
98
+ */
99
+
100
+ /**
101
+ * VUETIFY DERIVES THE LABEL ON A FILLED COLOUR, AND ITS GUESS IS WHITE.
102
+ *
103
+ * Measured in a browser, not assumed: given `warning`, `info`, `error`,
104
+ * `success` or `primary` as a fill, Vuetify picks a WHITE foreground. On this
105
+ * palette's dark theme that is 2.04:1 to 2.99:1 - every filled chip, alert,
106
+ * badge and button in dark mode. So each `on-*` is set explicitly below rather
107
+ * than left to the derivation. Light's fills are dark enough that white is in
108
+ * fact right, and it is still written down there, so that no component in
109
+ * eleven applications is left depending on Vuetify guessing correctly.
110
+ */
111
+ export const PALETTE = {
112
+ light: {
113
+ /** Page background. */
114
+ bg: "#f6f6f2",
115
+ /** The navigation rail. */
116
+ sidebar: "#fbfbf8",
117
+ /** Cards, tables, modals, menus. */
118
+ card: "#ffffff",
119
+ /** Card and table edges. As designed - see the note in the test. */
120
+ border: "#e8e8e1",
121
+ /** Primary text. 17.24:1 on a card. */
122
+ text: "#191b1f",
123
+ /** Secondary text. 9.62:1 on a card. */
124
+ text2: "#41454d",
125
+ /** Table headers, breadcrumbs, KPI sub-lines. 4.98:1 on a card. */
126
+ muted: "#6b7079",
127
+ /** Completed / Paid / Available / Open. 4.58:1 on its chip. */
128
+ ok: "#0e7c58",
129
+ /** Pending. 4.56:1 on its chip. */
130
+ warn: "#8e6611",
131
+ /** In Use / Closed / Checkout. 4.55:1 on its chip. */
132
+ err: "#bc4444",
133
+ /** Role and info tags. 4.58:1 on its chip. */
134
+ info: "#3869c9",
135
+ /** The brand accent, exactly as designed. Never carries white lettering. */
136
+ accent: "#5b8def",
137
+ /** The only place the accent is darkened: fills with a white label. 4.57:1. */
138
+ accentStrong: "#4b74c4",
139
+ /** Links, active nav item, active tab. 4.57:1 on `--accent-soft`. */
140
+ accentText: "#476aaf",
141
+ /** Labels on the filled colours above. Light's fills carry white. */
142
+ onFill: "#ffffff",
143
+ },
144
+ dark: {
145
+ bg: "#131418",
146
+ sidebar: "#17181d",
147
+ card: "#1c1e24",
148
+ border: "#2a2d35",
149
+ text: "#eef0f3",
150
+ text2: "#c6cad2",
151
+ /** Passes as designed at 4.93:1 - the dark theme needed no darkening. */
152
+ muted: "#868c98",
153
+ ok: "#41c795",
154
+ warn: "#dcaf4e",
155
+ err: "#e57373",
156
+ info: "#7da6ec",
157
+ accent: "#5b8def",
158
+ /** Same value as light: the failure was theme-independent, so the fix is. */
159
+ accentStrong: "#4b74c4",
160
+ accentText: "#78a2f4",
161
+ /**
162
+ * Dark's fills are LIGHT colours, so their label is the page ink, not
163
+ * white. White on them measures 2.04:1 to 2.99:1; this reads 6.16:1 to
164
+ * 9.01:1. Not a new colour - it is `bg` above, so a filled chip reads as a
165
+ * hole punched in the page.
166
+ */
167
+ onFill: "#131418",
168
+ },
169
+ } as const;
170
+
171
+ /**
172
+ * The values are widened back to `string`. `PALETTE` is `as const` so a test
173
+ * can assert an exact hex, but the builder below takes EITHER theme - and with
174
+ * the literal types intact, "#131418" is not assignable to "#f6f6f2" and the
175
+ * dark theme will not compile.
176
+ */
177
+ export type TPalette = { readonly [K in keyof (typeof PALETTE)["light"]]: string };
178
+
179
+ type Palette = TPalette;
180
+
181
+ /**
182
+ * One theme builder for both themes, so a colour can never be wired into light
183
+ * and forgotten in dark - which is the specific way this file broke before.
184
+ */
185
+ const theme = (dark: boolean, p: Palette) => ({
186
+ dark,
187
+ colors: {
188
+ // --- the design's surfaces -------------------------------------------
189
+ background: p.bg,
190
+ surface: p.card,
191
+ "on-background": p.text,
192
+ "on-surface": p.text,
193
+ /** Menus and raised sheets sit on the card colour, not Vuetify's grey. */
194
+ "surface-bright": p.card,
195
+ "surface-light": p.card,
196
+ "surface-variant": p.sidebar,
197
+ "on-surface-variant": p.text2,
198
+
199
+ // --- the split accent -------------------------------------------------
200
+ /** A FOREGROUND. See the note above before making this a fill colour. */
201
+ primary: p.accentText,
202
+ "on-primary": p.onFill,
203
+ /** The FILL that carries a white label. White on it: 4.57:1, both themes. */
204
+ "primary-button": p.accentStrong,
205
+ "on-primary-button": "#ffffff",
206
+ /** The navigation drawer's fill = the design's sidebar colour. */
207
+ "brand-surface": p.sidebar,
208
+ "on-brand-surface": p.text,
209
+ /** ~80 call sites across the layer and the apps. Now the design's ink. */
210
+ "text-primary": p.text,
211
+
212
+ // --- semantic status, mapped as the design specifies ------------------
213
+ /** Completed / Paid / Available / Open. */
214
+ success: p.ok,
215
+ "on-success": p.onFill,
216
+ /** Pending. */
217
+ warning: p.warn,
218
+ "on-warning": p.onFill,
219
+ /** In Use / Closed / Checkout. */
220
+ error: p.err,
221
+ "on-error": p.onFill,
222
+ /** Role and info tags. */
223
+ info: p.info,
224
+ "on-info": p.onFill,
225
+
226
+ // --- design tokens with no Vuetify equivalent -------------------------
227
+ // These exist so `assets/css/tokens.css` can alias `--text2`, `--muted`,
228
+ // `--border`, `--accent` and friends to a `--v-theme-*` that Vuetify keeps
229
+ // in step with the active theme, rather than restating hexes in CSS.
230
+ accent: p.accent,
231
+ "accent-strong": p.accentStrong,
232
+ "accent-text": p.accentText,
233
+ text2: p.text2,
234
+ muted: p.muted,
235
+ border: p.border,
236
+ sidebar: p.sidebar,
237
+ },
238
+ variables: {
239
+ /**
240
+ * AN OPACITY IS ONLY AS DARK AS THE INK IT IS APPLIED TO.
241
+ *
242
+ * Both values below were tuned when `on-surface` was Vuetify's pure black.
243
+ * The design's ink is `#191b1f`, which is lighter, so the SAME opacity now
244
+ * composites to a paler grey - and it showed up the moment the two screens
245
+ * that shipped today were re-rendered: every `text-medium-emphasis` line
246
+ * fell from 5.74:1 to 4.53:1 on a card and 4.41:1 on the page, taking the
247
+ * dashboard's site name and the preference screen's summary below AA.
248
+ *
249
+ * That is one cause with about two hundred call sites across eleven
250
+ * applications, so it is fixed once, here, rather than in the two places
251
+ * the measurement happened to catch.
252
+ *
253
+ * `medium-emphasis-opacity` 0.64: 5.11:1 on a card, 4.94:1 on the page,
254
+ * 5.04:1 on the sidebar. The minimum that clears AA is 0.61 and this keeps
255
+ * a margin, while staying clearly lighter than `--text2`'s 9.62:1 - it is
256
+ * still medium emphasis, not body text.
257
+ *
258
+ * `disabled-opacity` 0.62: the value that composites `#191b1f` to the same
259
+ * grey `#000000` reached at 0.55, so a disabled field is exactly as dark as
260
+ * it was before this palette. It is NOT 4.5:1 as rendered - Vuetify
261
+ * multiplies its own field alpha on top and the result measures 1.9:1 -
262
+ * but that compounding predates this work and un-compounding it means
263
+ * changing how an input draws, which is Phase 3's job, not a token's.
264
+ *
265
+ * The same number in both themes on purpose: a value that drifts apart
266
+ * between light and dark is a bug nobody sees until they flip the switch.
267
+ */
268
+ /**
269
+ * `high-emphasis-opacity` 1, and this one REMOVES a dimming layer rather
270
+ * than retuning it. Vuetify's 0.87 exists to soften pure black into body
271
+ * ink; the design already ships body ink, so applying 0.87 on top of
272
+ * `#191b1f` dims text that was never black to begin with.
273
+ *
274
+ * It also compounds. A field label is high emphasis AND medium emphasis -
275
+ * 0.87 x 0.64 - which is how "Search work orders" was rendering at 3.95:1
276
+ * while every token it is built from passed on its own. Taking this to 1
277
+ * leaves one opacity on that label instead of two, and it measures 5.02:1.
278
+ */
279
+ "high-emphasis-opacity": 1,
280
+ "medium-emphasis-opacity": 0.64,
281
+ "disabled-opacity": 0.62,
282
+ /**
283
+ * Vuetify draws borders as `on-surface` at this opacity. The design's
284
+ * `--border` is a much softer edge than Material's default; these are the
285
+ * opacities that land closest to it while staying visible. Components that
286
+ * need a real boundary raise it on themselves - see `NavigationDrawer.vue`.
287
+ */
288
+ "border-opacity": dark ? 0.35 : 0.26,
289
+ },
290
+ });
291
+
292
+ export const LIGHT_THEME = theme(false, PALETTE.light);
293
+
294
+ export const DARK_THEME = theme(true, PALETTE.dark);