Implement dashboard data logic

This commit is contained in:
Oliver Traber 2025-05-25 21:07:28 +00:00
parent 1c810e6293
commit 5fa13de642
Signed by: Bluemedia
GPG key ID: C0674B105057136C
7 changed files with 115 additions and 78 deletions

View file

@ -25,18 +25,20 @@
<p class="text-2xl font-semibold"> <p class="text-2xl font-semibold">
{props.value}{#if props.unit}{' ' + props.unit}{/if} {props.value}{#if props.unit}{' ' + props.unit}{/if}
</p> </p>
{#if diff > 0} {#if !Number.isNaN(diff)}
<div class="badge badge-soft badge-success badge-sm gap-0.5 px-1 font-medium"> {#if diff > 0}
<i class="bi bi-arrow-up-right"></i>{diff}% <div class="badge badge-outline badge-success badge-sm gap-0.5 px-1 font-medium">
</div> <i class="bi bi-arrow-up-right"></i>{diff}%
{:else if diff < 0} </div>
<div class="badge badge-soft badge-error badge-sm gap-0.5 px-1 font-medium"> {:else if diff < 0}
<i class="bi bi-arrow-down-right"></i>{diff}% <div class="badge badge-outline badge-error badge-sm gap-0.5 px-1 font-medium">
</div> <i class="bi bi-arrow-down-right"></i>{diff}%
{:else} </div>
<div class="badge badge-soft badge-warning badge-sm gap-0.5 px-1 font-medium"> {:else}
<i class="bi bi-arrow-right"></i>{diff}% <div class="badge badge-outline badge-warning badge-sm gap-0.5 px-1 font-medium">
</div> <i class="bi bi-arrow-right"></i>{diff}%
</div>
{/if}
{/if} {/if}
</div> </div>
</div> </div>

View file

@ -1,18 +1,9 @@
<script lang="ts"> <script lang="ts">
import i18n from '$lib/i18n' import i18n from '$lib/i18n'
import type { Transaction } from '$lib/types/transaction'
let props: { let props: {
transactions: { transactions: Transaction[]
id: string
begin: Date
end: Date
chargepoint: {
name: string
id: string
}
energyAmmount: number
cost: number
}[]
} = $props() } = $props()
</script> </script>
@ -36,7 +27,7 @@
<div> <div>
<p> <p>
{$i18n.t('common:transactionTable.startTime', { {$i18n.t('common:transactionTable.startTime', {
time: transaction.begin, time: new Date(transaction.started_at),
formatParams: { formatParams: {
time: { time: {
year: 'numeric', year: 'numeric',
@ -51,7 +42,7 @@
</p> </p>
<p> <p>
{$i18n.t('common:transactionTable.endTime', { {$i18n.t('common:transactionTable.endTime', {
time: transaction.end, time: new Date(transaction.ended_at!),
formatParams: { formatParams: {
time: { time: {
year: 'numeric', year: 'numeric',
@ -70,11 +61,15 @@
<td> <td>
<a href="/chargepoint/{transaction.chargepoint.id}" class="btn btn-sm btn-primary"> <a href="/chargepoint/{transaction.chargepoint.id}" class="btn btn-sm btn-primary">
<i class="bi bi-plug-fill text-lg"></i> <i class="bi bi-plug-fill text-lg"></i>
{transaction.chargepoint.name} {transaction.chargepoint.identity}
</a> </a>
</td> </td>
<td class="font-bold">{transaction.energyAmmount} kWh</td> <td class="font-bold"
<td class="font-bold">{transaction.cost}</td> >{(transaction.meter_end - transaction.meter_start).toFixed(2)} kWh</td
>
<td class="font-bold"
>{((transaction.meter_end - transaction.meter_start) * transaction.price).toFixed(2)}</td
>
<th> <th>
<a href="/transaction/{transaction.id}" class="btn btn-sm btn-primary"> <a href="/transaction/{transaction.id}" class="btn btn-sm btn-primary">
{$i18n.t('common:transactionTable.detailButton')} {$i18n.t('common:transactionTable.detailButton')}

View file

@ -17,3 +17,9 @@ export type ChargePoint = {
} }
] ]
} }
export type ChargePointThumb = {
id: string;
identity: string;
price: number;
}

View file

@ -0,0 +1,14 @@
import type { Transaction } from "./transaction";
export type Dashboard = {
stats: {
transaction_count: number;
transaction_count_previous: number;
transaction_energy_total: number;
transaction_energy_total_previous: number;
transaction_cost_total: number;
transaction_cost_total_previous: number;
};
current_transaction: Transaction | undefined;
recent_transactions: Transaction[];
}

View file

@ -0,0 +1,17 @@
import type { ChargePointThumb } from "./chargepoint";
import type { UserThumb } from "./user";
export type TransactionEventTriggerReason = 'Authorized' | 'CablePluggedIn' | 'ChargingRateChanged' | 'ChargingStateChanged' | 'Deauthorized' | 'EnergyLimitReached' | 'EVCommunicationLost' | 'EVConnectTimeout' | 'MeterValueClock' | 'MeterValuePeriodic' | 'TimeLimitReached' | 'Trigger' | 'UnlockCommand' | 'StopAuthorized' | 'EVDeparted' | 'EVDetected' | 'RemoteStop' | 'RemoteStart' | 'AbnormalCondition' | 'SignedDataReceived' | 'ResetCommand';
export type Transaction = {
id: string;
status: 'ongoing' | 'ended';
started_at: string;
ended_at: string | undefined;
meter_start: number;
meter_end: number;
end_reason: TransactionEventTriggerReason;
price: number;
user: UserThumb;
chargepoint: ChargePointThumb;
}

View file

@ -0,0 +1,4 @@
export type UserThumb = {
id: string;
friendly_name: string;
}

View file

@ -4,10 +4,39 @@
import i18n from '$lib/i18n' import i18n from '$lib/i18n'
import DashboardCard from '$lib/component/DashboardCard.svelte' import DashboardCard from '$lib/component/DashboardCard.svelte'
import TransactionTable from '$lib/component/TransactionTable.svelte' import TransactionTable from '$lib/component/TransactionTable.svelte'
import type { Dashboard } from '$lib/types/dashboard'
import axios from '$lib/axios.svelte'
import { onMount } from 'svelte'
$i18n.loadNamespaces('dashboard') $i18n.loadNamespaces('dashboard')
let hasActiveTransaction = $state(true) let dashboardData: Dashboard = $state({
stats: {
transaction_count: 0,
transaction_count_previous: 0,
transaction_energy_total: 0,
transaction_energy_total_previous: 0,
transaction_cost_total: 0,
transaction_cost_total_previous: 0,
},
current_transaction: undefined,
recent_transactions: [],
})
function refreshDashboard() {
axios.get('/me/dashboard').then((res) => {
dashboardData = res.data
})
}
onMount(() => {
refreshDashboard()
const interval = setInterval(() => {
refreshDashboard()
}, 15000)
return () => clearInterval(interval)
})
</script> </script>
<div class="w-full h-full mt-10 flex flex-col"> <div class="w-full h-full mt-10 flex flex-col">
@ -23,18 +52,21 @@
{$i18n.t('dashboard:cards.currentTransaction')} {$i18n.t('dashboard:cards.currentTransaction')}
</p> </p>
<div class="mt-3 flex items-center gap-2"> <div class="mt-3 flex items-center gap-2">
{#if hasActiveTransaction} {#if dashboardData.current_transaction}
<div class="inline-grid *:[grid-area:1/1]"> <div class="inline-grid *:[grid-area:1/1]">
<div class="status status-success animate-ping"></div> <div class="status status-success animate-ping"></div>
<div class="status status-success"></div> <div class="status status-success"></div>
</div> </div>
{/if} {/if}
<p class="text-2xl font-semibold"> <p class="text-2xl font-semibold">
{#if hasActiveTransaction}0,25 kWh{:else}-{/if} {#if dashboardData.current_transaction}{dashboardData.current_transaction
.meter_end - dashboardData.current_transaction.meter_start} kWh{:else}-{/if}
</p> </p>
{#if hasActiveTransaction} {#if dashboardData.current_transaction}
<div class="badge badge-soft badge-success badge-sm gap-0.5 px-1 font-medium"> <div class="badge badge-soft badge-success badge-sm gap-0.5 px-1 font-medium">
2,30 € {(dashboardData.current_transaction.meter_end -
dashboardData.current_transaction.meter_start) *
dashboardData.current_transaction.price} €
</div> </div>
{/if} {/if}
</div> </div>
@ -43,13 +75,15 @@
</div> </div>
<div class="w-full flex flex-row items-center"> <div class="w-full flex flex-row items-center">
<p class="text-base-content/60 text-sm"> <p class="text-base-content/60 text-sm">
{#if hasActiveTransaction} {#if dashboardData.current_transaction}
{$i18n.t('dashboard:cards.chargepoint', { name: 'DE-EXMPL-0001' })} {$i18n.t('dashboard:cards.chargepoint', {
name: dashboardData.current_transaction.chargepoint.identity,
})}
{:else} {:else}
{$i18n.t('dashboard:cards.noCurrentTransaction')} {$i18n.t('dashboard:cards.noCurrentTransaction')}
{/if} {/if}
</p> </p>
{#if hasActiveTransaction} {#if dashboardData.current_transaction}
<button class="btn btn-xs btn-primary"> <button class="btn btn-xs btn-primary">
{$i18n.t('dashboard:cards.toCurrentTransactionButton')} {$i18n.t('dashboard:cards.toCurrentTransactionButton')}
<i class="bi bi-arrow-right"></i> <i class="bi bi-arrow-right"></i>
@ -61,61 +95,26 @@
<DashboardCard <DashboardCard
title={$i18n.t('dashboard:cards.transactionCount')} title={$i18n.t('dashboard:cards.transactionCount')}
icon={'bi-battery-charging'} icon={'bi-battery-charging'}
value={3} value={dashboardData.stats.transaction_count}
previousValue={1} previousValue={dashboardData.stats.transaction_count_previous}
/> />
<DashboardCard <DashboardCard
title={$i18n.t('dashboard:cards.transactionEnergyTotal')} title={$i18n.t('dashboard:cards.transactionEnergyTotal')}
icon={'bi-lightning-charge-fill'} icon={'bi-lightning-charge-fill'}
value={180} value={+dashboardData.stats.transaction_energy_total.toFixed(2)}
previousValue={50} previousValue={+dashboardData.stats.transaction_energy_total_previous.toFixed(2)}
unit={'kWh'} unit={'kWh'}
/> />
<DashboardCard <DashboardCard
title={$i18n.t('dashboard:cards.transactionCostTotal')} title={$i18n.t('dashboard:cards.transactionCostTotal')}
icon={'bi-currency-exchange'} icon={'bi-currency-exchange'}
value={30.56} value={+dashboardData.stats.transaction_cost_total.toFixed(2)}
previousValue={30.56} previousValue={+dashboardData.stats.transaction_cost_total_previous.toFixed(2)}
unit={'€'} unit={'€'}
/> />
</div> </div>
<div class="w-full flex flex-col mt-10"> <div class="w-full flex flex-col mt-10">
<p class="text-xl font-bold">{$i18n.t('dashboard:table.title')}</p> <p class="text-xl font-bold">{$i18n.t('dashboard:table.title')}</p>
<TransactionTable <TransactionTable transactions={dashboardData.recent_transactions} />
transactions={[
{
id: '1',
begin: new Date(Date.UTC(2025, 3, 15, 14, 12, 23)),
end: new Date(Date.UTC(2025, 3, 15, 16, 18, 46)),
chargepoint: { name: 'DE-EXMPL-0001', id: '1' },
energyAmmount: 58.65,
cost: 36.45,
},
{
id: '2',
begin: new Date(Date.UTC(2025, 3, 15, 14, 12, 23)),
end: new Date(Date.UTC(2025, 3, 15, 16, 18, 46)),
chargepoint: { name: 'DE-EXMPL-0001', id: '1' },
energyAmmount: 58.65,
cost: 36.45,
},
{
id: '3',
begin: new Date(Date.UTC(2025, 3, 15, 14, 12, 23)),
end: new Date(Date.UTC(2025, 3, 15, 16, 18, 46)),
chargepoint: { name: 'DE-EXMPL-0001', id: '1' },
energyAmmount: 58.65,
cost: 36.45,
},
{
id: '4',
begin: new Date(Date.UTC(2025, 3, 15, 14, 12, 23)),
end: new Date(Date.UTC(2025, 3, 15, 16, 18, 46)),
chargepoint: { name: 'DE-EXMPL-0001', id: '1' },
energyAmmount: 58.65,
cost: 36.45,
},
]}
/>
</div> </div>
</div> </div>