A serial the account does not list is still a serial worth showing
The dropdown could only show a serial it had an option for. A remembered one the account does not report — a charger imported before the account was linked, one the cloud is quiet about today — selected nothing, so the field sat blank while that serial was the one every command went to. Nothing on screen said which charger was being driven, or let it be corrected. The field now falls back to the text box in that case, the way it already does when the account lists no chargers at all, and shows the serial actually in force. One link switches between picking and typing, so a serial off the list is not a dead end and the list is not the only way in; coming back to it lands on a charger the list holds rather than blanking the dropdown again. Which of the two is showing is decided when the list arrives, not on every keystroke — recomputing it as the serial is typed would turn the text box into a dropdown mid-word, the moment what had been typed happened to match. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
7ccd785742
commit
1aedc0ddc7
@@ -82,6 +82,8 @@
|
|||||||
"connected": "Forbundet",
|
"connected": "Forbundet",
|
||||||
"disconnected": "Offline",
|
"disconnected": "Offline",
|
||||||
"serialPlaceholder": "Laderens serienummer (f.eks. A5191-XXXXXXXX)",
|
"serialPlaceholder": "Laderens serienummer (f.eks. A5191-XXXXXXXX)",
|
||||||
|
"enterSerial": "Indtast et serienummer i stedet",
|
||||||
|
"pickSerial": "Vælg blandt dine ladere",
|
||||||
"refresh": "Opdater",
|
"refresh": "Opdater",
|
||||||
"status": "Stik",
|
"status": "Stik",
|
||||||
"meter": "Energi",
|
"meter": "Energi",
|
||||||
|
|||||||
@@ -68,6 +68,8 @@
|
|||||||
"connected": "Connected",
|
"connected": "Connected",
|
||||||
"disconnected": "Offline",
|
"disconnected": "Offline",
|
||||||
"serialPlaceholder": "Charger serial (e.g. A5191-XXXXXXXX)",
|
"serialPlaceholder": "Charger serial (e.g. A5191-XXXXXXXX)",
|
||||||
|
"enterSerial": "Enter a serial instead",
|
||||||
|
"pickSerial": "Choose from your chargers",
|
||||||
"refresh": "Refresh",
|
"refresh": "Refresh",
|
||||||
"status": "Connector",
|
"status": "Connector",
|
||||||
"meter": "Energy",
|
"meter": "Energy",
|
||||||
|
|||||||
@@ -84,6 +84,8 @@
|
|||||||
"connected": "Połączono",
|
"connected": "Połączono",
|
||||||
"disconnected": "Offline",
|
"disconnected": "Offline",
|
||||||
"serialPlaceholder": "Numer seryjny ładowarki (np. A5191-XXXXXXXX)",
|
"serialPlaceholder": "Numer seryjny ładowarki (np. A5191-XXXXXXXX)",
|
||||||
|
"enterSerial": "Wpisz numer seryjny zamiast tego",
|
||||||
|
"pickSerial": "Wybierz ze swoich ładowarek",
|
||||||
"refresh": "Odśwież",
|
"refresh": "Odśwież",
|
||||||
"status": "Złącze",
|
"status": "Złącze",
|
||||||
"meter": "Energia",
|
"meter": "Energia",
|
||||||
|
|||||||
@@ -99,6 +99,15 @@ async function loadCtlMode() {
|
|||||||
// a list; without them (account not linked, or the cloud unreachable) the field
|
// a list; without them (account not linked, or the cloud unreachable) the field
|
||||||
// stays a plain text box so a serial can still be typed in by hand.
|
// stays a plain text box so a serial can still be typed in by hand.
|
||||||
const chargers = ref([]);
|
const chargers = ref([]);
|
||||||
|
// Typing the serial rather than picking it. A dropdown can only show a serial it
|
||||||
|
// has an option for, so a remembered serial the account does not report — a
|
||||||
|
// charger imported before the account was linked, one the cloud is quiet about
|
||||||
|
// today — would render as a blank field with no way to read or fix it. Falling
|
||||||
|
// back to the text box shows the serial that is actually in force.
|
||||||
|
const manualSerial = ref(false);
|
||||||
|
|
||||||
|
const serialInList = computed(() => chargers.value.some((c) => c.sn === ctlSerial.value.trim()));
|
||||||
|
const pickingFromList = computed(() => chargers.value.length > 0 && !manualSerial.value);
|
||||||
|
|
||||||
async function loadChargers() {
|
async function loadChargers() {
|
||||||
try {
|
try {
|
||||||
@@ -111,6 +120,22 @@ async function loadChargers() {
|
|||||||
if (!ctlSerial.value.trim() && chargers.value.length) {
|
if (!ctlSerial.value.trim() && chargers.value.length) {
|
||||||
ctlSerial.value = chargers.value[0].sn;
|
ctlSerial.value = chargers.value[0].sn;
|
||||||
}
|
}
|
||||||
|
// Decided when the list arrives rather than on every keystroke: recomputing it
|
||||||
|
// as the serial is typed would swap the text box for a dropdown mid-word, the
|
||||||
|
// moment what had been typed happened to match a charger.
|
||||||
|
manualSerial.value = chargers.value.length > 0 && !serialInList.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
// One control for both directions, so a serial that is not on the account is
|
||||||
|
// never a dead end and the list is never the only option.
|
||||||
|
function toggleSerialEntry() {
|
||||||
|
manualSerial.value = !manualSerial.value;
|
||||||
|
// Returning to a list that does not hold this serial would blank the dropdown
|
||||||
|
// again, which is the thing being fixed; land on a charger it does hold.
|
||||||
|
if (!manualSerial.value && !serialInList.value && chargers.value.length) {
|
||||||
|
ctlSerial.value = chargers.value[0].sn;
|
||||||
|
refreshCtl();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function chargerLabel(c) {
|
function chargerLabel(c) {
|
||||||
@@ -590,7 +615,7 @@ onMounted(async () => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mt-3 flex gap-2">
|
<div class="mt-3 flex gap-2">
|
||||||
<select v-if="chargers.length" v-model="ctlSerial" class="dh-input" @change="refreshCtl">
|
<select v-if="pickingFromList" v-model="ctlSerial" class="dh-input" @change="refreshCtl">
|
||||||
<option v-for="c in chargers" :key="c.sn" :value="c.sn">{{ chargerLabel(c) }}</option>
|
<option v-for="c in chargers" :key="c.sn" :value="c.sn">{{ chargerLabel(c) }}</option>
|
||||||
</select>
|
</select>
|
||||||
<input
|
<input
|
||||||
@@ -599,9 +624,19 @@ onMounted(async () => {
|
|||||||
class="dh-input"
|
class="dh-input"
|
||||||
:placeholder="t('charging.control.serialPlaceholder')"
|
:placeholder="t('charging.control.serialPlaceholder')"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
|
spellcheck="false"
|
||||||
|
@keyup.enter="refreshCtl"
|
||||||
/>
|
/>
|
||||||
<button class="dh-btn dh-btn-ghost shrink-0" @click="refreshCtl">{{ t("charging.control.refresh") }}</button>
|
<button class="dh-btn dh-btn-ghost shrink-0" @click="refreshCtl">{{ t("charging.control.refresh") }}</button>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- Only worth offering when there is a list to switch to or from. -->
|
||||||
|
<button
|
||||||
|
v-if="chargers.length"
|
||||||
|
class="mt-1.5 text-[11px] text-muted underline-offset-2 transition-colors hover:text-body hover:underline"
|
||||||
|
@click="toggleSerialEntry"
|
||||||
|
>
|
||||||
|
{{ pickingFromList ? t("charging.control.enterSerial") : t("charging.control.pickSerial") }}
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- Modbus mode dials the charger, so it needs the charger's address on
|
<!-- Modbus mode dials the charger, so it needs the charger's address on
|
||||||
this network rather than a token installed into the charger. -->
|
this network rather than a token installed into the charger. -->
|
||||||
|
|||||||
Reference in New Issue
Block a user