Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions assets/js/components/Helper/AnimatedNumber.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ const DURATION = 0.5;
export default defineComponent({
name: "AnimatedNumber",
props: {
to: { type: Number, default: 0 },
to: { type: [String, Number], default: 0 },
format: { type: Function as PropType<(n: number) => string>, required: true },
duration: { type: Number, default: DURATION },
},
Expand All @@ -30,8 +30,8 @@ export default defineComponent({
if (this.instance) {
return;
}
this.instance = new CountUp(this.$el, this.to, {
startVal: this.to,
this.instance = new CountUp(this.$el, Number(this.to), {
startVal: Number(this.to),
formattingFn: this.format,
duration: this.duration,
decimalPlaces: 3,
Expand All @@ -49,7 +49,7 @@ export default defineComponent({
methods: {
forceUpdate() {
this.instance?.reset();
this.update(this.to);
this.update(Number(this.to));
},
update(value: number) {
// debounced to avoid rendering issues
Expand Down
16 changes: 0 additions & 16 deletions assets/js/components/Savings/LiveCommunity.stories.js

This file was deleted.

17 changes: 17 additions & 0 deletions assets/js/components/Savings/LiveCommunity.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import LiveCommunity from "./LiveCommunity.vue";
import type { Meta, StoryFn } from "@storybook/vue3";

export default {
title: "Savings/LiveCommunity",
component: LiveCommunity,
parameters: {
layout: "centered",
},
} as Meta<typeof LiveCommunity>;

const Template: StoryFn<typeof LiveCommunity> = () => ({
components: { LiveCommunity },
template: "<LiveCommunity />",
});

export const Default = Template.bind({});
21 changes: 13 additions & 8 deletions assets/js/components/Savings/LiveCommunity.vue
Original file line number Diff line number Diff line change
Expand Up @@ -35,23 +35,26 @@
</div>
</template>

<script>
<script lang="ts">
import Tile from "./Tile.vue";

import formatter from "@/mixins/formatter";
import communityApi from "./communityApi";
import communityApi from "./communityApi.ts";
import { defineComponent } from "vue";
import type { Timeout } from "@/types/evcc";
import type { LiveCommunityData } from "./types";

const UPDATE_INTERVAL_SECONDS = 10;

export default {
export default defineComponent({
name: "LiveCommunity",
components: { SavingsTile: Tile },
mixins: [formatter],
props: {},
data() {
return {
refresh: null,
result: {},
refresh: null as Timeout,
result: {} as LiveCommunityData,
};
},
computed: {
Expand Down Expand Up @@ -88,7 +91,9 @@ export default {
await this.update();
},
unmounted() {
clearInterval(this.refresh);
if (this.refresh) {
clearInterval(this.refresh);
}
},
methods: {
async update() {
Expand All @@ -99,12 +104,12 @@ export default {
console.error(err);
}
},
fmtAnimation(number) {
fmtAnimation(number: number) {
let decimals = 0;
if (number < 100) decimals = 1;
if (number < 10) decimals = 2;
return this.fmtNumber(number, decimals);
},
},
};
});
</script>
39 changes: 22 additions & 17 deletions assets/js/components/Savings/Savings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
:value="priceConfigured ? avgPriceFormatted.value : '__'"
:unit="avgPriceFormatted.unit"
:sub1="
priceConfigured
priceConfigured && referenceGrid
? `${fmtMoney(
(referenceGrid - avgPrice) * totalCharged,
currency,
Expand Down Expand Up @@ -190,7 +190,7 @@
</div>
<div v-else class="my-4">
<LiveCommunity />
<TelemetrySettings :sponsorActive="!!sponsor.name" />
<TelemetrySettings :sponsorActive="!!sponsor?.name" />
</div>
<Sponsor v-bind="sponsor" />
</div>
Expand All @@ -201,36 +201,39 @@
</div>
</template>

<script>
<script lang="ts">
import Modal from "bootstrap/js/dist/modal";
import formatter from "@/mixins/formatter";
import Sponsor from "./Sponsor.vue";
import Tile from "./Tile.vue";
import LiveCommunity from "./LiveCommunity.vue";
import TelemetrySettings from "../TelemetrySettings.vue";
import CustomSelect from "../Helper/CustomSelect.vue";
import co2Reference from "./co2Reference";
import settings from "@/settings";
import api, { allowClientError } from "@/api";
import { docsPrefix } from "@/i18n";
import { defineComponent, type PropType } from "vue";
import type { CURRENCY, Rate, SelectOption, Sponsor as SponsorType } from "@/types/evcc";
import type { Period } from "./types";
import co2Reference from "./co2Reference.ts";

export default {
export default defineComponent({
name: "Savings",
components: { Sponsor, SavingsTile: Tile, LiveCommunity, TelemetrySettings, CustomSelect },
mixins: [formatter],
props: {
statistics: { type: Object, default: () => ({}) },
co2Configured: Boolean,
sponsor: Object,
currency: String,
sponsor: Object as PropType<SponsorType>,
currency: String as PropType<CURRENCY>,
},
data() {
return {
communityView: false,
telemetryEnabled: false,
period: settings.savingsPeriod || "30d",
selectedRegion: settings.savingsRegion || "Germany",
referenceGrid: undefined,
period: settings.savingsPeriod || ("30d" as Period),
selectedRegion: settings.savingsRegion || ("Germany" as string),
referenceGrid: undefined as number | undefined,
};
},
computed: {
Expand Down Expand Up @@ -258,8 +261,8 @@ export default {
// first region
return co2Reference.regions[0];
},
periodOptions() {
return ["30d", "365d", "thisYear", "total"].map((p) => ({
periodOptions(): SelectOption<Period>[] {
return (["30d", "365d", "thisYear", "total"] as Period[]).map((p) => ({
value: p,
name: this.$t(`footer.savings.period.${p}`),
}));
Expand Down Expand Up @@ -307,22 +310,24 @@ export default {
this.communityView = false;
},
openModal() {
const modal = Modal.getOrCreateInstance(document.getElementById("savingsModal"));
const modal = Modal.getOrCreateInstance(
document.getElementById("savingsModal") as HTMLElement
);
modal.show();
this.updateReferenceGrid();
},
selectPeriod(period) {
selectPeriod(period: Period) {
this.period = period;
settings.savingsPeriod = period;
},
selectRegion(region) {
selectRegion(region: string) {
this.selectedRegion = region;
settings.savingsRegion = region;
},
async updateReferenceGrid() {
try {
const res = await api.get(`tariff/grid`, allowClientError);
const { rates } = res.data.result;
const { rates } = res.data.result as { rates: Rate[] };
this.referenceGrid =
rates.reduce((acc, slot) => {
return acc + slot.value;
Expand All @@ -333,5 +338,5 @@ export default {
}
},
},
};
});
</script>
59 changes: 0 additions & 59 deletions assets/js/components/Savings/Sponsor.stories.js

This file was deleted.

60 changes: 60 additions & 0 deletions assets/js/components/Savings/Sponsor.stories.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import Sponsor from "./Sponsor.vue";
import type { Meta, StoryFn } from "@storybook/vue3";

export default {
title: "Savings/Sponsor",
component: Sponsor,
parameters: {
layout: "centered",
},
argTypes: {
name: {
control: "text",
description: "Sponsor name (use 'trial' or 'victron' for special states)",
},
expiresAt: {
control: "text",
description: "When the sponsorship expires (ISO date string)",
},
expiresSoon: {
control: "boolean",
description: "Whether the sponsorship is expiring soon",
},
},
} as Meta<typeof Sponsor>;

// Template for rendering the component
const Template: StoryFn<typeof Sponsor> = (args) => ({
components: { Sponsor },
setup() {
return { args };
},
template: '<Sponsor v-bind="args" />',
});

// Create stories for each variant
export const NoSponsor = Template.bind({});
NoSponsor.args = {};

export const Trial = Template.bind({});
Trial.args = {
name: "trial",
};

export const IndividualSponsor = Template.bind({});
IndividualSponsor.args = {
name: "naltatis",
};

export const VictronDevice = Template.bind({});
VictronDevice.args = {
name: "victron",
};

// Add an extra story showing the expiring state
export const ExpiringSponsor = Template.bind({});
ExpiringSponsor.args = {
name: "naltatis",
expiresSoon: true,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(), // 7 days from now
};
Loading
Loading