chore: upgrade npm dependencies; stricter typescript (#23472)

This commit is contained in:
Michael Geers 2025-09-09 13:24:05 +02:00 • committed by GitHub
parent ce1fbbc9c6
commit b679e4da4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
40 changed files with 1218 additions and 1955 deletions

View file

@ -2,9 +2,7 @@
const config = {
stories: ["../assets/js/**/*.stories.@(js|ts)"],
addons: [
"@storybook/addon-essentials",
"@chromatic-com/storybook",
"@storybook/addon-interactions",
],
framework: {
name: "@storybook/vue3-vite",

View file

@ -452,7 +452,7 @@ export default defineComponent({
const options = this.bufferStartOptions.map((option) => option.value);
const index = options.findIndex((value) => this.bufferStartSoc >= value);
const nextIndex = index === 0 ? options.length - 1 : index - 1;
this.setBufferStartSoc(options[nextIndex]);
this.setBufferStartSoc(options[nextIndex]!);
},
async setBufferStartSoc(soc: number) {
this.selectedBufferStartSoc = soc;

View file

@ -139,7 +139,7 @@ export default defineComponent({
return this.repeatingPlans.length !== 0;
},
selectedPreviewPlanTitle(): string {
return this.previewPlanOptions[this.selectedPreviewId - 1]?.name;
return this.previewPlanOptions[this.selectedPreviewId - 1]?.name || "";
},
chargingPlanWarningsProps(): any {
return this.collectProps(Warnings);

View file

@ -44,35 +44,35 @@ describe("basics", () => {
});
test("slots should be an hour apart", () => {
expect(result[0].startHour).eq(11);
expect(result[0].endHour).eq(12);
expect(result[0].day).eq("Mi");
expect(result[0]!.startHour).eq(11);
expect(result[0]!.endHour).eq(12);
expect(result[0]!.day).eq("Mi");
expect(result[1].startHour).eq(12);
expect(result[1].endHour).eq(13);
expect(result[1].day).eq("Mi");
expect(result[1]!.startHour).eq(12);
expect(result[1]!.endHour).eq(13);
expect(result[1]!.day).eq("Mi");
expect(result[11].startHour).eq(22);
expect(result[11].endHour).eq(23);
expect(result[11].day).eq("Mi");
expect(result[11]!.startHour).eq(22);
expect(result[11]!.endHour).eq(23);
expect(result[11]!.day).eq("Mi");
expect(result[24].startHour).eq(11);
expect(result[24].endHour).eq(12);
expect(result[24].day).eq("Do");
expect(result[24]!.startHour).eq(11);
expect(result[24]!.endHour).eq(12);
expect(result[24]!.day).eq("Do");
});
test("slots after target should be toLate", () => {
expect(result[0].toLate).eq(false);
expect(result[1].toLate).eq(false);
expect(result[2].toLate).eq(true);
expect(result[3].toLate).eq(true);
expect(result[0]!.toLate).eq(false);
expect(result[1]!.toLate).eq(false);
expect(result[2]!.toLate).eq(true);
expect(result[3]!.toLate).eq(true);
});
test("slots are marked if charging is happening in them", () => {
expect(result[0].charging).eq(false);
expect(result[1].charging).eq(true);
expect(result[2].charging).eq(false);
expect(result[3].charging).eq(false);
expect(result[0]!.charging).eq(false);
expect(result[1]!.charging).eq(true);
expect(result[2]!.charging).eq(false);
expect(result[3]!.charging).eq(false);
});
test("all slots have the same fixed value", () => {
@ -124,17 +124,17 @@ describe("zoned tariffs", () => {
});
test("handle multiple charging slots", () => {
expect(result[0].charging).eq(true);
expect(result[1].charging).eq(true);
expect(result[2].charging).eq(false);
expect(result[3].charging).eq(true);
expect(result[4].charging).eq(true);
expect(result[5].charging).eq(false);
expect(result[0]!.charging).eq(true);
expect(result[1]!.charging).eq(true);
expect(result[2]!.charging).eq(false);
expect(result[3]!.charging).eq(true);
expect(result[4]!.charging).eq(true);
expect(result[5]!.charging).eq(false);
});
test("first slot is cheap, others are expensive", () => {
const [first, ...others] = result;
expect(first.value).eq(0.2);
expect(first!.value).eq(0.2);
others.forEach((slot) => expect(slot.value).eq(0.4));
});
});

View file

@ -61,7 +61,7 @@ export default defineComponent({
if (!this.plan?.length) {
return null;
}
const end = this.plan[this.plan.length - 1].end;
const end = this.plan[this.plan.length - 1]?.end;
return end ? new Date(end) : null;
},
timeWarning(): boolean {
@ -110,7 +110,7 @@ export default defineComponent({
: this.fmtPricePerKWh(value, this.currency);
},
activeSlot(): Slot | null {
return this.activeIndex !== null ? this.slots[this.activeIndex] : null;
return this.activeIndex !== null ? (this.slots[this.activeIndex] ?? null) : null;
},
activeSlotName(): string | null {
if (this.activeSlot) {

View file

@ -49,7 +49,7 @@ export default defineComponent({
return null;
}
const { plan } = this.plan;
return plan[plan.length - 1].end;
return plan[plan.length - 1]!.end;
},
overrunFmt(): string {
if (!this.planOverrun) {

View file

@ -81,7 +81,7 @@ export default defineComponent({
},
getProductName() {
const select = this.$refs["select"] as HTMLSelectElement;
return select.options[select.selectedIndex].text;
return select.options[select.selectedIndex]?.text || "";
},
},
});

View file

@ -706,9 +706,9 @@ export default {
key?: number;
name: string;
}[];
result[0].name = "0 (default)";
result[0].key = undefined;
result[10].name = "10 (highest)";
result[0]!.name = "0 (default)";
result[0]!.key = undefined;
result[10]!.name = "10 (highest)";
return result;
},
phasesOptions() {

View file

@ -340,9 +340,9 @@ export default defineComponent({
{ length: 11 },
(_, i) => ({ key: i, name: `${i}` })
);
result[0].name = "0 (default)";
result[0].key = undefined;
result[10].name = "10 (highest)";
result[0]!.name = "0 (default)";
result[0]!.key = undefined;
result[10]!.name = "10 (highest)";
return result;
},
showActions() {

View file

@ -257,7 +257,8 @@ export default defineComponent({
return context.dataset.borderColor;
},
align({ chart, dataset, dataIndex }: Context) {
const { min, max } = chart.scales["x"];
const scale = chart.scales["x"] as any;
const { min, max } = scale;
// @ts-expect-error no-explicit-any
const time = new Date(dataset.data[dataIndex]?.x).getTime();
@ -477,12 +478,12 @@ export default defineComponent({
},
maxIndex(slots: ForecastSlot[] = []) {
return slots.reduce((max, slot, index) => {
return slot.value > slots[max].value ? index : max;
return slot.value > (slots[max]?.value || 0) ? index : max;
}, 0);
},
minIndex(slots: ForecastSlot[] = []) {
return slots.reduce((min, slot, index) => {
return slot.value < slots[min].value ? index : min;
return slot.value < (slots[min]?.value || 0) ? index : min;
}, 0);
},
maxValue(slots: ForecastSlot[] = []) {
@ -493,7 +494,7 @@ export default defineComponent({
},
maxEntryIndex(entries: TimeseriesEntry[] = []) {
return entries.reduce((max, entry, index) => {
return entry.val > entries[max].val ? index : max;
return entry.val > (entries[max]?.val || 0) ? index : max;
}, 0);
},
yScaleOptions(type: ForecastType) {

View file

@ -145,7 +145,7 @@ export default defineComponent({
// fallback to first available type
this.selectedType =
Object.values(ForecastType).find((type) => availableTypes[type]) ||
Object.values(ForecastType)[0];
Object.values(ForecastType)[0]!;
},
changeAdjusted() {
settings.solarAdjusted = !settings.solarAdjusted;

View file

@ -141,7 +141,7 @@ export default defineComponent({
return { value: key, name: value[1] };
});
// sort by name
locales.sort((a, b) => (a.name < b.name ? -1 : 1));
locales.sort((a, b) => ((a.name || "") < (b.name || "") ? -1 : 1));
return locales;
},
fullscreenAvailable: () => {

View file

@ -49,7 +49,8 @@ export default defineComponent({
},
isPhaseActive(num: number) {
if (this.chargeCurrentsActive && this.chargeCurrents) {
return this.chargeCurrents[num - 1] >= MIN_ACTIVE_CURRENT;
const current = this.chargeCurrents[num - 1];
return current !== undefined && current >= MIN_ACTIVE_CURRENT;
}
return num <= (this.phasesActive || 0);
},

View file

@ -120,13 +120,13 @@ export default defineComponent({
);
},
label() {
return this.$t(`main.loadpoint.${this.selectedOption.key}`);
return this.$t(`main.loadpoint.${this.selectedOption?.key || ""}`);
},
value() {
return this.selectedOption.value;
return this.selectedOption?.value;
},
valueSm() {
return this.selectedOption.valueSm;
return this.selectedOption?.valueSm;
},
showSm() {
return this.valueSm !== undefined;

View file

@ -67,7 +67,7 @@ export default defineComponent({
},
timestamp: {
type: String,
required: true,
default: "",
},
currency: {
type: String as PropType<CURRENCY>,
@ -75,7 +75,7 @@ export default defineComponent({
},
batteryColors: {
type: Array as PropType<string[]>,
required: true,
default: () => [],
},
},
computed: {
@ -312,7 +312,7 @@ export default defineComponent({
convertWhToKW(wh: number, index: number): number {
// Convert Wh to kW by normalizing against time duration
// Power (kW) = Energy (Wh) / Time (h) / 1000
const dtSeconds = this.evopt.req.time_series.dt[index];
const dtSeconds = this.evopt.req.time_series.dt[index] || 0;
const hours = dtSeconds / 3600; // Convert seconds to hours
return wh / hours / 1000;
},

View file

@ -61,7 +61,7 @@ export default defineComponent({
},
timestamp: {
type: String,
required: true,
default: "",
},
currency: {
type: String as PropType<CURRENCY>,

View file

@ -54,7 +54,7 @@ export default defineComponent({
},
timestamp: {
type: String,
required: true,
default: "",
},
currency: {
type: String as PropType<CURRENCY>,
@ -62,7 +62,7 @@ export default defineComponent({
},
batteryColors: {
type: Array as PropType<string[]>,
required: true,
default: () => [],
},
},
computed: {

View file

@ -216,7 +216,7 @@ export default defineComponent({
},
timestamp: {
type: String,
required: true,
default: "",
},
currency: {
type: String as PropType<CURRENCY>,
@ -224,11 +224,11 @@ export default defineComponent({
},
batteryColors: {
type: Array as PropType<string[]>,
required: true,
default: () => [],
},
dimmedBatteryColors: {
type: Array as PropType<string[]>,
required: true,
default: () => [],
},
},
computed: {
@ -246,7 +246,7 @@ export default defineComponent({
methods: {
formatEnergyToPower(wh: number, index: number): string {
// Convert Wh to kW by normalizing against time duration
const dtSeconds = this.evopt.req.time_series.dt[index];
const dtSeconds = this.evopt.req.time_series.dt[index] || 0;
const hours = dtSeconds / 3600; // Convert seconds to hours
const watts = wh / hours; // Convert to W (power)
return this.fmtW(watts, this.POWER_UNIT.KW, false, 1);

View file

@ -77,11 +77,15 @@ export default defineComponent({
};
},
legends() {
return this.chartData.labels.map((label, index) => ({
label: label,
color: this.chartData.datasets[0].borderColor[index],
value: this.formatValue(this.chartData.datasets[0].data[index]),
}));
return this.chartData.labels.map((label, index) => {
const dataset = this.chartData.datasets[0]!;
const dataValue = dataset.data[index] as number;
return {
label: label,
color: dataset.borderColor[index],
value: this.formatValue(dataValue),
};
});
},
options() {
return {

View file

@ -72,16 +72,17 @@ export default defineComponent({
};
},
legends() {
const total = this.chartData.datasets[0].data.reduce((acc, curr) => acc + curr, 0);
const dataset = this.chartData.datasets[0]!;
const total = dataset.data.reduce((acc, curr) => acc + curr, 0);
const fmtShare = (value: number) => this.fmtPercentage((100 / total) * value, 1);
return this.chartData.labels.map((label, index) => ({
label: label,
color: this.chartData.datasets[0].backgroundColor[index],
value: [
this.formatValue(this.chartData.datasets[0].data[index]),
fmtShare(this.chartData.datasets[0].data[index]),
],
}));
return this.chartData.labels.map((label, index) => {
const dataValue = dataset.data[index] as number;
return {
label: label,
color: dataset.backgroundColor[index],
value: [this.formatValue(dataValue), fmtShare(dataValue)],
};
});
},
options() {
return {

View file

@ -60,7 +60,7 @@ export default defineComponent({
if (this.sessions.length === 0) {
return null;
}
return new Date(this.sessions[0].created);
return new Date(this.sessions[0]!.created);
},
month() {
return (this.firstDay?.getMonth() || 0) + 1;
@ -72,7 +72,7 @@ export default defineComponent({
if (this.sessions.length === 0) {
return null;
}
return new Date(this.sessions[this.sessions.length - 1].created);
return new Date(this.sessions[this.sessions.length - 1]!.created);
},
chartData(): ChartData<"bar", number[], unknown> {
console.log("update cost history data");
@ -133,11 +133,12 @@ export default defineComponent({
? session.price || 0
: (session.co2PerKWh || 0) * (session.chargedEnergy || 0);
result[index][groupKey] = (result[index][groupKey] || 0) + value;
const item = result[index]!;
item[groupKey] = (item[groupKey] || 0) + value;
result[index].totalCost = (result[index].totalCost || 0) + value;
result[index].totalKWh = (result[index].totalKWh || 0) + session.chargedEnergy;
result[index].avgCost = result[index].totalCost / result[index].totalKWh;
item.totalCost = (item.totalCost || 0) + value;
item.totalKWh = (item.totalKWh || 0) + session.chargedEnergy;
item.avgCost = item.totalCost / item.totalKWh;
});
}
@ -257,7 +258,7 @@ export default defineComponent({
},
callbacks: {
title: (tooltipItem: TooltipItem<"bar">[]) => {
const { label } = tooltipItem[0];
const { label } = tooltipItem[0] || { label: "" };
if (this.period === PERIODS.TOTAL) {
return label;
} else if (this.period === PERIODS.YEAR) {

View file

@ -201,7 +201,10 @@ export default defineComponent({
},
emitMonthYear(monthYear: string) {
const [year, month] = monthYear.split("-");
this.$emit("update-date", { year: parseInt(year), month: parseInt(month) });
this.$emit("update-date", {
year: parseInt(year || "0"),
month: parseInt(month || "0"),
});
},
emitYear(year: number) {
this.$emit("update-date", { year, month: undefined });

View file

@ -78,21 +78,22 @@ export default defineComponent({
};
},
legends() {
const total = this.chartData.datasets[0].data.reduce((acc, curr) => acc + curr, 0);
const maxEnergy = Math.max(...this.chartData.datasets[0].data);
const dataset = this.chartData.datasets[0]!;
const total = dataset.data.reduce((acc, curr) => acc + curr, 0);
const maxEnergy = Math.max(...dataset.data);
// sync energy units for label grid view
const unit =
maxEnergy < 1 ? POWER_UNIT.W : maxEnergy > 1e4 ? POWER_UNIT.MW : POWER_UNIT.KW;
const fmtShare = (value: number) => this.fmtPercentage((100 / total) * value, 1);
const fmtValue = (value: number) => this.fmtWh(value * 1e3, unit);
return this.chartData.labels.map((label, index) => ({
label: label,
color: this.chartData.datasets[0].backgroundColor[index],
value: [
fmtValue(this.chartData.datasets[0].data[index]),
fmtShare(this.chartData.datasets[0].data[index]),
],
}));
return this.chartData.labels.map((label, index) => {
const dataValue = dataset.data[index] as number;
return {
label: label,
color: dataset.backgroundColor[index],
value: [fmtValue(dataValue), fmtShare(dataValue)],
};
});
},
options() {
return {

View file

@ -45,7 +45,7 @@ export default defineComponent({
if (this.sessions.length === 0) {
return null;
}
return new Date(this.sessions[0].created);
return new Date(this.sessions[0]!.created);
},
month() {
return (this.firstDay?.getMonth() || 0) + 1;
@ -57,7 +57,7 @@ export default defineComponent({
if (this.sessions.length === 0) {
return null;
}
return new Date(this.sessions[this.sessions.length - 1].created);
return new Date(this.sessions[this.sessions.length - 1]!.created);
},
chartData(): ChartData<"bar", number[], unknown> {
console.log("update energy history data");
@ -106,13 +106,14 @@ export default defineComponent({
const charged = session.chargedEnergy;
const self = (charged / 100) * session.solarPercentage;
const grid = charged - self;
result[index]["self"] = (result[index]["self"] || 0) + self;
result[index]["grid"] = (result[index]["grid"] || 0) + grid;
const item = result[index]!;
item["self"] = (item["self"] || 0) + self;
item["grid"] = (item["grid"] || 0) + grid;
} else {
const groupKey = session[this.groupBy];
groups.add(groupKey);
result[index][groupKey] =
(result[index][groupKey] || 0) + session.chargedEnergy;
result[index]![groupKey] =
(result[index]![groupKey] || 0) + session.chargedEnergy;
}
});
}
@ -186,7 +187,7 @@ export default defineComponent({
},
callbacks: {
title: (tooltipItem: TooltipItem<"bar">[]) => {
const { label } = tooltipItem[0];
const { label } = tooltipItem[0] || { label: "" };
if (this.period === PERIODS.TOTAL) {
return label;
} else if (this.period === PERIODS.YEAR) {

View file

@ -75,11 +75,15 @@ export default defineComponent({
};
},
legends() {
return this.chartData.labels.map((label, index) => ({
label: label,
color: this.chartData.datasets[0].borderColor[index],
value: this.fmtPercentage(this.chartData.datasets[0].data[index], 1),
}));
const dataset = this.chartData.datasets[0]!;
return this.chartData.labels.map((label, index) => {
const dataValue = dataset.data[index] as number;
return {
label: label,
color: dataset.borderColor[index],
value: this.fmtPercentage(dataValue, 1),
};
});
},
options() {
return {
@ -101,7 +105,7 @@ export default defineComponent({
title: () => null,
label: (tooltipItem: TooltipItem<"polarArea">) => {
const { label, dataset, dataIndex } = tooltipItem;
const d = dataset.data[dataIndex];
const d = dataset.data[dataIndex] as number;
return label + ": " + this.fmtPercentage(d, 1);
},

View file

@ -41,13 +41,13 @@ export default defineComponent({
if (this.sessions.length === 0) {
return null;
}
return new Date(this.sessions[0].created);
return new Date(this.sessions[0]!.created);
},
lastDay() {
if (this.sessions.length === 0) {
return null;
}
return new Date(this.sessions[this.sessions.length - 1].created);
return new Date(this.sessions[this.sessions.length - 1]!.created);
},
chartData() {
console.log("update solar month data");
@ -84,22 +84,24 @@ export default defineComponent({
const charged = session.chargedEnergy;
const self = (charged / 100) * session.solarPercentage;
const grid = charged - self;
result[year][month].self += self;
result[year][month].grid += grid;
const monthData = result[year]![month]!;
monthData.self += self;
monthData.grid += grid;
});
const datasets = years.map((year) => {
const borderColor = colors.selfPalette[years.indexOf(year)];
const backgroundColor = years.length === 1 ? dimColor(borderColor) : "transparent";
const borderColor = colors.selfPalette[years.indexOf(year)] || undefined;
const backgroundColor =
years.length === 1 && borderColor ? dimColor(borderColor) : "transparent";
return {
backgroundColor,
borderColor,
label: year,
data: Object.values(result[year]).map(({ self = 0, grid = 0 }) => {
data: Object.values(result[year] || {}).map(({ self = 0, grid = 0 }) => {
const total = self + grid;
return total === 0 ? null : (self / total) * 100;
}),
yearData: Object.values(result[year]).reduce(
yearData: Object.values(result[year] || {}).reduce(
(acc, { self = 0, grid = 0 }) => ({
self: acc.self + self,
grid: acc.grid + grid,
@ -109,7 +111,7 @@ export default defineComponent({
};
});
const labels = Object.keys(result[firstYear]).map((month) =>
const labels = Object.keys(result[firstYear] || {}).map((month) =>
this.fmtMonth(new Date(firstYear, parseInt(month) - 1, 1), true)
);
@ -132,18 +134,14 @@ export default defineComponent({
};
});
} else {
const dataset = this.chartData.datasets[0]!;
return this.chartData.labels.map((label, index) => {
const value = this.chartData.datasets[0].data[index];
const value = dataset.data[index];
return {
label,
color: null,
value:
value === null
? "- %"
: this.fmtPercentage(
this.chartData.datasets[0].data[index] || 0,
1
),
value === null ? "- %" : this.fmtPercentage((value as number) || 0, 1),
};
});
}

View file

@ -91,13 +91,13 @@ export default defineComponent({
const excludeIndices = [this.leftIconIndex, this.centerIconIndex, this.rightIconIndex];
switch (this.updatePosition) {
case 0:
this.leftIconIndex = this.getRandomIcon(excludeIndices);
this.leftIconIndex = this.getRandomIcon(excludeIndices) || 0;
break;
case 1:
this.centerIconIndex = this.getRandomIcon(excludeIndices);
this.centerIconIndex = this.getRandomIcon(excludeIndices) || 0;
break;
case 2:
this.rightIconIndex = this.getRandomIcon(excludeIndices);
this.rightIconIndex = this.getRandomIcon(excludeIndices) || 0;
break;
}
this.updatePosition = (this.updatePosition + Math.ceil(Math.random() * 2)) % 3;

View file

@ -247,7 +247,7 @@ export default defineComponent({
return this.fmtCostRange(this.costRange(this.activeSlots));
},
activeSlot(): Slot | null {
return this.activeIndex !== null ? this.slots[this.activeIndex] : null;
return this.activeIndex !== null ? this.slots[this.activeIndex] || null : null;
},
activeSlotCost() {
const value = this.activeSlot?.value;
@ -343,7 +343,7 @@ export default defineComponent({
this.activeIndex = index;
},
slotSelected(index: number) {
const value = this.slots[index].value;
const value = this.slots[index]?.value;
if (value !== undefined) {
// 3 decimal precision
const valueRounded = Math.ceil(value * 1000) / 1000;

View file

@ -117,7 +117,8 @@ export default defineComponent({
return this.uniqueNames.length == 1;
},
singleIcon() {
return icons[this.uniqueNames[0]] || `shopicon-regular-car3`;
const firstName = this.uniqueNames[0];
return (firstName && icons[firstName]) || `shopicon-regular-car3`;
},
},
});

View file

@ -199,7 +199,8 @@ export default defineComponent({
const rtf = new Intl.RelativeTimeFormat(this.$i18n?.locale, {
numeric: "auto",
});
return rtf.formatToParts(1, "day")[0].value;
const part = rtf.formatToParts(1, "day")[0];
return part?.value || "";
} catch (e) {
console.warn("weekdayPrefix: Intl.RelativeTimeFormat not supported", e);
return "tomorrow";

View file

@ -17,22 +17,22 @@ export function cleanYaml(text: string, removeKey: string) {
const lines = text.split("\n");
// remove first comment lines
while (lines[0].startsWith("#")) lines.shift();
while (lines[0]?.startsWith("#")) lines.shift();
const [firstLine, ...restLines] = lines;
if (!firstLine.startsWith(prefix)) {
if (!firstLine?.startsWith(prefix)) {
// does not start with key, skip
return text;
} else {
const first = firstLine.slice(prefix.length).trim();
const first = firstLine?.slice(prefix.length).trim();
if (first) {
result.push(first);
}
}
if (restLines.length > 0) {
const indentChars = restLines[0].match(/^(\s+)/)?.[0] || "";
const indentChars = restLines[0]?.match(/^(\s+)/)?.[0] || "";
restLines
.map((l) => (l.startsWith(indentChars) ? l.slice(indentChars.length) : l))
.map((l) => l.trimEnd())

View file

@ -40,7 +40,8 @@ export function highestSlotIndexByDay(entries: TimeseriesEntry[], day: number =
const dayString = dayStringByOffset(day);
const dayEntries = filterEntriesByDate(entries, dayString);
const sortedEntries = dayEntries.sort((a, b) => b.val - a.val);
const highestEntry = sortedEntries[0] || {};
const highestEntry = sortedEntries[0];
if (!highestEntry) return -1;
return entries.findIndex((entry) => entry.ts === highestEntry.ts);
}

View file

@ -430,6 +430,7 @@ import VehicleIcon from "../components/VehicleIcon";
import VehicleModal from "../components/Config/VehicleModal.vue";
import { defineComponent } from "vue";
import type {
Circuit,
ConfigCharger,
ConfigVehicle,
ConfigCircuit,
@ -1007,7 +1008,8 @@ export default defineComponent({
},
circuitTags(circuit: ConfigCircuit) {
const circuits = store.state?.circuits || {};
const data = circuits[circuit.name] || {};
const data =
(circuits[circuit.name] as Circuit | undefined) || ({} as Partial<Circuit>);
const result: Record<string, object> = {};
const p = data.power || 0;
if (data.maxPower) {

View file

@ -164,7 +164,8 @@ export default defineComponent({
occurrences.set(key, count + 1);
key = `${key}-${count + 1}`;
const className = `log log-${levelMatcher.exec(line)?.[1].toLowerCase() || "none"}`;
const match = levelMatcher.exec(line)?.[1];
const className = `log log-${match?.toLowerCase() || "none"}`;
return { key, className, line };
});

View file

@ -184,11 +184,11 @@ export default defineComponent({
if (!this.evopt?.res.batteries) return [];
return this.evopt.res.batteries.map(
(_, index) => colors.palette[index % colors.palette.length]
(_, index) => colors.palette[index % colors.palette.length] || ""
);
},
dimmedBatteryColors() {
return this.batteryColors.map((color) => this.dimColorBy25Percent(color));
return (this.batteryColors || []).map((color) => this.dimColorBy25Percent(color));
},
formattedRequest() {
return this.evopt?.req ? formatCompactJson(this.evopt.req) : "";

View file

@ -519,8 +519,8 @@ export default defineComponent({
// Assign colors by used energy in the last three months
const sortedEntries = Object.entries(energyAggregation).sort((a, b) => b[1] - a[1]);
sortedEntries.forEach(([key]) => {
if (!result[key]) {
result[key] = colors.palette[colorIndex % colors.palette.length];
if (key && !result[key]) {
result[key] = colors.palette[colorIndex % colors.palette.length] || "";
colorIndex++;
}
});
@ -528,8 +528,8 @@ export default defineComponent({
// Assign colors to remaining entries
this.sessionsWithDefaults.forEach((session) => {
const key = session[colorType];
if (!result[key]) {
result[key] = colors.palette[colorIndex % colors.palette.length];
if (key && !result[key]) {
result[key] = colors.palette[colorIndex % colors.palette.length] || "";
colorIndex++;
}
});
@ -727,7 +727,7 @@ export default defineComponent({
if (arr.length === 0) return null;
const sorted = arr.sort((a, b) => a - b);
const index = (p / 100) * (sorted.length - 1);
return sorted[Math.floor(index)];
return sorted[Math.floor(index)] ?? null;
},
},
});

2754
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -17,60 +17,60 @@
"type": "module",
"main": "index.js",
"dependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/eslintrc": "^3.3.1",
"@eslint/js": "^9.13.0",
"@guolao/vue-monaco-editor": "^1.5.1",
"@h2d2/shopicons": "^1.2.0",
"@playwright/test": "^1.46.1",
"@popperjs/core": "^2.11.5",
"@guolao/vue-monaco-editor": "^1.5.5",
"@h2d2/shopicons": "^1.9.0",
"@playwright/test": "^1.55.0",
"@popperjs/core": "^2.11.8",
"@types/bootstrap": "^5.2.10",
"@types/canvas-confetti": "^1.9.0",
"@unhead/vue": "^2.0.8",
"@vitejs/plugin-legacy": "^6.0.1",
"@vitejs/plugin-vue": "^5.0.4",
"@unhead/vue": "^2.0.14",
"@vitejs/plugin-legacy": "^7.2.1",
"@vitejs/plugin-vue": "^6.0.1",
"@vue/compiler-sfc": "^3.2.30",
"@vue/eslint-config-prettier": "^10.1.0",
"@vue/eslint-config-typescript": "^14.1.3",
"@vue/test-utils": "^2.2.7",
"@vue/tsconfig": "^0.7.0",
"axios": "^1.7.4",
"@vue/eslint-config-prettier": "^10.2.0",
"@vue/eslint-config-typescript": "^14.6.0",
"@vue/test-utils": "^2.4.6",
"@vue/tsconfig": "^0.8.1",
"axios": "^1.11.0",
"body-parser": "^2.2.0",
"bootstrap": "^5.2.2",
"canvas-confetti": "^1.4.0",
"chart.js": "^4.4.4",
"bootstrap": "^5.3.8",
"canvas-confetti": "^1.9.3",
"chart.js": "^4.5.0",
"chartjs-adapter-dayjs-4": "^1.0.4",
"chartjs-plugin-datalabels": "^2.2.0",
"countup.js": "^2.3.2",
"dayjs": "^1.11.13",
"eslint": "^9.13.0",
"eslint-config-prettier": "^10.0.1",
"eslint-plugin-import": "^2.25.3",
"countup.js": "^2.9.0",
"dayjs": "^1.11.18",
"eslint": "^9.35.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.2.1",
"eslint-plugin-promise": "^7.1.0",
"eslint-plugin-vue": "^9.3.0",
"globals": "^16.0.0",
"eslint-plugin-prettier": "^5.5.4",
"eslint-plugin-promise": "^7.2.1",
"eslint-plugin-vue": "^10.4.0",
"globals": "^16.3.0",
"happy-dom": "^18.0.1",
"kill-port": "^2.0.1",
"monaco-editor": "^0.52.2",
"mqtt": "^5.11.0",
"prettier": "^3.0.0",
"mqtt": "^5.14.1",
"prettier": "^3.6.2",
"prettier-plugin-sh": "^0.18.0",
"rollup-plugin-visualizer": "^6.0.1",
"rollup-plugin-visualizer": "^6.0.3",
"smoothscroll-polyfill": "^0.4.4",
"snarkdown": "^2.0.0",
"typescript": "^5.2.2",
"typescript": "^5.9.2",
"typescript-eslint-language-service": "^5.0.5",
"vite": "^6.1.0",
"vitest": "^3.0.5",
"vite": "^7.1.5",
"vitest": "^3.2.4",
"vue": "^3.2.29",
"vue-chartjs": "^5.3.1",
"vue-eslint-parser": "^9.1.0",
"vue-chartjs": "^5.3.2",
"vue-eslint-parser": "^10.2.0",
"vue-hot-reload-api": "^2.3.4",
"vue-i18n": "^11.1.10",
"vue-router": "^4.0.12",
"vue-tsc": "^2.2.8",
"wait-on": "^8.0.0"
"vue-i18n": "^11.1.12",
"vue-router": "^4.5.1",
"vue-tsc": "^3.0.6",
"wait-on": "^8.0.4"
},
"engines": {
"npm": ">=10.0.0",
@ -78,26 +78,17 @@
},
"license": "MIT",
"repository": "github:evcc-io/evcc",
"eslintConfig": {
"extends": [
"plugin:storybook/recommended"
]
},
"devDependencies": {
"@chromatic-com/storybook": "^3.2.5",
"@storybook/addon-essentials": "^8.6.4",
"@storybook/addon-interactions": "^8.6.4",
"@storybook/blocks": "^8.6.4",
"@storybook/test": "^8.6.4",
"@storybook/vue3": "^8.6.4",
"@storybook/vue3-vite": "^8.6.4",
"@chromatic-com/storybook": "^4.2.0-next.1",
"@storybook/vue3": "^10.0.0-beta.2",
"@storybook/vue3-vite": "^10.0.0-beta.2",
"@types/body-parser": "^1.19.6",
"@types/kill-port": "^2.0.3",
"@types/smoothscroll-polyfill": "^0.3.4",
"@types/wait-on": "^5.3.4",
"cross-env": "^10.0.0",
"eslint-plugin-storybook": "^0.12.0",
"prettier-plugin-sort-json": "^4.1.1",
"storybook": "^8.6.4"
"storybook": "^10.0.0-beta.2",
"terser": "^5.44.0"
}
}

View file

@ -72,7 +72,8 @@ const teslaloggerMiddleware = (
next: Connect.NextFunction
) => {
if (req.method === "GET" && req.originalUrl && req.originalUrl.startsWith("/currentjson/")) {
const id = parseInt(req.originalUrl.split("/")[2]);
const idPart = req.originalUrl.split("/")[2];
const id = idPart ? parseInt(idPart) : 0;
const vehicle = state.vehicles[id - 1];
if (!vehicle) {
res.statusCode = 404;

View file

@ -294,18 +294,24 @@ export default defineComponent({
},
addVehicle() {
// push a duplacate of the last entry
this.state?.vehicles.push({
...this.state.vehicles[this.state.vehicles.length - 1],
});
const vehicles = this.state?.vehicles;
if (!vehicles) return;
const lastVehicle = vehicles[vehicles.length - 1];
if (lastVehicle) {
vehicles.push({ ...lastVehicle });
}
},
removeVehicle(index: number) {
this.state?.vehicles.splice(index, 1);
},
addLoadpoint() {
// push a duplacate of the last entry
this.state?.loadpoints.push({
...this.state.loadpoints[this.state.loadpoints.length - 1],
});
const loadpoints = this.state?.loadpoints;
if (!loadpoints) return;
const lastLoadpoint = loadpoints[loadpoints.length - 1];
if (lastLoadpoint) {
loadpoints.push({ ...lastLoadpoint });
}
},
removeLoadpoint(index: number) {
this.state?.loadpoints.splice(index, 1);