Initiale Version: Stromverbrauch-Web-App (strom-next)
Next.js-App zur Visualisierung des Stromverbrauchs aus InfluxDB (Bucket strom, measurement vzlogger, Feld arbeit = kumulativer Zaehlerstand in Wh). - 4 Balken-Diagramme: 24h (stuendlich), Woche (stuendlich), 31 Tage (taeglich), Jahr (pro Monat, immer 12 Monate) - Verbrauch via difference des Zaehlerstands, zeitzonen-korrekt - 24h liest Rohdaten (aktuell), 7d/31d/365d den stuendlichen Downsampling-Rollup (arbeit_hourly) -> sub-sekunde - Gesamtverbrauch je Zeitraum unter dem Chart - Design/Layout angelehnt an Werte-Log "Verlauf" Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { NextRequest, NextResponse } from 'next/server';
|
||||
import { resolveRange } from '@/lib/ranges';
|
||||
import { fetchVerbrauch } from '@/lib/influx';
|
||||
import { RangeKey } from '@/types/strom';
|
||||
|
||||
const VALID_RANGES: RangeKey[] = ['24h', '7d', '31d', '365d'];
|
||||
|
||||
// GET /api/verbrauch?range=24h&date=YYYY-MM-DD
|
||||
// ?range=7d&date=YYYY-MM-DD
|
||||
// ?range=31d&month=YYYY-MM
|
||||
// ?range=365d&year=YYYY
|
||||
export async function GET(request: NextRequest) {
|
||||
const sp = request.nextUrl.searchParams;
|
||||
const range = sp.get('range') as RangeKey | null;
|
||||
|
||||
if (!range || !VALID_RANGES.includes(range)) {
|
||||
return NextResponse.json(
|
||||
{ success: false, error: `Ungueltiger range-Parameter. Erlaubt: ${VALID_RANGES.join(', ')}` },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
|
||||
try {
|
||||
const resolved = resolveRange(range, {
|
||||
date: sp.get('date'),
|
||||
month: sp.get('month'),
|
||||
year: sp.get('year'),
|
||||
});
|
||||
const data = await fetchVerbrauch(resolved);
|
||||
|
||||
return NextResponse.json(
|
||||
{ success: true, data },
|
||||
{
|
||||
headers: {
|
||||
'Cache-Control': 'no-store, no-cache, must-revalidate',
|
||||
'Pragma': 'no-cache',
|
||||
'Expires': '0',
|
||||
},
|
||||
}
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('InfluxDB error:', error);
|
||||
return NextResponse.json(
|
||||
{ success: false, error: 'Fehler beim Laden der Verbrauchsdaten.' },
|
||||
{ status: 500 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* stylelint-disable at-rule-no-unknown */
|
||||
@import "tailwindcss";
|
||||
@source "../components/**/*.tsx";
|
||||
@source "../app/**/*.tsx";
|
||||
|
||||
:root {
|
||||
--background: #ffffff;
|
||||
--foreground: #171717;
|
||||
}
|
||||
|
||||
@theme inline {
|
||||
--color-background: var(--background);
|
||||
--color-foreground: var(--foreground);
|
||||
--font-sans: var(--font-geist-sans);
|
||||
--font-mono: var(--font-geist-mono);
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--background);
|
||||
color: var(--foreground);
|
||||
font-family: Arial, Helvetica, sans-serif;
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
import type { Metadata } from "next";
|
||||
import { Geist, Geist_Mono } from "next/font/google";
|
||||
import "./globals.css";
|
||||
|
||||
const geistSans = Geist({
|
||||
variable: "--font-geist-sans",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
const geistMono = Geist_Mono({
|
||||
variable: "--font-geist-mono",
|
||||
subsets: ["latin"],
|
||||
});
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Stromverbrauch",
|
||||
description: "Visualisierung des Stromverbrauchs",
|
||||
};
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
}>) {
|
||||
return (
|
||||
<html lang="de">
|
||||
<body
|
||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
||||
>
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
'use client';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
|
||||
const VerbrauchCharts = dynamic(() => import('@/components/VerbrauchCharts'), {
|
||||
ssr: false,
|
||||
loading: () => (
|
||||
<div className="min-h-screen bg-white flex items-center justify-center">
|
||||
<span className="text-gray-500">Lade Grafiken…</span>
|
||||
</div>
|
||||
),
|
||||
});
|
||||
|
||||
export default function Home() {
|
||||
return <VerbrauchCharts />;
|
||||
}
|
||||
Reference in New Issue
Block a user