Controls first, because that is what the page is opened for

The column opened with the charger picker and its address — the two things
touched once and then never again — and put the start button below them. Order
reversed: control, then connection, then readings, then information.

The comments on both cards described the old order, so they move with it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
tajniak81
2026-09-01 21:06:02 +02:00
co-authored by Claude Opus 5
parent b48019b08f
commit 2a5d21d328
+104 -101
View File
@@ -762,6 +762,110 @@ onMounted(async () => {
<div v-show="chargerTab === 'home'" class="grid gap-6 lg:grid-cols-[1fr_360px] lg:items-start">
<!-- Real OCPP control (Own/Proxy CSMS) the real home-charger control -->
<div class="flex flex-col gap-4">
<!-- Acting on the charger first, because it is what the page is
opened for. It appears only once there is a connection to act
over, which the card below is where you set up. -->
<div v-if="ctlActive && ctlConnected" class="dh-card p-4">
<button
type="button"
class="flex w-full items-center justify-between gap-3 text-left"
:aria-expanded="isOpen('control')"
@click="toggleCard('control')"
>
<p class="text-sm font-semibold text-strong">{{ t("charging.control.title") }}</p>
<svg
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
class="h-4 w-4 shrink-0 text-muted transition-transform" :class="isOpen('control') ? '' : '-rotate-90'"
><path stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6" /></svg>
</button>
<div v-show="isOpen('control')">
<div class="mt-3 grid grid-cols-2 gap-2">
<div class="rounded-control bg-sunken px-3 py-2">
<div class="data text-sm font-semibold text-strong">{{ ctlStatusLabel }}</div>
<div class="text-[11px] text-muted">{{ t("charging.control.status") }}</div>
</div>
<div class="rounded-control bg-sunken px-3 py-2">
<div class="data text-sm font-semibold text-strong">{{ ctlMeterKwh }} kWh</div>
<div class="text-[11px] text-muted">{{ t("charging.control.meter") }}</div>
</div>
</div>
<div class="mt-3 flex gap-2">
<button class="dh-btn dh-btn-primary grow" :disabled="ctlBusy === 'start'" @click="doAction('start')">
{{ t("charging.control.start") }}
</button>
<button class="dh-btn dh-btn-ghost grow" :disabled="ctlBusy === 'stop'" @click="doAction('stop')">
{{ t("charging.control.stop") }}
</button>
</div>
<div class="mt-3">
<label class="dh-label flex justify-between">
<span>{{ t("charging.control.limit") }}</span><span class="data text-body">{{ limitAmps }} A</span>
</label>
<input v-model.number="limitAmps" type="range" min="6" max="32" step="1" class="w-full accent-[var(--accent)]" />
<div class="mt-2 flex gap-2">
<button class="dh-btn dh-btn-ghost grow" :disabled="ctlBusy === 'limit'" @click="doAction('limit', { amps: limitAmps })">
{{ t("charging.control.applyLimit") }}
</button>
<!-- Clearing a limit is an OCPP command; the register map takes an
explicit ceiling, so in Modbus mode there is nothing to clear to. -->
<button
v-if="!ctlIsModbus"
class="dh-btn dh-btn-ghost grow"
:disabled="ctlBusy === 'clear-limit'"
@click="doAction('clear-limit')"
>
{{ t("charging.control.clearLimit") }}
</button>
</div>
</div>
<!-- Boost is a Modbus command, and lasts for the current session only. -->
<button
v-if="ctlIsModbus"
class="dh-btn dh-btn-ghost mt-3 w-full"
:disabled="ctlBusy === 'boost'"
@click="doAction('boost', { on: true })"
>
{{ t("charging.control.boost") }}
</button>
<!-- Reset reboots the charger over OCPP; the register map has no
equivalent, so the button is not offered on the local path. -->
<button
v-if="!ctlIsModbus && !resetPrompt"
class="dh-btn dh-btn-ghost mt-3 w-full"
:disabled="ctlBusy === 'reset'"
@click="askReset"
>
{{ t("charging.control.reset") }}
</button>
<!-- Step-up: destructive reset requires re-entering the password. -->
<div v-else-if="resetPrompt" class="mt-3 rounded-control border border-danger/40 bg-danger-soft p-3">
<p class="text-xs font-medium text-danger">{{ t("charging.control.resetConfirm") }}</p>
<input
v-model="resetPassword"
type="password"
autocomplete="current-password"
class="dh-input mt-2"
:placeholder="t('charging.control.resetPassword')"
@keyup.enter="confirmReset"
/>
<div class="mt-2 flex gap-2">
<button class="dh-btn dh-btn-ghost grow" @click="cancelReset">{{ t("common.cancel") }}</button>
<button class="dh-btn dh-btn-danger grow" :disabled="!resetPassword || ctlBusy === 'reset'" @click="confirmReset">
{{ t("charging.control.reset") }}
</button>
</div>
</div>
</div>
</div>
<!-- Which charger, and how to reach it. Below the controls: it is
touched once and then left alone. -->
<div v-if="ctlActive" class="dh-card p-4">
<button
type="button"
@@ -862,107 +966,6 @@ onMounted(async () => {
</div>
</div>
<!-- Acting on the charger, once there is a connection to act over. The
card above is where that connection is set up. -->
<div v-if="ctlActive && ctlConnected" class="dh-card p-4">
<button
type="button"
class="flex w-full items-center justify-between gap-3 text-left"
:aria-expanded="isOpen('control')"
@click="toggleCard('control')"
>
<p class="text-sm font-semibold text-strong">{{ t("charging.control.title") }}</p>
<svg
viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
class="h-4 w-4 shrink-0 text-muted transition-transform" :class="isOpen('control') ? '' : '-rotate-90'"
><path stroke-linecap="round" stroke-linejoin="round" d="m6 9 6 6 6-6" /></svg>
</button>
<div v-show="isOpen('control')">
<div class="mt-3 grid grid-cols-2 gap-2">
<div class="rounded-control bg-sunken px-3 py-2">
<div class="data text-sm font-semibold text-strong">{{ ctlStatusLabel }}</div>
<div class="text-[11px] text-muted">{{ t("charging.control.status") }}</div>
</div>
<div class="rounded-control bg-sunken px-3 py-2">
<div class="data text-sm font-semibold text-strong">{{ ctlMeterKwh }} kWh</div>
<div class="text-[11px] text-muted">{{ t("charging.control.meter") }}</div>
</div>
</div>
<div class="mt-3 flex gap-2">
<button class="dh-btn dh-btn-primary grow" :disabled="ctlBusy === 'start'" @click="doAction('start')">
{{ t("charging.control.start") }}
</button>
<button class="dh-btn dh-btn-ghost grow" :disabled="ctlBusy === 'stop'" @click="doAction('stop')">
{{ t("charging.control.stop") }}
</button>
</div>
<div class="mt-3">
<label class="dh-label flex justify-between">
<span>{{ t("charging.control.limit") }}</span><span class="data text-body">{{ limitAmps }} A</span>
</label>
<input v-model.number="limitAmps" type="range" min="6" max="32" step="1" class="w-full accent-[var(--accent)]" />
<div class="mt-2 flex gap-2">
<button class="dh-btn dh-btn-ghost grow" :disabled="ctlBusy === 'limit'" @click="doAction('limit', { amps: limitAmps })">
{{ t("charging.control.applyLimit") }}
</button>
<!-- Clearing a limit is an OCPP command; the register map takes an
explicit ceiling, so in Modbus mode there is nothing to clear to. -->
<button
v-if="!ctlIsModbus"
class="dh-btn dh-btn-ghost grow"
:disabled="ctlBusy === 'clear-limit'"
@click="doAction('clear-limit')"
>
{{ t("charging.control.clearLimit") }}
</button>
</div>
</div>
<!-- Boost is a Modbus command, and lasts for the current session only. -->
<button
v-if="ctlIsModbus"
class="dh-btn dh-btn-ghost mt-3 w-full"
:disabled="ctlBusy === 'boost'"
@click="doAction('boost', { on: true })"
>
{{ t("charging.control.boost") }}
</button>
<!-- Reset reboots the charger over OCPP; the register map has no
equivalent, so the button is not offered on the local path. -->
<button
v-if="!ctlIsModbus && !resetPrompt"
class="dh-btn dh-btn-ghost mt-3 w-full"
:disabled="ctlBusy === 'reset'"
@click="askReset"
>
{{ t("charging.control.reset") }}
</button>
<!-- Step-up: destructive reset requires re-entering the password. -->
<div v-else-if="resetPrompt" class="mt-3 rounded-control border border-danger/40 bg-danger-soft p-3">
<p class="text-xs font-medium text-danger">{{ t("charging.control.resetConfirm") }}</p>
<input
v-model="resetPassword"
type="password"
autocomplete="current-password"
class="dh-input mt-2"
:placeholder="t('charging.control.resetPassword')"
@keyup.enter="confirmReset"
/>
<div class="mt-2 flex gap-2">
<button class="dh-btn dh-btn-ghost grow" @click="cancelReset">{{ t("common.cancel") }}</button>
<button class="dh-btn dh-btn-danger grow" :disabled="!resetPassword || ctlBusy === 'reset'" @click="confirmReset">
{{ t("charging.control.reset") }}
</button>
</div>
</div>
</div>
</div>
<!-- What the charger reports over Modbus. Its own card rather than a
tail on the control one: control is for acting on the charger, and
this is a long read that pushed the buttons off the screen. -->