@7365admin1/layer-common 4.2.4 → 4.2.5-staging.271
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.
|
@@ -193,7 +193,6 @@ const today = computed(() => {
|
|
|
193
193
|
const yyyy = d.getFullYear();
|
|
194
194
|
const mm = String(d.getMonth() + 1).padStart(2, "0");
|
|
195
195
|
const dd = String(d.getDate()).padStart(2, "0");
|
|
196
|
-
console.log("${yyyy}-${mm}-${dd}", `${yyyy}-${mm}-${dd}`);
|
|
197
196
|
return `${yyyy}-${mm}-${dd}`;
|
|
198
197
|
});
|
|
199
198
|
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
<InputLabel class="text-capitalize" title="Block" required />
|
|
59
59
|
<v-autocomplete
|
|
60
60
|
v-model="vehicle.block"
|
|
61
|
-
:items="
|
|
61
|
+
:items="blockItems"
|
|
62
62
|
autocomplete="off"
|
|
63
63
|
:key="
|
|
64
64
|
blockListDataPending ? 'block-list-pending' : 'block-list-ready'
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
<InputLabel class="text-capitalize" title="Level" required />
|
|
77
77
|
<v-autocomplete
|
|
78
78
|
v-model="vehicle.level"
|
|
79
|
-
:items="
|
|
79
|
+
:items="levelItems"
|
|
80
80
|
autocomplete="off"
|
|
81
81
|
:key="
|
|
82
82
|
levelListDataPending ? 'level-list-pending' : 'level-list-ready'
|
|
@@ -93,7 +93,7 @@
|
|
|
93
93
|
<InputLabel class="text-capitalize" title="Unit" required />
|
|
94
94
|
<v-autocomplete
|
|
95
95
|
v-model="vehicle.unit"
|
|
96
|
-
:items="
|
|
96
|
+
:items="unitItems"
|
|
97
97
|
autocomplete="off"
|
|
98
98
|
:key="
|
|
99
99
|
unitListDataPending ? 'unit-list-pending' : 'unit-list-ready'
|
|
@@ -433,6 +433,13 @@ const prop = defineProps({
|
|
|
433
433
|
type: Object as PropType<Partial<TVehicle> | null>,
|
|
434
434
|
default: null,
|
|
435
435
|
},
|
|
436
|
+
// Id -> name for this record's block/level/unit, taken from the list row,
|
|
437
|
+
// which the API already resolved. Lets the dialog name a value its own
|
|
438
|
+
// dropdown cannot offer, instead of falling through to the raw id.
|
|
439
|
+
nameHints: {
|
|
440
|
+
type: Object as PropType<Record<string, string>>,
|
|
441
|
+
default: () => ({}),
|
|
442
|
+
},
|
|
436
443
|
plateNumberId: {
|
|
437
444
|
type: String,
|
|
438
445
|
default: "",
|
|
@@ -532,10 +539,110 @@ const shouldShowField = (fieldKey: string): boolean => {
|
|
|
532
539
|
return visibleFields?.includes(fieldKey);
|
|
533
540
|
};
|
|
534
541
|
|
|
542
|
+
/**
|
|
543
|
+
* Block / Level / Unit are ids bound to autocompletes whose `:items` come from
|
|
544
|
+
* the site's own lists. Vuetify renders the RAW model value when it is not in
|
|
545
|
+
* that list, which is how the Season Pass edit dialog came to show
|
|
546
|
+
* `6a31fd37917fb18ef1a21a0a` in all three fields instead of names.
|
|
547
|
+
*
|
|
548
|
+
* A Mongo id is never shown to a person. The name is available: the vehicle
|
|
549
|
+
* LIST resolves it server-side (`getVehicles` $lookup's buildings /
|
|
550
|
+
* building-levels / building-units) while the edit dialog's own fetch
|
|
551
|
+
* (`getSpecificVehicleById`, a plain findOne) returns raw ids. `VehicleManagement`
|
|
552
|
+
* therefore hands the already-resolved names down as `nameHints`, an id -> name
|
|
553
|
+
* map taken from the list row - no extra request, no change to what is stored.
|
|
554
|
+
*
|
|
555
|
+
* Three cases, in order:
|
|
556
|
+
* - the list has not arrived yet -> "Loading unit..."
|
|
557
|
+
* - a name is known but not in the list -> "Tower Ten (no longer selectable)"
|
|
558
|
+
* - no name can be found at all -> plain sentence, still no id
|
|
559
|
+
*
|
|
560
|
+
* Measured on staging, both non-loading cases are real. A block drops out of
|
|
561
|
+
* the picker once it has no ACTIVE units left (`getBuildingWithUnitsList` lists
|
|
562
|
+
* only buildings that still have units), so "Tower Ten" is a live building the
|
|
563
|
+
* form cannot offer; and a stored unit id can point at a building unit that has
|
|
564
|
+
* since been deleted, which no name lookup can answer.
|
|
565
|
+
*
|
|
566
|
+
* The stored value is left untouched - the field still submits the same id, so
|
|
567
|
+
* saving an unrelated edit does not silently rewrite someone's unit.
|
|
568
|
+
*/
|
|
569
|
+
function withUnresolved(
|
|
570
|
+
items: TDefaultOptionObj[],
|
|
571
|
+
value: unknown,
|
|
572
|
+
pending: boolean,
|
|
573
|
+
noun: string,
|
|
574
|
+
nameHints: Record<string, string>
|
|
575
|
+
): TDefaultOptionObj[] {
|
|
576
|
+
const id = idOf(value);
|
|
577
|
+
if (!id || items.some((item) => item.value === id)) return items;
|
|
578
|
+
|
|
579
|
+
const name = nameHints[id];
|
|
580
|
+
|
|
581
|
+
// The id belongs in a log, not on screen. Support needs it; the property
|
|
582
|
+
// manager does not.
|
|
583
|
+
if (!pending && !name) {
|
|
584
|
+
console.warn(`[VehicleForm] no name resolved for ${noun} id ${id}`);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
return [
|
|
588
|
+
{
|
|
589
|
+
title: pending
|
|
590
|
+
? `Loading ${noun}...`
|
|
591
|
+
: name
|
|
592
|
+
? `${name} (no longer selectable)`
|
|
593
|
+
: `This ${noun} is no longer on the site list`,
|
|
594
|
+
value: id,
|
|
595
|
+
},
|
|
596
|
+
...items,
|
|
597
|
+
];
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/** Block/level/unit arrive either as a raw id string or as a `{_id, name}` object. */
|
|
601
|
+
function idOf(value: unknown): string {
|
|
602
|
+
return typeof value === "string"
|
|
603
|
+
? value
|
|
604
|
+
: String((value as any)?._id ?? "");
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
const blockItems = computed(() =>
|
|
608
|
+
withUnresolved(
|
|
609
|
+
blocksArray.value,
|
|
610
|
+
vehicle.block,
|
|
611
|
+
blockListDataPending.value,
|
|
612
|
+
"block",
|
|
613
|
+
prop.nameHints
|
|
614
|
+
)
|
|
615
|
+
);
|
|
616
|
+
|
|
617
|
+
const levelItems = computed(() =>
|
|
618
|
+
withUnresolved(
|
|
619
|
+
levelsArray.value,
|
|
620
|
+
vehicle.level,
|
|
621
|
+
levelListDataPending.value,
|
|
622
|
+
"level",
|
|
623
|
+
prop.nameHints
|
|
624
|
+
)
|
|
625
|
+
);
|
|
626
|
+
|
|
627
|
+
const unitItems = computed(() =>
|
|
628
|
+
withUnresolved(
|
|
629
|
+
unitsArray.value,
|
|
630
|
+
vehicle.unit,
|
|
631
|
+
unitListDataPending.value,
|
|
632
|
+
"unit",
|
|
633
|
+
prop.nameHints
|
|
634
|
+
)
|
|
635
|
+
);
|
|
636
|
+
|
|
535
637
|
const searchUnitName = computed(() => {
|
|
638
|
+
// Same rule as the fields: never put a raw id in front of a person. The
|
|
639
|
+
// dialog title used to fall through to `vehicle.unit`, so a deleted unit
|
|
640
|
+
// titled the panel with its ObjectId.
|
|
641
|
+
const id = idOf(vehicle.unit);
|
|
536
642
|
return (
|
|
537
|
-
unitsArray.value.find((u) => u.value ===
|
|
538
|
-
|
|
643
|
+
unitsArray.value.find((u) => u.value === id)?.title ||
|
|
644
|
+
prop.nameHints[id] ||
|
|
645
|
+
"-"
|
|
539
646
|
);
|
|
540
647
|
});
|
|
541
648
|
|
|
@@ -1047,10 +1154,12 @@ const resetVehicleDetails = () => {
|
|
|
1047
1154
|
};
|
|
1048
1155
|
|
|
1049
1156
|
function handleCloseMatchDialog() {
|
|
1157
|
+
// Close only. This used to null `vehicle.unit` whenever the panel had found
|
|
1158
|
+
// matches, so backing out of the picker with the header X wiped a Unit the
|
|
1159
|
+
// user had chosen BEFORE opening it and painted the field required-in-red.
|
|
1160
|
+
// The panel's own close button never did that; the two now agree. Backing out
|
|
1161
|
+
// of a picker is not a decision to discard what you already had.
|
|
1050
1162
|
showMatchingPeopleDialog.value = false;
|
|
1051
|
-
if (matchingPeople.value.length > 0) {
|
|
1052
|
-
vehicle.unit = null;
|
|
1053
|
-
}
|
|
1054
1163
|
}
|
|
1055
1164
|
|
|
1056
1165
|
onMounted(() => {
|
|
@@ -85,13 +85,13 @@
|
|
|
85
85
|
|
|
86
86
|
<v-dialog v-model="dialog.createVehicle" v-if="vehicleType" width="450" persistent>
|
|
87
87
|
<VehicleForm :type="vehicleType" :mode="mode" :vehicle-data="selectedVehicleObject"
|
|
88
|
-
:plate-number-id="selectedPlateNumberId" @back="handleBackToSelection" @done="handleAddVehicleComplete"
|
|
88
|
+
:plate-number-id="selectedPlateNumberId" :name-hints="nameHints" @back="handleBackToSelection" @done="handleAddVehicleComplete"
|
|
89
89
|
:org="org" :site="props.site" @close:all="handleCloseAll" />
|
|
90
90
|
</v-dialog>
|
|
91
91
|
|
|
92
92
|
<v-dialog v-model="dialog.updateVehicle" v-if="vehicleType" width="450" persistent>
|
|
93
93
|
<VehicleForm :type="vehicleType" :mode="mode" :vehicle-data="selectedPlateNumberObject"
|
|
94
|
-
:plate-number-id="selectedPlateNumberId" @back="handleBackToSelection" @done="handleAddVehicleComplete"
|
|
94
|
+
:plate-number-id="selectedPlateNumberId" :name-hints="nameHints" @back="handleBackToSelection" @done="handleAddVehicleComplete"
|
|
95
95
|
:org="org" :site="props.site" @close:all="handleCloseAll" @close="handleCloseAll" />
|
|
96
96
|
</v-dialog>
|
|
97
97
|
|
|
@@ -371,6 +371,34 @@ const selectedVehicleObject = computed(() => {
|
|
|
371
371
|
return items.value.find(item => item?._id === selectedVehicleId.value) || {}
|
|
372
372
|
})
|
|
373
373
|
|
|
374
|
+
/**
|
|
375
|
+
* The edit dialog fetches its record with `getSpecificVehicleById`, a plain
|
|
376
|
+
* findOne, so block/level/unit come back as raw ObjectIds. The LIST does the
|
|
377
|
+
* work already - `getVehicles` $lookup's buildings, building-levels and
|
|
378
|
+
* building-units, which is why this table can print "Tower Ten" for the same
|
|
379
|
+
* record. Hand those resolved names down so the dialog never has to fall back
|
|
380
|
+
* to an id it cannot name.
|
|
381
|
+
*
|
|
382
|
+
* Id -> name, not the objects themselves: the form binds ids, and passing a
|
|
383
|
+
* `{_id, name}` object into its autocompletes is what broke it before.
|
|
384
|
+
*/
|
|
385
|
+
const nameHints = computed<Record<string, string>>(() => {
|
|
386
|
+
const plateId = selectedPlateNumberId.value;
|
|
387
|
+
const row: any =
|
|
388
|
+
items.value.find(
|
|
389
|
+
(r: any) =>
|
|
390
|
+
r?._id === plateId ||
|
|
391
|
+
(Array.isArray(r?.plates) && r.plates.some((p: any) => p?._id === plateId))
|
|
392
|
+
) || selectedVehicleObject.value;
|
|
393
|
+
|
|
394
|
+
const hints: Record<string, string> = {};
|
|
395
|
+
for (const key of ["block", "level", "unit"]) {
|
|
396
|
+
const field = row?.[key];
|
|
397
|
+
if (field?._id && field?.name) hints[String(field._id)] = String(field.name);
|
|
398
|
+
}
|
|
399
|
+
return hints;
|
|
400
|
+
})
|
|
401
|
+
|
|
374
402
|
const { data: getVehiclesReq, refresh: getVehiclesRefresh, pending: getVehiclesPending } =
|
|
375
403
|
await useLazyAsyncData(
|
|
376
404
|
`get-all-vehicles-${props.site}`,
|
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.2.
|
|
5
|
+
"version": "4.2.5-staging.271",
|
|
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.",
|