Compare commits
1 commit
a61231957b
...
255895bcb8
Author | SHA1 | Date | |
---|---|---|---|
255895bcb8 |
25 changed files with 136 additions and 261 deletions
|
@ -25,4 +25,3 @@ class ChargePoint(Base):
|
|||
connectors = relationship("Connector", cascade="delete, delete-orphan")
|
||||
transactions = relationship("Transaction", cascade="delete, delete-orphan")
|
||||
variables = relationship("ChargepointVariable", cascade="delete, delete-orphan")
|
||||
firmware_updates = relationship("FirmwareUpdate", cascade="delete, delete-orphan")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import uuid
|
||||
from sqlalchemy import Column, DateTime, Enum, ForeignKey, Integer, String, Text, Uuid
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
from app.schemas.firmware_update import FirmwareUpdateStatus
|
||||
|
@ -21,4 +20,3 @@ class FirmwareUpdate(Base):
|
|||
signature = Column(String, nullable=True)
|
||||
|
||||
chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True)
|
||||
chargepoint = relationship("ChargePoint", back_populates="firmware_updates")
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import uuid
|
||||
from sqlalchemy import Uuid, Column, DateTime, Enum, Float, ForeignKey, String
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
from app.schemas.meter_value import Measurand, PhaseType
|
||||
|
@ -15,5 +14,4 @@ class MeterValue(Base):
|
|||
unit = Column(String, nullable=True)
|
||||
value = Column(Float)
|
||||
|
||||
transaction_id = Column(String, ForeignKey("transactions.id"), index=True)
|
||||
transaction = relationship("Transaction", back_populates="meter_values")
|
||||
transaction_id = Column(String, ForeignKey("transactions.id"), index=True)
|
|
@ -1,6 +1,5 @@
|
|||
import uuid
|
||||
from sqlalchemy import Column, DateTime, ForeignKey, String, Uuid
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.database import Base
|
||||
|
||||
|
@ -12,5 +11,4 @@ class Session(Base):
|
|||
refresh_token = Column(String, nullable=False, unique=True, index=True)
|
||||
last_used = Column(DateTime(timezone=True))
|
||||
|
||||
user_id = Column(Uuid, ForeignKey("users.id"), nullable=False, index=True)
|
||||
user = relationship("User", back_populates="sessions")
|
||||
user_id = Column(Uuid, ForeignKey("users.id"), nullable=False, index=True)
|
|
@ -1,5 +1,4 @@
|
|||
from sqlalchemy import String, Uuid, Column, DateTime, Enum, Numeric, ForeignKey
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from app.schemas.transaction import TransactionEventTriggerReason, TransactionStatus
|
||||
from app.database import Base
|
||||
|
@ -12,14 +11,9 @@ class Transaction(Base):
|
|||
started_at = Column(DateTime, index=True)
|
||||
ended_at = Column(DateTime, nullable=True, index=True)
|
||||
meter_start = Column(Numeric(10,2))
|
||||
meter_end = Column(Numeric(10,2))
|
||||
meter_end = Column(Numeric(10,2), nullable=True)
|
||||
end_reason = Column(Enum(TransactionEventTriggerReason), nullable=True)
|
||||
price = Column(Numeric(10,2))
|
||||
|
||||
user_id = Column(Uuid, ForeignKey("users.id"), nullable=True, index=True)
|
||||
user = relationship("User", back_populates="transactions")
|
||||
|
||||
chargepoint_id = Column(Uuid, ForeignKey("chargepoints.id"), index=True)
|
||||
chargepoint = relationship("ChargePoint", back_populates="transactions")
|
||||
|
||||
meter_values = relationship("MeterValue", cascade="delete, delete-orphan")
|
||||
|
|
|
@ -17,4 +17,3 @@ class User(Base):
|
|||
|
||||
id_tokens = relationship("IdToken", back_populates="owner", cascade="delete, delete-orphan")
|
||||
transactions = relationship("Transaction", cascade="delete, delete-orphan")
|
||||
sessions = relationship("Session", cascade="delete, delete-orphan")
|
||||
|
|
|
@ -3,7 +3,6 @@ from datetime import datetime
|
|||
from pydantic import BaseModel
|
||||
|
||||
from app.schemas.user import Role
|
||||
from app.util.encoders import force_utc_datetime
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -20,6 +19,3 @@ class TokenResponse(BaseModel):
|
|||
access_token: str
|
||||
refresh_token: str
|
||||
not_after: datetime
|
||||
|
||||
class Config:
|
||||
json_encoders = {datetime: force_utc_datetime}
|
||||
|
|
|
@ -8,7 +8,7 @@ from app.schemas.connector import Connector
|
|||
|
||||
from ocpp.v201.enums import ResetEnumType, ResetStatusEnumType
|
||||
|
||||
from app.util.encoders import decimal_encoder, force_utc_datetime
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class ChargePointBase(BaseModel):
|
||||
identity: str
|
||||
|
@ -32,15 +32,6 @@ class ChargePoint(ChargePointBase):
|
|||
firmware_version: str | None
|
||||
connectors: list[Connector] = []
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder, datetime: force_utc_datetime}
|
||||
|
||||
class ChargePointThumb(BaseModel):
|
||||
id: UUID
|
||||
identity: str
|
||||
price: Decimal
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
|
|
@ -5,7 +5,7 @@ from typing import Optional
|
|||
from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.util.encoders import decimal_encoder, force_utc_datetime
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class PhaseType(enum.Enum):
|
||||
L1 = "L1"
|
||||
|
@ -57,4 +57,4 @@ class MeterValue(BaseModel):
|
|||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder, datetime: force_utc_datetime}
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
|
|
@ -2,8 +2,6 @@ from datetime import datetime
|
|||
from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.util.encoders import force_utc_datetime
|
||||
|
||||
|
||||
class Session(BaseModel):
|
||||
id: UUID
|
||||
|
@ -11,5 +9,4 @@ class Session(BaseModel):
|
|||
last_used: datetime
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {datetime: force_utc_datetime}
|
||||
from_attributes = True
|
|
@ -1,12 +1,11 @@
|
|||
import enum
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
import enum
|
||||
from typing import Optional
|
||||
from uuid import UUID
|
||||
from pydantic import BaseModel
|
||||
|
||||
from app.schemas.chargepoint import ChargePointThumb
|
||||
from app.schemas.user import UserThumb
|
||||
from app.util.encoders import decimal_encoder, force_utc_datetime
|
||||
from app.util.encoders import decimal_encoder
|
||||
|
||||
class TransactionStatus(enum.Enum):
|
||||
ONGOING = "ongoing"
|
||||
|
@ -45,15 +44,15 @@ class Transaction(BaseModel):
|
|||
started_at: datetime
|
||||
ended_at: Optional[datetime] = None
|
||||
meter_start: Decimal
|
||||
meter_end: Decimal
|
||||
meter_end: Optional[Decimal] = None
|
||||
end_reason: Optional[TransactionEventTriggerReason] = None
|
||||
price: Decimal
|
||||
user: UserThumb
|
||||
chargepoint: ChargePointThumb
|
||||
user_id: Optional[UUID] = None
|
||||
chargepoint_id: UUID
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
json_encoders = {Decimal: decimal_encoder, datetime: force_utc_datetime}
|
||||
json_encoders = {Decimal: decimal_encoder}
|
||||
|
||||
class RemoteTransactionStartStopResponse(BaseModel):
|
||||
status: RemoteTransactionStartStopStatus
|
||||
|
|
|
@ -30,13 +30,6 @@ class User(UserBase):
|
|||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class UserThumb(BaseModel):
|
||||
id: UUID
|
||||
friendly_name: str
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
class PasswordUpdate(BaseModel):
|
||||
old_password: str = Field(max_length=100)
|
||||
new_password: str = Field(max_length=100)
|
||||
|
|
|
@ -21,23 +21,19 @@ async def create_transaction(
|
|||
with SessionLocal() as db:
|
||||
chargepoint = db.query(ChargePoint).filter(ChargePoint.identity == chargepoint_identity).first()
|
||||
meter_start=0
|
||||
meter_end=0
|
||||
if "meter_value" in transaction_data.keys():
|
||||
for meter_value_entry in transaction_data['meter_value']:
|
||||
for sampled_value in meter_value_entry['sampled_value']:
|
||||
if "measurand" in sampled_value.keys():
|
||||
if sampled_value['measurand'] == str(Measurand.ENERGY_ACTIVE_IMPORT_REGISTER):
|
||||
meter_start = sampled_value['value']
|
||||
meter_end = sampled_value['value']
|
||||
else:
|
||||
meter_start = sampled_value['value']
|
||||
meter_end = sampled_value['value']
|
||||
transaction = Transaction(
|
||||
id=transaction_info["transaction_id"],
|
||||
status=TransactionStatus.ONGOING,
|
||||
started_at=timestamp,
|
||||
meter_start=meter_start,
|
||||
meter_end=meter_end,
|
||||
price=chargepoint.price,
|
||||
chargepoint_id=chargepoint.id,
|
||||
user_id=user_id
|
||||
|
@ -59,12 +55,6 @@ async def update_transaction(
|
|||
transaction_id=transaction.id,
|
||||
meter_value_data=meter_value_entry
|
||||
)
|
||||
# Update current meter_end value
|
||||
for sampled_value in meter_value_entry['sampled_value']:
|
||||
if "measurand" in sampled_value.keys():
|
||||
if sampled_value['measurand'] == str(Measurand.ENERGY_ACTIVE_IMPORT_REGISTER):
|
||||
transaction.meter_end = sampled_value['value']
|
||||
db.commit()
|
||||
|
||||
async def end_transaction(
|
||||
transaction_id: str,
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
from datetime import datetime, timezone
|
||||
from decimal import Decimal
|
||||
from typing import Union
|
||||
|
||||
|
@ -20,8 +19,4 @@ def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
|
|||
if isinstance(exponent, int) and exponent >= 0:
|
||||
return int(dec_value)
|
||||
else:
|
||||
return float(dec_value)
|
||||
|
||||
def force_utc_datetime(datetime_value: datetime) -> datetime:
|
||||
"""Force a datetime to be in the UTC timzone"""
|
||||
return datetime_value.replace(tzinfo=timezone.utc)
|
||||
return float(dec_value)
|
|
@ -25,20 +25,18 @@
|
|||
<p class="text-2xl font-semibold">
|
||||
{props.value}{#if props.unit}{' ' + props.unit}{/if}
|
||||
</p>
|
||||
{#if !Number.isNaN(diff)}
|
||||
{#if diff > 0}
|
||||
<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}%
|
||||
</div>
|
||||
{:else if diff < 0}
|
||||
<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}%
|
||||
</div>
|
||||
{:else}
|
||||
<div class="badge badge-outline badge-warning badge-sm gap-0.5 px-1 font-medium">
|
||||
<i class="bi bi-arrow-right"></i>{diff}%
|
||||
</div>
|
||||
{/if}
|
||||
{#if diff > 0}
|
||||
<div class="badge badge-soft badge-success badge-sm gap-0.5 px-1 font-medium">
|
||||
<i class="bi bi-arrow-up-right"></i>{diff}%
|
||||
</div>
|
||||
{:else if diff < 0}
|
||||
<div class="badge badge-soft badge-error badge-sm gap-0.5 px-1 font-medium">
|
||||
<i class="bi bi-arrow-down-right"></i>{diff}%
|
||||
</div>
|
||||
{:else}
|
||||
<div class="badge badge-soft badge-warning badge-sm gap-0.5 px-1 font-medium">
|
||||
<i class="bi bi-arrow-right"></i>{diff}%
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,9 +1,18 @@
|
|||
<script lang="ts">
|
||||
import i18n from '$lib/i18n'
|
||||
import type { Transaction } from '$lib/types/transaction'
|
||||
|
||||
let props: {
|
||||
transactions: Transaction[]
|
||||
transactions: {
|
||||
id: string
|
||||
begin: Date
|
||||
end: Date
|
||||
chargepoint: {
|
||||
name: string
|
||||
id: string
|
||||
}
|
||||
energyAmmount: number
|
||||
cost: number
|
||||
}[]
|
||||
} = $props()
|
||||
</script>
|
||||
|
||||
|
@ -27,7 +36,7 @@
|
|||
<div>
|
||||
<p>
|
||||
{$i18n.t('common:transactionTable.startTime', {
|
||||
time: new Date(transaction.started_at),
|
||||
time: transaction.begin,
|
||||
formatParams: {
|
||||
time: {
|
||||
year: 'numeric',
|
||||
|
@ -42,7 +51,7 @@
|
|||
</p>
|
||||
<p>
|
||||
{$i18n.t('common:transactionTable.endTime', {
|
||||
time: new Date(transaction.ended_at!),
|
||||
time: transaction.end,
|
||||
formatParams: {
|
||||
time: {
|
||||
year: 'numeric',
|
||||
|
@ -61,15 +70,11 @@
|
|||
<td>
|
||||
<a href="/chargepoint/{transaction.chargepoint.id}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-plug-fill text-lg"></i>
|
||||
{transaction.chargepoint.identity}
|
||||
{transaction.chargepoint.name}
|
||||
</a>
|
||||
</td>
|
||||
<td class="font-bold"
|
||||
>{(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
|
||||
>
|
||||
<td class="font-bold">{transaction.energyAmmount} kWh</td>
|
||||
<td class="font-bold">{transaction.cost} €</td>
|
||||
<th>
|
||||
<a href="/transaction/{transaction.id}" class="btn btn-sm btn-primary">
|
||||
{$i18n.t('common:transactionTable.detailButton')}
|
||||
|
|
|
@ -16,10 +16,4 @@ export type ChargePoint = {
|
|||
status: 'Available' | 'Occupied' | 'Reserved' | 'Unavailable' | 'Faulted'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export type ChargePointThumb = {
|
||||
id: string;
|
||||
identity: string;
|
||||
price: number;
|
||||
}
|
|
@ -1,14 +0,0 @@
|
|||
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[];
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
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;
|
||||
}
|
|
@ -1,4 +0,0 @@
|
|||
export type UserThumb = {
|
||||
id: string;
|
||||
friendly_name: string;
|
||||
}
|
|
@ -7,23 +7,4 @@ export const currentDaytime = function(): string {
|
|||
return 'day'
|
||||
}
|
||||
return 'evening'
|
||||
}
|
||||
|
||||
export function relativeTimeFromDate(date: Date): {val: number, range: Intl.RelativeTimeFormatUnit} | undefined {
|
||||
const units: {unit: Intl.RelativeTimeFormatUnit; ms: number}[] = [
|
||||
{unit: "year", ms: 31536000000},
|
||||
{unit: "month", ms: 2628000000},
|
||||
{unit: "day", ms: 86400000},
|
||||
{unit: "hour", ms: 3600000},
|
||||
{unit: "minute", ms: 60000},
|
||||
{unit: "second", ms: 1000},
|
||||
];
|
||||
|
||||
const elapsed = date.getTime() - new Date().getTime();
|
||||
for (const {unit, ms} of units) {
|
||||
if (Math.abs(elapsed) >= ms || unit === "second") {
|
||||
return {val: Math.round(elapsed / ms), range: unit};
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
|
@ -4,39 +4,10 @@
|
|||
import i18n from '$lib/i18n'
|
||||
import DashboardCard from '$lib/component/DashboardCard.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')
|
||||
|
||||
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)
|
||||
})
|
||||
let hasActiveTransaction = $state(true)
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full mt-10 flex flex-col">
|
||||
|
@ -52,21 +23,18 @@
|
|||
{$i18n.t('dashboard:cards.currentTransaction')}
|
||||
</p>
|
||||
<div class="mt-3 flex items-center gap-2">
|
||||
{#if dashboardData.current_transaction}
|
||||
{#if hasActiveTransaction}
|
||||
<div class="inline-grid *:[grid-area:1/1]">
|
||||
<div class="status status-success animate-ping"></div>
|
||||
<div class="status status-success"></div>
|
||||
</div>
|
||||
{/if}
|
||||
<p class="text-2xl font-semibold">
|
||||
{#if dashboardData.current_transaction}{dashboardData.current_transaction
|
||||
.meter_end - dashboardData.current_transaction.meter_start} kWh{:else}-{/if}
|
||||
{#if hasActiveTransaction}0,25 kWh{:else}-{/if}
|
||||
</p>
|
||||
{#if dashboardData.current_transaction}
|
||||
{#if hasActiveTransaction}
|
||||
<div class="badge badge-soft badge-success badge-sm gap-0.5 px-1 font-medium">
|
||||
{(dashboardData.current_transaction.meter_end -
|
||||
dashboardData.current_transaction.meter_start) *
|
||||
dashboardData.current_transaction.price} €
|
||||
2,30 €
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
@ -75,15 +43,13 @@
|
|||
</div>
|
||||
<div class="w-full flex flex-row items-center">
|
||||
<p class="text-base-content/60 text-sm">
|
||||
{#if dashboardData.current_transaction}
|
||||
{$i18n.t('dashboard:cards.chargepoint', {
|
||||
name: dashboardData.current_transaction.chargepoint.identity,
|
||||
})}
|
||||
{#if hasActiveTransaction}
|
||||
{$i18n.t('dashboard:cards.chargepoint', { name: 'DE-EXMPL-0001' })}
|
||||
{:else}
|
||||
{$i18n.t('dashboard:cards.noCurrentTransaction')}
|
||||
{/if}
|
||||
</p>
|
||||
{#if dashboardData.current_transaction}
|
||||
{#if hasActiveTransaction}
|
||||
<button class="btn btn-xs btn-primary">
|
||||
{$i18n.t('dashboard:cards.toCurrentTransactionButton')}
|
||||
<i class="bi bi-arrow-right"></i>
|
||||
|
@ -95,26 +61,61 @@
|
|||
<DashboardCard
|
||||
title={$i18n.t('dashboard:cards.transactionCount')}
|
||||
icon={'bi-battery-charging'}
|
||||
value={dashboardData.stats.transaction_count}
|
||||
previousValue={dashboardData.stats.transaction_count_previous}
|
||||
value={3}
|
||||
previousValue={1}
|
||||
/>
|
||||
<DashboardCard
|
||||
title={$i18n.t('dashboard:cards.transactionEnergyTotal')}
|
||||
icon={'bi-lightning-charge-fill'}
|
||||
value={+dashboardData.stats.transaction_energy_total.toFixed(2)}
|
||||
previousValue={+dashboardData.stats.transaction_energy_total_previous.toFixed(2)}
|
||||
value={180}
|
||||
previousValue={50}
|
||||
unit={'kWh'}
|
||||
/>
|
||||
<DashboardCard
|
||||
title={$i18n.t('dashboard:cards.transactionCostTotal')}
|
||||
icon={'bi-currency-exchange'}
|
||||
value={+dashboardData.stats.transaction_cost_total.toFixed(2)}
|
||||
previousValue={+dashboardData.stats.transaction_cost_total_previous.toFixed(2)}
|
||||
value={30.56}
|
||||
previousValue={30.56}
|
||||
unit={'€'}
|
||||
/>
|
||||
</div>
|
||||
<div class="w-full flex flex-col mt-10">
|
||||
<p class="text-xl font-bold">{$i18n.t('dashboard:table.title')}</p>
|
||||
<TransactionTable transactions={dashboardData.recent_transactions} />
|
||||
<TransactionTable
|
||||
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>
|
||||
|
|
|
@ -1,42 +1,13 @@
|
|||
<script lang="ts">
|
||||
import axios from '$lib/axios.svelte'
|
||||
import i18n from '$lib/i18n'
|
||||
import type { ChargePoint } from '$lib/types/chargepoint'
|
||||
import { relativeTimeFromDate } from '$lib/util'
|
||||
import { onMount } from 'svelte'
|
||||
|
||||
$i18n.loadNamespaces('chargepoint')
|
||||
|
||||
let chargepoints: ChargePoint[] = $state([])
|
||||
const pageSize = 25
|
||||
let page: number = $state(1)
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search)
|
||||
if (urlParams.has('page')) {
|
||||
page = Number.parseInt(urlParams.get('page')!)
|
||||
}
|
||||
|
||||
function load() {
|
||||
axios
|
||||
.get('/chargepoints', { params: { skip: (page - 1) * pageSize, limit: pageSize } })
|
||||
.then((res) => {
|
||||
chargepoints = res.data
|
||||
})
|
||||
}
|
||||
|
||||
function nextPage() {
|
||||
++page
|
||||
load()
|
||||
}
|
||||
|
||||
function previousPage() {
|
||||
--page
|
||||
load()
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
load()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<div class="w-full h-full mt-4 flex flex-col">
|
||||
|
@ -59,22 +30,49 @@
|
|||
{#each chargepoints as chargepoint}
|
||||
<tr>
|
||||
<td>
|
||||
{chargepoint.identity}
|
||||
<div class="flex items-center gap-3">
|
||||
<div>
|
||||
<p>
|
||||
{$i18n.t('common:transactionTable.startTime', {
|
||||
time: transaction.begin,
|
||||
formatParams: {
|
||||
time: {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
},
|
||||
},
|
||||
})}
|
||||
</p>
|
||||
<p>
|
||||
{$i18n.t('common:transactionTable.endTime', {
|
||||
time: transaction.end,
|
||||
formatParams: {
|
||||
time: {
|
||||
year: 'numeric',
|
||||
month: 'numeric',
|
||||
day: 'numeric',
|
||||
hour: 'numeric',
|
||||
minute: 'numeric',
|
||||
second: 'numeric',
|
||||
},
|
||||
},
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
{#if chargepoint.is_active}
|
||||
{$i18n.t('chargepoint:state.enabled')}
|
||||
{:else}
|
||||
{$i18n.t('chargepoint:state.disabled')}
|
||||
{/if}
|
||||
<a href="/chargepoint/{transaction.chargepoint.id}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-plug-fill text-lg"></i>
|
||||
{transaction.chargepoint.name}
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
{$i18n.t(
|
||||
'chargepoint:tableFormatting.lastSeen',
|
||||
relativeTimeFromDate(new Date(chargepoint.last_seen))!
|
||||
)}
|
||||
</td>
|
||||
<td>{chargepoint.price} €</td>
|
||||
<td class="font-bold">{transaction.energyAmmount} kWh</td>
|
||||
<td class="font-bold">{transaction.cost} €</td>
|
||||
<th>
|
||||
<a href="/chargepoint/{chargepoint.id}" class="btn btn-sm btn-primary">
|
||||
{$i18n.t('common:transactionTable.detailButton')}
|
||||
|
@ -93,14 +91,8 @@
|
|||
{/if}
|
||||
</div>
|
||||
<div class="join mt-4">
|
||||
<button onclick={previousPage} disabled={page === 1 || null} class="join-item btn bg-base-100"
|
||||
>«</button
|
||||
>
|
||||
<button class="join-item btn bg-base-100">«</button>
|
||||
<button class="join-item btn bg-base-100">Page {page}</button>
|
||||
<button
|
||||
onclick={nextPage}
|
||||
disabled={chargepoints.length < pageSize || null}
|
||||
class="join-item btn bg-base-100">»</button
|
||||
>
|
||||
<button class="join-item btn bg-base-100">»</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -9,9 +9,5 @@
|
|||
"tableFormatting": {
|
||||
"lastSeen": "{{val, relativetime}}"
|
||||
},
|
||||
"state": {
|
||||
"enabled": "Aktiv",
|
||||
"disabled": "Deaktiviert"
|
||||
},
|
||||
"noChargepoints": "Noch keine Ladepunkte vorhanden."
|
||||
}
|
|
@ -9,9 +9,5 @@
|
|||
"tableFormatting": {
|
||||
"lastSeen": "{{val, relativetime}}"
|
||||
},
|
||||
"state": {
|
||||
"enabled": "Active",
|
||||
"disabled": "Disabled"
|
||||
},
|
||||
"noChargepoints": "There are no chargepoints yet."
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue