@7365admin1/layer-common 4.97.1-staging.502 → 4.98.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.
- package/CHANGELOG.md +885 -4
- package/components/AccessCardDetailsDialog.vue +2 -2
- package/components/AccessCardPreviewDialog.vue +9 -86
- package/components/HidAccessPermissions.vue +38 -146
- package/components/HidUserEnrollment.vue +39 -679
- package/components/VehicleQrStickerDialog.vue +8 -0
- package/composables/useAccessManagement.ts +1 -17
- package/composables/useServiceProvider.ts +4 -9
- package/package.json +1 -1
- package/types/site.d.ts +1 -7
- package/utils/hid-enrolment-subject.ts +4 -226
- package/utils/hid-permission-assignments.ts +6 -75
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,886 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 4.98.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 7c54796: Let an `AppSelect` be searched, and search the intercom contact name picker.
|
|
8
|
+
|
|
9
|
+
The Add Contact name picker lists every SIP account on the site. That list is
|
|
10
|
+
short on a new site and grows with every resident who opens Web Call, and the
|
|
11
|
+
only way through it was to scroll — on an estate with a few hundred homes, past
|
|
12
|
+
several people called Harris.
|
|
13
|
+
|
|
14
|
+
`AppSelect` takes a `searchable` prop that puts a filter box at the top of its
|
|
15
|
+
popover, with `search-placeholder` for its wording. It is opt-in, so the 44
|
|
16
|
+
consumers that do not ask for it render exactly the DOM they render today: a
|
|
17
|
+
list short enough to read does not want a search field in front of it. The
|
|
18
|
+
filter is a case-insensitive substring over the option's TITLE, not its value —
|
|
19
|
+
the title is the only part anyone can see to type — so on the contact picker it
|
|
20
|
+
finds a person by name or by where they live, the unit being part of every
|
|
21
|
+
option's text.
|
|
22
|
+
|
|
23
|
+
`close-on-content-click` had to come off for the single-value popover while
|
|
24
|
+
searching, and that is the whole reason this needed a prop rather than a
|
|
25
|
+
component. It fires on the click that puts the caret in the box, so the popover
|
|
26
|
+
shut the moment it was typed into; with it off, picking an option closes the
|
|
27
|
+
popover by hand instead. The multi-value popover already ran with it off and is
|
|
28
|
+
unchanged in that respect.
|
|
29
|
+
|
|
30
|
+
Enter picks the first match, Escape closes, the term is dropped on every open so
|
|
31
|
+
a stale filter cannot hide the list, and the caret lands in the box — opening a
|
|
32
|
+
searchable select is almost always a prelude to typing. A filtered-to-nothing
|
|
33
|
+
list says "No matches" rather than showing an empty popover.
|
|
34
|
+
|
|
35
|
+
- 2374f0e: Offer the module permissions no role editor could grant yet (owner answers Q5,
|
|
36
|
+
Q9 and Q10). Catalogue-only: role editors offer more, no gate changes, and
|
|
37
|
+
nobody is granted anything.
|
|
38
|
+
|
|
39
|
+
- `useMechanicalElectricalPermission`, `usePestControlPermission` and
|
|
40
|
+
`usePoolMaintenancePermission` now offer `emergency-contact` with the seven
|
|
41
|
+
actions Cleaning and Landscape already offer (`see-all`, `see-details`,
|
|
42
|
+
`add-/update-/delete-emergency-contact`,
|
|
43
|
+
`add-/delete-emergency-contact-category`).
|
|
44
|
+
- `useSecurityPermission` now offers `emergency-contact` (the same seven),
|
|
45
|
+
`facility-booking-mgmt` (Property's five actions),
|
|
46
|
+
`passes-and-keys-mgmt:see-all-passes-and-keys`,
|
|
47
|
+
`resident-transactions:see-all-resident-transactions`,
|
|
48
|
+
`nfc-patrol:see-all-nfc-patrol`, `incident-reports:review-incident-report`,
|
|
49
|
+
and two new strings: `nfc-patrol:start-nfc-patrol` and `cctv-mgmt:see-all-cctv`.
|
|
50
|
+
- `usePropertyPermission` now offers `cctv-mgmt:see-all-cctv`.
|
|
51
|
+
- `constants/permissions.ts` records the two new strings as canonical.
|
|
52
|
+
- Bulletin was already offered by all five fleet catalogues as `bulletin-board`;
|
|
53
|
+
unchanged.
|
|
54
|
+
- Module counts: Security 30, Property 33, Pest 17, Pool 17, M&E 16.
|
|
55
|
+
|
|
56
|
+
- 544c9ea: CCTV camera wall: Recorded footage — pick a start time and length and play what the camera recorded (server link, plain refusal messages); `cctv-mgmt:view-recordings` added to the Property Management and Security catalogues.
|
|
57
|
+
- e85696e: Client invitations: an accepted invitation whose site never reached the company's Sites list now says so in plain words and offers "Add <site> to your Sites" (`linkInvitationSite`, `PUT /service-provider-invitations/:id/link-site`). Needs core 3.100.0.
|
|
58
|
+
- a1fb6bf: Client invitations: a dedicated module for a service-provider company to answer invitations from its clients (Pending / Accepted / Declined / Expired, plain-language status on every card, Accept / Decline only while waiting). Accepting adds the site to the company on screen. The bell's invitation row opens it. The inviting site's list now also shows accepted invitations. Removes the combined prompt from the member-invitations page.
|
|
59
|
+
- dd146ec: Invite Client: for a new client, Seven365 now sets the owner's role permissions per application next to the modules (all permissions of the ticked modules ticked by default, only those offered) and sends them as `permissions`. Role editors (`RolePermissionMain`) offer only what that owner role allows for its application; new `ownerGrant` / `catalogueForOwnerGrant` in `utils/org-modules`. The form's site value is now actually sent.
|
|
60
|
+
- ac2e7c8: Members: the "role from another site" banner gets "Fix roles from other sites" — a preview of each member, their site, the current role and the role proposed at their own site (warnings for fewer permissions or several/no matches, never pre-ticked), Apply, an automatic undo file download and an Undo button. Uses `GET/POST /api/members/cross-site-roles/fix` and `POST /api/members/cross-site-roles/undo` (core). The list and the banner count refresh after it.
|
|
61
|
+
- 3c787e6: HERO building map and lift queue show the HERO Fleet Core building at sites with fleet robots: walls, doors (open doors highlighted) and lift shafts from the fleet, refreshed every 5 s, read-only (no Edit map); the lift queue shows each fleet lift's car floor and the robot holding it. The map editor's duplicate "Full screen" button is removed - the page header's one full-screens the viewer or the editor.
|
|
62
|
+
- 47829e0: HERO robots phase e: site Alerts (`RobotSiteAlerts`) and Activity log (`RobotActivityLog`, site or network) as RobotSiteScreen views `alerts` / `log`; the header bell shows the site's open faults on every view; the Add / edit robot dialog rebuilt to the handoff (520px, type-filtered brands with capability, replace-only credentials) and the Remove dialog's wording; the building map reads the site's map whichever robot type saved it; mini-map name tags stay inside the plan.
|
|
63
|
+
- 414cc4f: HERO phase (d): 3D building map (`RobotBuildingMap` - CSS 3D stacked floors, ISO/TOP/SIDE/UNDER, drag/pan/zoom, floor filter, auto-orbit, full screen, robots with pins, patrol routes, lift shafts, "Floors · top down" legend + Floor unknown tray), map editor (`RobotMapEditor` - Wall/Zone/Door/Lift shaft/Move/Delete, Undo + Ctrl/Cmd+Z, plain-language save checks, keyboard), lift queue (`RobotLiftQueue` - animated shaft, riding/requested/released/timed-out rows), `RobotSiteScreen` `view` prop, `utils/robot-building.ts`, `useRobotBuilding`. Floors read "L1"/"B1"; a security robot's "Not reported" line lists spot clean; route tags and checkpoint labels stay inside the plan; plan grids repeat again.
|
|
64
|
+
- b91fb57: Robots: shared pieces for the HERO network view - Needs attention, Clients drill-down, Faults & alerts table, fault bell, live indicator, a feed-agnostic live-state store (`useRobotLive` with a 4 s polling feed, paused while the tab is hidden, swappable for a push feed), summary cells that show "—" for unmeasured values, and display helpers (split lines, since/ago text, Reconnecting instead of stale facts). Not used by any app until the org network page adopts them.
|
|
65
|
+
|
|
66
|
+
A robot with no interface now shows an UNKNOWN status chip (error tone); "No interface" appears only on the capability badge.
|
|
67
|
+
|
|
68
|
+
- a7935b0: Robots: add the HERO robot theme (scoped `hero-light` / `hero-dark`, robot pages only) and the shared robot components (status chip, battery, capability badge, command bar, summary strip, table, card, lift row, map toolbar, toast, notices). The capability badge reads the brand's COMMAND level; overlays opened from a robot page take the HERO look, all others are unchanged. Not used by any app yet.
|
|
69
|
+
- f929838: HERO robot fleet phase c: `RobotSiteScreen` (shared shell header + site fleet overview + robot detail) for Property Management, Hygiene, Security and the console drill-down, built from new `RobotSiteHeader`, `RobotSiteFleet`, `RobotMiniMap`, `RobotDetail`, `RobotLivePlan`, `RobotFloorPlan`, `RobotPatrolRoutes`, `RobotMark`. `siteStatesFeed` (estate-scoped 4 s poll); `useRobots` gains `getSiteFleet`, `getSiteSummary`, `getRobot`, `getPatrolRoutes` and the pause / resume / cancel / navigate / relocalize command kinds. Pure rules in `utils/robot-display.ts` (list paging - fixes the old fleet never paging past page 1 -, site summary, mini-map placement, patrol drawing and schedule rows, facts grid, optimistic command status, trail, spot points). `RobotTable` gains `paged-foot`; `notReportedLine` uses the handoff's words (battery, floor, spot clean, live position).
|
|
70
|
+
- 5beb877: Show doors opened from the resident app on the HID Access Log dashboard.
|
|
71
|
+
|
|
72
|
+
A resident on an intercom call opens the door by sending the reader's door
|
|
73
|
+
code as a DTMF tone on the SIP call, and the door station acts on it. The
|
|
74
|
+
reader never reports that as an access event — its own access log only knows
|
|
75
|
+
cards, PINs, faces and QR codes — so those door opens never appeared on the
|
|
76
|
+
dashboard.
|
|
77
|
+
|
|
78
|
+
The resident app now records each one through the API as an
|
|
79
|
+
`intercom_door_open` event, and the dashboard fetches those by type alongside
|
|
80
|
+
the reader's live logs and renders them with the code it already has: the
|
|
81
|
+
resident's name and unit from their HID identity, "Authorized", the reader's
|
|
82
|
+
location, and a new "Intercom" access method, which is also a filter option.
|
|
83
|
+
|
|
84
|
+
Fetched by type on purpose. The dashboard's general page of persisted events
|
|
85
|
+
is shared with every other event a reader emits and is discarded whenever the
|
|
86
|
+
reader has live logs — which it always has — so an intercom event on that page
|
|
87
|
+
would never have been shown.
|
|
88
|
+
|
|
89
|
+
- bf29ed6: Add an Access Permissions screen to the HID reader row, and send the
|
|
90
|
+
organisation with an enrolment so the access it now grants is stored against
|
|
91
|
+
the right one.
|
|
92
|
+
|
|
93
|
+
Enrolling somebody makes the reader RECOGNISE them; it is a separate chain of
|
|
94
|
+
rules on the device, starting at a `user_groups` row, that decides whether the
|
|
95
|
+
door opens. Until now nothing in any app wrote that second half: the backend
|
|
96
|
+
and the `useHidAmico` wrappers were both finished and had no callers, so an
|
|
97
|
+
enrolled resident was recognised at the door and refused — access log event 6,
|
|
98
|
+
which reads as a broken face reader rather than a missing assignment.
|
|
99
|
+
|
|
100
|
+
The new dialog opens from the HID Readers row menu and lists the residents
|
|
101
|
+
enrolled on that reader, each with a Has access / No access switch. Switching
|
|
102
|
+
somebody off removes their assignment and the reconcile deletes their
|
|
103
|
+
`user_groups` row; their identity, face, card and PIN are untouched, so they
|
|
104
|
+
stay recognisable at the door and are simply refused. The reader must have a
|
|
105
|
+
location set — the server will not manage permissions without one — and the
|
|
106
|
+
dialog says so rather than letting the first request fail.
|
|
107
|
+
|
|
108
|
+
Residents are narrowed by the query rather than after it, because the pager's
|
|
109
|
+
count comes back from the same request and filtering on this side would print a
|
|
110
|
+
total that never matched the rows beneath it. Rows the screen cannot act on are
|
|
111
|
+
drawn with the reason in place of the switch instead of being dropped, for the
|
|
112
|
+
same reason.
|
|
113
|
+
|
|
114
|
+
`HidUserEnrollment` gains an `org` prop and sends `orgId` when creating an
|
|
115
|
+
identity. A site reached through `customer.sites` is owned by one organisation
|
|
116
|
+
and managed by another, and without this the grant lands in the owning org's
|
|
117
|
+
permission document, which the screen does not read.
|
|
118
|
+
|
|
119
|
+
The editing model lives in `utils/hid-permission-assignments.ts` with tests:
|
|
120
|
+
the save replaces the reader's whole assignment set while the list arrives one
|
|
121
|
+
page at a time, so the draft is held for the entire reader and the payload is
|
|
122
|
+
always built from all of it. Building it from what is on screen would silently
|
|
123
|
+
revoke everybody who is not.
|
|
124
|
+
|
|
125
|
+
- ff460b0: Bring back Read at Reader in the physical HID card dialog.
|
|
126
|
+
|
|
127
|
+
Assigning a card by hand needs its facility code and card number, and neither is
|
|
128
|
+
printed on the card. An operator holding a blank fob has no way to look them up,
|
|
129
|
+
which left the dialog with one route in that could not be walked. Presenting the
|
|
130
|
+
card to the reader is the route that works: the reader listens for 30 seconds
|
|
131
|
+
and reports what it read.
|
|
132
|
+
|
|
133
|
+
Assign Manually stays, for the cards whose values are already known.
|
|
134
|
+
|
|
135
|
+
Nothing behind the screen changed. `POST
|
|
136
|
+
/readers/:readerId/users/:hidUserId/cards/enroll` and its `enrollment/cancel`
|
|
137
|
+
counterpart were never removed, and the `useHidAmico` wrappers for both were
|
|
138
|
+
still exported with no callers — this restores the only thing that was taken
|
|
139
|
+
away, which was the buttons.
|
|
140
|
+
|
|
141
|
+
Cancelling tells the DEVICE to stop rather than only abandoning the request, so
|
|
142
|
+
the reader does not sit out its remaining seconds and enrol whatever is
|
|
143
|
+
presented in the meantime.
|
|
144
|
+
|
|
145
|
+
It also hands the dialog straight back. The server cannot tell a cancelled read
|
|
146
|
+
from a failed one — the reader simply reports no card — so the enrol still in
|
|
147
|
+
flight ends as a 400 seconds later. Waiting for that was the whole problem:
|
|
148
|
+
Cancel appeared to do nothing until the rejection arrived. The dialog is now
|
|
149
|
+
released as soon as the reader confirms it has stopped, and the doomed enrol
|
|
150
|
+
lands silently. The button reads "Cancelling…" for the second or two that
|
|
151
|
+
confirmation takes, and the reading state is only cleared once it arrives —
|
|
152
|
+
re-enabling Read at Reader any earlier invites a second attempt the reader
|
|
153
|
+
refuses with "already running", because the enrollment lock is released by the
|
|
154
|
+
cancel call itself.
|
|
155
|
+
|
|
156
|
+
- 2f58eb4: Tell the operator when the selected resident is already enrolled on the HID
|
|
157
|
+
reader, instead of letting the enrolment run and fail.
|
|
158
|
+
|
|
159
|
+
Picking a resident in the enrolment dialog now asks the identities endpoint
|
|
160
|
+
whether that person already holds a HID user on the selected reader. If they
|
|
161
|
+
do, the field carries an inline message naming the existing user and the submit
|
|
162
|
+
button is disabled; the check runs again on submit, before anything is written
|
|
163
|
+
to the device, because the dialog can sit open while somebody else enrols the
|
|
164
|
+
same person.
|
|
165
|
+
|
|
166
|
+
The check is reader-scoped — being enrolled at another gate is normal and says
|
|
167
|
+
nothing here — and it fails open: if the lookup itself errors the operator is
|
|
168
|
+
not blocked, because the server refuses the duplicate regardless.
|
|
169
|
+
|
|
170
|
+
- fc8122b: Let HID enrolment enrol property-management staff, not only residents.
|
|
171
|
+
|
|
172
|
+
The enrolment form could only ever pick a resident. The person came from a
|
|
173
|
+
Block → Level → Unit → Resident cascade running on People Management's occupancy
|
|
174
|
+
endpoints with `type = "resident,tenant"`, so a guard, admin or technician could
|
|
175
|
+
never appear in it, and the field was labelled `Resident` in the markup. Staff
|
|
176
|
+
could be granted a group assignment from Access Permissions, but that does not
|
|
177
|
+
enrol credentials — a face, PIN or password — on the reader.
|
|
178
|
+
|
|
179
|
+
An "Enrolling" dropdown now sits at the top of the dialog. Choosing Member
|
|
180
|
+
replaces the whole cascade with one searchable picker, because staff hold no unit
|
|
181
|
+
to narrow by. Their role is shown beside the name in the
|
|
182
|
+
picker and mirrored into a read-only Role field, on the same rule the form
|
|
183
|
+
already applies to Name: restated from the record, never typed.
|
|
184
|
+
|
|
185
|
+
Nothing new was needed on the server. The write path has always carried three
|
|
186
|
+
subject categories — it maps them onto `person`, `member` or `serviceProvider` —
|
|
187
|
+
and `createIdentity` already validates a member against the site's active
|
|
188
|
+
members, resolves their account, refuses a duplicate with "This staff member is
|
|
189
|
+
enrolled on this reader", and grants access under the `property_management`
|
|
190
|
+
category. Only the picker was missing. Candidates come from `GET /api/members` — the same
|
|
191
|
+
endpoint, with the same filters AND the same page size that the Members menu
|
|
192
|
+
reads — so the picker and that menu always agree, and no new endpoint was needed
|
|
193
|
+
either.
|
|
194
|
+
|
|
195
|
+
The page size is load-bearing. `member.repo.ts` caches each page under a key
|
|
196
|
+
that includes `limit`, so asking for a different one gets a different cache
|
|
197
|
+
entry with its own 15-minute life. An earlier version asked for 50 and landed in
|
|
198
|
+
a rarely-touched slot that went stale, showing six members where the Members menu
|
|
199
|
+
showed nine — the same query, the same moment, two answers. Ten is the menu's
|
|
200
|
+
own page size, so the two now read the same entry.
|
|
201
|
+
|
|
202
|
+
The rest of the staff arrive as the dropdown is scrolled. `AppSelect` grew two
|
|
203
|
+
opt-in props, `has-more` and `loading-more`, and a `load-more` emit: it asks for
|
|
204
|
+
the next page when the list is scrolled near its end, and again when a page
|
|
205
|
+
settles without filling the menu — a page of ten whose rows are mostly filtered
|
|
206
|
+
out can draw four options in a box built for eight, and a reader with nothing to
|
|
207
|
+
scroll would otherwise be stranded. Every other AppSelect in the product is
|
|
208
|
+
untouched: leave the props alone and there is no footer, no listener and no
|
|
209
|
+
emit.
|
|
210
|
+
|
|
211
|
+
Requests carry a sequence number, so a page still in flight when the dialog
|
|
212
|
+
reopens cannot append onto a list that has since been replaced — the same guard
|
|
213
|
+
`EntryPassInformation.vue` uses for its own card pager.
|
|
214
|
+
|
|
215
|
+
Reading staff from the Members endpoint rather than the HID permission-candidate
|
|
216
|
+
endpoint is deliberate. The candidate endpoint applies no `type` filter, so it
|
|
217
|
+
returns every active members row at the site: service providers and residents
|
|
218
|
+
included. A picker offering a resident as a "Member" is worse than no picker.
|
|
219
|
+
Which type to ask for comes from the running app, the same way
|
|
220
|
+
`HidQrCodeConfiguration` reads it, so the property app lists its own staff and
|
|
221
|
+
the security app lists its own. Service providers will get their own option
|
|
222
|
+
rather than being folded in here.
|
|
223
|
+
|
|
224
|
+
Three details worth knowing:
|
|
225
|
+
|
|
226
|
+
The duplicate pre-check used to give up on anything that was not a resident, so
|
|
227
|
+
it now runs for any subject. It only moves the server's refusal in front of the
|
|
228
|
+
operator before the reader is touched.
|
|
229
|
+
|
|
230
|
+
The type is offered while enrolling and merely stated while editing. An
|
|
231
|
+
identity's subject cannot change category after the fact, and the picker is
|
|
232
|
+
hidden on edit so an existing record cannot be re-pointed at somebody else.
|
|
233
|
+
|
|
234
|
+
A member with no app account is not listed, because reader access is bound by
|
|
235
|
+
user id and there is nothing to bind. A note under the picker says how many were
|
|
236
|
+
left out and why, so an absent colleague does not read as a broken screen. The
|
|
237
|
+
count is worked out here, from the `user` field the members endpoint already
|
|
238
|
+
returns, so it needs nothing new from the server.
|
|
239
|
+
|
|
240
|
+
The name shown is the one on the member's ACCOUNT, falling back to the
|
|
241
|
+
membership's. A membership carries whatever was typed when the invitation was
|
|
242
|
+
sent, and the person may have set their own name since. `userName` comes from
|
|
243
|
+
`@7365admin1/core`; until a release carrying it is installed the picker falls
|
|
244
|
+
back to the membership name on its own, so this is safe to deploy ahead of it.
|
|
245
|
+
|
|
246
|
+
- 0945265: Add "Re-apply settings" to the HID reader's Identification Methods dialog.
|
|
247
|
+
|
|
248
|
+
A reader's configuration can change without iService365 knowing — a firmware
|
|
249
|
+
update, somebody using the reader's own touchscreen or web UI, a restore, a
|
|
250
|
+
factory reset. The screen then says a credential is off while the door happily
|
|
251
|
+
accepts it, and the screen is what people trust.
|
|
252
|
+
|
|
253
|
+
Saving the dialog only writes what is on screen. This reads the device first,
|
|
254
|
+
puts back anything that had drifted, and names the credentials it corrected.
|
|
255
|
+
|
|
256
|
+
Two outcomes are reported rather than hidden. A reader whose identification
|
|
257
|
+
methods have never been set has nothing to re-apply and says so. A reader that
|
|
258
|
+
shares a device with another registered record is refused outright: the device
|
|
259
|
+
holds one configuration, so two records would each read the other's values as
|
|
260
|
+
drift and revert them, and a person has to remove one record first.
|
|
261
|
+
|
|
262
|
+
Requires the reconcile endpoint from `@7365admin1/core`; see the merge order in
|
|
263
|
+
the PR.
|
|
264
|
+
|
|
265
|
+
- 7c54796: Name an intercom contact after the unit it rings, not just the person.
|
|
266
|
+
|
|
267
|
+
Add Contact picks the name from the site's SIP accounts, so a contact always
|
|
268
|
+
names someone who holds an extension. It offered the bare name, which is not
|
|
269
|
+
enough on either side of the glass: staff picking between two residents called
|
|
270
|
+
Harris had nothing to tell them apart, and the contact list a visitor scrolls on
|
|
271
|
+
the door panel named a person without saying whose home it called.
|
|
272
|
+
|
|
273
|
+
The picker now offers, and stores, `Harris - Blk 1/Level 1/Unit 1`. This is the
|
|
274
|
+
name the reader keeps, so the door panel, the Intercom Contacts table and its
|
|
275
|
+
search all show the unit — the address the API now returns as `unitLabel` on
|
|
276
|
+
each SIP account. Somebody who does not live here carries their membership type
|
|
277
|
+
in the same place, `Dexter Tan - Security Agency`, so the list reads the same
|
|
278
|
+
way for staff.
|
|
279
|
+
|
|
280
|
+
`unitLabel` is optional. An API still on the `@7365admin1/core` release that
|
|
281
|
+
predates it answers without the field, and an owner the site knows neither an
|
|
282
|
+
address nor a membership for gets `""`; both fall back to the bare name rather
|
|
283
|
+
than trailing a dangling separator, so the picker degrades to exactly its old
|
|
284
|
+
behaviour instead of offering `Harris - `.
|
|
285
|
+
|
|
286
|
+
A contact created before the unit was part of the name resolves to its SIP
|
|
287
|
+
account on edit all the same: the bare name is matched as well as the composed
|
|
288
|
+
one, so its extension and the Address hint stay right instead of the name
|
|
289
|
+
looking unrecognised. Its stored name is left alone until it is re-picked —
|
|
290
|
+
`applySipAccountToContact` returns early on no match and never blanks an address
|
|
291
|
+
the reader already holds.
|
|
292
|
+
|
|
293
|
+
- 3b75773: Move the identification-method settings onto the reader, and hide visitor QR
|
|
294
|
+
issuance.
|
|
295
|
+
|
|
296
|
+
Which credentials a door accepts — facial, card, QR, and PIN versus
|
|
297
|
+
ID-and-password — is now set per reader, from HID Readers > Identification
|
|
298
|
+
Methods. The device's own `identifier` module is what holds them, so a
|
|
299
|
+
site-wide copy could only ever describe one reader.
|
|
300
|
+
|
|
301
|
+
The old controls sat on the site settings screen and were not what they
|
|
302
|
+
appeared to be. Facial and QR Code were written to site metadata and never
|
|
303
|
+
reached any device; only Card and PIN were sent, and always to a single reader
|
|
304
|
+
borrowed from the Visitor QR gate list. On a site where those gates had never
|
|
305
|
+
been saved the reader id was empty and all five toggles were disabled with no
|
|
306
|
+
explanation, which is what made this worth fixing rather than restyling.
|
|
307
|
+
|
|
308
|
+
The new dialog talks to `PUT /readers/:readerId/identification-methods`, which
|
|
309
|
+
validates each method against the reader's own `capabilities` and confirms the
|
|
310
|
+
write by reading the device back. So the screen disables a control the hardware
|
|
311
|
+
cannot do and says why, shows when the READER last confirmed the settings
|
|
312
|
+
rather than when somebody saved them, and on refusal reports the credential the
|
|
313
|
+
device would not take and reloads instead of keeping the rejected choice on
|
|
314
|
+
screen. Switching a credential off decides what stops opening a door, so it
|
|
315
|
+
takes a confirmation naming the reader and what is being turned off.
|
|
316
|
+
|
|
317
|
+
PIN and ID-and-password are one setting with two modes, not two switches — the
|
|
318
|
+
reader has a single `pin_identification_enabled` flag. There is no Bluetooth
|
|
319
|
+
control: the HID API has no Bluetooth identification field, and the old toggle
|
|
320
|
+
of that name referred to the phone-as-a-card feature in the mobile app.
|
|
321
|
+
|
|
322
|
+
Saving the site settings screen no longer touches any of this. `enabled` and
|
|
323
|
+
the stored `identificationMethods` are passed through from what was loaded
|
|
324
|
+
rather than recomputed from toggles that are no longer there, so saving printer
|
|
325
|
+
or template settings cannot erase or flip them. The deprecated site-level
|
|
326
|
+
fields stay in the schema; the backend still validates them.
|
|
327
|
+
|
|
328
|
+
Visitor QR passes are hidden at the manager's request (2026-09-23). This is one
|
|
329
|
+
flag, `HID_VISITOR_QR_ENABLED`, in `VisitorForm.vue` — issuing, the preview
|
|
330
|
+
dialog, printing and the regenerate handler are all intact, so the feature
|
|
331
|
+
comes back by flipping it. The resident mobile app has its own visitor QR
|
|
332
|
+
screen and is deliberately untouched.
|
|
333
|
+
|
|
334
|
+
- b29c28b: Add a HID QR Code screen where a signed-in member generates their own pass.
|
|
335
|
+
|
|
336
|
+
Every other HID screen acts on somebody else, a resident being enrolled or a
|
|
337
|
+
visitor being issued a pass. This one has no subject picker, because the server
|
|
338
|
+
takes the caller from the session and there is no user id to send.
|
|
339
|
+
|
|
340
|
+
Pick a reader, generate, and the code is drawn in the browser from the value the
|
|
341
|
+
server returns. It can be downloaded as a PNG straight off that rendered image,
|
|
342
|
+
so the credential never travels again just to be saved. Regenerating replaces
|
|
343
|
+
the previous code and the old one stops working immediately. Removing withdraws
|
|
344
|
+
it from the device without touching the member's identity or their facial
|
|
345
|
+
enrollment.
|
|
346
|
+
|
|
347
|
+
Issuing a code does not grant access. A member's door permission comes from the
|
|
348
|
+
HID permissions screen, so the screen reports whether that screen actually
|
|
349
|
+
admits them to the reader and warns when a code would be refused at the door,
|
|
350
|
+
rather than handing out something that silently fails. It also refuses to offer
|
|
351
|
+
one on a reader with no QR capability.
|
|
352
|
+
|
|
353
|
+
The menu entry sits between HID Reader Users and HID Cards. The menu test now
|
|
354
|
+
pins the entries by title rather than by count, so the next change to it says
|
|
355
|
+
which entry moved.
|
|
356
|
+
|
|
357
|
+
- cab34a3: Carry the chosen HID reader between the HID screens instead of resetting it on
|
|
358
|
+
every one.
|
|
359
|
+
|
|
360
|
+
HID Reader Users, HID Cards, Access Logs and HID QR Code each held their own
|
|
361
|
+
reader and each defaulted it to the first one the site returned. Because they
|
|
362
|
+
are separate pages, the component unmounted on every navigation and that default
|
|
363
|
+
ran again — so picking HID 2 on HID Reader Users and clicking through to HID
|
|
364
|
+
Cards put you back on HID 1, with no way to make the choice stick. On a site
|
|
365
|
+
with several doors it had to be re-picked on every screen.
|
|
366
|
+
|
|
367
|
+
The choice now lives in one place, `useHidReaderSelection`, and every screen
|
|
368
|
+
seeds from it and writes back to it. The dropdowns are untouched: the web
|
|
369
|
+
already used `AppSelect` everywhere, so this is the state and not the control.
|
|
370
|
+
|
|
371
|
+
A remembered reader is only used when it belongs to the current site and is
|
|
372
|
+
still in the list. That one condition covers a site switch, a reader being
|
|
373
|
+
deleted, and the first load; each falls back to the first reader instead of
|
|
374
|
+
querying an id the site does not own, which returns nothing and reads on screen
|
|
375
|
+
as a reader with no users, no cards and no logs.
|
|
376
|
+
|
|
377
|
+
Kept in memory rather than stored. Moving between these pages is a route change
|
|
378
|
+
rather than a page load — the menu's items carry a `route` and
|
|
379
|
+
`NavigationItem.vue` renders them through `:to` — so a module-scoped ref
|
|
380
|
+
survives exactly the navigation that was losing the choice. Surviving a hard
|
|
381
|
+
refresh as well is a separate and much smaller benefit.
|
|
382
|
+
|
|
383
|
+
Intercom keeps its own reader. Its question is which panel you are calling, not
|
|
384
|
+
which door you are administering, and joining them would mean switching reader
|
|
385
|
+
on Access Logs changed who you would dial.
|
|
386
|
+
|
|
387
|
+
- 91c29e1: Inventory: packing on items (form, stock list, item detail, movement report), "Fault reported by" on Record Usage and in the usage report, and each technician's stock shows left and used this month (My Stock, Technicians). Items used up this month stay listed at 0 left without the Record usage button.
|
|
388
|
+
- cc838c5: Inventory roles: one permission per job (view stock, request items, record usage, approve requests, issue and receive stock, view reports, inventory admin) with plain names in the role editor, and one-click presets ("Staff: request & use", "Supervisor", "Storekeeper", "Inventory admin"). The Inventory screen shows only the tabs and buttons the role allows.
|
|
389
|
+
- 3aa7779: Inventory: "Main store · <company>" with who manages it, a request-first view for other companies, pending-approval banner for the owner; Used by on items (form, chips, column, filter, bulk edit, report filter); Give to colleague (My Stock) with confirm/decline; Hand-overs report; name · company · service on requests, holdings, history, reports; Issue Stock "Give to" grouped by company with a Used-by warning.
|
|
390
|
+
- 83b11aa: Inventory: Purchases report (by month and supplier, up to 12 months), supervisor "Mark checked" on usage records with a not-checked-yet filter, "Copy from old Equipment list", and the dashboard supply tile shown to inventory roles and named "Low Stock" when it lists items at or below their alert level.
|
|
391
|
+
- 7c892e6: Inventory: a store that supplies several properties names them all ("Supplies Winsland House I and Winsland House II") for every viewer, including service providers; requests show which property they are for; the Stock Movement report can be filtered to one property.
|
|
392
|
+
- 4f08426: Add Inventory: `InventoryMain` and its tabs (Stock, My Stock, Requests, Technicians, Reports, Settings) with receive, request, approve/issue, record usage, return, stock count, Excel import and Excel report export, backed by `/api/inventory`. Adds `useInventory`, `useInventoryPermission` and the `inventory` permissions to every app's role catalogue.
|
|
393
|
+
- 0fccd91: Inventory: supplier list. Receive Stock and opening stock pick the supplier from a searchable list (or add a new one, with a near-duplicate warning); new Suppliers tab with add/edit (only the name required), inactive, and Excel import.
|
|
394
|
+
- 221ea52: Inventory is easier to use: a new store shows only **Add Items** until it has items; the item form explains Unit, Packing, the low-stock alert and unit price; the item picker is labelled; requests read "Waiting for approval" / "Approved" with "N handed over (asked M)" and who decided and when; cancelling a request asks first; item history shows who did each step; Record Usage says "Tenant unit"; the Usage report shows the before/after photos the supervisor checks; "Count diff." reads "Stock count +/-"; a technician's activity says "Taken from store".
|
|
395
|
+
- 69501a3: Inventory: usage drafts (save the before photo first, add the after photo and submit later; edit or discard with a confirm; My drafts cards with an old-draft warning; "in drafts / free" on holdings); History per item and "My usage" with date, item and place filters; Usage report as the store owner's usage tracking (status incl. in-progress drafts, person, item, place, work order, flags, search; totals by item / person / company; photo thumbnails with times; draft and submit times; how the stock reached the person; Excel with the same filters and photo links; click a person or an item for its full history); a provider's supervisor gets "Company usage" for its own people at the main store.
|
|
396
|
+
- c256b3b: Inventory Record Usage: a before photo and an after photo are required (owner decision 2026-09-27). Save is refused with "Add a before photo and an after photo to record usage." until both are added; the API enforces the same rule.
|
|
397
|
+
- 2af39e9: Invite Client: for a new client the form asks for the modules of each chosen service (all ticked by default, always-included modules locked) instead of a role, and sends them with the invitation. The client's owner gets the client's own owner role, bounded by those modules. Inviting into an existing organisation is unchanged.
|
|
398
|
+
- 1173d74: Invite Client: a Property Management client also gets the Service365 resident app modules, all ticked to start
|
|
399
|
+
- 66e6fd3: Invite member: from inside a site the Site field is fixed to that site (read-only), and the Role list shows only the organisation's own active roles for the invited app and that site, each labelled "Site role" or "Organisation role". Org-less template roles are no longer offered on an invitation into an organisation.
|
|
400
|
+
- e7ccdd5: Members list and Members → Service Providers: a "Last active" column (last sign-in: "Today, 9:05 AM", "Yesterday, …", or the date; "—" until the person next signs in).
|
|
401
|
+
- ef816aa: Excel templates made live from the property: every import (inventory items, receive stock, building units, areas, units, vehicles, cameras) downloads a template with dropdowns, an example row and an Instructions sheet, and the server-checked imports show every row before saving and save nothing while a row needs fixing. Add Item records opening stock like a delivery (date, supplier, PO no., received by).
|
|
402
|
+
- 99cfad0: Camera wall: show the live view from the server's signed address when it sends one
|
|
403
|
+
|
|
404
|
+
The CCTV relay's live routes have no authentication today — anyone who knows a
|
|
405
|
+
channel number can watch a live camera. The relay is being closed behind a
|
|
406
|
+
signature the server mints per request and carries on the address as query
|
|
407
|
+
parameters.
|
|
408
|
+
|
|
409
|
+
The signature cannot be written onto `host`: that is the stored record, copied
|
|
410
|
+
into patrol-route documents and re-read by the server's own relay-URL check,
|
|
411
|
+
which rejects an address carrying a query. So the server hands the signed
|
|
412
|
+
address over beside it as `liveUrl`, and the wall now prefers it.
|
|
413
|
+
|
|
414
|
+
Additive and backward compatible. A camera whose relay is not configured gets no
|
|
415
|
+
`liveUrl` and still plays from `host`, exactly as before. The address is used
|
|
416
|
+
verbatim — never rebuilt from a host and a channel — so the signature survives
|
|
417
|
+
to the player.
|
|
418
|
+
|
|
419
|
+
- b40a5f2: Building map editor: drag to move, resize handles, drag wall/door ends and zone corners, add/remove zone corners, turn doors and lift shafts, flip a door's swing, multi-select (Shift+click, box select), snap to grid, keyboard nudge/delete/escape and redo. A whole drag is one undo step.
|
|
420
|
+
- bd40309: Members: a banner "N members here have a role from another site. Assign each one a role for this site." with a one-click filter to list them (server `roleState=other-site`); disappears at 0. `useMember().getAll` accepts `roleState`.
|
|
421
|
+
- 3f44414: Members list: a member holding a role that belongs to another site shows a warning chip ("<role> — belongs to <site>. Assign a role for this site.", server roleState "other-site"), and the Assign Role action in the row menu is highlighted whenever the role chip is a warning.
|
|
422
|
+
- f536ef0: Members list: the role chip shows the member's real role name, and a clear warning ("No role assigned - assign one", "<name> (deleted) - assign a new role", "Invalid role - assign a new role") when the server's roleState says the role is missing, deleted or another organisation's. Rows from an older server render as before.
|
|
423
|
+
- e17c43c: Members → Site access (organisation-level Members screen, owner request 2026-09-28):
|
|
424
|
+
the organisation's owner/admin chooses which of the organisation's sites a member
|
|
425
|
+
can open and the role at each (only roles created for that site), with "Same role
|
|
426
|
+
for all selected sites", and can remove the member's organisation-wide access so
|
|
427
|
+
they see only the chosen sites. New `MemberSiteAccess.vue`, `utils/site-access.ts`,
|
|
428
|
+
`useMember().getSiteAccess/saveSiteAccess`.
|
|
429
|
+
|
|
430
|
+
Fix: Assign Role pre-fills the member's current role again when the server marks it
|
|
431
|
+
usable (`roleState: "ok"`); 4.65.3 treated "ok" as unusable.
|
|
432
|
+
|
|
433
|
+
- f8c2009: Members > Service Providers: new `MemberServiceProviders` component listing every person from the site's service providers who can open its information - company chips with head counts, service and company filters, search, role at the site, date added, status, "Invited by" for subcontractors, Export to Excel and an "Email company" action. `MemberMain` takes `extraTabs` so an app can add the tab beside Active / Inactive.
|
|
434
|
+
- 075676d: Members table: one compact "Role & access" cell (each role once; one site by name, several as "N sites" opening the full site list), name with email underneath, one-line dates, clean role labels.
|
|
435
|
+
- 43b1741: Phones: below 600px every data table (`VDataTable`, `VDataTableServer`, `VDataTableVirtual`) shows each row as a stacked card (label: value) instead of a wide grid scrolled sideways, and the 720px `.app-table-scroll` floor no longer applies on phones. Tablets and desktop are unchanged.
|
|
436
|
+
- baac4ab: Add a derived module-to-application map (`utils/module-applications.ts`).
|
|
437
|
+
|
|
438
|
+
The Seven365 console listed the whole 47-entry catalogue as one flat list, so a
|
|
439
|
+
security client was offered Cleaning schedule management. R1 is written in terms
|
|
440
|
+
of applications, so the console needs to know which application each module
|
|
441
|
+
belongs to.
|
|
442
|
+
|
|
443
|
+
The map is DERIVED from each application's own `use*Permission` composable — the
|
|
444
|
+
same object that app passes to its role editor — so the console cannot disagree
|
|
445
|
+
with the apps. It is many-to-many on purpose: M&E reuses the cleaning schedule
|
|
446
|
+
module, and members/roles/attendance are shared by nearly all of them.
|
|
447
|
+
|
|
448
|
+
The seven app composables now import `useCommonPermissions` explicitly instead of
|
|
449
|
+
relying on Nuxt auto-import. Identical inside the app, and it is what makes the
|
|
450
|
+
catalogue readable outside Nuxt so the map can be derived at all.
|
|
451
|
+
|
|
452
|
+
- 0e6002e: Offer Emergency Contacts in the Cleaning and Landscape role editors, and stop the
|
|
453
|
+
client module list from hiding what a client or a role already has.
|
|
454
|
+
|
|
455
|
+
- `useCleaningPermission` and `useLandscapingPermission` now offer
|
|
456
|
+
`emergency-contact` with the seven actions `constants/permissions.ts` defines
|
|
457
|
+
(`see-all`, `see-details`, `add-/update-/delete-emergency-contact`,
|
|
458
|
+
`add-/delete-emergency-contact-category`). The long `see-all-emergency-contacts`
|
|
459
|
+
spelling is not added.
|
|
460
|
+
- `orgAllowsModule` treats every spelling of one module as the same module
|
|
461
|
+
(`MODULE_SPELLING_GROUPS`, derived from `LEGACY_PERMISSION_ALIASES`) and always
|
|
462
|
+
offers `ALWAYS_ALLOWED_MODULES` (sites, subscriptions, customers,
|
|
463
|
+
payment-methods, members, invitations, roles, roles-and-permissions,
|
|
464
|
+
site-settings). Widening only: everything offered before is still offered, and
|
|
465
|
+
the whole-catalogue fallback is decided exactly as before.
|
|
466
|
+
- The role dialog lists grants a role holds outside the client's offer as
|
|
467
|
+
"Granted — not offered to this client", read-only. Saving keeps them.
|
|
468
|
+
|
|
469
|
+
- f0f8f0c: Module gate on the web (RP-5): read the client's module deny list from `/api/members/user/:id/app/:type/modules`, refetch it when the org|site in the route changes, and hide the menu entries, dashboard widgets and pages of denied modules. Fails open: no list, a 404 or any error denies nothing, so it changes nothing until a client has a saved list and the API route is live. The member and role lookup is unchanged. The Assign Role picker always offers the member's current role.
|
|
470
|
+
- 15a7d59: NFC patrol report: the route picker also lists deleted routes, labelled "(deleted)" and after the routes still in use, so past patrols on a deleted route can still be reported on. `useNFCPatrolRoute().getAll` takes `includeDeleted`. Needs `@7365admin1/core` with NFC route soft delete in API-core; against an older API, which refuses the parameter, the picker asks again without it and lists what it did before.
|
|
471
|
+
- b7bd8e6: NFC routes: drag a checkpoint onto another in the Add/Edit Route form to swap their positions; the new order is saved with the route. Travel time is removed from NFC routes: the form no longer has a Travel time column or Total Travel Time / Tolerance Balance, the routes list and Route Details no longer show travel time, and the patrol report drops its Travel time (Actual/Expected) column, renames "Total travel time" to "Total time" and shows the route's own tolerance. Needs the matching @7365admin1/core release, which stops requiring travelTime on checkpoints.
|
|
472
|
+
|
|
473
|
+
NFC patrol report times are shown on the site's clock: scan times no longer read as UTC (a 10:50 scan showed as 02:50), and on patrol logs from the updated core every time follows the log's timezone. The Actual row now runs from the guard pressing Start Patrolling to the last scan or skip, so a patrol started early in the Early Start Allowance shows as early.
|
|
474
|
+
|
|
475
|
+
NFC route form: Tolerance takes digits only (letters and symbols cannot be typed or pasted) and is at most 60 minutes; opening a route saved with more shows the message instead of a silently disabled Submit.
|
|
476
|
+
|
|
477
|
+
- 0584632: Notification bell: robot alarms open the site's Robot alerts page, inventory alerts open their store on the right tab (Requests / Stock / My Stock), older "work-order" and "other" inventory rows route, and a click with nowhere to go now says why instead of doing nothing.
|
|
478
|
+
- b8ce3a0: Notification bell: every notification now opens its page. Rows route on `params.kind` (else their `module`) to the matching page — work order, feedback, incident, visitors, cameras, patrol logs, documents, emergency contacts, events, facility bookings, online forms, bulletin board, inventory, service providers, marketplace orders, and the Seven365 staff robot/client pages — in this app when it has the page, otherwise in the app that does (by its `APP_*` URL), with the organisation and site in the link. Rows with nowhere to go are no longer drawn as links. New `chooseNavigation`, `notificationKind`, `WEB_TARGET_KINDS`.
|
|
479
|
+
- 03ea14d: Notification Settings: Email per module, with an "Email address for notifications" card (send code, verify, resend, change). New `/notifications/open` page turns a notification email link into the right page, using the bell's own target table.
|
|
480
|
+
- 800eee1: `getVerifications` takes `origin`; the console Client List's Pending tab lists only invitations Seven365 sent.
|
|
481
|
+
- 11a3ac2: Organization app Members: lists every person of the organisation, including people limited to specific sites (one row each, labelled "Limited to: Site A, Site B"; organisation-wide people read "All sites"); a Site access button on every row; the actions column stays pinned on the right; shorter dates; at organisation level the "role from another site" banner is gone and such a role reads "Security (The Loft)". Needs core with `GET /members?people=1` (older servers refuse the flag).
|
|
482
|
+
- 7e671c3: Site access lists "Organisation roles" and "<Site> roles" in every site's Role list and in "Same role for all selected sites" (organisation roles offered before any site is ticked), with a "Create an organisation role" link. RolePermissionMain takes `scope` ("organisation" = roles with no site); getRoles sends it only when set.
|
|
483
|
+
- 21cfefc: Add / Edit robot: Ounitech credential fields read "AppID" / "Secret" (demo: "any value"); a Robot picker lists the robots the company credential covers (Find robots) so the serial is picked, and adding with no pair typed reuses the client's saved Ounitech credentials.
|
|
484
|
+
- 8e082b8: Robot detail: while a robot reports a fault, "How to clear this fault" lists its maker's manual steps (from the robot profile; Ounitech today), and the fault bar shows the code once, then the English words and fix.
|
|
485
|
+
- 7387b63: Robot detail: virtual walls, safe and slow zones and the taught route drawn on the robot map (with a line naming them); records card adds status history, map details (walls, zones, versions, floor map history, obstacle picture), changes made in the maker's app and read-only robot settings; the 0-jobs state says the maker reports no finished cleaning jobs. Mobile robot-rules copy now carries the records rows.
|
|
486
|
+
- 79733b1: PadBot security robots on the HERO robot screens: Add robot labels the fields "AccessKey ID" / "AccessKey Secret" and picks the robot from the account; robot detail offers Start patrol / Resume patrol (brands with `patrolControl`), Live video (minted per click), Mark resolved on an alarm, an ALARM badge for security alerts, and a Patrol history panel. Pause / Resume now need the brand's `pauseResume`, Cancel its `pauseProcess` (no change for existing brands).
|
|
487
|
+
- ecec8a6: Robots (PadBot S2 and all brands): a robot no longer vanishes from the map on one missed or failed reading - the last position is kept for up to 15 s, drawn faded with "last seen N s ago", then "Reconnecting"; the floor becomes unknown only when the robot says so; an online robot without a position reads "Position not reported right now". Patrol routes whose stops cannot be placed on the plan are listed ("Stops: Gate → Lobby"), never drawn at a guessed spot; when the brand keeps schedules to itself (`scheduleShared: false`) the panel says so and shows recent patrol runs plus a clearly labelled guess ("Looks like ... runs daily around 20:00"). Live view opens in the page (HD / Smooth, re-minted before expiry, never stored; iframe / native video / "Open in new tab" / RTMP explanation), also from the fleet row menu. Alert photos ("Photo", "Thermal photo") on the fault bar and Alerts rows. Go to point / Set position pick from the robot's saved points when its maps list them (`go-to-point`; Set position only on the robot's floor, with a confirmation line). Manual control (only when the server reports `manualDrive` and the user can update robots): hold-to-move with a big STOP, head controls, heartbeat-guarded session that ends on release, tab hide, close or connection loss.
|
|
488
|
+
- beca2f4: Dashboard: the Property Management and Security dashboards show a **Low Stock** tile (inventory items at or below their alert level) wherever their own inventory store serves the property; it can be hidden in Customize. Sites without a store are unchanged. Five tiles share one row.
|
|
489
|
+
- 390c5ad: Add Resident Checklists and Security Checklists toggles to facility booking
|
|
490
|
+
setup, bound to the facility's `isResidentChecklistEnabled` and
|
|
491
|
+
`isSecurityChecklistEnabled`. The two are mutually exclusive: enabling one
|
|
492
|
+
disables the other.
|
|
493
|
+
- 84434c3: Inventory: show "Equipment is now in Inventory." when a retired Equipment page forwards here (?moved=equipment).
|
|
494
|
+
- 330cb76: HERO map editor - Fleet Core (Open-RMF) parity: Waypoint, Lane, Fiducial and Measure tools; lanes on route graphs 0-8 (graph picker, names, robot brands), two-way / one-way, speed limit, facing, shared-space group; waypoint settings (charger, parking, holding, pass-through, cleaning zone, dock name, spawn, pick-up / drop-off, merge radius); waypoints drag their lanes, deleting one deletes its lanes; the four door types with hinge / swing / degrees / leaf ratio; lift shafts at any angle with cabin doors per floor (which door opens where) and size fields; fiducial level alignment and scale by measurement (plan and drawing); plain-language checks (duplicate names, lanes through walls, charger without a dock, lanes that lost a waypoint, lift floors with no door); zoom and pan; the drawing area is the building's real size (plans larger than 40 x 26 m are no longer cut off; turned plans); import a building file or a route map into a graph. The editor now saves the map's `rmf` part. The 3D view draws door types, cabin doors, turned lifts, the building's real shape and a Routes toggle; the mini-map shows robot lanes.
|
|
495
|
+
- 321257a: The building in 3D, generated from the robot's own floor plan
|
|
496
|
+
|
|
497
|
+
Adds `RobotMap3D` and `utils/building-map.ts`. The robot's map image is a SLAM
|
|
498
|
+
occupancy grid — walls are already dark pixels — so it is fetched into a canvas,
|
|
499
|
+
thresholded, traced into wall segments and extruded. Nobody draws walls.
|
|
500
|
+
|
|
501
|
+
Reachable from the fleet list's "View layout" action, so every application that
|
|
502
|
+
shows robots gets it without a change of its own.
|
|
503
|
+
|
|
504
|
+
No 3D engine: the scene is extruded boxes on flat floors, which a parallel
|
|
505
|
+
projection and a back-to-front draw order render correctly — and this layer is
|
|
506
|
+
consumed by eleven web apps.
|
|
507
|
+
|
|
508
|
+
The robot is drawn only when its position AND its floor are both known. The
|
|
509
|
+
vendor's tracking stream carries no floor, so drawing it on whichever level
|
|
510
|
+
happens to be selected would put it on a storey nobody confirmed.
|
|
511
|
+
|
|
512
|
+
- b510338: Robot detail: a cleaning robot whose brand reports them (Ounitech) now shows Clean water and Dirty water (Low / Medium / High with the %), a "Tanks and covers" line ("Tanks and covers OK", or which cover is open / tank not fitted) and, while it cleans, "This job" (% done and m² cleaned of planned). Warning tags for clean water low, dirty water full and any open cover or missing tank. Nothing is shown for brands that do not report them. The rule (`cleaningReadings`) is part of the robot rules synced to the mobile apps; "What this robot can do" lists "water levels and tanks".
|
|
513
|
+
- 26f2091: Robot screens: brand picker, live positions, and cleaning by area
|
|
514
|
+
|
|
515
|
+
The Add Robot form now asks which brand a robot is, offering each with what its
|
|
516
|
+
protocol actually allows in plain words. There is no safe default — sending a
|
|
517
|
+
robot a command in another brand's protocol is the one mistake the adapter layer
|
|
518
|
+
exists to prevent — so a brand is preselected only when there is no choice.
|
|
519
|
+
|
|
520
|
+
The fleet screen follows each robot's position while it is open, via the
|
|
521
|
+
`/track` stream, and stops every stream on unmount: the server holds one
|
|
522
|
+
connection to the robot cloud for each open one.
|
|
523
|
+
|
|
524
|
+
Losing a stream marks a robot `UNAVAILABLE` rather than leaving its last known
|
|
525
|
+
mode, which would advertise it as ready for work forever.
|
|
526
|
+
|
|
527
|
+
Adds "Clean area", so zones can be chosen from the robot's own maps rather than
|
|
528
|
+
cleaning everything. Battery is shown only when the brand reports one — a brand
|
|
529
|
+
that cannot measure it must not render as 0%, which reads as flat.
|
|
530
|
+
|
|
531
|
+
- 6e8bf3d: Robot detail laid out for the operator: header (name, status, site, model · serial, Edit/Remove), one-row status strip, camera and live map side by side above the fold with the commands under the map (map full width for robots without a camera; stacks camera, map, commands, patrol when narrow), then Patrol schedule next to Recent patrols (5 rows, See all), this robot's Alerts, and "What this robot can do" folded at the bottom. New `detectPatrolSchedule` / `patrolSlotText`: for brands without a schedule API (PadBot) the patrol schedule is detected from the last 30 days of runs ("Patrolling Lvl3 · daily 20:00 · last ran 18 h ago · next expected 20:00 today"), clearly labelled.
|
|
532
|
+
- 91b60e3: Robot fleet screens, shared by every application that shows robots
|
|
533
|
+
|
|
534
|
+
Adds `useRobots` (the `/api/robots` calls), `utils/robot-display.ts` (one set of
|
|
535
|
+
words and colours for a robot's state), and two components: `RobotFleet` — the
|
|
536
|
+
list with live status, floor and the Clean / Return actions — and `RobotForm`
|
|
537
|
+
for registering a robot.
|
|
538
|
+
|
|
539
|
+
One component rather than a copy per app, so Property Management, Hygiene, the
|
|
540
|
+
Seven365 console and the two mobile apps cannot drift into describing the same
|
|
541
|
+
robot four different ways. Each app passes only which robot `type` it presents
|
|
542
|
+
and which actions its user holds a permission for; the API narrows the result
|
|
543
|
+
again on its own side.
|
|
544
|
+
|
|
545
|
+
An unrecognised mode renders as "Unknown" and is never offered a Clean button,
|
|
546
|
+
and an unknown floor is shown as unknown rather than guessed. Return-to-dock
|
|
547
|
+
stays available while a robot is busy, because that is how an operator recovers
|
|
548
|
+
one.
|
|
549
|
+
|
|
550
|
+
- 406f423: Robot building map editor: floor plans per floor (upload / replace / remove, stored privately in the map; Set scale by measuring a known length; Fit), HERO Fleet Core import into the saved map, and downloads of the building file and route maps. The 3D building map draws saved floor plans. Edit map is also offered at HERO Fleet Core sites (it edits the site's saved copy). The Add robot note says "stored encrypted" only when the server reports it.
|
|
551
|
+
- 7b9f224: Robots: the live view shows the PadBot (EZVIZ) camera's H.265 picture. HLS now plays through hls.js wherever the browser has Media Source Extensions (native HLS only as the Safari fallback), the player owns `video.src` so a re-render can no longer reset the stream (the cause of the fatal bufferAppendError / black picture), and it recovers by itself: media-error recovery, then a fresh URL with backoff on network errors, expired URLs and stalls. When no picture can be shown it says so in plain words with a Retry button. The robot detail gains an inline Live view panel that plays automatically, with an "Always show live view" switch remembered per browser; it releases the stream while the tab is hidden, the panel is far off-screen or the robot is offline, and Full screen reuses the same stream.
|
|
552
|
+
- c130550: Robots: building layout editor in the 3D view (place, move and remove doors and lift shafts, floor heights, save/remove the layout), doors and lift shafts drawn in 3D with shafts spanning stacked floors, and a live lift queue panel (simulated lift) with request/release for demo robots. New useRobots calls for building maps and lift sessions.
|
|
553
|
+
- ccf0b17: Robot detail: an Ounitech robot shows its real floor map with its area layer, area names and live pin ("position approximate" until Ounitech confirms the map corner); Clean area picks the areas to clean; new records card: recent cleaning jobs, alert history, every robot and base-station reading, parts wear, timed switch on/off and lifts. The site mini-map shows cleaning robots on their own map.
|
|
554
|
+
- 567f2d7: Robot maps: a robot with more than one map (PadBot) now has a map selector on robot detail and the site mini-map, opening on the map the robot is on ("Robot is here"). Each map shows its own image at its real shape, its saved points and its routes. Go to point offers another map's points only when PadBot can take the robot there (same map group), otherwise says "The robot must be on this map first."; Set position lists only the robot's current map. The building map draws a robot's map on its floor when the floor number is known and the floor has no saved plan.
|
|
555
|
+
- ff47855: Robots: draw walls and zones on the building layout. The layout editor can now draw, move, reshape and remove wall segments and named zone polygons per floor, with undo of the last change and keyboard shortcuts; walls and zones are saved with the building map and drawn in the 3D view.
|
|
556
|
+
- a63f6b4: Robot detail: "What this robot can do" card built from the robot's brand + model manifest; information grouped Status / Location / Patrol|Cleaning / Camera; unsupported facts hidden (never greyed); commands confirm what will happen and report the result; "Return to charger" everywhere; maker-app schedule text from the manifest. Site fleet Brand column names the model; the capability badge's tooltip lists what that robot does.
|
|
557
|
+
- cd32225: Robots: when a robot is online but its position isn't arriving, robot detail, the site mini-map and the building map say so in a calm banner ("<Robot> is online, but we're not receiving its position right now.") and detail keeps the last position seen faded, tagged "last known · HH:MM". The live-view segment fix now also catches a stray H.264 delimiter split across two TS packets. New `scripts/sync-robot-live.mjs` copies the web live-view player into the mobile apps.
|
|
558
|
+
- 5c0af24: Robots: edit and remove, schedule, spot clean on the floor plan, faults, stacked levels
|
|
559
|
+
|
|
560
|
+
- `RobotFleet` gains a row menu: Schedule (read-only view of the robot's plans),
|
|
561
|
+
Spot clean (only for brands whose state reports `capabilities.spotClean`),
|
|
562
|
+
Edit (name, serial) and Remove, behind new `canEdit` / `canDelete` props.
|
|
563
|
+
- A robot's reported fault is shown under its status chip.
|
|
564
|
+
- `RobotMap3D` offers "All levels (stacked)" when a robot maps more than one
|
|
565
|
+
floor; the view fits the whole stack.
|
|
566
|
+
- `useRobots` gains `updateRobot` and `deleteRobot`.
|
|
567
|
+
|
|
568
|
+
- 1b71827: Add a search to both role dialogs, and stop Unselect All clearing what is hidden (R5).
|
|
569
|
+
|
|
570
|
+
A client builds a role out of up to 47 modules and about 127 actions inside a
|
|
571
|
+
modal. The search matches the names a person reads as well as the raw keys, so
|
|
572
|
+
"statements" and "soa" both find `SOA-mgmt`.
|
|
573
|
+
|
|
574
|
+
It filters the DISPLAY only — a permission ticked and then filtered out of view
|
|
575
|
+
is still ticked and still saved. Select All and Unselect All now act on the
|
|
576
|
+
visible set: with no search that is the whole catalogue, unchanged; with a search
|
|
577
|
+
active, Unselect All no longer discards every permission outside the filter,
|
|
578
|
+
which on the edit dialog would have been a role somebody is already using.
|
|
579
|
+
|
|
580
|
+
- d455aeb: Roles and permissions (owner approval 2026-09-25): Robots offered in the Property Management and Cleaning role editors; the PM editor's camera-wall row reads "CCTV" (label only, `virtual-patrol:*` strings unchanged); PM's CCTV page follows the client's module list (`org-site-cctv` -> `virtual-patrol`, fails open); `NavigationDrawer` takes `sectionLabels` so an app can rename a sidebar heading.
|
|
581
|
+
- 43c0574: Add `useSiteActions(org)` and `utils/site-actions.ts`: whether the signed-in person may add or delete sites (and add organisations) in an organisation, as core answers it. Site-confined members are never offered these buttons.
|
|
582
|
+
- e13160f: Settings → Site Information is read-only on a site the organisation serves but does not own: a note says "This site belongs to <Client>. Only they can change its details.", cover photo, description and documents are inert and Save is hidden. Blocks and guard posts stay editable. Driven by `detailsEditable`/`ownerOrgName` on the site read (core); absent on an older API = unchanged.
|
|
583
|
+
- 475a46a: Service Providers: "Change or end provider" on provider rows and a "Provider changes" dialog (preview in plain words, plan for a date, undo within 14 days, cancel a plan, change site owner).
|
|
584
|
+
- 52930d4: Service Providers > Invitations: the status line says who must act while an invitation waits ("Waiting for a Seven365 Super Admin to approve - the email is sent after approval") and, once approved, whether the email went out ("Email sent 30 Sept, 14:14" or "Email failed: <reason> - use Resend", in red). Reads `emailStatus` from core 3.134+; older rows keep "Sent - waiting for their answer".
|
|
585
|
+
- 62ffde8: Service-provider invitations can be answered in the app: the notification bell opens "<Company> has invited you to <site>" on the organisation app's Invitations page (other apps send the click there by URL), the new `ServiceProviderInvitationsReceived` section lists invitations from other organisations with Accept / Decline, `useServiceProviderInvitation` gains `acceptInvitation` and `getReceivedInvitations`, and the invitation card shows a failed accept or decline instead of swallowing it.
|
|
586
|
+
- 5c6f3a1: Service Providers screen (inviter's side): accepted providers appear under Service Providers for the site; the Invitations tab shows only this site's invitations with the company that accepted, who invited and when, when it was answered or expired, and a View provider panel; "Members" is now "Staff on site" and is not shown for invitations.
|
|
587
|
+
- 497960c: Members > Service Providers: the row of company chips above the table is replaced by a Company filter in the toolbar (each company with its service, head count and "via" inviter; narrowed by the Service filter) and a one-line summary ("7 companies · 37 people · 1 company has not added anyone yet").
|
|
588
|
+
- 9d50ba2: Service Providers screen: a subcontractor another provider brought onto the site reads "Invited by <provider>" (list and details), the invite form says Seven365 approves every invitation and calls the field "Service", and the bell opens SP Approvals for a waiting invitation and the hygiene Service Providers page for an answered one.
|
|
589
|
+
- 9a370cb: "Open as Seven365 staff": persistent staff-view banner with Exit in every app's top bar, and staff in an open view work at that client with full setup rights without a membership.
|
|
590
|
+
- ca1ba92: Suspended access: when the API answers ACCESS_SUSPENDED (every membership of the person is suspended), the app signs out and the sign-in page shows the server's message. Role names are unique per site, so role pickers and the roles table show "Admin · St. Regis Residences" (`roleLabel`), and the Members role chip uses the same "Role · Site" form.
|
|
591
|
+
- 378a99b: Unit Info > Management tab: the Service Providers panel now lists and assigns the site's provider PEOPLE (the same list as Members > Service Providers) instead of provider companies. `useServiceProvider().getSiteMembers(siteId, unit)` takes a `unit` filter and `updateSiteMemberUnit(id, unit)` attaches/detaches one person. Needs `@7365admin1/core` with `updateSiteMemberUnit` and API-core's `PATCH /api/service-providers/site-members/:id/unit`.
|
|
592
|
+
- fc851aa: Building Management > Unit Info: new Management tab (`BuildingUnitManagement.vue`) with Management and Service Providers panels. Add lists only active members / providers not yet on a unit; remove detaches. Suspended, inactive or deleted ones drop off on their own (the server detaches them). `useMember().updateMemberUnit`, `useServiceProvider().updateUnit`, and a `unit` filter on both `getAll`s. Needs `@7365admin1/core` with the unit-management-assignment change and the API-core routes.
|
|
593
|
+
- c1797de: Vehicle QR stickers: the QR code itself can now take a shape - circle, heart, star badge, hexagon, octagon, shield, speech bubble, rounded square, blob, droplet or car - with the dots filling the whole shape (the real code in the middle with a 2-module white ring, the plate number on a dark tag at the bottom). New "Frame" choice (none = the shape is the sticker, or a card round it) and "Sticker size" choice (Small / Standard / Large, only sizes that keep the QR at least 25 mm). The print preview and the foot of every sheet now show the QR size, shape size, sticker size after cutting, stickers per A4 and gap, "print at 100%", and a 10 mm size-check bar. All codes stay error correction Q (H was tested and read worse); round "circle" QR corners were removed because tilted stickers did not scan reliably. Every shape x size x style x colour decodes under seven camera conditions.
|
|
594
|
+
- 4abf631: Notification bell on by default in the shared top bar, for every signed-in web app: unread count, the latest notifications, mark one or all as read, an in-page pop-up for new ones (checked every 30 s and when the tab regains focus), optional desktop notifications, and a link to Notification settings. The list shows everything addressed to the person (it was site-scoped, which disagreed with the badge).
|
|
595
|
+
- eaafd63: Resolve a member's role per SITE, not once per signed-in session.
|
|
596
|
+
|
|
597
|
+
`secure-member.client.ts` keyed the role by the user alone, so someone holding a
|
|
598
|
+
different role at two sites kept the first site's role for the whole session.
|
|
599
|
+
The lookup is now keyed by user and the route's site, and `getByUserType` can
|
|
600
|
+
send `?site=`, which the server already answers (core's `pickSiteMembership`).
|
|
601
|
+
|
|
602
|
+
Sending the site is held behind `SEND_SITE` (default `false`), matching the
|
|
603
|
+
mobile apps' switch, because a site role can be narrower than the
|
|
604
|
+
organisation-wide one. While it is held the request is unchanged, so no role
|
|
605
|
+
changes; a re-resolve that fails now keeps the last-known role instead of
|
|
606
|
+
blanking it.
|
|
607
|
+
|
|
608
|
+
- 489d8d2: Security headers on every app (HSTS, nosniff, frame-ancestors/X-Frame-Options, Referrer-Policy, Permissions-Policy, COOP, minimal enforced CSP + full Report-Only CSP), drop `x-powered-by`, explicit `SameSite=Lax` on the session cookies, `sanitizeHtml` for rich text rendered with `v-html`, html2pdf.js 0.14 (jsPDF 4.2.1).
|
|
609
|
+
- 5446089: Worker nouns follow the account type: one term map (`utils/worker-terms.ts`, `useWorkerTerm()`), keyed by the app's `appSlug`. Cleaning says Cleaners, security Security officers, pest control Pest technicians, M&E Technicians, landscape Gardeners, pool Pool attendants, property management Staff / Service staff, anything else Staff. Applied to Inventory (tab, report, usage column, export file name and sheet headers via `appSlug` on the export), Attendance, the schedule checklists, the security dashboard and NFC patrol report. Display only - field names and API keys are unchanged. `scripts/sync-worker-terms.mjs` copies the map into the mobile apps and API-core.
|
|
610
|
+
|
|
611
|
+
### Patch Changes
|
|
612
|
+
|
|
613
|
+
- d7302ad: Make the access card screens use the site's own Entrypass URL. Add Access
|
|
614
|
+
Card, the card preview dialog and Assign to Unit all called
|
|
615
|
+
`/api/encrypt-acm-url` with no `url`, so their door/lift levels, access
|
|
616
|
+
groups and card assignments went to the route's hardcoded default host
|
|
617
|
+
whatever the site had configured under Entry Pass settings. Only the
|
|
618
|
+
contractor pass in `VisitorForm` honoured the setting. A new
|
|
619
|
+
`useSiteEntryPassSettings().getEncryptedAcmUrl(siteId)` resolves the site's
|
|
620
|
+
URL first and falls back to the default when none is set or the settings
|
|
621
|
+
cannot be read; the three screens now call it.
|
|
622
|
+
- 947d818: Let an access card be assigned to Management and Service Providers, not only
|
|
623
|
+
the unit's residents. The Assign Card dialog now asks which group first and
|
|
624
|
+
then who, listing the site members and the service providers attached to that
|
|
625
|
+
unit (core #2515's `unit` filter). Management assigns end to end; service
|
|
626
|
+
providers are listed but not yet assignable, because that endpoint returns
|
|
627
|
+
provider COMPANIES and a card is written onto a person's user account - the
|
|
628
|
+
picker enables itself as soon as a row carries one.
|
|
629
|
+
- 512a053: Sign-in card and every page: the member, role and organisation lookups the router guard awaits before each navigation are now bounded (5 s, same as the module deny list). A lookup that never answered used to hold the first page - every app's sign-in card included - blank for ever; past the bound it now counts as a transient failure (role unresolved, retried on the next navigation) and the page renders. Successful and 404 answers are unchanged.
|
|
630
|
+
- 13857a6: Edit Camera: the device password is write-only. On edit the Password field is optional, starts blank with the placeholder "Saved · leave blank to keep", and is sent only when a new one is typed, so an admin can switch a camera's Status without re-typing its login. A status-only change now enables Submit. Password stays required when adding a camera.
|
|
631
|
+
- 10b9603: CCTV wall: the grid now uses only the columns it has tiles for. One camera on a four- or nine-tile wall filled half (or a third) of the width and left the rest black; the layout button means "up to this many tiles", so the wall no longer keeps empty columns.
|
|
632
|
+
- 82073b9: CCTV wall: full screen now gives the wall the whole screen (the capabilities panel is not drawn while it is on, so the picture is no longer squeezed into 68% of the height), and a player that reports its first frame and then goes quiet is shown as "Not confirmed" instead of "The picture stopped arriving" — the relay reports only the first decoded frame, so the old rule called every working camera dead after 12 seconds.
|
|
633
|
+
- a33011f: Connect the feedback and work-order Chat panel to the real comment thread. It
|
|
634
|
+
shipped as a mockup - five hardcoded messages from "Tan Wei" dated 06/28/2024, a
|
|
635
|
+
textarea with no model and a Send button wired to nothing - while a complete
|
|
636
|
+
`useComment` composable sat in the layer with no callers. The panel now loads,
|
|
637
|
+
groups by day, aligns by sender, sends, and attaches photos. The bubble matches
|
|
638
|
+
the mobile apps field for field, its avatar renders an actual profile picture,
|
|
639
|
+
and a load failure is reported rather than shown as an empty thread.
|
|
640
|
+
- 28feb30: Checklist: the tick that finishes a set now always opens the photo dialog.
|
|
641
|
+
A set holding a rejected unit counts as finished once every unit is approved
|
|
642
|
+
or rejected (the server's rule), and roles without add-remarks get the dialog
|
|
643
|
+
too, without the remarks field, instead of an auto-approve the server refuses.
|
|
644
|
+
A server photo refusal on a single tick opens the dialog instead of an error.
|
|
645
|
+
- c6efd2a: Dashboard Customize: the widget toggles use the platform switch (`app-switch`), so ON and OFF are clearly different in light and dark mode, and Submit uses the theme's primary colour. The Customize choice is now remembered per signed-in user and per app (one `dashboard-widgets` cookie storing hidden widgets), so another user on the same browser, or another service app, no longer inherits it, and new widgets appear by default. Widget visibility follows the estate permission rule (`grantsPermission`): a saved role with an empty permission list sees every widget, and every shipped spelling of a grant counts. Hiding a KPI tile no longer leaves a gap in the row.
|
|
646
|
+
- 955c89c: Let the entry pass card picker be searched and paged. The "Select Cards"
|
|
647
|
+
field was a `v-select` over the first 100 available cards, which only let
|
|
648
|
+
the operator scroll. It is now a `v-autocomplete`: typing narrows the list
|
|
649
|
+
at once and, after a 500ms debounce, sends the term to the available-cards
|
|
650
|
+
endpoint as `search`; reaching the end of the list fetches the next page and
|
|
651
|
+
appends it. Responses that arrive after a newer request are dropped, so a slow
|
|
652
|
+
search cannot overwrite a fast one and a late page cannot be appended onto a
|
|
653
|
+
list that was replaced. API-core does not read `search` yet; until it does,
|
|
654
|
+
Vuetify's client-side match on `cardNo` is what narrows the list.
|
|
655
|
+
- 7c54796: One date format in the Access Logs date column.
|
|
656
|
+
|
|
657
|
+
Reader rows read `15:18:43 23/9/2026` and the intercom door-open row directly
|
|
658
|
+
below read `9/23/2026, 2:15:29 PM`. `formatDate` branched on the value's type
|
|
659
|
+
and only the number branch was formatted: a reader reports its clock as an
|
|
660
|
+
epoch, while an intercom door open never touches the reader, so the API writes
|
|
661
|
+
it as an ISO string — and that branch fell through to `toLocaleString`.
|
|
662
|
+
|
|
663
|
+
Both branches now build the same `HH:mm:ss D/M/YYYY`. Local parts for the
|
|
664
|
+
string, UTC parts for the epoch, deliberately: the reader encodes its own wall
|
|
665
|
+
clock as if it were UTC, an ISO string is a real instant, and reading each the
|
|
666
|
+
way it was written is what makes both rows show the time that was on the clock
|
|
667
|
+
at the door.
|
|
668
|
+
|
|
669
|
+
Nothing else changes — sorting and the export date range already worked off
|
|
670
|
+
`parseDate`, not the display string.
|
|
671
|
+
|
|
672
|
+
- b29c28b: Remove the Read at Reader action from the physical HID card dialog.
|
|
673
|
+
|
|
674
|
+
Cards are now assigned by entering the value. The button's progress alert and
|
|
675
|
+
its Cancel Reading button went with it, since both existed only to narrate the
|
|
676
|
+
30-second wait while the reader listened for a card, and neither could be
|
|
677
|
+
reached once the trigger was gone. The same is true of the two handlers behind
|
|
678
|
+
them and the state they set.
|
|
679
|
+
|
|
680
|
+
The `enroll` and `enrollment/cancel` endpoints are untouched and still served by
|
|
681
|
+
the API, so this is a change to the screen, not to what the backend offers.
|
|
682
|
+
|
|
683
|
+
- fd92ac8: Deleting a HID user now actually deletes the stored identity, so the person stays deleted.
|
|
684
|
+
|
|
685
|
+
The HID Users list is read from the **reader**, and `listReaderUsers` builds each row out of the device's own columns — it carries no identity `_id`, so `toHidUser` left `_id` undefined for every row on screen. `deleteUser` then called `deleteIdentity` only `if (selectedUser.value._id)`, so for every row in that list it never called it at all. The device user was destroyed and the identity row stayed `active`, with no error, because the guard was an `if` and not a failure.
|
|
686
|
+
|
|
687
|
+
An orphaned identity is not inert:
|
|
688
|
+
|
|
689
|
+
- `findIdentityBySubject` excludes only `status: "deleted"`, so the duplicate guard still answers _"this person is already enrolled on this reader"_ and refuses to enrol somebody the operator can no longer see.
|
|
690
|
+
- `findProfileIdentity` finds it too, so the next reconcile reuses its stored `hidUserId` and rebuilds the device user from the access grant — the deleted user returns, same UID, in the reader's group.
|
|
691
|
+
|
|
692
|
+
The identity is now resolved by the reader's own keys before the device user is destroyed, the same way `saveUser` already resolves it through `findExistingIdentityOnReader`. The lookup reads our own records, so doing it first means a failure there costs nothing; if it cannot resolve a row the device delete still proceeds and the next delete can clear it.
|
|
693
|
+
|
|
694
|
+
Pairs with the core change that makes `deleteIdentity` remove the access grant reliably. Either half helps on its own; both are needed for a delete that revokes.
|
|
695
|
+
|
|
696
|
+
- 2f58eb4: Ask the identities endpoint for the whole human, not one link field, when
|
|
697
|
+
checking whether the selected resident is already enrolled.
|
|
698
|
+
|
|
699
|
+
The enrolment dialog filtered on `person`, which misses a resident enrolled
|
|
700
|
+
through the permissions screen, because that screen files them under their user
|
|
701
|
+
account instead. The check now uses the `subject` filter, which follows the
|
|
702
|
+
record to its account and matches every link field, so the inline warning agrees
|
|
703
|
+
with what the server will do on submit.
|
|
704
|
+
|
|
705
|
+
- 2f58eb4: Show the reader's facial reference when editing a HID user that has one.
|
|
706
|
+
|
|
707
|
+
The face photo is never shown or kept once registration completes, so the
|
|
708
|
+
dialog reported only that somebody is enrolled. It now prints the reader's own
|
|
709
|
+
reference for that enrollment under a Facial Data heading, directly below the
|
|
710
|
+
Face Enrolled button. That value is the only handle on the enrollment there is:
|
|
711
|
+
the reader holds the image and the biometric template, we hold neither.
|
|
712
|
+
|
|
713
|
+
It reuses the roster's Facial Data column so the dialog and the table cannot
|
|
714
|
+
disagree, and it is drawn only when there is a value, so enrolling a new user
|
|
715
|
+
shows nothing until the reader has accepted a photo.
|
|
716
|
+
|
|
717
|
+
- 2f58eb4: Take block, level and unit out of the HID edit form, and let a credentials-only
|
|
718
|
+
edit save.
|
|
719
|
+
|
|
720
|
+
Editing a HID user does not move anybody. The record already carries its block,
|
|
721
|
+
level and unit; the dialog loads them on open and writes them back untouched on
|
|
722
|
+
save, so the location and the Unit column survive an edit without the operator
|
|
723
|
+
being asked for them. The cascade is now drawn while enrolling and nowhere else.
|
|
724
|
+
|
|
725
|
+
A resident is required to enrol, because an identity has to be linked to
|
|
726
|
+
somebody, and is no longer required to edit. The reader owns its own user table
|
|
727
|
+
and holds users we never enrolled, and changing a photo, PIN or password on one
|
|
728
|
+
of those is a reader-side operation that says nothing about who they are.
|
|
729
|
+
Demanding a resident there refused the save for a field the dialog does not
|
|
730
|
+
draw. Such an edit now applies to the device and writes no identity.
|
|
731
|
+
|
|
732
|
+
The resident field itself is drawn only where it can offer something: while
|
|
733
|
+
enrolling, or while editing a record whose stored unit gives it a list. Opening
|
|
734
|
+
an existing user loads the people in that unit, so the field shows a name rather
|
|
735
|
+
than the raw id it printed before.
|
|
736
|
+
|
|
737
|
+
The link is sent only when the form holds one. An empty string reads to the
|
|
738
|
+
server as a link being removed, not as "leave it alone", so the keys are omitted
|
|
739
|
+
instead and the stored subject is kept.
|
|
740
|
+
|
|
741
|
+
Registration number joins ID as read-only in both modes. It was read-only while
|
|
742
|
+
enrolling and editable while editing, which is backwards: both values are
|
|
743
|
+
allocated by the reader and neither was meant to be typed.
|
|
744
|
+
|
|
745
|
+
- b29c28b: Make the HID user search fire when you type.
|
|
746
|
+
|
|
747
|
+
Both reader-user screens reloaded on `@keyup.enter` and on nothing else. Typing
|
|
748
|
+
sent no request at all, so a search box that looks like every other one did
|
|
749
|
+
nothing unless you happened to guess that Enter was required.
|
|
750
|
+
|
|
751
|
+
Typing now searches, debounced by 400ms because each reload is a
|
|
752
|
+
login/load/logout round trip to a reader over a VPN rather than a database
|
|
753
|
+
query. Enter still works and skips the wait.
|
|
754
|
+
|
|
755
|
+
Debounced typing means several loads can be in flight at once, so both screens
|
|
756
|
+
gained the monotonic sequence guard `HidAccessLogDashboard` already uses: a
|
|
757
|
+
reply that arrives after a newer load started is discarded instead of
|
|
758
|
+
repopulating the table with stale rows, and only the newest load clears the
|
|
759
|
+
spinner.
|
|
760
|
+
|
|
761
|
+
The enrollment screen's placeholder now says what is searchable - name, UID or
|
|
762
|
+
registration number - matching the roster's.
|
|
763
|
+
|
|
764
|
+
- 07ffe7f: Inventory: show "Copy from old Equipment list" only when the store's sites have old Equipment records not yet copied.
|
|
765
|
+
- 9f85e34: Show the completion photos on the area checklist history screen. The API has
|
|
766
|
+
always returned the file ids of the photos a technician took when completing a
|
|
767
|
+
unit; nothing drew them, so the office had no way to see the proof of work.
|
|
768
|
+
Each unit now shows its photos as thumbnails that open full size.
|
|
769
|
+
- a209e3f: useSite().getSites lists an organisation's sites from GET /api/sites?org= (the old /api/sites/organization/:org route does not exist and returned 404), so Inventory's "Properties this store supplies" and the set-up form offer every property of the organisation again.
|
|
770
|
+
- ab8140d: Inventory store switcher: when a person can see several stores of the same kind, each is named with its organisation ("Property store · Margaret Ville"); more than three stores are chosen from a list instead of a row of identical buttons.
|
|
771
|
+
- ecb24c1: Invitations list: a site app now lists only the current site's invitations.
|
|
772
|
+
`InvitationMain` sends the page's `siteId` to `GET /api/verifications` (it was
|
|
773
|
+
commented out, so every site showed the whole organisation's invitations - owner
|
|
774
|
+
report 2026-09-28, Wing Tai / The LakeGarden Residences). The org console sends
|
|
775
|
+
no site and is unchanged. The empty table now reads "No pending invitations for
|
|
776
|
+
<site>. Use Invite member to add someone." / "No expired invitations."
|
|
777
|
+
|
|
778
|
+
Members → Assign Role: a member's role from ANOTHER site (roleState other-site,
|
|
779
|
+
other-org, deleted, missing) is no longer pre-filled or offered ("Role already
|
|
780
|
+
assigned"); only this site's roles are listed. A site with no role says "No roles
|
|
781
|
+
created for <site> yet. Create one in Roles & Permissions." with a link that
|
|
782
|
+
returns to Members after the role is created (`?returnTo=`).
|
|
783
|
+
|
|
784
|
+
- 1916cfe: Invite Client form: the Site field can appear again when an organisation is known (it compared the app list to a single string, so it never did), no site list is requested without an organisation, Role and Site are marked required as their rules already were, a disabled app now says "Already uses this app in another client" (what is actually checked), and an existing account is no longer announced in red as if it were an error.
|
|
785
|
+
- b086810: Invite Client -> Role: list organisation roles only, not the platform staff roles
|
|
786
|
+
|
|
787
|
+
The picker sent `type: props.app` and its only caller passes no `app`, so it
|
|
788
|
+
asked for no role type at all and the endpoint returned every org-less role --
|
|
789
|
+
including the two `type: "admin"` platform staff roles that the console's own
|
|
790
|
+
`/super-admin/role-permissions` screen edits. It now asks for
|
|
791
|
+
`type: "organization"`, which is what a client administrator's role is.
|
|
792
|
+
|
|
793
|
+
- 211c3a3: Invite Client takes one application per invitation, its dialog scrolls so every module, the owner role and Submit are reachable, and Feedback is offered once (both spellings sent together). Screen modals shown outside a v-dialog now scroll their body.
|
|
794
|
+
- 1f3f3b7: Invite Client offers Real Estate Developer, which gets the Property Management modules and the Service365 resident app modules like a Property Management client.
|
|
795
|
+
- bebef55: Invite member: when the Role list cannot be loaded (for example no access to the organisation's roles), it says so instead of "No roles have been created for this site yet".
|
|
796
|
+
- ced091b: Invite member: "Create more" keeps the same app and site, so the Role list after an invitation is the same as before it (it used to switch to the organisation-type roles). When no role exists for the site, the list says so and links to Roles & Permissions.
|
|
797
|
+
- d831861: Members > Service Providers: a provider entry with no company name and nobody at the site no longer shows as an empty chip.
|
|
798
|
+
- d1ec0e6: Module gate: a page of a denied module now redirects to the site dashboard by path (`/:org/:site/dashboard`) instead of by route name, and only in apps that have that dashboard; elsewhere the page is left open (fail open). Fixes web-app-main's route-names check, which rejected the route name, and stops the layer's visitor-management preview page from redirecting to a page that does not exist in the main, org, account and admin apps.
|
|
799
|
+
- 5a07f04: Inventory Stock Movement: the Received column labels opening stock received in the month ("incl. N opening stock").
|
|
800
|
+
- 597b1dd: Organization Members: keep the "Fix roles from other sites" banner at organisation level, worded for the organisation ("N members hold a role created for one particular site…"), without the per-site "Show them" filter. 4.75.0 had hidden the whole banner there.
|
|
801
|
+
- 6a8f745: Signed-in QA sweep fixes (plain words and readable controls):
|
|
802
|
+
- Inventory Settings names every property it supplies (was "Current property" when the organisation's site list did not include it).
|
|
803
|
+
- Inventory item lines label the packing ("Code 001 · Bulb · Packing: 25") instead of a bare number.
|
|
804
|
+
- Dashboard Low Stock tile shows 0 and "Every item is above its alert level" instead of "No data to display".
|
|
805
|
+
- Dashboard activity chart y-axis shows distinct whole numbers (was "0 0 0 1 1 1"); the Customize entry is called "Activity chart" and describes what it draws.
|
|
806
|
+
- A plain on/off switch now has a visible off track in light mode.
|
|
807
|
+
- Bell panel title "Notifications"; notification settings say "iService365"; the robot mini-map no longer shows "(placeholder)".
|
|
808
|
+
- c0c033c: A Real Estate Developer client links to the Property Management app (app switcher, cross-app redirects, client details service link).
|
|
809
|
+
- 22dce74: Require a photo before a checklist task can be completed.
|
|
810
|
+
|
|
811
|
+
The Submit button in the completion dialog stays disabled until at least one
|
|
812
|
+
photo is attached, and the uploader is marked required. It gates on the uploaded
|
|
813
|
+
ids rather than previews, so a half-finished upload cannot slip through to a
|
|
814
|
+
server that then refuses it.
|
|
815
|
+
|
|
816
|
+
The reject dialog is deliberately not gated — a reject is somebody reporting a
|
|
817
|
+
problem, and demanding a photograph first would stop them reporting it. The
|
|
818
|
+
server scopes the rule the same way.
|
|
819
|
+
|
|
820
|
+
- 7f8599b: Robot detail: after a command the line follows the robot ("Leaving the charger…", "On the way to P4…", "Arrived at P4", "Docking…", "Back on charger") instead of a false "didn't start"; says up front that undocking takes about 10 s, with a spinner while it follows; reads the robot every 1 s for a minute after a command (then every 4 s) so the status and map pin keep up; a note under the live view says the video is about 8 s behind and the map is real time.
|
|
821
|
+
- 0b155fb: Robot detail: a robot's own map image follows the layout's height cap (280 px beside the camera, 360 px full width) instead of a fixed 440 px, and opening a robot scrolls its header to the top so the camera, map and commands are on screen.
|
|
822
|
+
- c709924: Robot (HERO) screens: the robot header no longer slides under the app's top bar - `.hero-page` and `.hero-screen` drop their `margin: -24px` (the apps pad 8-16px, so the page was pulled above its content box). The HERO fault-count bell is removed from the shared robot header (`RobotSiteHeader`), and `RobotBell` is deleted, so the app's own top-bar bell is the only bell. `RobotSiteHeader` loses its `faults` prop and `bell` emit, and `RobotSiteScreen` its `bell` emit and 30 s fault poll; a page still passing them keeps working (they fall through unused). Open faults stay on the Faults summary, Needs attention and Alerts.
|
|
823
|
+
- f9e6778: Robot live view: turn on hls.js low-latency mode so the 1.2x catch-up actually runs after a hiccup (without it hls.js never changes the playback rate).
|
|
824
|
+
- 63edcab: Robot live view: sit 6 s behind the newest picture instead of hls.js's default 12 s, catch up at up to 1.2x after a hiccup, jump forward past 14 s.
|
|
825
|
+
- dd238f3: Robot live view (PadBot S2): the camera's stray H.264 delimiters are now rewritten into real H.265 access-unit delimiters (after a 4-byte start code). The S2 sends VPS/SPS/PPS each in its own PES; without an H.265 delimiter hls.js 1.7.3 dropped the PPS from the init segment and Chrome decoded 0 frames (black picture, clock running).
|
|
826
|
+
- 24956ec: Robot live view: no more silent reload every ~6 s. PadBot's video URL expiry comes without a time zone ("2026-09-26 16:26:02"); read as local time it was already past, so the panel fetched a new URL every 5 s forever. An expiry that has no zone, or is under a minute away, no longer schedules a re-fetch (the stream's own failure still reconnects, with capped backoff, then Retry). The no-picture and stall watchdogs no longer count while the page is hidden.
|
|
827
|
+
- dd42b0d: Robot live view: the PadBot S2's EZVIZ camera puts an H.264 access-unit delimiter in front of every H.265 frame, which made Chrome refuse every frame (decode error, black picture). The player now neutralises those units in each segment before playback, so the picture shows. The "couldn't be decoded" message no longer blames EZVIZ encryption (disproved).
|
|
828
|
+
- 9673bed: Robots: live view says honestly when the camera video cannot be decoded (MEDIA_ERR_DECODE, e.g. EZVIZ video encryption switched on) instead of retrying: at most two hls.js recoveries, then a plain message with Retry. The message no longer blames the browser when the decoder reports support.
|
|
829
|
+
- 232eb16: Robots: a robot's name tag stays inside the plan. Near the right edge it sits to the robot's left, and near the top it goes below: on robot detail (name + status) and on the building map, as the mini-map already did. Before, the S2 docked at the top-right showed only "HERO-OT".
|
|
830
|
+
- d7575d9: Robot detail: each command's outcome line names its command ("Return to charger: …"), and a fault that was already showing when the command was sent is reported as an earlier fault to clear first, not as the new command's result.
|
|
831
|
+
- e13cc29: Fix the robot pages crashing in the browser on 4.50.0/4.51.0 ("process is not defined" / "Couldn't resolve component default"). A parameter named `measure` in `utils/rmf-editor.ts` was picked up by Nuxt auto-imports as the `measure` export of `utils/theme-aa-ledger.ts`, whose top-level command-line check read `process.argv`. The parameter is renamed and the ledger's check is guarded; a new test evaluates every util in a browser-like scope with the auto-imports injected.
|
|
832
|
+
- 05dadfc: Robots: Start patrol (security) picks the route from the routes on the robot's current map and the confirm names the route, its stops and the map; after "accepted" the detail watches the robot for 30 s and says "<robot> started <route>." or "<robot> didn't start - <reason>" (kept on screen); Resume patrol shows only when a patrol is paused or unfinished; Cancel only while a job is running; Start patrol hidden while patrolling.
|
|
833
|
+
- 0fc34f8: HERO robot rules for the mobile apps: `scripts/sync-robot-rules.mjs` writes a generated, hash-stamped copy of `utils/robot-display.ts` (+ lift-queue rows, HERO_PALETTE) and shared test vectors into iClean365, iMA365 and iSecure365. Repo tooling only; nothing a consuming app loads changes.
|
|
834
|
+
- ec366e8: Robot maps: a robot that reports its floor but no position on the plan (Ounitech, now that its API reports the floor) is listed under the map ("can't be placed on the map, so it is listed but not drawn") instead of silently missing.
|
|
835
|
+
- 46c51a6: Robot detail: when the robot cannot be read, show the API's plain reason (e.g. "Ounitech rejected the robot list (sign-in) ...") under the title instead of only "Reconnecting". `mergeLiveStates` carries the optional `reason` from `/robots/states`.
|
|
836
|
+
- 8899156: Role editor: a service provider editing its roles on a client's site is no longer narrowed by the client's site module list. The site tier applies only when the site belongs to the organisation whose roles are being edited; otherwise the organisation's own list applies, as before.
|
|
837
|
+
- a9491ca: Create Role / Edit Role: a refused or failed save now shows "Couldn't save: <reason>" above the buttons (it was printed below the whole module list, out of sight). Catalogue snapshot test ties the dialog catalogues to core's full-catalogue create test.
|
|
838
|
+
- d19240e: The client's module list now governs the permission gates this package spreads into the staff apps. `useAreaPermission`, `useUnitPermission`, `useAttendancePermission`, `useBulletinBoardPermission`, `useScheduleTaskPermission`, `useServiceProviderPermission`, `useEquipmentItemPermission` and `useEquipmentManagementPermission` — 43 gates — now ask the deny list immediately before their `*` short-circuit, the order `DashboardMain` already uses. The apps closed the same hole in their own gates, but these are re-exported from here and an app PR could not reach them (40 of hygiene's 47 spread-in gates, 43 of landscape's 43, 38 of M&E's 45).
|
|
839
|
+
|
|
840
|
+
Inert and fails open: no client has an enforced list, so an absent, empty or unreadable list denies nothing. `useSettingsPermission` is deliberately untouched (`site-settings` is always allowed and can never be denied), as are the schedule gates that match any `*-schedule-mgmt` string.
|
|
841
|
+
|
|
842
|
+
- bf5c6ec: Print the entry pass with one large QR code instead of five. The pass carried
|
|
843
|
+
four small copies at the corners around a centre one so a strip could be cut
|
|
844
|
+
apart; the small copies were downscaled from the centre render and scanned
|
|
845
|
+
poorly on thermal paper. `createReceiptLayout` now draws a single 350px QR,
|
|
846
|
+
generated at the size it is printed, in the same band above the company name.
|
|
847
|
+
The `QrTemplatePreview` dialog shows the same single 260px QR so the preview
|
|
848
|
+
matches what comes out of the printer.
|
|
849
|
+
- a015eee: Robots: fix the site fleet capability tooltip line break (a literal line break inside a string broke every consuming app build on 4.56.0/4.57.0). The SFC compile test now compiles each component's script as well as its template, so this kind of break fails CI here instead of in the apps.
|
|
850
|
+
- 5628ad6: Service Providers screen: extracted the service-type code-to-label lookup (the one behind "security_agency" showing as "Security") into a tested pure function, with unit tests covering every known service type code.
|
|
851
|
+
- 9571194: Service Providers screen: the TYPE column still showed the raw code (e.g. "security_agency" instead of "Security") for every row, because the API's `nature` field is not free text - it mirrors the same raw code as `type` - and the label lookup treated any truthy `nature` as an override. Only treat `nature` as free text when it is not itself a known service type code.
|
|
852
|
+
- febb72a: Inventory supplier picker: show "Add ... as a new supplier" also when the supplier list is empty.
|
|
853
|
+
- c80ea00: Inventory supplier picker: one "Add ... as a new supplier" row (it showed twice when nothing matched).
|
|
854
|
+
- 04e8637: Fix the context switcher showing a tick against two sites at once.
|
|
855
|
+
|
|
856
|
+
Not a comparison bug: `selected === item.value` was true for both rows because
|
|
857
|
+
both rows were the same site. The seven service apps build this list from
|
|
858
|
+
`customer-sites` with `value: i.site`, and a customer-sites row is an ENGAGEMENT,
|
|
859
|
+
not a site — one site can carry more than one. Production has two active rows for
|
|
860
|
+
the same site, identical except for a stale `name` snapshot.
|
|
861
|
+
|
|
862
|
+
The switcher now offers each destination once. First occurrence wins, so the
|
|
863
|
+
surviving label is the current one rather than the stale snapshot. Items with no
|
|
864
|
+
`value` are kept — they cannot collide, and dropping them would hide a malformed
|
|
865
|
+
row.
|
|
866
|
+
|
|
867
|
+
- de70cb1: Table card search (SOA Mgmt, Online Billing, Facility Mgmt, Incident Reports and the other `TableMain` screens with search): the box beside the title-row button no longer shrinks to "Se". It is the design's search field, 260px wide, the same 40px height and radius as the button, with the magnifier in front and a clear button once something is typed.
|
|
868
|
+
- f6d2f6d: Building Management → Units import dialog: the instructions no longer ask for a block number (the import takes it from the chosen building).
|
|
869
|
+
- b541a00: `useMember()` now returns `getDeniedModules`. It was declared but never returned, so the module deny list never reached the web apps (the router guard failed open) and the Client details role dialog threw and never stopped loading.
|
|
870
|
+
- b6d2cae: List a contractor's members in the Visitor Management preview dialog.
|
|
871
|
+
|
|
872
|
+
The list row has counted them beside the name (`( +2 members)`) for as long as the field has existed, with nowhere to go for the names themselves. The preview now draws each member as an inset card - `Card/MemberInfoSummary`'s `--hover` on `--border` at `--r-inner`, so a member looks the same here as in the form that created it - showing the name plus whichever of NRIC, contact and vehicle number they carry.
|
|
873
|
+
|
|
874
|
+
- Both NRICs in the preview - the visitor's and each member's - are revealed by pressing the masked number, which carries an eye as its affordance. It replaces a hover tooltip, which gave no sign there was anything to reach and could not be reached at all on a touch screen. The eye alone was not enough on its own: a 14-16px glyph is well under the 24px target floor, and the layer's 32px `AppButton variant="icon"` would have made the NRIC line half again the height of the Contact line beneath it. Pressing the digits gives a target the width of the value at no cost to the rhythm, takes a tab stop, and reads the right way round. Each member reveals separately, and all of them clear when the dialog closes.
|
|
875
|
+
- Read off the raw list row rather than `selectedVisitorObject`, which filters itself to `typeFieldMap` plus five fixed keys; widening that computed would change what four other dialogs on the screen see.
|
|
876
|
+
- Not gated on the Guests tab, because the row's own member count is not either.
|
|
877
|
+
- Blank entries are dropped. The payload carries `""` for every detail nobody filled in, so a member with neither a name nor any detail would otherwise render as an empty box.
|
|
878
|
+
- `plateNumber` is read defensively: the API returns it on `members[]`, but neither `TMemberInfo` here nor `TContractorMembers` in API-core's visitor model (name and nric only) declares it. The types have drifted from the document and from each other; reconciling them is separate work.
|
|
879
|
+
|
|
880
|
+
- 88030ea: Visitor Management search box: waits 350 ms after typing and searches from 2 characters (trimmed), so a burst of typing, a paste or a scanner no longer leaves the table showing the first letter's results; a late reply for an older search is never shown; a new search goes back to page 1; clearing the box resets at once; the address-bar sync no longer puts back an older value over letters typed since; the empty table says "No visitors match '<search>'"; Download Report uses the same search as the table.
|
|
881
|
+
- 1832d39: Visitor Mgmt: the Unregistered tab now appears on first load and after switching site, without a hard refresh. `useANPRSettings` takes a ref or getter for the site and keeps one entry per site; the Visitor Mgmt page reads its own site's ANPR switches instead of relying on the layout's plate toast, which kept the first site's switches after a site switch. A plate-toast link to the Unregistered tab is no longer bounced to Registered while the switches are still loading.
|
|
882
|
+
- 25dae49: Worker nouns: fall back to the deployment's `APP` code when the app sets no `appSlug` (hygiene, M&E, pest, pool, landscape), so hygiene shows Cleaners instead of the neutral Staff.
|
|
883
|
+
|
|
3
884
|
## 4.97.0
|
|
4
885
|
|
|
5
886
|
### Minor Changes
|
|
@@ -2082,11 +2963,11 @@
|
|
|
2082
2963
|
`utils/console-tier.ts` + `composables/useConsoleTier.ts` mirror the server's
|
|
2083
2964
|
own rule from two endpoints the console already calls, unprojected:
|
|
2084
2965
|
|
|
2085
|
-
|
|
2086
|
-
|
|
2966
|
+
GET /api/members/user/:user/app/admin the Seven365 staff membership
|
|
2967
|
+
GET /api/roles/id/:role that membership's role document
|
|
2087
2968
|
|
|
2088
|
-
|
|
2089
|
-
|
|
2969
|
+
owner = member.type === "admin" && role.type === "admin" && role.default === true
|
|
2970
|
+
staff = member.type === "admin" && role.type === "admin"
|
|
2090
2971
|
|
|
2091
2972
|
`role.default` is the marker because it is the only property of a platform
|
|
2092
2973
|
staff role no API caller can set - `role.controller.ts` validates create and
|