UI: force plan start rerender on day change (#26479)

This commit is contained in:
Michael Geers 2026-01-07 09:43:11 +01:00 • committed by GitHub
parent a4d704164b
commit 63ba00ba73
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 51 additions and 41 deletions

View file

@ -92,10 +92,11 @@ import Arrival from "./Arrival.vue";
import formatter from "@/mixins/formatter";
import collector from "@/mixins/collector";
import minuteTicker from "@/mixins/minuteTicker";
import api from "@/api";
import { optionStep, fmtEnergy } from "@/utils/energyOptions.ts";
import { defineComponent, type PropType } from "vue";
import type { CURRENCY, Timeout, Vehicle } from "@/types/evcc";
import type { CURRENCY, Vehicle } from "@/types/evcc";
import type {
StaticPlan,
StaticSocPlan,
@ -104,7 +105,6 @@ import type {
PlanStrategy,
} from "./types";
import type { Forecast } from "@/types/evcc.ts";
const ONE_MINUTE = 60 * 1000;
export default defineComponent({
name: "ChargingPlan",
@ -114,7 +114,7 @@ export default defineComponent({
ChargingPlansSettings: PlansSettings,
ChargingPlanArrival: Arrival,
},
mixins: [formatter, collector],
mixins: [formatter, collector, minuteTicker],
props: {
currency: String as PropType<CURRENCY>,
disabled: Boolean,
@ -148,7 +148,6 @@ export default defineComponent({
isModalVisible: false,
activeTab: "departure",
targetTimeLabel: "",
interval: null as Timeout,
};
},
computed: {
@ -243,6 +242,9 @@ export default defineComponent({
effectivePlanTime(): void {
this.updateTargetTimeLabel();
},
everyMinute(): void {
this.updateTargetTimeLabel();
},
"$i18n.locale": {
handler(): void {
this.updateTargetTimeLabel();
@ -250,14 +252,8 @@ export default defineComponent({
},
},
mounted(): void {
this.interval = setInterval(this.updateTargetTimeLabel, ONE_MINUTE);
this.updateTargetTimeLabel();
},
unmounted(): void {
if (this.interval) {
clearInterval(this.interval);
}
},
methods: {
modalVisible(): void {
this.isModalVisible = true;

View file

@ -38,13 +38,14 @@
<script lang="ts">
import { defineComponent, type PropType } from "vue";
import formatter from "@/mixins/formatter";
import minuteTicker from "@/mixins/minuteTicker";
import TariffChart from "../Tariff/TariffChart.vue";
import { SMART_COST_TYPE, type CURRENCY, type Rate, type Slot } from "@/types/evcc";
export default defineComponent({
name: "ChargingPlanPreview",
components: { TariffChart },
mixins: [formatter],
mixins: [formatter, minuteTicker],
props: {
duration: Number,
power: Number,
@ -55,7 +56,10 @@ export default defineComponent({
currency: String as PropType<CURRENCY>,
},
data() {
return { activeIndex: null as number | null, startTime: new Date() };
return {
activeIndex: null as number | null,
startTime: new Date(),
};
},
computed: {
endTime(): Date | null {
@ -172,6 +176,9 @@ export default defineComponent({
rates(): void {
this.startTime = new Date();
},
everyMinute(): void {
this.startTime = new Date();
},
},
methods: {
convertDates(list: Rate[] | undefined): Rate[] {

View file

@ -65,8 +65,9 @@
<script lang="ts">
import { defineComponent, type PropType } from "vue";
import formatter, { POWER_UNIT } from "@/mixins/formatter";
import minuteTicker from "@/mixins/minuteTicker";
import AnimatedNumber from "../Helper/AnimatedNumber.vue";
import type { CURRENCY, Timeout } from "@/types/evcc";
import type { CURRENCY } from "@/types/evcc";
import { ForecastType, findLowestSumSlotIndex } from "@/utils/forecast";
import type { ForecastSlot, SolarDetails } from "./types";
const LOCALES_WITHOUT_DAY_AFTER_TOMORROW = ["en", "tr"];
@ -90,7 +91,7 @@ export default defineComponent({
components: {
AnimatedNumber,
},
mixins: [formatter],
mixins: [formatter, minuteTicker],
props: {
type: { type: String as () => ForecastType, required: true },
grid: { type: Array as PropType<ForecastSlot[]> },
@ -101,7 +102,6 @@ export default defineComponent({
data() {
return {
now: new Date(),
interval: null as Timeout,
};
},
computed: {
@ -155,16 +155,13 @@ export default defineComponent({
}
},
},
watch: {
everyMinute(): void {
this.now = new Date();
},
},
mounted() {
this.now = new Date();
this.interval = setInterval(() => {
this.now = new Date();
}, 1000 * 60);
},
beforeUnmount() {
if (this.interval) {
clearInterval(this.interval);
}
},
methods: {
label(key: string) {

View file

@ -55,8 +55,9 @@ import "@h2d2/shopicons/es/regular/angledoublerightsmall";
import "@h2d2/shopicons/es/regular/clock";
import { DEFAULT_LOCALE } from "@/i18n.ts";
import formatter from "@/mixins/formatter";
import minuteTicker from "@/mixins/minuteTicker";
import { defineComponent, type PropType } from "vue";
import { SMART_COST_TYPE, type CURRENCY, type Timeout } from "@/types/evcc";
import { SMART_COST_TYPE, type CURRENCY } from "@/types/evcc";
import ClimaterIcon from "../MaterialIcon/Climater.vue";
import DynamicPriceIcon from "../MaterialIcon/DynamicPrice.vue";
@ -85,7 +86,7 @@ export default defineComponent({
components: {
StatusItem,
},
mixins: [formatter],
mixins: [formatter, minuteTicker],
props: {
vehicleSoc: { type: Number, default: 0 },
batteryBoostActive: Boolean,
@ -126,11 +127,6 @@ export default defineComponent({
vehicleLimitSoc: { type: Number, default: 0 },
},
emits: ["open-loadpoint-settings", "open-minsoc-settings", "open-plan-modal"],
data() {
return {
interval: null as Timeout,
};
},
computed: {
pvTimerActive() {
return (
@ -217,6 +213,9 @@ export default defineComponent({
const t = (key: string, params?: Record<string, unknown>) =>
this.$t(`main.vehicleStatus.${key}`, params ?? {});
// ensure periodic recomputation even without data change
void this.everyMinute;
const items = [
{
id: "pvTimer",
@ -394,17 +393,6 @@ export default defineComponent({
return items.filter((item) => item.visible);
},
},
mounted() {
this.interval = setInterval(() => {
// Force reactivity update for time-dependent content
this.$forceUpdate();
}, 1000 * 60);
},
beforeUnmount() {
if (this.interval) {
clearInterval(this.interval);
}
},
methods: {
openLoadpointSettings() {
this.$emit("open-loadpoint-settings");

View file

@ -0,0 +1,22 @@
import { defineComponent } from "vue";
import type { Timeout } from "@/types/evcc";
export default defineComponent({
data() {
return {
everyMinute: new Date(),
minuteInterval: null as Timeout,
};
},
mounted() {
this.minuteInterval = setInterval(() => {
// force time-based computed data to update at least once a minute
this.everyMinute = new Date();
}, 1000 * 60);
},
beforeUnmount() {
if (this.minuteInterval) {
clearInterval(this.minuteInterval);
}
},
});