@7365admin1/layer-common 4.39.0 → 4.39.1
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
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 4.39.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b1c2177: 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.
|
|
8
|
+
|
|
3
9
|
## 4.39.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -1447,11 +1453,11 @@
|
|
|
1447
1453
|
`utils/console-tier.ts` + `composables/useConsoleTier.ts` mirror the server's
|
|
1448
1454
|
own rule from two endpoints the console already calls, unprojected:
|
|
1449
1455
|
|
|
1450
|
-
|
|
1451
|
-
|
|
1456
|
+
GET /api/members/user/:user/app/admin the Seven365 staff membership
|
|
1457
|
+
GET /api/roles/id/:role that membership's role document
|
|
1452
1458
|
|
|
1453
|
-
|
|
1454
|
-
|
|
1459
|
+
owner = member.type === "admin" && role.type === "admin" && role.default === true
|
|
1460
|
+
staff = member.type === "admin" && role.type === "admin"
|
|
1455
1461
|
|
|
1456
1462
|
`role.default` is the marker because it is the only property of a platform
|
|
1457
1463
|
staff role no API caller can set - `role.controller.ts` validates create and
|
|
@@ -1502,8 +1502,17 @@ const { org: orgId, site: siteId } = route.params as {
|
|
|
1502
1502
|
site: string;
|
|
1503
1503
|
};
|
|
1504
1504
|
|
|
1505
|
-
|
|
1505
|
+
// The page reads its own site's switches rather than relying on the layout's
|
|
1506
|
+
// toast to have fetched them - that copy could belong to the previous site.
|
|
1507
|
+
const {
|
|
1508
|
+
enableUnregistered,
|
|
1509
|
+
loaded: anprSettingsLoaded,
|
|
1510
|
+
ensureANPRSettings,
|
|
1511
|
+
} = useANPRSettings(siteId);
|
|
1506
1512
|
const enableUnregisteredTab = computed(() => enableUnregistered.value);
|
|
1513
|
+
ensureANPRSettings(true).catch(() => {
|
|
1514
|
+
// No settings = no Unregistered tab, same as before.
|
|
1515
|
+
});
|
|
1507
1516
|
|
|
1508
1517
|
const {
|
|
1509
1518
|
getVisitors,
|
|
@@ -2933,7 +2942,11 @@ watch([searchInput, dateFrom, dateTo, filterTypes, activeTab], () => {
|
|
|
2933
2942
|
const syncVisitorFiltersFromRoute = () => {
|
|
2934
2943
|
const requestedTab = (route.query.tab as string) || "registered";
|
|
2935
2944
|
const nextTab =
|
|
2936
|
-
|
|
2945
|
+
// Until the switches are read, keep a requested Unregistered tab (the plate
|
|
2946
|
+
// toast links here) instead of bouncing it to Registered.
|
|
2947
|
+
(anprSettingsLoaded.value &&
|
|
2948
|
+
!enableUnregisteredTab.value &&
|
|
2949
|
+
requestedTab === "unregistered") ||
|
|
2937
2950
|
(!showResidentTransactionsTab.value && requestedTab === "resident-transactions")
|
|
2938
2951
|
? "registered"
|
|
2939
2952
|
: requestedTab;
|
|
@@ -2992,9 +3005,9 @@ const { socketUnregisteredVisitorTrigger, visitorSocketData } =
|
|
|
2992
3005
|
useVisitorSocket();
|
|
2993
3006
|
|
|
2994
3007
|
watch(
|
|
2995
|
-
enableUnregisteredTab,
|
|
2996
|
-
(enabled) => {
|
|
2997
|
-
if (!enabled && activeTab.value === "unregistered") {
|
|
3008
|
+
[enableUnregisteredTab, anprSettingsLoaded],
|
|
3009
|
+
([enabled]) => {
|
|
3010
|
+
if (!enabled && anprSettingsLoaded.value && activeTab.value === "unregistered") {
|
|
2998
3011
|
activeTab.value = "registered";
|
|
2999
3012
|
}
|
|
3000
3013
|
},
|
|
@@ -137,8 +137,9 @@ const config = useRuntimeConfig();
|
|
|
137
137
|
const publicConfig = config.public as Record<string, string | undefined>;
|
|
138
138
|
const route = useRoute();
|
|
139
139
|
const { tab } = useVisitor();
|
|
140
|
+
// A getter, not the string: this component outlives site switches.
|
|
140
141
|
const { enableUnregistered, ensureANPRSettings } = useANPRSettings(
|
|
141
|
-
props.siteId
|
|
142
|
+
() => props.siteId
|
|
142
143
|
);
|
|
143
144
|
const enableUnregisteredSnackbar = computed(() => enableUnregistered.value);
|
|
144
145
|
const { socketUnregisteredVisitorTrigger, visitorSocketData } =
|
|
@@ -1,19 +1,50 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { computed, toValue, type MaybeRefOrGetter } from "vue";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* A site's ANPR switches (e.g. "Enable Unregistered Vehicles"), shared by every
|
|
5
|
+
* caller on the page: the Visitor Mgmt tabs, the plate toast in the layout, and
|
|
6
|
+
* Site Settings.
|
|
7
|
+
*
|
|
8
|
+
* `siteId` may be a ref or a getter. The layout's toast lives across site
|
|
9
|
+
* switches, so a plain string captured once kept answering for the FIRST site
|
|
10
|
+
* it saw - and the Visitor Mgmt page never fetched at all, so its
|
|
11
|
+
* "Unregistered" tab showed only when that stale copy happened to be for the
|
|
12
|
+
* right site (i.e. after a hard refresh on the page itself).
|
|
13
|
+
*
|
|
14
|
+
* One map keyed by site id: a key that is present means "fetched", even when
|
|
15
|
+
* the site has no settings yet (stored as `{}`).
|
|
16
|
+
*/
|
|
17
|
+
export default function (siteId?: MaybeRefOrGetter<string | undefined>) {
|
|
18
|
+
const site = computed(() => toValue(siteId) || "");
|
|
5
19
|
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
() =>
|
|
20
|
+
const bySite = useState<Record<string, Record<string, any>>>(
|
|
21
|
+
"anpr-settings-by-site",
|
|
22
|
+
() => ({})
|
|
9
23
|
);
|
|
10
24
|
|
|
25
|
+
const anprSettings = computed<Record<string, any> | null>({
|
|
26
|
+
get: () => bySite.value[site.value] ?? null,
|
|
27
|
+
set: (value) => {
|
|
28
|
+
bySite.value = { ...bySite.value, [site.value]: value ?? {} };
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
/** True once this site's settings have been read (or saved) at least once. */
|
|
33
|
+
const loaded = computed(() => site.value in bySite.value);
|
|
34
|
+
|
|
11
35
|
const enableUnregistered = computed(
|
|
12
36
|
() => anprSettings.value?.ANPRSwitches?.enableUnregistered === true
|
|
13
37
|
);
|
|
14
38
|
|
|
39
|
+
function store(forSite: unknown, response: Record<string, any> | null) {
|
|
40
|
+
const key = String(forSite ?? "");
|
|
41
|
+
if (!key) return;
|
|
42
|
+
bySite.value = { ...bySite.value, [key]: response ?? {} };
|
|
43
|
+
}
|
|
44
|
+
|
|
15
45
|
async function fetchANPRSettings(data?: any) {
|
|
16
|
-
const payload =
|
|
46
|
+
const payload =
|
|
47
|
+
data ?? (site.value ? { site: site.value, userType: "site" } : undefined);
|
|
17
48
|
|
|
18
49
|
if (!payload) {
|
|
19
50
|
return anprSettings.value;
|
|
@@ -27,23 +58,21 @@ export default function (siteId?: string) {
|
|
|
27
58
|
}
|
|
28
59
|
);
|
|
29
60
|
|
|
30
|
-
|
|
31
|
-
anprSettings.value = response;
|
|
32
|
-
}
|
|
61
|
+
store(payload.site, response);
|
|
33
62
|
|
|
34
63
|
return response;
|
|
35
64
|
}
|
|
36
65
|
|
|
37
66
|
async function ensureANPRSettings(force = false) {
|
|
38
|
-
if (!
|
|
67
|
+
if (!site.value) {
|
|
39
68
|
return anprSettings.value;
|
|
40
69
|
}
|
|
41
70
|
|
|
42
|
-
if (!force &&
|
|
71
|
+
if (!force && loaded.value) {
|
|
43
72
|
return anprSettings.value;
|
|
44
73
|
}
|
|
45
74
|
|
|
46
|
-
return await fetchANPRSettings({ site:
|
|
75
|
+
return await fetchANPRSettings({ site: site.value, userType: "site" });
|
|
47
76
|
}
|
|
48
77
|
|
|
49
78
|
async function getANPRSettingBySite(data: any) {
|
|
@@ -60,7 +89,7 @@ export default function (siteId?: string) {
|
|
|
60
89
|
);
|
|
61
90
|
|
|
62
91
|
if (response) {
|
|
63
|
-
|
|
92
|
+
store(data?.site, response);
|
|
64
93
|
}
|
|
65
94
|
|
|
66
95
|
return response;
|
|
@@ -68,6 +97,7 @@ export default function (siteId?: string) {
|
|
|
68
97
|
|
|
69
98
|
return {
|
|
70
99
|
anprSettings,
|
|
100
|
+
loaded,
|
|
71
101
|
enableUnregistered,
|
|
72
102
|
ensureANPRSettings,
|
|
73
103
|
getANPRSettingBySite,
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@7365admin1/layer-common",
|
|
3
3
|
"license": "MIT",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "4.39.
|
|
5
|
+
"version": "4.39.1",
|
|
6
6
|
"author": "7365admin1",
|
|
7
7
|
"main": "./nuxt.config.ts",
|
|
8
8
|
"//files": "What a consumer extending this layer actually loads. Without this npm ships the whole working tree - the changesets, the CI workflows, the render harness in tools/ and any scratch directory that happened to exist at publish time. Nuxt resolves a layer by directory, so every runtime directory below has to stay listed; adding a new top-level runtime directory means adding it here too.",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"build": "nuxt build .playground",
|
|
37
37
|
"generate": "nuxt generate .playground",
|
|
38
38
|
"preview": "nuxt preview .playground",
|
|
39
|
-
"test": "esbuild composables/useVisitorSocket.ts --format=esm --outfile=test/.build/useVisitorSocket.mjs --log-level=error && esbuild composables/usePlateToastWindow.ts --format=esm --outfile=test/.build/usePlateToastWindow.mjs --log-level=error && esbuild utils/client-invite-modules.ts --bundle --platform=node --format=esm --outfile=test/.build/client-invite-modules.mjs --log-level=error && esbuild utils/robot-display.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-display.mjs --log-level=error && esbuild utils/building-map.ts --bundle --platform=node --format=esm --outfile=test/.build/building-map.mjs --log-level=error && esbuild utils/robot-building.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-building.mjs --log-level=error && esbuild utils/robot-fleet-core.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-fleet-core.mjs --log-level=error && node --experimental-strip-types --test \"test/*.test.mjs\" && yarn test:units",
|
|
39
|
+
"test": "esbuild composables/useANPRSettings.ts --format=esm --outfile=test/.build/useANPRSettings.mjs --log-level=error && esbuild composables/useVisitorSocket.ts --format=esm --outfile=test/.build/useVisitorSocket.mjs --log-level=error && esbuild composables/usePlateToastWindow.ts --format=esm --outfile=test/.build/usePlateToastWindow.mjs --log-level=error && esbuild utils/client-invite-modules.ts --bundle --platform=node --format=esm --outfile=test/.build/client-invite-modules.mjs --log-level=error && esbuild utils/robot-display.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-display.mjs --log-level=error && esbuild utils/building-map.ts --bundle --platform=node --format=esm --outfile=test/.build/building-map.mjs --log-level=error && esbuild utils/robot-building.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-building.mjs --log-level=error && esbuild utils/robot-fleet-core.ts --bundle --platform=node --format=esm --outfile=test/.build/robot-fleet-core.mjs --log-level=error && node --experimental-strip-types --test \"test/*.test.mjs\" && yarn test:units",
|
|
40
40
|
"test:units": "node --experimental-strip-types --test \"utils/*.test.ts\"",
|
|
41
41
|
"release": "yarn run build && changeset publish"
|
|
42
42
|
},
|