@7365admin1/layer-common 4.112.0 → 4.113.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
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# @iservice365/layer-common
|
|
2
2
|
|
|
3
|
+
## 4.113.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 92f08bca: Vehicle Mgmt: new "QR sticker" column (Linked / None / n of m) and an All / With sticker / Without sticker filter, shown only while the site's QR switch is on
|
|
8
|
+
|
|
3
9
|
## 4.112.0
|
|
4
10
|
|
|
5
11
|
### Minor Changes
|
|
@@ -48,9 +48,18 @@
|
|
|
48
48
|
|
|
49
49
|
<!-- Search joins the toolbar row rather than stacking a second one. -->
|
|
50
50
|
<template #prepend-additional>
|
|
51
|
+
<v-select v-if="qrEnabled" v-model="qrStickerFilter" :items="qrStickerFilterItems" label="QR sticker"
|
|
52
|
+
density="compact" variant="outlined" hide-details class="vehicle-qr-filter" style="max-width: 190px"
|
|
53
|
+
data-test="vehicle-qr-filter" />
|
|
51
54
|
<AppField v-model="searchInput" search compact placeholder="Search" klass="app-toolbar__search" />
|
|
52
55
|
</template>
|
|
53
56
|
|
|
57
|
+
<template #item.qrSticker="{ item }">
|
|
58
|
+
<v-chip size="small" variant="tonal" :color="qrStickerSummary(rowPlates(item)).color" data-test="vehicle-qr-cell">
|
|
59
|
+
{{ qrStickerSummary(rowPlates(item)).label }}
|
|
60
|
+
</v-chip>
|
|
61
|
+
</template>
|
|
62
|
+
|
|
54
63
|
<template #item.category="{ value }">
|
|
55
64
|
{{ formatValues("category", value) }}
|
|
56
65
|
</template>
|
|
@@ -439,7 +448,7 @@ import {
|
|
|
439
448
|
// `error.message` put ofetch's own developer string in the delete dialog:
|
|
440
449
|
// `[PUT] "/api/vehicles/6a9691f25648d7633171c9e3": 502`.
|
|
441
450
|
import { errorConverter } from '../utils/data';
|
|
442
|
-
import { stickerCodeHint } from '../utils/vehicle-qr-blank';
|
|
451
|
+
import { qrStickerSummary, stickerCodeHint } from '../utils/vehicle-qr-blank';
|
|
443
452
|
import { isPermissionRefusalError } from '../utils/permission-refusal';
|
|
444
453
|
|
|
445
454
|
|
|
@@ -456,9 +465,11 @@ const props = defineProps({
|
|
|
456
465
|
});
|
|
457
466
|
|
|
458
467
|
|
|
459
|
-
const headers = [
|
|
468
|
+
const headers = computed(() => [
|
|
460
469
|
{ title: "Name", value: "name" },
|
|
461
470
|
{ title: "Vehicle Numbers", value: "plates" },
|
|
471
|
+
// Only while the site's QR switch is on (like the print button).
|
|
472
|
+
...(qrEnabled.value ? [{ title: "QR sticker", value: "qrSticker", sortable: false }] : []),
|
|
462
473
|
{ title: "NRIC", value: "nric" },
|
|
463
474
|
{ title: "Block", value: "block.name" },
|
|
464
475
|
{ title: "Floor", value: "level.name" },
|
|
@@ -467,7 +478,7 @@ const headers = [
|
|
|
467
478
|
// { title: "Type", value: "type" },
|
|
468
479
|
// { title: "Status", value: "status" },
|
|
469
480
|
{ title: "", value: "action" },
|
|
470
|
-
]
|
|
481
|
+
])
|
|
471
482
|
|
|
472
483
|
const plateHeaders = [
|
|
473
484
|
{ title: "Plate Number", value: "plateNumber" },
|
|
@@ -546,6 +557,12 @@ const { getVehicles, deleteVehicle, formatVehicleStatus, approveVehicle, blockVe
|
|
|
546
557
|
// Vehicle QR stickers: the site's switch (Settings) decides whether the print
|
|
547
558
|
// and re-issue actions exist; only people who may update vehicles get them.
|
|
548
559
|
const qrEnabled = ref(false);
|
|
560
|
+
const qrStickerFilter = ref("");
|
|
561
|
+
const qrStickerFilterItems = [
|
|
562
|
+
{ title: "All", value: "" },
|
|
563
|
+
{ title: "With sticker", value: "with" },
|
|
564
|
+
{ title: "Without sticker", value: "without" },
|
|
565
|
+
];
|
|
549
566
|
const qrStickersOn = computed(() => qrEnabled.value && props.canUpdateVehicle);
|
|
550
567
|
const qrVehicleIds = ref<string[]>([]);
|
|
551
568
|
const qrReissuePlate = ref<TPlateNumber | null>(null);
|
|
@@ -838,6 +855,7 @@ const { data: getVehiclesReq, refresh: getVehiclesRefresh, pending: getVehiclesP
|
|
|
838
855
|
search: searchInput.value,
|
|
839
856
|
view: activeTab.value,
|
|
840
857
|
site: props.site,
|
|
858
|
+
qrSticker: qrEnabled.value ? qrStickerFilter.value : "",
|
|
841
859
|
}),
|
|
842
860
|
{
|
|
843
861
|
watch: [page],
|
|
@@ -1321,6 +1339,11 @@ watch(searchInput, () => {
|
|
|
1321
1339
|
debouncedSearch()
|
|
1322
1340
|
})
|
|
1323
1341
|
|
|
1342
|
+
watch(qrStickerFilter, () => {
|
|
1343
|
+
page.value = 1;
|
|
1344
|
+
getVehiclesRefresh();
|
|
1345
|
+
})
|
|
1346
|
+
|
|
1324
1347
|
// A new tab starts from its first page and loads straight away.
|
|
1325
1348
|
watch(activeTab, () => {
|
|
1326
1349
|
page.value = 1;
|
|
@@ -9,12 +9,13 @@ export default function useVehicle() {
|
|
|
9
9
|
site = "",
|
|
10
10
|
status = "",
|
|
11
11
|
view = "",
|
|
12
|
+
qrSticker = "",
|
|
12
13
|
} = {}) {
|
|
13
14
|
// `view` is the screen's tab: residents, season-passes or block-listed.
|
|
14
15
|
// Every status comes back unless `status` narrows it.
|
|
15
16
|
return await useNuxtApp().$api<Record<string, any>>("/api/vehicles", {
|
|
16
17
|
method: "GET",
|
|
17
|
-
query: { search, page, limit, order, type, category, site, status, view },
|
|
18
|
+
query: { search, page, limit, order, type, category, site, status, view, qrSticker },
|
|
18
19
|
});
|
|
19
20
|
}
|
|
20
21
|
|
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.
|
|
5
|
+
"version": "4.113.0",
|
|
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.",
|
package/types/people.d.ts
CHANGED
|
@@ -48,7 +48,7 @@ declare type TOccupancyRoleOption = {
|
|
|
48
48
|
description: string;
|
|
49
49
|
};
|
|
50
50
|
|
|
51
|
-
declare type TPlateNumber = { plateNumber: string, recNo?: string, _id?: string, status: TVehicleStatus, type: TVehicleType, anprCameras?: TVehicleAnprCamera[] };
|
|
51
|
+
declare type TPlateNumber = { plateNumber: string, recNo?: string, _id?: string, status: TVehicleStatus, type: TVehicleType, anprCameras?: TVehicleAnprCamera[], hasQrSticker?: boolean };
|
|
52
52
|
|
|
53
53
|
|
|
54
54
|
// `""` is the FORM's "nothing picked yet" - it is stripped from the payload
|
|
@@ -52,3 +52,12 @@ export const isBlankCode = (plate: unknown): boolean => /^([0-9A-Z]{4}-){5}[0-9A
|
|
|
52
52
|
export function plateLines(plate: string): string[] {
|
|
53
53
|
return isBlankCode(plate) ? [plate.slice(0, 14), plate.slice(15)] : [plate];
|
|
54
54
|
}
|
|
55
|
+
|
|
56
|
+
/** The list's "QR sticker" cell for one row: how many of its plates already hold a sticker. */
|
|
57
|
+
export function qrStickerSummary(plates: Array<{ hasQrSticker?: boolean }>): { label: string; color: string } {
|
|
58
|
+
const total = plates.length;
|
|
59
|
+
const linked = plates.filter((p) => p?.hasQrSticker === true).length;
|
|
60
|
+
if (!total || !linked) return { label: "None", color: "grey" };
|
|
61
|
+
if (linked === total) return { label: "Linked", color: "success" };
|
|
62
|
+
return { label: `${linked} of ${total}`, color: "warning" };
|
|
63
|
+
}
|