feat(history): add electricity price chart
This commit is contained in:
@@ -9,12 +9,11 @@ use Spatie\LaravelData\Data;
|
|||||||
|
|
||||||
class HistoryData extends Data
|
class HistoryData extends Data
|
||||||
{
|
{
|
||||||
/**
|
|
||||||
* @property Collection<ElectricityData> $electricity
|
|
||||||
* @property Collection<GasData> $gas
|
|
||||||
*/
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
|
|
||||||
|
/** @var Collection<ElectricityData> */
|
||||||
public Collection $electricity,
|
public Collection $electricity,
|
||||||
|
/** @var Collection<GasData> */
|
||||||
public Collection $gas,
|
public Collection $gas,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+31
-1
@@ -10,6 +10,7 @@
|
|||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"axios": "^1.19.0",
|
"axios": "^1.19.0",
|
||||||
|
"chart.js": "^4.5.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"i18next": "^26.3.6",
|
"i18next": "^26.3.6",
|
||||||
@@ -20,7 +21,8 @@
|
|||||||
"tailwindcss": "^4.1.1",
|
"tailwindcss": "^4.1.1",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.0",
|
||||||
"vue": "^3.5.13"
|
"vue": "^3.5.13",
|
||||||
|
"vue-chartjs": "^5.3.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.19.0",
|
"@eslint/js": "^9.19.0",
|
||||||
@@ -484,6 +486,12 @@
|
|||||||
"@jridgewell/sourcemap-codec": "^1.4.14"
|
"@jridgewell/sourcemap-codec": "^1.4.14"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@kurkle/color": {
|
||||||
|
"version": "0.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz",
|
||||||
|
"integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@laravel/vite-plugin-wayfinder": {
|
"node_modules/@laravel/vite-plugin-wayfinder": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
"resolved": "https://registry.npmjs.org/@laravel/vite-plugin-wayfinder/-/vite-plugin-wayfinder-0.1.7.tgz",
|
"resolved": "https://registry.npmjs.org/@laravel/vite-plugin-wayfinder/-/vite-plugin-wayfinder-0.1.7.tgz",
|
||||||
@@ -2420,6 +2428,18 @@
|
|||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/chart.js": {
|
||||||
|
"version": "4.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz",
|
||||||
|
"integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@kurkle/color": "^0.3.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"pnpm": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cliui": {
|
"node_modules/cliui": {
|
||||||
"version": "8.0.1",
|
"version": "8.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||||
@@ -6856,6 +6876,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-chartjs": {
|
||||||
|
"version": "5.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-chartjs/-/vue-chartjs-5.3.4.tgz",
|
||||||
|
"integrity": "sha512-x3Fqob8RQvrTdssfi9ecsCzEkFOd8JPmNwSkSQzdfKj/uBsRJs/Y88cZcZIEcPsTVfMGwMo4MOoihoDG2DoE/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"peerDependencies": {
|
||||||
|
"chart.js": "^4.1.1",
|
||||||
|
"vue": "^3.0.0-0 || ^2.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-eslint-parser": {
|
"node_modules/vue-eslint-parser": {
|
||||||
"version": "10.4.1",
|
"version": "10.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.4.1.tgz",
|
||||||
|
|||||||
+3
-1
@@ -34,6 +34,7 @@
|
|||||||
"@tailwindcss/vite": "^4.1.11",
|
"@tailwindcss/vite": "^4.1.11",
|
||||||
"@vitejs/plugin-vue": "^6.0.0",
|
"@vitejs/plugin-vue": "^6.0.0",
|
||||||
"axios": "^1.19.0",
|
"axios": "^1.19.0",
|
||||||
|
"chart.js": "^4.5.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"concurrently": "^9.0.1",
|
"concurrently": "^9.0.1",
|
||||||
"i18next": "^26.3.6",
|
"i18next": "^26.3.6",
|
||||||
@@ -44,7 +45,8 @@
|
|||||||
"tailwindcss": "^4.1.1",
|
"tailwindcss": "^4.1.1",
|
||||||
"typescript": "^5.2.2",
|
"typescript": "^5.2.2",
|
||||||
"vite": "^8.0.0",
|
"vite": "^8.0.0",
|
||||||
"vue": "^3.5.13"
|
"vue": "^3.5.13",
|
||||||
|
"vue-chartjs": "^5.3.4"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@rollup/rollup-linux-x64-gnu": "4.9.5",
|
"@rollup/rollup-linux-x64-gnu": "4.9.5",
|
||||||
|
|||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { computed } from 'vue';
|
||||||
|
import { Line } from 'vue-chartjs';
|
||||||
|
import {
|
||||||
|
Chart as ChartJS,
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
Filler,
|
||||||
|
} from 'chart.js';
|
||||||
|
|
||||||
|
ChartJS.register(
|
||||||
|
CategoryScale,
|
||||||
|
LinearScale,
|
||||||
|
PointElement,
|
||||||
|
LineElement,
|
||||||
|
Tooltip,
|
||||||
|
Legend,
|
||||||
|
Filler,
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface PricePoint {
|
||||||
|
label: string;
|
||||||
|
value: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps<{
|
||||||
|
data: PricePoint[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
const chartData = computed(() => ({
|
||||||
|
labels: props.data.map((d) => d.label),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
data: props.data.map((d) => d.value),
|
||||||
|
borderColor: '#4ba66a',
|
||||||
|
backgroundColor: 'rgba(75, 166, 106, 0.1)',
|
||||||
|
fill: true,
|
||||||
|
tension: 0.4,
|
||||||
|
pointRadius: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}));
|
||||||
|
|
||||||
|
const chartOptions = {
|
||||||
|
responsive: true,
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="flex h-64 flex-1">
|
||||||
|
<Line :data="chartData" :options="chartOptions" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref, watch } from 'vue';
|
import { computed, onMounted, ref, watch } from 'vue';
|
||||||
import useHistoryViewModel from '../viewmodels/HistoryViewModel';
|
import useHistoryViewModel from '../viewmodels/HistoryViewModel';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import Chart from '../components/Chart.vue';
|
||||||
|
|
||||||
const model = useHistoryViewModel();
|
const model = useHistoryViewModel();
|
||||||
|
|
||||||
@@ -14,10 +15,19 @@ onMounted(() => {
|
|||||||
watch(date, (value) => {
|
watch(date, (value) => {
|
||||||
model.getHistoryData(moment(value).format('YYYY-MM-DD'));
|
model.getHistoryData(moment(value).format('YYYY-MM-DD'));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const chartData = computed(
|
||||||
|
() =>
|
||||||
|
model.historyData.value?.electricity
|
||||||
|
.filter((e) => e.totalPriceTaxIncluded != null)
|
||||||
|
.map((e) => ({
|
||||||
|
label: moment(e.startDate).format('HH:mm'),
|
||||||
|
value: e.totalPriceTaxIncluded,
|
||||||
|
})) ?? [],
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<input type="date" v-model="date" />
|
||||||
<input type="date" v-model="date" />
|
<Chart :data="chartData" />
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import { create } from 'axios';
|
import { create } from 'axios';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import HistoryData = App.Features.History.Data.HistoryData;
|
import HistoryData = App.Features.History.Data.HistoryData;
|
||||||
|
import moment from 'moment';
|
||||||
|
|
||||||
export default function useHistoryViewModel() {
|
export default function useHistoryViewModel() {
|
||||||
const isLoading = ref<boolean>(false);
|
const isLoading = ref<boolean>(false);
|
||||||
@@ -17,6 +18,18 @@ export default function useHistoryViewModel() {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
console.log(
|
||||||
|
res.data?.electricity.flatMap((e) => {
|
||||||
|
if (!e.totalPriceTaxIncluded) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
label: moment(e.startDate).format('DD-MM-YYYY'),
|
||||||
|
value: e.totalPriceTaxIncluded?.toPrecision(2),
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
historyData.value = res.data;
|
historyData.value = res.data;
|
||||||
})
|
})
|
||||||
.finally(() => (isLoading.value = false));
|
.finally(() => (isLoading.value = false));
|
||||||
|
|||||||
+6
-6
@@ -17,10 +17,10 @@ declare namespace App {
|
|||||||
startDate: string;
|
startDate: string;
|
||||||
endDate: string;
|
endDate: string;
|
||||||
period: string;
|
period: string;
|
||||||
marketPrice: number;
|
marketPrice: number | null;
|
||||||
totalPriceTaxIncluded: number;
|
totalPriceTaxIncluded: number | null;
|
||||||
priceInclHandlingVat: number;
|
priceInclHandlingVat: number | null;
|
||||||
priceTaxWithVat: number;
|
priceTaxWithVat: number | null;
|
||||||
startDateDatetime: string;
|
startDateDatetime: string;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -41,8 +41,8 @@ declare namespace App {
|
|||||||
namespace History {
|
namespace History {
|
||||||
namespace Data {
|
namespace Data {
|
||||||
export type HistoryData = {
|
export type HistoryData = {
|
||||||
electricity: Array<any>;
|
electricity: App.Data.ElectricityData[];
|
||||||
gas: Array<any>;
|
gas: App.Data.GasData[];
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
namespace Requests {
|
namespace Requests {
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"generated.d.ts": "7ca8a9c7f0ae5e5498bdee87bb2f005d"
|
"generated.d.ts": "596cfad66b8ff3f8b6b98271630919ae"
|
||||||
}
|
}
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import i18next from 'i18next';
|
import i18next from 'i18next';
|
||||||
import resources from './translations';
|
import resources from './translations';
|
||||||
|
|
||||||
await i18next.init(() => ({
|
await i18next.init({
|
||||||
lng: 'nl',
|
lng: 'nl',
|
||||||
resources,
|
resources,
|
||||||
}));
|
});
|
||||||
|
|
||||||
export default i18next;
|
export default i18next;
|
||||||
|
|||||||
@@ -16,5 +16,5 @@ import history from '@/routes/history';
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="absolute top-0 left-64 p-4"><slot /></div>
|
<div class="absolute top-0 right-0 bottom-0 left-64 p-4"><slot /></div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
Reference in New Issue
Block a user