|
|
|
@@ -22,6 +22,22 @@ function thisMonthStr(): string {
|
|
|
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function fmtDate(d: Date): string {
|
|
|
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addDays(dateStr: string, delta: number): string {
|
|
|
|
|
const d = dateStr ? new Date(`${dateStr}T00:00:00`) : new Date();
|
|
|
|
|
d.setDate(d.getDate() + delta);
|
|
|
|
|
return fmtDate(d);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function addMonths(monthStr: string, delta: number): string {
|
|
|
|
|
const [y, m] = (monthStr || thisMonthStr()).split('-').map(Number);
|
|
|
|
|
const d = new Date(y, m - 1 + delta, 1);
|
|
|
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const CURRENT_YEAR = new Date().getFullYear();
|
|
|
|
|
const YEARS = Array.from({ length: 11 }, (_, i) => CURRENT_YEAR - i);
|
|
|
|
|
|
|
|
|
@@ -94,7 +110,18 @@ function ChartCard({ range, every, title, color, selector }: ChartCardProps) {
|
|
|
|
|
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
|
|
|
const json = await res.json();
|
|
|
|
|
if (json.success) {
|
|
|
|
|
setData(json.data);
|
|
|
|
|
// nur valide [number, number]-Tupel uebernehmen (robust gegen
|
|
|
|
|
// unerwartete Elemente, damit die Darstellung nie crasht)
|
|
|
|
|
const clean: VerbrauchPoint[] = Array.isArray(json.data)
|
|
|
|
|
? (json.data as unknown[]).filter(
|
|
|
|
|
(p): p is VerbrauchPoint =>
|
|
|
|
|
Array.isArray(p) &&
|
|
|
|
|
p.length >= 2 &&
|
|
|
|
|
typeof p[0] === 'number' &&
|
|
|
|
|
typeof p[1] === 'number'
|
|
|
|
|
)
|
|
|
|
|
: [];
|
|
|
|
|
setData(clean);
|
|
|
|
|
} else {
|
|
|
|
|
setError(json.error || 'Fehler beim Laden der Daten.');
|
|
|
|
|
}
|
|
|
|
@@ -118,6 +145,25 @@ function ChartCard({ range, every, title, color, selector }: ChartCardProps) {
|
|
|
|
|
fetchLaden(value);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Navigation per Pfeil: einen Tag/Monat/Jahr zurueck (-1) oder vor (+1)
|
|
|
|
|
const step = (delta: number) => {
|
|
|
|
|
let nv: string;
|
|
|
|
|
if (selector === 'month') nv = addMonths(value, delta);
|
|
|
|
|
else if (selector === 'year') nv = String(Number(value || CURRENT_YEAR) + delta);
|
|
|
|
|
else nv = addDays(value, delta); // date24 + date
|
|
|
|
|
setValue(nv);
|
|
|
|
|
fetchData(nv);
|
|
|
|
|
fetchLaden(nv);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Vorwaerts nicht in die Zukunft
|
|
|
|
|
const canForward =
|
|
|
|
|
selector === 'month'
|
|
|
|
|
? (value || thisMonthStr()) < thisMonthStr()
|
|
|
|
|
: selector === 'year'
|
|
|
|
|
? Number(value || CURRENT_YEAR) < CURRENT_YEAR
|
|
|
|
|
: (value || todayStr()) < todayStr();
|
|
|
|
|
|
|
|
|
|
// Jahres-Chart: Achse immer ueber das komplette Kalenderjahr -> alle 12 Monate
|
|
|
|
|
// sichtbar, Restmonate bleiben ohne Balken.
|
|
|
|
|
let xMin: number | undefined;
|
|
|
|
@@ -131,8 +177,8 @@ function ChartCard({ range, every, title, color, selector }: ChartCardProps) {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Gesamtverbrauch des angezeigten Zeitraums
|
|
|
|
|
const total = data.reduce((sum, [, v]) => sum + v, 0);
|
|
|
|
|
// Gesamtverbrauch des angezeigten Zeitraums (robust ohne Element-Destrukturierung)
|
|
|
|
|
const total = data.reduce((sum, p) => sum + (Array.isArray(p) ? Number(p[1]) || 0 : 0), 0);
|
|
|
|
|
const totalLabel = range === '365d' ? `Gesamtverbrauch ${value}` : 'Gesamtverbrauch';
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
@@ -147,6 +193,17 @@ function ChartCard({ range, every, title, color, selector }: ChartCardProps) {
|
|
|
|
|
{selector === 'year' && 'Jahr:'}
|
|
|
|
|
</label>
|
|
|
|
|
|
|
|
|
|
{/* Pfeil zurueck */}
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => step(-1)}
|
|
|
|
|
aria-label="zurück"
|
|
|
|
|
title="zurück"
|
|
|
|
|
className="bg-[#85B7D7] hover:bg-[#6a9fc5] px-2.5 py-1.5 text-sm rounded shadow-sm transition-colors"
|
|
|
|
|
>
|
|
|
|
|
◀
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
|
|
{selector === 'year' ? (
|
|
|
|
|
<select
|
|
|
|
|
value={value}
|
|
|
|
@@ -167,6 +224,18 @@ function ChartCard({ range, every, title, color, selector }: ChartCardProps) {
|
|
|
|
|
className="border border-gray-400 rounded px-3 py-1.5 text-sm focus:outline-none focus:ring-2 focus:ring-gray-500"
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
|
|
{/* Pfeil vorwaerts (nicht in die Zukunft) */}
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
onClick={() => step(1)}
|
|
|
|
|
disabled={!canForward}
|
|
|
|
|
aria-label="vorwärts"
|
|
|
|
|
title="vorwärts"
|
|
|
|
|
className="bg-[#85B7D7] hover:bg-[#6a9fc5] px-2.5 py-1.5 text-sm rounded shadow-sm transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
|
|
|
|
>
|
|
|
|
|
▶
|
|
|
|
|
</button>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<button
|
|
|
|
|