Implement dashboard data logic
This commit is contained in:
parent
1c810e6293
commit
5fa13de642
7 changed files with 115 additions and 78 deletions
|
@ -25,19 +25,21 @@
|
||||||
<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 !Number.isNaN(diff)}
|
||||||
{#if diff > 0}
|
{#if diff > 0}
|
||||||
<div class="badge badge-soft badge-success badge-sm gap-0.5 px-1 font-medium">
|
<div class="badge badge-outline badge-success badge-sm gap-0.5 px-1 font-medium">
|
||||||
<i class="bi bi-arrow-up-right"></i>{diff}%
|
<i class="bi bi-arrow-up-right"></i>{diff}%
|
||||||
</div>
|
</div>
|
||||||
{:else if diff < 0}
|
{:else if diff < 0}
|
||||||
<div class="badge badge-soft badge-error badge-sm gap-0.5 px-1 font-medium">
|
<div class="badge badge-outline badge-error badge-sm gap-0.5 px-1 font-medium">
|
||||||
<i class="bi bi-arrow-down-right"></i>{diff}%
|
<i class="bi bi-arrow-down-right"></i>{diff}%
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="badge badge-soft badge-warning badge-sm gap-0.5 px-1 font-medium">
|
<div class="badge badge-outline badge-warning badge-sm gap-0.5 px-1 font-medium">
|
||||||
<i class="bi bi-arrow-right"></i>{diff}%
|
<i class="bi bi-arrow-right"></i>{diff}%
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<i class="bi {props.icon} text-primary text-4xl"></i>
|
<i class="bi {props.icon} text-primary text-4xl"></i>
|
||||||
|
|
|
@ -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')}
|
||||||
|
|
|
@ -17,3 +17,9 @@ export type ChargePoint = {
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ChargePointThumb = {
|
||||||
|
id: string;
|
||||||
|
identity: string;
|
||||||
|
price: number;
|
||||||
|
}
|
14
frontend/src/lib/types/dashboard.ts
Normal file
14
frontend/src/lib/types/dashboard.ts
Normal 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[];
|
||||||
|
}
|
17
frontend/src/lib/types/transaction.ts
Normal file
17
frontend/src/lib/types/transaction.ts
Normal 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;
|
||||||
|
}
|
4
frontend/src/lib/types/user.ts
Normal file
4
frontend/src/lib/types/user.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
export type UserThumb = {
|
||||||
|
id: string;
|
||||||
|
friendly_name: string;
|
||||||
|
}
|
|
@ -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>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue