What a charger is, readable without a CSMS to control it with
The Home chargers tab put a card where the control panel goes and, with neither Own nor Proxy CSMS turned on, filled it with a single sentence pointing at Settings — an empty box beside a list of two real chargers the app already knew the vendor, model and serial of. That knowledge now has somewhere to be. Charger information lists every imported charger with the fields its record actually holds; a field the service never sent is left out rather than shown as a dash, and the provider's own id appears only when it is something other than the serial. The selected charger carries the same accent ring the list beside it does, so picking one still reads as picking the one control drives. The card stands on its own either way: with control on it sits beneath it, and with control off the Settings hint becomes a footnote under something worth reading instead of the whole card. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
d71c1b4691
commit
4203ca93db
@@ -94,6 +94,18 @@
|
||||
"resetConfirm": "Genstart denne lader nu? En igangværende opladning bliver afbrudt. Indtast din adgangskode igen for at bekræfte.",
|
||||
"resetPassword": "Din kontoadgangskode",
|
||||
"connectHint": "Indtast laderens serienummer og opdater. Laderen skal være forbundet til DriverVaults OCPP-backend (opsættes under Indstillinger → Integrationer)."
|
||||
},
|
||||
"info": {
|
||||
"title": "Laderoplysninger",
|
||||
"empty": "Intet importeret endnu — en lader, du importerer, viser alt, den melder, her.",
|
||||
"vendor": "Producent",
|
||||
"model": "Model",
|
||||
"serial": "Serienummer",
|
||||
"site": "Placering",
|
||||
"power": "Effekt",
|
||||
"connector": "Stik",
|
||||
"providerId": "Id hos tjenesten",
|
||||
"added": "Tilføjet"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -81,6 +81,18 @@
|
||||
"resetPassword": "Your account password",
|
||||
"connectHint": "Enter your charger's serial and refresh. The charger must be connected to DriverVault's OCPP backend (set up in Settings → Integrations)."
|
||||
},
|
||||
"info": {
|
||||
"title": "Charger information",
|
||||
"empty": "Nothing imported yet — a charger you import shows everything it reports here.",
|
||||
"vendor": "Vendor",
|
||||
"model": "Model",
|
||||
"serial": "Serial",
|
||||
"site": "Site",
|
||||
"power": "Power",
|
||||
"connector": "Connector",
|
||||
"providerId": "Service id",
|
||||
"added": "Added"
|
||||
},
|
||||
"stations": {
|
||||
"heading": "Nearby",
|
||||
"homeHeading": "Your chargers",
|
||||
|
||||
@@ -96,6 +96,18 @@
|
||||
"resetConfirm": "Zrestartować teraz tę ładowarkę? Trwająca sesja ładowania zostanie przerwana. Wpisz ponownie hasło, aby potwierdzić.",
|
||||
"resetPassword": "Hasło do Twojego konta",
|
||||
"connectHint": "Wpisz numer seryjny ładowarki i odśwież. Ładowarka musi być połączona z backendem OCPP DriverVault (konfiguracja w Ustawienia → Integracje)."
|
||||
},
|
||||
"info": {
|
||||
"title": "Informacje o ładowarce",
|
||||
"empty": "Nic jeszcze nie zaimportowano — zaimportowana ładowarka pokaże tutaj wszystko, co zgłasza.",
|
||||
"vendor": "Producent",
|
||||
"model": "Model",
|
||||
"serial": "Numer seryjny",
|
||||
"site": "Lokalizacja",
|
||||
"power": "Moc",
|
||||
"connector": "Złącze",
|
||||
"providerId": "Identyfikator w usłudze",
|
||||
"added": "Dodano"
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ref, computed, onMounted } from "vue";
|
||||
import { t } from "../i18n";
|
||||
import { api } from "../api";
|
||||
import { formatDateTime } from "../lib/format.js";
|
||||
import ChargerImportModal from "../components/ChargerImportModal.vue";
|
||||
|
||||
// Charging & map screen, mirroring the web-dashboard UI kit. There is no live
|
||||
@@ -103,9 +104,16 @@ function chargerLabel(c) {
|
||||
const homeChargers = ref([]);
|
||||
const homeChargersError = ref("");
|
||||
const showChargerImport = ref(false);
|
||||
// Importing only makes sense once a charger service is connected, so the button
|
||||
// appears only then — same rule the garage's import button follows.
|
||||
const canImportCharger = ref(false);
|
||||
// The charger services this user could import from. Importing only makes sense
|
||||
// once one of them is connected, so the button appears only then — same rule the
|
||||
// garage's import button follows — and the list doubles as the id → label map
|
||||
// the information card names a charger's origin with.
|
||||
const chargerProviders = ref([]);
|
||||
const canImportCharger = computed(() => chargerProviders.value.some((p) => p.connected));
|
||||
|
||||
function providerLabel(id) {
|
||||
return chargerProviders.value.find((p) => p.id === id)?.label || id;
|
||||
}
|
||||
|
||||
async function loadHomeChargers() {
|
||||
homeChargersError.value = "";
|
||||
@@ -145,6 +153,26 @@ function homeChargerSubtitle(c) {
|
||||
return [c.serial, c.model, c.siteName].filter(Boolean).join(" · ");
|
||||
}
|
||||
|
||||
// Everything the record holds about one charger, as label/value rows. A field
|
||||
// the service never sent is left out rather than shown as a dash: the point of
|
||||
// the card is what is known. The provider's own id for the charger is the serial
|
||||
// for both services we speak to, so it appears only when it is something else.
|
||||
function chargerInfoRows(c) {
|
||||
const rows = [
|
||||
["vendor", c.vendor],
|
||||
["model", c.model],
|
||||
["serial", c.serial],
|
||||
["site", c.siteName],
|
||||
["power", c.powerKw ? `${c.powerKw} kW` : ""],
|
||||
["connector", c.connector],
|
||||
["providerId", c.providerChargerId === c.serial ? "" : c.providerChargerId],
|
||||
["added", c.created ? formatDateTime(c.created) : ""],
|
||||
];
|
||||
return rows
|
||||
.filter(([, value]) => value !== "" && value != null)
|
||||
.map(([key, value]) => ({ key, label: t(`charging.info.${key}`), value }));
|
||||
}
|
||||
|
||||
async function refreshCtl() {
|
||||
const sn = ctlSerial.value.trim();
|
||||
if (!sn) {
|
||||
@@ -204,8 +232,8 @@ onMounted(async () => {
|
||||
await loadHomeChargers();
|
||||
api
|
||||
.listChargerProviders()
|
||||
.then((list) => (canImportCharger.value = list.some((p) => p.connected)))
|
||||
.catch(() => (canImportCharger.value = false));
|
||||
.then((list) => (chargerProviders.value = list))
|
||||
.catch(() => (chargerProviders.value = []));
|
||||
await loadCtlMode();
|
||||
if (ctlActive.value) await loadChargers();
|
||||
await refreshCtl();
|
||||
@@ -442,10 +470,39 @@ onMounted(async () => {
|
||||
<p v-if="ctlError" class="mt-2 text-sm text-danger">{{ ctlError }}</p>
|
||||
</div>
|
||||
|
||||
<!-- No control mode active: point the user at Settings to enable it. -->
|
||||
<div v-else class="dh-card p-4">
|
||||
<p class="text-sm font-semibold text-strong">{{ t("charging.control.title") }}</p>
|
||||
<p class="mt-2 text-xs text-muted">{{ t("charging.stations.noControlHint") }}</p>
|
||||
<!-- Charger information: everything the record holds about each imported
|
||||
charger. It stands on its own — control needs Own/Proxy CSMS, but
|
||||
what the charger *is* is known either way, so with control off this
|
||||
card is what fills the column instead of a bare hint. -->
|
||||
<div class="dh-card p-4">
|
||||
<p class="text-sm font-semibold text-strong">{{ t("charging.info.title") }}</p>
|
||||
|
||||
<div
|
||||
v-for="c in homeChargers"
|
||||
:key="c.id"
|
||||
class="mt-3 rounded-control bg-sunken p-3"
|
||||
:class="selected === c.id ? 'ring-2 ring-accent' : ''"
|
||||
>
|
||||
<div class="flex items-center justify-between gap-2">
|
||||
<p class="truncate text-sm font-semibold text-strong">{{ c.name }}</p>
|
||||
<span v-if="c.provider" class="dh-badge dh-badge-neutral shrink-0">
|
||||
{{ providerLabel(c.provider) }}
|
||||
</span>
|
||||
</div>
|
||||
<dl class="mt-2 grid grid-cols-[auto_1fr] gap-x-4 gap-y-1">
|
||||
<template v-for="row in chargerInfoRows(c)" :key="row.key">
|
||||
<dt class="text-[11px] text-muted">{{ row.label }}</dt>
|
||||
<dd class="data break-all text-[11px] text-body">{{ row.value }}</dd>
|
||||
</template>
|
||||
</dl>
|
||||
</div>
|
||||
|
||||
<p v-if="homeChargers.length === 0" class="mt-2 text-xs text-muted">
|
||||
{{ t("charging.info.empty") }}
|
||||
</p>
|
||||
<!-- Control mode off: say why the card above is missing, here, where
|
||||
there is now something to read it against. -->
|
||||
<p v-if="!ctlActive" class="mt-3 text-xs text-muted">{{ t("charging.stations.noControlHint") }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user