Sessions UI: Add odometer (mileage) column in table view (#29263)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: andig <184815+andig@users.noreply.github.com>
Co-authored-by: naltatis <152287+naltatis@users.noreply.github.com>
Co-authored-by: Michael Geers <michael@geers.tv>
This commit is contained in:
Copilot 2026-04-29 08:23:58 +00:00 • committed by GitHub
parent e0c819a921
commit 54a1b0cc0b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 36 additions and 6 deletions

View file

@ -119,9 +119,10 @@
:key="column.name"
:data-testid="`sessions-foot-${column.name}`"
scope="col"
class="align-top text-end"
class="align-top text-end tabular"
>
{{ column.format(column.total || 0) }}
<span v-if="column.total === null"> </span>
<span v-else>{{ column.format(column.total || 0) }}</span>
</th>
</tr>
</tfoot>
@ -133,7 +134,7 @@
data-testid="sessions-entry"
@click="showDetails(session.id)"
>
<td class="ps-0">
<td class="ps-0 tabular">
{{ fmtFullDateTime(new Date(session.created), true) }}
</td>
<td class="d-none d-md-table-cell">
@ -146,7 +147,11 @@
<div>{{ session.loadpoint }}</div>
<div>{{ session.vehicle }}</div>
</td>
<td v-for="column in columnsPerBreakpoint" :key="column.name" class="text-end">
<td
v-for="column in columnsPerBreakpoint"
:key="column.name"
class="text-end tabular"
>
<span v-if="column.value(session) === null" class="text-gray"> - </span>
<span v-else>{{ column.format(column.value(session) || 0) }}</span>
</td>
@ -162,6 +167,7 @@ import CustomSelect from "../Helper/CustomSelect.vue";
import formatter, { POWER_UNIT } from "@/mixins/formatter";
import breakpoint from "@/mixins/breakpoint.ts";
import settings from "@/settings.ts";
import { distanceUnit, distanceValue } from "@/units";
import type { CURRENCY } from "@/types/evcc";
import type { Session, Column } from "./types";
@ -244,6 +250,13 @@ export default defineComponent({
value: (session) => session.chargeDuration,
format: (value) => this.fmtDurationNs(value, false, "h"),
},
{
name: "odometer",
unit: distanceUnit(),
total: null,
value: (session) => session.odometer || null,
format: (value) => this.fmtNumber(distanceValue(value), 0),
},
{
name: "avgPower",
unit: "kW",

View file

@ -1445,6 +1445,7 @@
},
"loadpoint": "Ladepunkt",
"noData": "Noch keine Ladevorgänge in diesem Monat.",
"odometer": "Kilometerstand",
"overview": "Übersicht",
"period": {
"month": "Monat",

View file

@ -1445,6 +1445,7 @@
},
"loadpoint": "Charging point",
"noData": "No charging sessions this month.",
"odometer": "Mileage",
"overview": "Overview",
"period": {
"month": "Month",

View file

@ -245,10 +245,24 @@ test.describe("columns desktop", async () => {
await page.goto("/#/sessions?year=2023&month=3");
await expect(page.getByTestId("sessions-head-co2")).toBeVisible();
});
test("select odometer column if it has values", async ({ page }) => {
await page.goto("/#/sessions?year=2023&month=3");
await expect(
page.getByTestId("sessions-head-co2").locator("option[value=odometer]")
).toHaveCount(1);
await page.getByTestId("sessions-head-co2").getByRole("combobox").selectOption("odometer");
await expect(page.getByTestId("sessions-head-odometer")).toBeVisible();
await expect(page.getByTestId("sessions-entry").nth(0)).toContainText("12,345");
await expect(page.getByTestId("sessions-foot-odometer")).toHaveText("");
});
test("hide co2 column if it doesnt have values", async ({ page }) => {
await page.goto("/#/sessions?year=2023&month=5");
await expect(page.getByTestId("sessions-head-co2")).toHaveCount(0);
});
test("hide odometer column if it doesnt have values", async ({ page }) => {
await page.goto("/#/sessions?year=2023&month=5");
await expect(page.getByTestId("sessions-head-odometer")).toHaveCount(0);
});
});
const date = new Date();
@ -335,6 +349,7 @@ test.describe("session details", async () => {
await expect(modal.getByTestId("session-details-price")).toContainText("2.00 € 20.0 ct/kWh");
await expect(modal.getByTestId("session-details-co2")).toContainText("3 kg");
await expect(modal.getByTestId("session-details-co2")).toContainText("300 g/kWh");
await expect(modal.getByTestId("session-details-odometer")).toContainText("12,345 km");
});
test("edit session (session 5)", async ({ page }) => {

View file

@ -18,8 +18,8 @@ CREATE TABLE `sessions` (
PRIMARY KEY (`id`)
);
INSERT INTO `sessions` VALUES (1,'2023-03-01 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,10,NULL,100,2,0.2,300,3600000000000);
INSERT INTO `sessions` VALUES (1,'2023-03-01 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,10,12345,100,2,0.2,300,3600000000000);
INSERT INTO `sessions` VALUES (2,'2023-05-02 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,10,NULL,100,2,0.2,NULL,1800000000000);
INSERT INTO `sessions` VALUES (3,'2023-05-02 08:00:00.0+02:00','2023-05-02 12:00:00.0+02:00','Carport',NULL,'blauer e-Golf',NULL,NULL,2.5,NULL,88.21,0.75,0.3,NULL,NULL);
INSERT INTO `sessions` VALUES (4,'2023-05-03 16:00:00.0+02:00','2023-05-03 20:00:00.0+02:00','Carport',NULL,'weißes Model 3',NULL,NULL,2.5,NULL,50,0.25,0.1,NULL,NULL);
INSERT INTO `sessions` VALUES (5,'2023-05-04 22:00:00.0+02:00','2023-05-05 06:00:00.0+02:00','Garage',NULL,'weißes Model 3',NULL,NULL,5,NULL,0,2.5,0.5,null,3600000000000);
INSERT INTO `sessions` VALUES (5,'2023-05-04 22:00:00.0+02:00','2023-05-05 06:00:00.0+02:00','Garage',NULL,'weißes Model 3',NULL,NULL,5,NULL,0,2.5,0.5,null,3600000000000);