Plan UI: Show plan preview selection if plan has no duration (#20291)

Co-authored-by: Michael Geers <michael@geers.tv>
This commit is contained in:
Maschga 2025-04-02 19:38:23 +02:00 • committed by GitHub
parent 5fd22d44fa
commit 08b2267ef1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 25 additions and 21 deletions

View file

@ -51,6 +51,9 @@
{{ selectedPreviewPlanTitle }}
</span>
</CustomSelect>
<span v-else-if="alreadyReached">
{{ $t("main.targetCharge.goalReached") }}
</span>
<span v-else>
{{ nextPlanTitle }}
</span>
@ -168,6 +171,9 @@ export default defineComponent({
return options;
},
alreadyReached(): boolean {
return this.plan.duration === 0;
},
nextPlanTitle(): string {
return `${this.$t("main.targetCharge.nextPlan")} #${this.nextPlanId}`;
},
@ -175,14 +181,14 @@ export default defineComponent({
watch: {
effectivePlanTime(newValue: string) {
if (null !== newValue) {
this.fetchPlanDebounced();
this.updatePlanDebounced();
}
},
staticPlan: {
deep: true,
handler(vNew: StaticPlan, vOld: StaticPlan) {
if (!deepEqual(vNew, vOld)) {
this.fetchPlanDebounced();
this.updatePlanDebounced();
}
},
},
@ -191,20 +197,20 @@ export default defineComponent({
handler(vNew: RepeatingPlan[], vOld: RepeatingPlan[]) {
if (!deepEqual(vNew, vOld)) {
this.adjustPreviewId();
this.fetchPlanDebounced();
this.updatePlanDebounced();
}
},
},
},
mounted(): void {
this.fetchPlanDebounced();
this.updatePlanDebounced();
},
methods: {
selectPreviewPlan(id: number): void {
this.selectedPreviewId = id;
this.updatePlanPreviewDebounced();
this.updatePlanDebounced();
},
async fetchPlanDebounced() {
async updatePlanDebounced() {
if (this.noActivePlan) {
await this.updatePlanPreviewDebounced();
} else {
@ -217,6 +223,7 @@ export default defineComponent({
}
},
async updateActivePlan(): Promise<void> {
await this.updateTariff();
try {
const res = await this.apiFetchPlan(`loadpoints/${this.id}/plan`);
this.plan = res?.data.result ?? ({} as PlanWrapper);
@ -224,7 +231,6 @@ export default defineComponent({
} catch (e) {
console.error(e);
}
await this.updateTariff();
},
async fetchStaticPreviewSoc(plan: StaticSocPlan): Promise<PlanResponse | undefined> {
const timeISO = plan.time.toISOString();
@ -260,9 +266,11 @@ export default defineComponent({
}
},
async updatePreviewPlan(): Promise<void> {
// only show preview of no plan is active
// only show preview if no plan is active
if (!this.noActivePlan) return;
await this.updateTariff();
try {
let planRes: PlanResponse | undefined = undefined;
@ -295,7 +303,6 @@ export default defineComponent({
planRes = await this.fetchRepeatingPreview({ weekdays, soc, time, tz });
}
this.plan = planRes?.data.result ?? ({} as PlanWrapper);
await this.updateTariff();
} catch (e) {
console.error(e);
}

View file

@ -658,6 +658,7 @@ costLimitIgnore = "Die eingestellte {limit} wird in diesem Zeitraum ignoriert."
currentPlan = "Aktiver Plan"
descriptionEnergy = "Bis wann sollen {targetEnergy} ins Fahrzeug geladen sein?"
descriptionSoc = "Wann soll das Fahrzeug auf {targetSoc} geladen sein?"
goalReached = "Ladeziel bereits erreicht"
inactiveLabel = "Zielzeit"
nextPlan = "Nächster Plan"
notReachableInTime = "Zielzeit wird {overrun} später erreicht."

View file

@ -658,6 +658,7 @@ costLimitIgnore = "The configured {limit} will be ignored during this period."
currentPlan = "Active plan"
descriptionEnergy = "Until when should {targetEnergy} be loaded into the vehicle?"
descriptionSoc = "When should the vehicle be charged to {targetSoc}?"
goalReached = "Goal already reached"
inactiveLabel = "Target time"
nextPlan = "Next plan"
notReachableInTime = "Goal will be reached {overrun} later."

View file

@ -51,10 +51,6 @@ func planHandler(lp loadpoint.API) http.HandlerFunc {
goal, _ := lp.GetPlanGoal()
requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower)
plan := lp.GetPlan(planTime, requiredDuration)
if plan == nil {
w.WriteHeader(http.StatusNotFound)
return
}
res := struct {
PlanId int `json:"planId"`
@ -110,10 +106,6 @@ func staticPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
maxPower := lp.EffectiveMaxPower()
requiredDuration := lp.GetPlanRequiredDuration(goal, maxPower)
plan := lp.GetPlan(planTime, requiredDuration)
if plan == nil {
w.WriteHeader(http.StatusNotFound)
return
}
res := struct {
PlanTime time.Time `json:"planTime"`
@ -163,10 +155,6 @@ func repeatingPlanPreviewHandler(lp loadpoint.API) http.HandlerFunc {
maxPower := lp.EffectiveMaxPower()
requiredDuration := lp.GetPlanRequiredDuration(soc, maxPower)
plan := lp.GetPlan(planTime, requiredDuration)
if plan == nil {
w.WriteHeader(http.StatusNotFound)
return
}
res := struct {
PlanTime time.Time `json:"planTime"`

View file

@ -544,6 +544,13 @@ test.describe("repeating", async () => {
await plan2.getByTestId("repeating-plan-apply").click();
await expect(modal.getByTestId("plan-preview-title")).toHaveText("Next plan #1");
await expect(modal.getByTestId("target-text")).toContainText("9:30 AM");
// set lower targets than vehicle soc (50%)
await plan1.getByTestId("static-plan-soc").selectOption("40%");
await plan1.getByTestId("static-plan-apply").click();
await plan2.getByTestId("repeating-plan-soc").selectOption("40%");
await plan2.getByTestId("repeating-plan-apply").click();
await expect(modal.getByTestId("plan-preview-title")).toHaveText("Goal already reached");
});
test("repeating plan persistence", async ({ page }) => {