@7365admin1/layer-common 3.2.2-staging.121 → 3.2.2-staging.123

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 (34) hide show
  1. package/assets/css/primitives.css +92 -0
  2. package/assets/css/tokens.css +16 -0
  3. package/components/AppButton.vue +10 -1
  4. package/components/StatusChip.vue +63 -0
  5. package/components/VisitorManagement.vue +20 -13
  6. package/nuxt.config.ts +6 -1
  7. package/package.json +22 -1
  8. package/plugins/vuetify.ts +37 -1
  9. package/utils/materialSymbols.ts +363 -0
  10. package/.changeset/README.md +0 -8
  11. package/.changeset/camera-wall-gated-endpoint.md +0 -47
  12. package/.changeset/camera-wall-plain-english.md +0 -60
  13. package/.changeset/camera-wall-selection-and-guidance.md +0 -65
  14. package/.changeset/config.json +0 -11
  15. package/.changeset/dark-primary-contrast.md +0 -39
  16. package/.changeset/dashboard-dark-theme-contrast.md +0 -15
  17. package/.changeset/notification-preferences.md +0 -17
  18. package/.changeset/pm-sidebar-grouping-and-list-scaffold.md +0 -27
  19. package/.changeset/service-provider-invitation-actions.md +0 -28
  20. package/.editorconfig +0 -12
  21. package/.github/workflows/main.yml +0 -17
  22. package/.github/workflows/publish-staging.yml +0 -47
  23. package/.github/workflows/publish.yml +0 -39
  24. package/PUBLISHING.md +0 -269
  25. package/components/ScheduleTaskAreaFormDialog.vue +0 -141
  26. package/components/ScheduleTaskAreaUpdateMoreAction.vue +0 -104
  27. package/components/TableWithButton.vue +0 -94
  28. package/test/visitor-socket.test.mjs +0 -36
  29. package/tools/render-harness/README.md +0 -87
  30. package/tools/render-harness/baselines.json +0 -117
  31. package/tools/render-harness/harness-init.ps1 +0 -127
  32. package/tools/render-harness/probe.mjs +0 -229
  33. package/tools/render-harness/render-check.mjs +0 -306
  34. package/tools/render-harness/render-check.selftest.mjs +0 -129
