@7365admin1/layer-common 3.2.3 → 3.2.5
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.
- package/CHANGELOG.md +251 -0
- package/components/CameraWall.vue +1034 -0
- package/components/CameraWallTile.vue +818 -0
- package/components/Layout/NavigationDrawer.vue +43 -2
- package/components/NavigationItem.vue +9 -0
- package/composables/useSiteSettings.ts +50 -0
- package/package.json +3 -1
- package/plugins/vuetify.ts +37 -9
- package/utils/camera-wall.test.ts +571 -0
- package/utils/camera-wall.ts +926 -0
- package/utils/theme.test.ts +201 -0
- package/utils/theme.ts +136 -0
- package/middleware/member.ts +0 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,256 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 3.2.5
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 1d6b57b: Draw the camera wall from the gated wall endpoint, and show the server's own
|
|
8
|
+
reason on a tile.
|
|
9
|
+
|
|
10
|
+
`CameraWall` fetched its cameras from `GET /api/site-cameras` - the Settings
|
|
11
|
+
panel's paginated CRUD list, which took its site from an optional query
|
|
12
|
+
parameter and checked nothing about the caller. Any signed-in user from any
|
|
13
|
+
organisation could read every camera in the estate through it, `host` and
|
|
14
|
+
`username` included.
|
|
15
|
+
|
|
16
|
+
It now calls `GET /api/site-cameras/site/:siteId/wall`, which already exists,
|
|
17
|
+
which the React Native monitoring app was built against, and which is
|
|
18
|
+
authorised server-side: the caller must be a member of the site, of its owning
|
|
19
|
+
organisation, or work for an organisation actively engaged to serve it. That
|
|
20
|
+
endpoint also returns `type: "ip"` cameras only and each camera's capability
|
|
21
|
+
descriptor, so a wall cannot be handed an ANPR unit and a tile can explain
|
|
22
|
+
itself.
|
|
23
|
+
|
|
24
|
+
Two consequences worth stating:
|
|
25
|
+
|
|
26
|
+
- **The pager is gone.** The wall endpoint returns the site's cameras in one
|
|
27
|
+
answer. Paging a video wall was an artefact of borrowing the CRUD list.
|
|
28
|
+
- **A tile now prefers the server's `unavailableReason`** over the reason it
|
|
29
|
+
used to work out itself. The server knows things the browser cannot - chiefly
|
|
30
|
+
"No recorder is configured for this camera's relay", which is the true answer
|
|
31
|
+
for every camera in the estate until the API host is configured, and which the
|
|
32
|
+
wall used to replace with its own guess. Offline still beats everything, since
|
|
33
|
+
it explains every tile at once.
|
|
34
|
+
|
|
35
|
+
No permission gate was invented here. Each application still gates its own menu
|
|
36
|
+
entry; this component draws what it is given, and the server decides.
|
|
37
|
+
|
|
38
|
+
Two smaller things in the same area:
|
|
39
|
+
|
|
40
|
+
- **A refused wall now says so.** The catch-all message told every failure to
|
|
41
|
+
"check your connection", which sends somebody who simply may not see that
|
|
42
|
+
site off to debug their wifi. A 401/403/404 now reads "You do not have access
|
|
43
|
+
to this site's cameras." The server answers "not yours" and "does not exist"
|
|
44
|
+
identically, so this wording does not distinguish them either.
|
|
45
|
+
- **`middleware/member.ts` is removed.** It read a cookie into an unused
|
|
46
|
+
variable and did nothing else - a file named like a membership gate that was
|
|
47
|
+
not one. No page in any of the eleven web apps referenced it. The real check
|
|
48
|
+
is `plugins/secure-member.client.ts`, driven by `memberOnly` page meta, which
|
|
49
|
+
is untouched.
|
|
50
|
+
|
|
51
|
+
- e3b3cd7: Rewrite the camera wall's detail panel for the people who read it, and stop
|
|
52
|
+
repeating the same sentence four times.
|
|
53
|
+
|
|
54
|
+
The panel under the tiles was written for a developer. On the live wall it
|
|
55
|
+
read:
|
|
56
|
+
|
|
57
|
+
> BBQ AREA
|
|
58
|
+
> The video page loaded, but this browser cannot confirm a picture is arriving.
|
|
59
|
+
> The player belongs to the video service and a browser cannot look inside it.
|
|
60
|
+
> Judge the tile by what you can see, and report a blank or frozen picture.
|
|
61
|
+
> ● Recorder responding Last picture this server received: none yet
|
|
62
|
+
> Firmware and device clock are not available: this recorder is reachable over
|
|
63
|
+
> video only.
|
|
64
|
+
|
|
65
|
+
Every one of those statements is true, and the screen was still wrong: it
|
|
66
|
+
explained cross-origin isolation to a guard, repeated the camera's name the
|
|
67
|
+
tile already showed, put a green light beside the words "none yet", and printed
|
|
68
|
+
the same reason on four capability cards in a row.
|
|
69
|
+
|
|
70
|
+
Changes, all copy and layout — **no behaviour, no capability gating and no
|
|
71
|
+
permission check is touched**:
|
|
72
|
+
|
|
73
|
+
- **The badge reads "Not confirmed"**, not "Not verified". "Not verified"
|
|
74
|
+
describes a check we failed to make; "Not confirmed" describes the picture,
|
|
75
|
+
which is the thing the reader cares about. Same claim, and the wall still
|
|
76
|
+
refuses to say "Live" until the video service reports a decoded frame.
|
|
77
|
+
- **The sentence behind it is now an instruction**: "This page cannot confirm
|
|
78
|
+
the picture is arriving, so check the tile yourself. If it is blank or frozen,
|
|
79
|
+
report the camera." Why the software cannot confirm it is our problem, and it
|
|
80
|
+
has been removed from the product.
|
|
81
|
+
- **The camera's name is no longer a heading of its own.** It is the subject of
|
|
82
|
+
the status line — "BBQ AREA · Recorder responding" — so a reader on a 3x3 wall
|
|
83
|
+
still knows which tile the panel describes, without a second caption under the
|
|
84
|
+
first one.
|
|
85
|
+
- **"Last picture" is drawn only when there has been one.** A green "Recorder
|
|
86
|
+
responding" beside "none yet" read as a contradiction. They were never in
|
|
87
|
+
conflict, but a screen that has to be explained is wrong. The tile's own
|
|
88
|
+
health line has always behaved this way; the panel now matches it.
|
|
89
|
+
- **The firmware-and-device-clock sentence is gone**, from the panel and from
|
|
90
|
+
the tile's tooltip. This screen never showed either value, so explaining their
|
|
91
|
+
absence gave the reader nothing to do. The server still sends
|
|
92
|
+
`detailUnavailableReason`; nothing draws it.
|
|
93
|
+
- **One reason for the group instead of four copies.** When every switched-off
|
|
94
|
+
control is off for the same reason, that reason and its next step are stated
|
|
95
|
+
once above the row and each card keeps only its title and its state. When the
|
|
96
|
+
reasons differ — "the recorder is unreachable" and "this camera cannot move"
|
|
97
|
+
are two problems with two different people to ask — the group note is refused
|
|
98
|
+
and each card carries its own again. New rule 7, in `camera-wall.ts`, with
|
|
99
|
+
four tests.
|
|
100
|
+
- Shorter next-step and no-signal wording, and a subject on the recorder's
|
|
101
|
+
status labels ("Recorder not checked yet" rather than "Health not checked
|
|
102
|
+
yet").
|
|
103
|
+
|
|
104
|
+
Rules 3 and 6 are intact: the server's own sentence is still shown verbatim and
|
|
105
|
+
never rewritten, and the recorder's state is still a separate fact from the
|
|
106
|
+
tile's badge.
|
|
107
|
+
|
|
108
|
+
- a02619c: Mark the selected camera on the wall, and stop telling the reader to raise a
|
|
109
|
+
ticket without saying what for.
|
|
110
|
+
|
|
111
|
+
Two things the owner found on the live wall after the last copy pass. Both are
|
|
112
|
+
copy and presentation only — **no behaviour, no capability gating, no permission
|
|
113
|
+
check and no request is changed.**
|
|
114
|
+
|
|
115
|
+
**1. The camera's name was still printed twice.**
|
|
116
|
+
|
|
117
|
+
The tile said `BBQ AREA` and the panel underneath said it again. The last pass
|
|
118
|
+
merged the panel's heading into its status line, which changed the layout and
|
|
119
|
+
left the repetition exactly where it was.
|
|
120
|
+
|
|
121
|
+
The name was there to answer "which of these nine tiles is this panel about?".
|
|
122
|
+
That is a real question and a name is the wrong answer to it — it makes the
|
|
123
|
+
reader scan the wall for a matching caption. So:
|
|
124
|
+
|
|
125
|
+
- **the selected tile is now visibly selected**: a 3 px accent ring with a scrim
|
|
126
|
+
hairline inside it, so the ring holds against a bright picture as well as a
|
|
127
|
+
dark one, plus the accent underline on the name chip that the toolbar already
|
|
128
|
+
uses to mark a selected tool. Both read at 1x1, 2x2 and 3x3;
|
|
129
|
+
- **the camera's name is removed from the panel entirely**. The panel opens on
|
|
130
|
+
the recorder's state — "● Recorder responding";
|
|
131
|
+
- `aria-current` marks the same tile for anyone not looking at the ring.
|
|
132
|
+
|
|
133
|
+
The highlight had never worked in the most common case, which is why the name
|
|
134
|
+
was load-bearing: it was bound to the raw clicked id, and before anyone clicks —
|
|
135
|
+
or after paging — nothing on the wall was marked while the panel was already
|
|
136
|
+
describing the first tile. It is now bound to the tile the panel actually
|
|
137
|
+
resolves to, so the two can never disagree.
|
|
138
|
+
|
|
139
|
+
**The panel can never be about a tile that is off screen**, so it never needs to
|
|
140
|
+
name one: the focused camera is resolved out of the current PAGE, so paging away
|
|
141
|
+
from a selection re-resolves to the first tile of the page in front of you and
|
|
142
|
+
the ring follows. Paging back restores the original selection. Extracted as
|
|
143
|
+
`focusedCamera()` in `camera-wall.ts` with four tests, including the 5-cameras
|
|
144
|
+
-across-2-pages case.
|
|
145
|
+
|
|
146
|
+
**2. "Ask your Seven365 administrator" was a dead end.**
|
|
147
|
+
|
|
148
|
+
It was the whole of the advice under every switched-off control, and it tells
|
|
149
|
+
the administrator nothing and a property manager only to go and wait. The screen
|
|
150
|
+
never said what the request should ask for.
|
|
151
|
+
|
|
152
|
+
The advice now names the two changes that are actually missing, because they are
|
|
153
|
+
two different jobs for two different people: **the site's network has to let our
|
|
154
|
+
server reach the camera recorder**, and **that recorder then has to be added to
|
|
155
|
+
the server's configuration**. Both are stated in plain words, with who arranges
|
|
156
|
+
them, and with the fact that neither is switchable from this page.
|
|
157
|
+
|
|
158
|
+
No environment variable name appears in any of it — those belong in
|
|
159
|
+
`iservice365-core/docs/camera-integration-config.md`, not on a property
|
|
160
|
+
manager's screen — and there is a test that keeps it that way. The other reason
|
|
161
|
+
codes (`device-http-disabled`, `device-http-unreachable`, `device-http-locked-out`,
|
|
162
|
+
`control-not-enabled`, `no-recorder-configured`, and the unknown-code fallback)
|
|
163
|
+
get the same treatment; the Site Settings ones already said what to do and are
|
|
164
|
+
unchanged.
|
|
165
|
+
|
|
166
|
+
The truthful-status rules are untouched: the wall still refuses to say "Live"
|
|
167
|
+
until the video service reports a decoded frame, and the recorder's state is
|
|
168
|
+
still a separate fact from the tile's badge.
|
|
169
|
+
|
|
170
|
+
- 9fdc87b: A shared CCTV camera wall for the web apps
|
|
171
|
+
|
|
172
|
+
`CameraWall.vue` and `CameraWallTile.vue` draw a site's cameras as a monitoring
|
|
173
|
+
wall, with the layout, tile state and refresh behaviour in `utils/camera-wall.ts`
|
|
174
|
+
so it can be measured by a test rather than only seen.
|
|
175
|
+
|
|
176
|
+
The wall is drawn from the site-scoped camera endpoint, so it shows the cameras
|
|
177
|
+
of the site the operator is actually on and nothing else. Property Management
|
|
178
|
+
and Security both mount the same component instead of each growing their own.
|
|
179
|
+
|
|
180
|
+
Also removes `middleware/member.ts`, a route middleware that read a cookie into
|
|
181
|
+
an unused variable and did nothing else. Nothing in this layer or in either
|
|
182
|
+
consuming app referenced it.
|
|
183
|
+
|
|
184
|
+
- 9fdc87b: Camera wall status made honest, plus zoom, health and paging
|
|
185
|
+
|
|
186
|
+
**A tile that used to say "Live" may now say "Not confirmed", and that is the
|
|
187
|
+
point.** The badge was never measured — it said "Live" because a tile had been
|
|
188
|
+
rendered, not because anything had confirmed the camera was answering. A camera
|
|
189
|
+
that had been down for a week still read as Live. Status is now reported from
|
|
190
|
+
what was actually observed, so a tile only claims Live when the health check
|
|
191
|
+
says so, and says "Not confirmed" when nothing has come back yet.
|
|
192
|
+
|
|
193
|
+
**Tell QA before this ships.** The honest badge reads as a regression if you are
|
|
194
|
+
not expecting it: the wall will look like it has _lost_ status on cameras that
|
|
195
|
+
previously appeared healthy, when in fact those tiles were never measuring
|
|
196
|
+
anything.
|
|
197
|
+
|
|
198
|
+
Also adds zoom on a tile, per-camera health, and paging for sites with more
|
|
199
|
+
cameras than fit a screen.
|
|
200
|
+
|
|
201
|
+
## 3.2.4
|
|
202
|
+
|
|
203
|
+
### Patch Changes
|
|
204
|
+
|
|
205
|
+
- ec40e1f: Split the dark theme's `primary` into a foreground colour and a fill, so text
|
|
206
|
+
using it is readable again.
|
|
207
|
+
|
|
208
|
+
Dark `primary` was set to the brand navy `#17506F` so the navigation drawer
|
|
209
|
+
would read as a surface. That is the right value for a fill — a white label on
|
|
210
|
+
it measures 8.70:1 — but Vuetify emits `.text-primary` from the same token, so
|
|
211
|
+
every `class="text-primary"` sentence and every `color="primary"` icon, spinner
|
|
212
|
+
and text button rendered navy on a dark background:
|
|
213
|
+
|
|
214
|
+
| | before | now |
|
|
215
|
+
| ----------------------------------------------- | ------ | ---------- |
|
|
216
|
+
| `primary` as text on the dark page `#0E1319` | 2.14:1 | **6.40:1** |
|
|
217
|
+
| `primary` as text on a dark card `#1B242F` | 1.80:1 | **5.38:1** |
|
|
218
|
+
| `primary` as text on `surface-bright` `#26313E` | 1.52:1 | **4.53:1** |
|
|
219
|
+
|
|
220
|
+
Dark `primary` is now `#5B9BE0`. That is not a new colour: it is the blue the
|
|
221
|
+
camera wall already draws its accents in (`--vms-accent`), so the two dark
|
|
222
|
+
surfaces in this product agree on one blue.
|
|
223
|
+
|
|
224
|
+
The navigation drawer no longer depends on `primary`. Both themes gain a
|
|
225
|
+
`brand-surface` colour — `#042134` light (identical to light `primary`, so
|
|
226
|
+
light mode renders exactly as before) and `#17506F` dark — and
|
|
227
|
+
`Layout/NavigationDrawer.vue` is painted with it. Its white label still reads
|
|
228
|
+
16.51:1 light and 8.70:1 dark. `floating` was dropped from the drawer so it
|
|
229
|
+
draws its own edge: the fill is 2.14:1 against the dark page, under the 3:1 a
|
|
230
|
+
boundary wants, and the border at `border-opacity` 0.35 is 3.22:1.
|
|
231
|
+
|
|
232
|
+
Where `primary` is still used as a fill, Vuetify derives `on-primary` from it
|
|
233
|
+
and picks black, which reads at 7.21:1. What a light `primary` cannot carry is
|
|
234
|
+
a hardcoded white label, and a handful of app-side sites still set one — see
|
|
235
|
+
the pull request for the list.
|
|
236
|
+
|
|
237
|
+
The light theme is unchanged. The colours moved to `utils/theme.ts` and
|
|
238
|
+
`utils/theme.test.ts` now measures them, so a token cannot be moved for one use
|
|
239
|
+
and quietly broken for the other again.
|
|
240
|
+
|
|
241
|
+
- e3dd56c: The light and dark themes are ours, not Vuetify's factory pair
|
|
242
|
+
|
|
243
|
+
Only a handful of colours were being overridden, so everything else fell through
|
|
244
|
+
to Vuetify's defaults. One click of the light/dark toggle threw the Seven365
|
|
245
|
+
brand away across all 11 web apps and replaced it with stock Material colours.
|
|
246
|
+
|
|
247
|
+
Both themes now define their full palette — surfaces, text, borders, states —
|
|
248
|
+
so the toggle switches between two deliberate looks instead of switching the
|
|
249
|
+
brand off.
|
|
250
|
+
|
|
251
|
+
The light theme is unchanged in appearance. The work is in making it explicit
|
|
252
|
+
rather than inherited, so a Vuetify default can no longer leak through.
|
|
253
|
+
|
|
3
254
|
## 3.2.3
|
|
4
255
|
|
|
5
256
|
### Patch Changes
|