diff --git a/Web App/web/src/i18n/da.json b/Web App/web/src/i18n/da.json index 68d1f67..9339a41 100644 --- a/Web App/web/src/i18n/da.json +++ b/Web App/web/src/i18n/da.json @@ -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" } }, diff --git a/Web App/web/src/i18n/en.json b/Web App/web/src/i18n/en.json index b911664..b389f6f 100644 --- a/Web App/web/src/i18n/en.json +++ b/Web App/web/src/i18n/en.json @@ -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", diff --git a/Web App/web/src/i18n/pl.json b/Web App/web/src/i18n/pl.json index ea4d6ba..317c669 100644 --- a/Web App/web/src/i18n/pl.json +++ b/Web App/web/src/i18n/pl.json @@ -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" } }, diff --git a/Web App/web/src/views/Charging.vue b/Web App/web/src/views/Charging.vue index ccef7e6..c96ee70 100644 --- a/Web App/web/src/views/Charging.vue +++ b/Web App/web/src/views/Charging.vue @@ -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 () => {
{{ ctlError }}
- -{{ t("charging.control.title") }}
-{{ t("charging.stations.noControlHint") }}
+ +{{ t("charging.info.title") }}
+ +{{ c.name }}
+ + {{ providerLabel(c.provider) }} + ++ {{ t("charging.info.empty") }} +
+ +{{ t("charging.stations.noControlHint") }}