@@ -1,94 +0,0 @@
1
- <template>
2
- <v-card flat border="sm black" :loading="loading" class="px-0" :ripple="false">
3
- <v-toolbar class="px-5">
4
- <template #prepend>
5
- <span class="text-subtitle-1 font-weight-black">{{ tableTitle }}</span>
6
- </template>
7
- <template #append>
8
- <v-btn color="primary" variant="flat" size="large" density="comfortable" :text="buttonLabel"
9
- class="text-capitalize" />
10
- </template>
11
- </v-toolbar>
12
- <v-card-text class="pa-0 bg-red">
13
- <v-data-table :headers="headers" :items="items" :item-value="itemValue" :items-per-page="itemsPerPage"
14
- fixed-header hide-default-footer hide-default-header @click:row="$emit('row-click', $event)"
15
- style="max-height: calc(100vh - (200px))">
16
- <template v-for="(_, slotName) in $slots" #[slotName]="slotProps">
17
- <slot :name="slotName" v-bind="slotProps" />
18
- </template>
19
- </v-data-table>
20
- </v-card-text>
21
- </v-card>
22
- </template>
23
-
24
- <script setup lang="ts">
25
- const selected = defineModel({
26
- type: Array as PropType<Array<string>>,
27
- default: () => [],
28
- });
29
- const page = defineModel("page", { type: Number, default: 0 });
30
-
31
- const props = defineProps({
32
- headers: {
33
- type: Array as PropType<Array<Record<string, string | number>>>,
34
- required: true,
35
- default: () => [],
36
- },
37
- items: {
38
- type: Array as PropType<Array<Record<string, any>>>,
39
- required: true,
40
- default: () => [],
41
- },
42
-
43
- itemValue: {
44
- type: String,
45
- default: "_id",
46
- },
47
- itemsPerPage: {
48
- type: Number,
49
- default: 20,
50
- },
51
- loading: {
52
- type: Boolean,
53
- required: true,
54
- default: true,
55
- },
56
- tableTitle: {
57
- type: String,
58
- default: "",
59
- required: false
60
- },
61
- buttonLabel: {
62
- type: String,
63
- default: "Add",
64
- required: false
65
- }
66
- });
67
-
68
- const emit = defineEmits(["refresh", "update:pagination", "row-click"]);
69
-
70
- function increment() {
71
- page.value++;
72
- emit("update:pagination", page.value);
73
- }
74
-
75
- function decrement() {
76
- page.value--;
77
- emit("update:pagination", page.value);
78
- }
79
-
80
- function tableRowClickHandler(_: any, row: any) {
81
- const item = props.items[row.index];
82
- emit("row-click", item);
83
- }
84
-
85
- const selectAll = ref(false);
86
-
87
- watch(selectAll, (curr) => {
88
- selected.value.splice(0, selected.value.length);
89
- if (curr) {
90
- const ids = props.items.map((i) => i._id as string);
91
- selected.value.push(...ids);
92
- }
93
- });
94
- </script>
@@ -1,36 +0,0 @@
1
- import assert from "node:assert/strict";
2
- import { test } from "node:test";
3
-
4
- import { cameraAlertMessage } from "./.build/useVisitorSocket.mjs";
5
-
6
- /*
7
- * The reported symptom was one camera raising a red toast every 10 seconds on
8
- * an unrelated page. API-core now paces the alert; this side has to make sure
9
- * several cameras failing at once still cannot become several snackbars.
10
- */
11
-
12
- test("nothing is shown when no camera is down", () => {
13
- assert.equal(cameraAlertMessage([]), "");
14
- });
15
-
16
- test("one failing camera shows the server's own message, which names it", () => {
17
- const text = "The entry and exit ANPR camera at Seventh Condominium is not responding.";
18
- assert.equal(cameraAlertMessage([text]), text);
19
- });
20
-
21
- test("several failing cameras collapse into a single line", () => {
22
- const many = ["camera one is down", "camera two is down", "camera three is down"];
23
- const text = cameraAlertMessage(many);
24
- assert.match(text, /^3 cameras at this site are not responding/);
25
- for (const message of many) assert.doesNotMatch(text, new RegExp(message));
26
- });
27
-
28
- test("the collapsed line points at the page that lists them", () => {
29
- assert.match(cameraAlertMessage(["a", "b"]), /Site Settings > Cameras/);
30
- });
31
-
32
- test("no camera message tells the operator to deactivate anything", () => {
33
- for (const messages of [["a camera is down"], ["a", "b"]]) {
34
- assert.doesNotMatch(cameraAlertMessage(messages), /inactive|deactivat/i);
35
- }
36
- });
@@ -1,87 +0,0 @@
1
- # Render harness hardening
2
-
3
- Tooling, not product code. Nothing here ships in `@7365admin1/layer-common` and
4
- nothing here is imported by any component — it exists to stop the theme-review
5
- harness reporting a pass it has not earned.
6
-
7
- The harness itself (vite + Vuetify + a mirror of this package, driven by
8
- Playwright) lives outside the repo, under
9
- `_scratch-theme-review/harness`. These four files attach to it.
10
-
11
- | File | What it does |
12
- |---|---|
13
- | `harness-init.ps1` | Makes a private harness for one worktree: fork directory and dev port both derived from the worktree path, own vite cache, mirror stamped with what was synced. |
14
- | `render-check.mjs` | The run. Refuses to measure an unprovable mirror, then measures each screen at 360/768/1024/1440 × light/dark. |
15
- | `probe.mjs` | The in-page half — what is actually read off the glass. |
16
- | `render-check.selftest.mjs` | Proves every detector fails the scenario it was written for. `node render-check.selftest.mjs`, no server needed. |
17
- | `baselines.json` | Per-screen recorded element and painted-text counts. |
18
-
19
- ## Use
20
-
21
- ```powershell
22
- cd <worktree>
23
- ..\iservice365-layer-common\tools\render-harness\harness-init.ps1 # prints fork + port
24
- Start-Process -WorkingDirectory <fork> cmd.exe -ArgumentList '/c','npx vite --port <port> --strictPort'
25
- cd <fork>; $env:PORT=<port>
26
- node render-check.mjs <label> CameraMain NotificationSettings
27
- ```
28
-
29
- Re-run `harness-init.ps1` after any edit to `assets`, `components`,
30
- `composables`, `constants`, `types` or `utils` — `render-check.mjs` compares the
31
- mirror to the worktree file by file and refuses to run when they disagree.
32
-
33
- ## What each guard is for
34
-
35
- Every one of these was a real false pass on 2026-08-14, not a hypothetical.
36
-
37
- - **Shared mirror.** One `lc/` copy meant two agents silently measured each
38
- other's branch. Isolation is now derived from the worktree, so there is
39
- nothing to remember, and the mirror carries a stamp the run checks.
40
- - **Empty state.** A panel scored 8/8 dark-clean while drawing "No access logs
41
- found". A table was signed off with zero header columns and "No data
42
- available", so the header and cell rules under review had never been drawn. A
43
- screen showing its empty state is a fixture bug and fails.
44
- - **Empty form.** The same bug in a shape nothing else can see — 1112 elements,
45
- nothing blank, and not one control holding a value.
46
- - **Blank screen.** Six elements, zero page errors, and a counter read it as a
47
- pass. Painted text is the honest signal.
48
- - **Element counts.** `MIN_ELS = 20` was at once too low (a cold partial paint
49
- at 32 scored ok where warm is 49) and too high (a drawing screen at 19 was
50
- reported NOT-MOUNTED). Screens legitimately range from 3 to 1100 elements, so
51
- each carries its own baseline and an un-baselined screen is refused rather
52
- than guessed at.
53
- - **Cold compile.** The run waits for the DOM to stop growing instead of a fixed
54
- timeout, warms every screen first, prints the discarded warm-up numbers so the
55
- gap is visible, and re-measures any row well under that screen's own best.
56
- - **Undefined custom properties.** `var(--success)` where the token is `--ok`:
57
- the declaration is invalid, the element inherits, and nothing reports it. Now
58
- caught even when the rule matches nothing on the screen — which is how the
59
- original survived a full 8-shot render.
60
- - **Disabled controls.** WCAG-1.4.3-exempt, so logging them as failures put
61
- fictional entries in an AA ledger. Classified as exempt, and the enabled state
62
- is probed separately so the exemption cannot hide a real failure.
63
- - **Vuetify defaults.** `VAutocomplete` was missing from the harness, and 19
64
- components in this package use one — the run would have invented a defect the
65
- product does not have. The defaults are diffed against `plugins/vuetify.ts`
66
- every run.
67
-
68
- ## Baselines
69
-
70
- `--record` writes the current run's element and painted-text counts:
71
-
72
- ```powershell
73
- node render-check.mjs --record base CameraMain
74
- ```
75
-
76
- Only record from a run whose **screenshots you have opened**. A baseline
77
- recorded from a broken render makes the break permanent. A conversion that
78
- genuinely shrinks a screen below 70% of its baseline will flag — look at the
79
- shot, then re-record.
80
-
81
- `baselines.json` accepts `"allowEmpty": true` per screen for the rare component
82
- whose empty state IS the thing under review.
83
-
84
- ## Rule that does not bend
85
-
86
- Fixtures are harness-only. Package code is never bent to suit the harness. If a
87
- screen will not draw, the fixture is wrong.
@@ -1,117 +0,0 @@
1
- {
2
- "CameraMain": {
3
- "els": 55,
4
- "textChars": 85,
5
- "recordedFrom": "0642279"
6
- },
7
- "NotificationSettings": {
8
- "els": 150,
9
- "textChars": 485,
10
- "recordedFrom": "0642279"
11
- },
12
- "OvernightParkingAvailability": {
13
- "els": 1132,
14
- "textChars": 385,
15
- "recordedFrom": "0642279"
16
- },
17
- "BulletinExpirationChip": {
18
- "els": 3,
19
- "textChars": 2,
20
- "recordedFrom": "0642279"
21
- },
22
- "AccessCardAssignToUnitForm": {
23
- "els": 177,
24
- "textChars": 131,
25
- "recordedFrom": "178ebf0"
26
- },
27
- "AccessCardReplaceForm": {
28
- "els": 68,
29
- "textChars": 83,
30
- "recordedFrom": "178ebf0"
31
- },
32
- "AddEqupmentForm": {
33
- "els": 90,
34
- "textChars": 99,
35
- "recordedFrom": "178ebf0"
36
- },
37
- "BuildingForm": {
38
- "els": 126,
39
- "textChars": 69,
40
- "recordedFrom": "178ebf0"
41
- },
42
- "BulletinBoardForm": {
43
- "els": 172,
44
- "textChars": 165,
45
- "recordedFrom": "178ebf0"
46
- },
47
- "CameraForm": {
48
- "els": 127,
49
- "textChars": 80,
50
- "recordedFrom": "178ebf0"
51
- },
52
- "DocumentForm": {
53
- "els": 52,
54
- "textChars": 57,
55
- "recordedFrom": "178ebf0"
56
- },
57
- "InvitationClientForm": {
58
- "els": 101,
59
- "textChars": 47,
60
- "recordedFrom": "178ebf0"
61
- },
62
- "InvitationForm": {
63
- "els": 86,
64
- "textChars": 43,
65
- "recordedFrom": "178ebf0"
66
- },
67
- "ServiceProviderFormCreate": {
68
- "els": 51,
69
- "textChars": 38,
70
- "recordedFrom": "178ebf0"
71
- },
72
- "AcceptDialog": {
73
- "els": 24,
74
- "textChars": 106,
75
- "recordedFrom": "178ebf0"
76
- },
77
- "Card/DeleteConfirmation": {
78
- "els": 21,
79
- "textChars": 129,
80
- "recordedFrom": "178ebf0"
81
- },
82
- "Dialog/DeleteConfirmation": {
83
- "els": 21,
84
- "textChars": 129,
85
- "recordedFrom": "178ebf0"
86
- },
87
- "HidAccessLogDashboard": {
88
- "els": 172,
89
- "textChars": 437,
90
- "recordedFrom": "178ebf0"
91
- },
92
- "HidIntercomManagement": {
93
- "els": 148,
94
- "textChars": 167,
95
- "recordedFrom": "178ebf0"
96
- },
97
- "HidUserEnrollment": {
98
- "els": 142,
99
- "textChars": 287,
100
- "recordedFrom": "178ebf0"
101
- },
102
- "HidReaderManagement": {
103
- "els": 112,
104
- "textChars": 377,
105
- "recordedFrom": "178ebf0"
106
- },
107
- "HidQrCodeConfiguration": {
108
- "els": 106,
109
- "textChars": 453,
110
- "recordedFrom": "178ebf0"
111
- },
112
- "HidServiceSettingsPanel": {
113
- "els": 18,
114
- "textChars": 164,
115
- "recordedFrom": "178ebf0"
116
- }
117
- }
@@ -1,127 +0,0 @@
1
- # Per-worktree render-harness isolation.
2
- #
3
- # WHY THIS EXISTS. The theme-review harness renders ONE `harness/lc/` mirror,
4
- # synced from a layer-common checkout by `sync-lc.ps1`. When two agents work at
5
- # once, the second `sync-lc.ps1` overwrites the first agent's mirror, and both
6
- # then measure whichever checkout synced last. That is silent - the run still
7
- # prints element counts, contrast readings and screenshots, all of the WRONG
8
- # branch. On 2026-08-14 it happened repeatedly, and every batch worked around it
9
- # by hand-forking the harness and hand-picking a port. Hand-picking is what
10
- # fails: forget it once and the whole run is fiction.
11
- #
12
- # So: isolation is DERIVED, never chosen. The fork directory and the dev port
13
- # both come from the worktree path, so the same worktree always gets the same
14
- # private harness and the same port, and two different worktrees cannot collide.
15
- #
16
- # cd <worktree>; ..\iservice365-layer-common\tools\render-harness\harness-init.ps1
17
- #
18
- # Prints the fork path and port. Re-running is safe: it re-syncs `lc/` and
19
- # leaves the port alone.
20
- [CmdletBinding()]
21
- param(
22
- # The layer-common worktree to render. Defaults to the current git worktree.
23
- [string]$Worktree = (git rev-parse --show-toplevel),
24
- # The canonical harness this fork is copied from.
25
- [string]$Canonical = "C:\Seven365Projects\iService365\_scratch-theme-review\harness"
26
- )
27
-
28
- $ErrorActionPreference = "Stop"
29
-
30
- if (-not (Test-Path "$Worktree\.git")) { throw "not a git worktree: $Worktree" }
31
- if (-not (Test-Path "$Canonical\src\main.js")) { throw "canonical harness not found: $Canonical" }
32
-
33
- $slug = Split-Path $Worktree -Leaf
34
- $fork = Join-Path (Split-Path $Canonical -Parent) "harness-$slug"
35
-
36
- # --- port, derived ----------------------------------------------------------
37
- # FNV-1a over the worktree path: same worktree -> same port, every session, on
38
- # every machine. Then step upward past anything already listening, so a hash
39
- # collision or a stale server degrades to "next free port" instead of two
40
- # agents sharing one vite and measuring each other.
41
- function Get-DerivedPort([string]$s) {
42
- $h = [uint32]2166136261
43
- foreach ($c in $s.ToLower().ToCharArray()) {
44
- # uint64 multiply, then modulo back into 32 bits. PowerShell widens past
45
- # uint32 and refuses the cast rather than wrapping, and `-band 0xFFFFFFFF`
46
- # is not the mask it looks like - 5.1 parses that literal as int -1.
47
- $h = [uint32]((([uint64]($h -bxor [uint32][int]$c)) * 16777619) % 4294967296)
48
- }
49
- $p = 5400 + [int]($h % 200)
50
- $busy = (Get-NetTCPConnection -State Listen -ErrorAction SilentlyContinue).LocalPort
51
- while ($busy -contains $p) { $p++ }
52
- return $p
53
- }
54
-
55
- $portFile = Join-Path $fork ".harness-port"
56
- if (Test-Path $portFile) {
57
- $port = [int](Get-Content $portFile -Raw).Trim()
58
- } else {
59
- $port = Get-DerivedPort $Worktree
60
- }
61
-
62
- # --- fork -------------------------------------------------------------------
63
- # Keyed on a file the copy produces, not on the directory: a run that died
64
- # half-way would otherwise leave a directory that looks finished forever.
65
- if (-not (Test-Path "$fork\vite.config.mjs")) {
66
- New-Item -ItemType Directory -Path $fork -Force | Out-Null
67
- # An ALLOWLIST, not "everything minus the big things": the canonical harness
68
- # has accumulated hundreds of one-off runners, .json measure dumps and logs,
69
- # and it also contains a stray `nul` that Copy-Item cannot read at all.
70
- # `node_modules` becomes a junction (a full copy is ~400MB per agent) and
71
- # `lc` is re-synced below.
72
- foreach ($n in "src", "apps", "public", "index.html", "package.json", "yarn.lock", "vite.config.mjs", "sync-lc.ps1") {
73
- Copy-Item (Join-Path $Canonical $n) -Destination $fork -Recurse -Force
74
- }
75
- cmd /c mklink /J "$fork\node_modules" "$Canonical\node_modules" | Out-Null
76
- }
77
-
78
- # The hardened runner lives in the repo, next to this script, so a fork always
79
- # gets the CURRENT one rather than whatever the canonical harness was carrying.
80
- Copy-Item (Join-Path $PSScriptRoot "render-check.mjs") -Destination $fork -Force
81
- # `probe.mjs` is imported by the runner, so a fork without it dies on
82
- # ERR_MODULE_NOT_FOUND before it measures anything. It was missing from this
83
- # list, which every fork made before now had to work around by hand.
84
- Copy-Item (Join-Path $PSScriptRoot "probe.mjs") -Destination $fork -Force
85
- Copy-Item (Join-Path $PSScriptRoot "baselines.json") -Destination $fork -Force -ErrorAction SilentlyContinue
86
-
87
- # A junctioned `node_modules` SHARES `node_modules/.vite` with the harness it
88
- # points at. Two servers writing one dep-cache produced 504s that photograph as
89
- # a blank screen - i.e. a fake "conversion broke the page". Own cache, always.
90
- $vite = Join-Path $fork "vite.config.mjs"
91
- $cfg = [System.IO.File]::ReadAllText($vite)
92
- if ($cfg -notmatch "cacheDir") {
93
- $cfg = $cfg -replace "(?m)^\s*server:\s*\{", " cacheDir: `"./.vite-cache`",`r`n server: {"
94
- # UTF8Encoding($false): Set-Content -Encoding utf8 writes a BOM on Windows
95
- # PowerShell 5.1, and a BOM in a config file makes PostCSS's loader throw,
96
- # which 500s EVERY .vue file - indistinguishable from a broken checkout.
97
- [System.IO.File]::WriteAllText($vite, $cfg, (New-Object System.Text.UTF8Encoding($false)))
98
- }
99
-
100
- $pkgPath = Join-Path $fork "package.json"
101
- $pkg = [System.IO.File]::ReadAllText($pkgPath)
102
- $pkg = $pkg -replace '--port \d+', "--port $port"
103
- [System.IO.File]::WriteAllText($pkgPath, $pkg, (New-Object System.Text.UTF8Encoding($false)))
104
- [System.IO.File]::WriteAllText($portFile, "$port", (New-Object System.Text.UTF8Encoding($false)))
105
-
106
- # --- mirror -----------------------------------------------------------------
107
- & "$fork\sync-lc.ps1" -Src $Worktree
108
-
109
- # Stamp WHAT was mirrored, so a measurement can prove which checkout it rendered
110
- # rather than assume it. `render-check.mjs` refuses to run when this disagrees
111
- # with the worktree it was pointed at - which is what a foreign `sync-lc.ps1`
112
- # stamping over the mirror looks like. Written after the sync, because
113
- # `sync-lc.ps1` deletes everything in `lc/` that is not one of its six dirs.
114
- $stamp = @{
115
- worktree = $Worktree
116
- head = (git -C $Worktree rev-parse HEAD)
117
- branch = (git -C $Worktree rev-parse --abbrev-ref HEAD)
118
- port = $port
119
- } | ConvertTo-Json -Compress
120
- [System.IO.File]::WriteAllText((Join-Path $fork "lc\.synced-from.json"), $stamp, (New-Object System.Text.UTF8Encoding($false)))
121
-
122
- Write-Output "harness : $fork"
123
- Write-Output "port : $port"
124
- Write-Output "renders : $Worktree @ $(git -C $Worktree rev-parse --short HEAD)"
125
- Write-Output ""
126
- Write-Output " Start-Process -WorkingDirectory '$fork' npx -ArgumentList 'vite','--port','$port','--strictPort'"
127
- Write-Output " cd '$fork'; `$env:PORT=$port; node render-check.mjs <label> <Screen> [Screen...]"