chore: Typescript: Rewrite remaining components (#21638)

Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: Michael Geers <michael@geers.tv>
This commit is contained in:
Maschga 2025-06-20 16:34:52 +02:00 • committed by GitHub
parent f927d5fa18
commit 4e619580ed
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 105 additions and 56 deletions

View file

@ -136,13 +136,14 @@
</Teleport>
</template>
<script>
<script lang="ts">
import Modal from "bootstrap/js/dist/modal";
import { docsPrefix } from "../i18n";
import { performRestart } from "../restart";
import { isLoggedIn, openLoginModal } from "./Auth/auth";
import { defineComponent } from "vue";
export default {
export default defineComponent({
name: "HelpModal",
props: {},
computed: {
@ -152,11 +153,15 @@ export default {
},
methods: {
openHelpModal() {
const modal = Modal.getOrCreateInstance(document.getElementById("helpModal"));
const modal = Modal.getOrCreateInstance(
document.getElementById("helpModal") as HTMLElement
);
modal.show();
},
openConfirmRestartModal() {
const modal = Modal.getOrCreateInstance(document.getElementById("confirmRestartModal"));
const modal = Modal.getOrCreateInstance(
document.getElementById("confirmRestartModal") as HTMLElement
);
if (!isLoggedIn()) {
openLoginModal(null, modal);
} else {
@ -167,5 +172,5 @@ export default {
await performRestart();
},
},
};
});
</script>

View file

@ -26,7 +26,7 @@ export default defineComponent({
props: {
id: String,
options: Array as PropType<SelectOption<string>[]>,
modelValue: [Number, String, Boolean],
modelValue: [Number, String, Boolean, null],
equalWidth: Boolean,
large: Boolean,
transparent: Boolean,

View file

@ -32,7 +32,7 @@ import LabelAndValue from "../Helper/LabelAndValue.vue";
import CustomSelect from "../Helper/CustomSelect.vue";
import formatter from "@/mixins/formatter";
import { getSessionInfo, setSessionInfo } from "./session";
import type { CURRENCY, SelectOption } from "@/types/evcc";
import type { CURRENCY, SelectOption, SessionInfoKey } from "@/types/evcc";
export default defineComponent({
name: "LoadpointSessionInfo",
@ -59,39 +59,44 @@ export default defineComponent({
};
},
computed: {
options() {
options(): Array<{
key: SessionInfoKey;
value: string;
valueSm?: string;
available?: boolean;
}> {
const result = [
{
key: "remaining",
key: "remaining" as const,
value: this.fmtDuration(this.chargeRemainingDurationInterpolated),
available: this.chargeRemainingDurationInterpolated > 0,
},
{
key: "finished",
key: "finished" as const,
value: this.fmtHourMinute(this.finishTime),
available: this.chargeRemainingDurationInterpolated > 0,
},
{
key: "duration",
key: "duration" as const,
value: this.fmtDuration(this.chargeDurationInterpolated),
},
{
key: "solar",
key: "solar" as const,
value: this.solarFormatted,
},
{
key: "avgPrice",
key: "avgPrice" as const,
value: this.fmtAvgPrice(this.sessionPricePerKWh),
valueSm: this.fmtAvgPriceShort(this.sessionPricePerKWh),
available: this.tariffGrid !== undefined,
},
{
key: "price",
key: "price" as const,
value: this.priceFormatted,
available: this.tariffGrid !== undefined,
},
{
key: "co2",
key: "co2" as const,
value: this.fmtCo2Medium(this.sessionCo2PerKWh),
valueSm: this.fmtCo2Short(this.sessionCo2PerKWh),
available: this.tariffCo2 !== undefined,
@ -100,10 +105,10 @@ export default defineComponent({
// only show options that are available
return result.filter(({ available }) => available === undefined || available);
},
optionKeys() {
optionKeys(): SessionInfoKey[] {
return this.options.map((option) => option.key);
},
selectOptions(): SelectOption<string>[] {
selectOptions(): SelectOption<SessionInfoKey>[] {
return this.optionKeys.map((key) => ({
name: this.$t(`main.loadpoint.${key}`),
value: key,
@ -151,16 +156,18 @@ export default defineComponent({
return this.fmtPricePerKWh(value, this.currency, true);
},
nextSessionInfo() {
const index = this.optionKeys.indexOf(this.selectedKey);
const index = this.selectedKey ? this.optionKeys.indexOf(this.selectedKey) : -1;
this.selectedKey = this.optionKeys[index + 1] || this.optionKeys[0];
this.presist();
},
selectOption(value: string) {
selectOption(value: SessionInfoKey) {
this.selectedKey = value;
this.presist();
},
presist() {
setSessionInfo(this.id, this.selectedKey);
if (this.selectedKey) {
setSessionInfo(this.id, this.selectedKey);
}
},
},
});

View file

@ -1,10 +1,11 @@
import settings from "@/settings";
import type { SessionInfoKey } from "@/types/evcc";
export function getSessionInfo(index: number, fallback?: string) {
return settings.sessionInfo[index - 1] || fallback;
export function getSessionInfo(index: number): SessionInfoKey | undefined {
return settings.sessionInfo[index - 1];
}
export function setSessionInfo(index: number, value: string) {
export function setSessionInfo(index: number, value: SessionInfoKey) {
const clone = [...settings.sessionInfo];
clone[index - 1] = value;
clone.map((v) => v || "");

View file

@ -209,7 +209,7 @@ import Tile from "./Tile.vue";
import LiveCommunity from "./LiveCommunity.vue";
import TelemetrySettings from "../TelemetrySettings.vue";
import CustomSelect from "../Helper/CustomSelect.vue";
import settings from "@/settings";
import settings from "@/settings.ts";
import api, { allowClientError } from "@/api.ts";
import { docsPrefix } from "@/i18n.ts";
import { defineComponent, type PropType } from "vue";

View file

@ -161,7 +161,7 @@ import { defineComponent, type PropType } from "vue";
import CustomSelect from "../Helper/CustomSelect.vue";
import formatter, { POWER_UNIT } from "@/mixins/formatter";
import breakpoint from "@/mixins/breakpoint.ts";
import settings from "@/settings";
import settings from "@/settings.ts";
import type { CURRENCY } from "@/types/evcc";
import type { Session, Column } from "./types";
@ -187,7 +187,7 @@ export default defineComponent({
emits: ["show-session"],
data() {
return {
selectedColumns: settings.sessionColumns,
selectedColumns: [] as string[],
};
},
computed: {
@ -417,7 +417,7 @@ export default defineComponent({
.filter(this.filterByVehicle)
.filter((s) => !loadpoint || s.loadpoint === loadpoint).length;
},
selectColumnPosition(index: number, value: Column) {
selectColumnPosition(index: number, value: string) {
this.selectedColumns[index] = value;
settings.sessionColumns = [...this.selectedColumns];
},

View file

@ -30,12 +30,14 @@
</div>
</template>
<script>
<script lang="ts">
import { defineComponent } from "vue";
import api from "../api";
import { docsPrefix } from "../i18n";
import settings from "../settings";
import settings from "@/settings.ts";
import type { AxiosError } from "axios";
function parseMarkdown(markdownText) {
function parseMarkdown(markdownText: string) {
const htmlText = markdownText
.replace(/\*\*(.*)\*\*/gim, "<b>$1</b>")
.replace(/\*(.*)\*/gim, "<i>$1</i>")
@ -43,12 +45,12 @@ function parseMarkdown(markdownText) {
return htmlText.trim();
}
export default {
export default defineComponent({
name: "TelemetrySettings",
props: { sponsorActive: Boolean },
data() {
return {
error: null,
error: null as string | null,
};
},
computed: {
@ -63,14 +65,17 @@ export default {
await this.update();
},
methods: {
async change(e) {
async change(e: Event) {
try {
this.error = null;
const response = await api.post(`settings/telemetry/${e.target.checked}`);
const response = await api.post(
`settings/telemetry/${(e.target as HTMLInputElement).checked}`
);
settings.telemetry = response.data.result;
} catch (err) {
if (err.response) {
this.error = parseMarkdown("**Error:** " + err.response.data.error);
const e = err as AxiosError<{ error: string }>;
if (e.response) {
this.error = parseMarkdown("**Error:** " + e.response.data.error);
settings.telemetry = false;
}
}
@ -91,7 +96,7 @@ export default {
}
},
},
};
});
</script>
<style scoped>
.form-check {

View file

@ -48,11 +48,11 @@ export function getLocalePreference() {
export function removeLocalePreference(i18n: VueI18nInstance) {
settings.locale = null;
setI18nLanguage(i18n, DEFAULT_BROWSER_LOCALE as typeof i18n.locale);
setI18nLanguage(i18n, DEFAULT_BROWSER_LOCALE as keyof typeof LOCALES);
ensureCurrentLocaleMessages(i18n);
}
export function setLocalePreference(i18n: VueI18nInstance, locale: typeof i18n.locale) {
export function setLocalePreference(i18n: VueI18nInstance, locale: keyof typeof LOCALES) {
if (!(locale in LOCALES)) {
console.error("unknown locale", locale);
return;

View file

@ -1,4 +1,6 @@
import { reactive, watch } from "vue";
import type { THEME, SessionInfoKey } from "./types/evcc";
import type { LOCALES } from "./i18n";
const SETTINGS_LOCALE = "settings_locale";
const SETTINGS_THEME = "settings_theme";
@ -16,12 +18,12 @@ const SAVINGS_REGION = "savings_region";
const SESSIONS_GROUP = "sessions_group";
const SESSIONS_TYPE = "sessions_type";
const SETTINGS_SOLAR_ADJUSTED = "settings_solar_adjusted";
function read(key) {
function read(key: string) {
return window.localStorage[key];
}
function save(key) {
return (value) => {
function save(key: string) {
return (value: string | null) => {
try {
if (value) {
window.localStorage[key] = value;
@ -34,29 +36,49 @@ function save(key) {
};
}
function readBool(key) {
function readBool(key: string) {
return read(key) === "true";
}
function saveBool(key) {
return (value) => {
function saveBool(key: string) {
return (value: boolean) => {
save(key)(value ? "true" : "false");
};
}
function readArray(key) {
function readArray(key: string) {
const value = read(key);
return value ? value.split(",") : [];
}
function saveArray(key) {
return (value) => {
function saveArray(key: string) {
return (value: string[]) => {
save(key)(value.join(","));
};
}
const settings = reactive({
telemetry: null,
export interface Settings {
telemetry: boolean;
locale: keyof typeof LOCALES | null;
theme: THEME | null;
unit: string;
is12hFormat: boolean;
hiddenFeatures: boolean;
energyflowDetails: boolean;
energyflowPv: boolean;
energyflowBattery: boolean;
energyflowLoadpoints: boolean;
sessionInfo: SessionInfoKey[];
sessionColumns: string[];
savingsPeriod: string;
savingsRegion: string;
sessionsGroup: string;
sessionsType: string;
solarAdjusted: boolean;
}
const settings: Settings = reactive({
telemetry: false,
locale: read(SETTINGS_LOCALE),
theme: read(SETTINGS_THEME),
unit: read(SETTINGS_UNIT),
@ -66,7 +88,7 @@ const settings = reactive({
energyflowPv: readBool(SETTINGS_ENERGYFLOW_PV),
energyflowBattery: readBool(SETTINGS_ENERGYFLOW_BATTERY),
energyflowLoadpoints: readBool(SETTINGS_ENERGYFLOW_LOADPOINTS),
sessionInfo: readArray(SESSION_INFO),
sessionInfo: readArray(SESSION_INFO) as SessionInfoKey[],
sessionColumns: readArray(SESSION_COLUMNS),
savingsPeriod: read(SAVINGS_PERIOD),
savingsRegion: read(SAVINGS_REGION),
@ -85,7 +107,7 @@ watch(() => settings.energyflowPv, saveBool(SETTINGS_ENERGYFLOW_PV));
watch(() => settings.energyflowBattery, saveBool(SETTINGS_ENERGYFLOW_BATTERY));
watch(() => settings.energyflowLoadpoints, saveBool(SETTINGS_ENERGYFLOW_LOADPOINTS));
watch(() => settings.sessionInfo, saveArray(SESSION_INFO));
watch(() => settings.sessionColumns, saveArray(SESSION_COLUMNS));
watch(() => settings.sessionColumns as string[], saveArray(SESSION_COLUMNS));
watch(() => settings.savingsPeriod, save(SAVINGS_PERIOD));
watch(() => settings.savingsRegion, save(SAVINGS_REGION));
watch(() => settings.sessionsGroup, save(SESSIONS_GROUP));

View file

@ -3,9 +3,9 @@ import { THEME } from "./types/evcc";
const darkModeMatcher = window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)");
export function getThemePreference(): THEME {
export function getThemePreference(): THEME | null {
const theme = settings.theme;
if (Object.values(THEME).includes(theme)) {
if (theme && Object.values(THEME).includes(theme)) {
return theme;
}
return THEME.AUTO;
@ -19,10 +19,10 @@ export function setThemePreference(theme: THEME) {
updateTheme();
}
function setMetaThemeColor(theme: Exclude<THEME, THEME.AUTO>) {
function setMetaThemeColor(theme: Exclude<THEME, THEME.AUTO> | null) {
const themeColors = { light: "#f3f3f7", dark: "#020318" };
const $metaThemeColor = document.querySelector("meta[name=theme-color]");
if ($metaThemeColor) {
if ($metaThemeColor && theme) {
$metaThemeColor.setAttribute("content", themeColors[theme]);
}
}

View file

@ -95,6 +95,15 @@ export enum PHASES {
THREE_PHASES = 3,
}
export type SessionInfoKey =
| "remaining"
| "finished"
| "duration"
| "solar"
| "avgPrice"
| "price"
| "co2";
export interface Sponsor {
name: string;
expiresAt: string;