7dd3215520
MQTT-Collector (Python/paho-mqtt) speichert abgeschlossene go-e Wallbox-Ladungen in SQLite; Next.js-Web-App zeigt sie als Tabelle mit Energie-Summen (Monat/Jahr/gesamt) im werte-next-Design. Zwei Docker-Container mit gemeinsamem DB-Volume. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
87 lines
2.8 KiB
TypeScript
87 lines
2.8 KiB
TypeScript
import { Charge } from "@/lib/db";
|
|
|
|
const WEEKDAYS = ["So", "Mo", "Di", "Mi", "Do", "Fr", "Sa"];
|
|
|
|
function parts(iso: string) {
|
|
// erwartet "YYYY-MM-DDTHH:MM:SS" (lokale Zeit vom Collector)
|
|
const [date = "", time = ""] = iso.split("T");
|
|
const [y, m, d] = date.split("-");
|
|
return { y, m, d, hm: time.slice(0, 5) };
|
|
}
|
|
|
|
function formatDate(iso: string): string {
|
|
const { y, m, d } = parts(iso);
|
|
return y && m && d ? `${d}.${m}.${y}` : iso;
|
|
}
|
|
|
|
function weekday(iso: string): string {
|
|
const { y, m, d } = parts(iso);
|
|
if (!y || !m || !d) return "-";
|
|
return WEEKDAYS[new Date(Number(y), Number(m) - 1, Number(d)).getDay()];
|
|
}
|
|
|
|
function formatTime(iso: string): string {
|
|
return parts(iso).hm || "-";
|
|
}
|
|
|
|
function formatDuration(seconds: number): string {
|
|
const h = Math.floor(seconds / 3600);
|
|
const min = Math.floor((seconds % 3600) / 60);
|
|
return `${h}:${String(min).padStart(2, "0")}`;
|
|
}
|
|
|
|
export default function ChargesTable({ charges }: { charges: Charge[] }) {
|
|
if (charges.length === 0) {
|
|
return (
|
|
<div className="text-center py-8 text-gray-500">
|
|
Noch keine Ladungen erfasst
|
|
</div>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="overflow-x-auto">
|
|
<table className="w-full border-collapse">
|
|
<thead>
|
|
<tr className="bg-[#CCCCFF] border-b-2 border-gray-400">
|
|
<th className="p-2 text-center">Datum</th>
|
|
<th className="p-2 text-center">Tag</th>
|
|
<th className="p-2 text-center">Start</th>
|
|
<th className="p-2 text-center">Ende</th>
|
|
<th className="p-2 text-center">
|
|
Dauer<br />
|
|
<span className="text-xs font-normal">h:mm</span>
|
|
</th>
|
|
<th className="p-2 text-center">
|
|
Energie<br />
|
|
<span className="text-xs font-normal">kWh</span>
|
|
</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{charges.map((c, index) => (
|
|
<tr
|
|
key={c.id}
|
|
className={`border-b border-gray-300 hover:bg-gray-50 ${
|
|
index % 2 === 0 ? "bg-white" : "bg-gray-50"
|
|
}`}
|
|
>
|
|
<td className="p-2 text-center">{formatDate(c.start_time)}</td>
|
|
<td className="p-2 text-center">{weekday(c.start_time)}</td>
|
|
<td className="p-2 text-center">{formatTime(c.start_time)}</td>
|
|
<td className="p-2 text-center">{formatTime(c.end_time)}</td>
|
|
<td className="p-2 text-center">{formatDuration(c.duration_s)}</td>
|
|
<td className="p-2 text-center">
|
|
{(c.energy_wh / 1000).toLocaleString("de-DE", {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
})}
|
|
</td>
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
);
|
|
}
|