initial commit

This commit is contained in:
TheOnlyMace
2026-06-18 23:45:08 +02:00
commit 3ebdc96381
104 changed files with 4778 additions and 0 deletions

218
frontend/index.html Normal file
View File

@@ -0,0 +1,218 @@
<!doctype html>
<html lang="de" data-theme="dark">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="HexaWetter Selfhosted Wetterportal mit DWD Radar, Warnungen und Forecast" />
<title>HexaWetter</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header class="site-header">
<div class="container header-inner">
<a class="brand-link" href="index.html">
<div class="brand-logo" aria-hidden="true">H</div>
<div>
<div class="brand-sub">HexaHost · Selfhosted Weather</div>
<div class="brand-title">HexaWetter</div>
</div>
</a>
<div class="header-actions">
<div class="status-pill" id="health">API: prüfe…</div>
<button id="themeToggle" class="btn btn-ghost icon-btn" title="Theme wechseln" aria-label="Theme wechseln">🌙</button>
<a href="admin.html" class="btn btn-secondary">Admin</a>
</div>
</div>
</header>
<main class="app-main">
<div class="container">
<section class="hero-copy">
<h1>Wetter für deinen Standort</h1>
<p>Forecast, DWD-Beobachtungen, interaktive Radar-Karte mit Zeitleiste und standortgenaue Warnungen gehostet auf deiner Infrastruktur.</p>
</section>
<section class="glass-card controls">
<div class="search-wrap">
<label for="placeSearch">Ortssuche / PLZ</label>
<input id="placeSearch" type="search" placeholder="z.B. Hauzenberg, 94051 Passau…" autocomplete="off" />
<ul id="searchResults" class="search-results hidden" role="listbox"></ul>
</div>
<div>
<label for="place">Ort / Label</label>
<input id="place" value="Hauzenberg" />
</div>
<div>
<label for="lat">Latitude</label>
<input id="lat" type="number" step="0.0001" value="48.6546" />
</div>
<div>
<label for="lon">Longitude</label>
<input id="lon" type="number" step="0.0001" value="13.6252" />
</div>
<div class="control-actions">
<button id="geoBtn" class="btn btn-secondary icon-btn" title="Browser-Standort">📍</button>
<button id="favBtn" class="btn btn-secondary icon-btn" title="Favorit"></button>
<button id="loadBtn" class="btn btn-primary">Aktualisieren</button>
</div>
</section>
<section class="glass-card favorites-panel hidden" id="favoritesPanel">
<div class="section-head">
<h2>Gespeicherte Orte</h2>
<span class="muted">Klicken zum Laden</span>
</div>
<div id="favoritesList" class="chip-row"></div>
</section>
<nav class="tabs" aria-label="Ansichten">
<button class="tab active" data-view="dashboard">Übersicht</button>
<button class="tab" data-view="radar">Radar-Karte</button>
<button class="tab" data-view="warnings">Warnungen</button>
<button class="tab" data-view="hourly">Stunden</button>
<button class="tab" data-view="daily">7 Tage</button>
<button class="tab" data-view="raw">Rohdaten</button>
<button class="tab" data-view="settings">Einstellungen</button>
</nav>
<section class="view active" data-view="dashboard">
<section class="grid">
<article class="glass-card card"><h2>Aktuelles Wetter</h2><p class="big" id="currentTemp"></p><p id="currentMeta" class="muted">Lade…</p></article>
<article class="glass-card card"><h2>DWD-Beobachtung</h2><p class="big" id="obsTemp"></p><p id="obsMeta" class="muted">Lade…</p></article>
<article class="glass-card card"><h2>Radar-Status</h2><p class="big" id="radarCount"></p><p id="radarMeta" class="muted">Lade…</p></article>
<article class="glass-card card"><h2>Warnungen</h2><p class="big" id="warnCount"></p><p id="warnMeta" class="muted">Lade…</p></article>
</section>
<section class="glass-card">
<div class="section-head"><h2>Details aktuell</h2><span class="muted" id="forecastSource"></span></div>
<div class="metrics" id="currentDetails"></div>
</section>
<section class="glass-card">
<div class="section-head"><h2>Kurzvorschau</h2><span class="muted">Nächste Stunden</span></div>
<div class="hour-strip" id="hourStrip"></div>
</section>
<section class="glass-card">
<div class="section-head"><h2>Datenquellen-Status</h2><span class="muted" id="sourceStatusSummary">Prüfe…</span></div>
<div class="source-status-grid" id="sourceStatusGrid"></div>
</section>
</section>
<section class="view" data-view="radar">
<section class="glass-card map-panel">
<div class="section-head">
<div><h2>Radar-Karte</h2><p class="muted no-margin">DWD WMS · OpenStreetMap · Zeitleiste</p></div>
<span class="muted" id="mapMeta">Lade…</span>
</div>
<div id="map"></div>
<div class="panel-inner" id="radarTimeline">
<div class="section-head"><strong>Radar-Zeitleiste</strong><span class="muted" id="radarTimeLabel"></span></div>
<div class="timeline-controls">
<button id="radarFirstBtn" class="btn btn-secondary icon-btn"></button>
<button id="radarPrevBtn" class="btn btn-secondary icon-btn"></button>
<button id="radarPlayBtn" class="btn btn-secondary icon-btn"></button>
<button id="radarNextBtn" class="btn btn-secondary icon-btn"></button>
<button id="radarLastBtn" class="btn btn-secondary icon-btn"></button>
<input id="radarSlider" type="range" min="0" max="0" value="0" />
</div>
<div class="timeline-meta">
<label class="inline-check"><input type="checkbox" id="useLocalRadar" /> Lokale Render-Frames</label>
<label class="inline-check"><input type="checkbox" id="showWarningsOnMap" checked /> Warnungen</label>
<span class="muted" id="radarFrameInfo"></span>
</div>
</div>
<div class="map-actions">
<button id="centerMapBtn" class="btn btn-secondary">Auf Standort zentrieren</button>
<button id="reloadRadarLayersBtn" class="btn btn-secondary">Layer neu laden</button>
</div>
</section>
</section>
<section class="view" data-view="warnings">
<section class="glass-card">
<div class="section-head">
<div>
<h2>Wetterwarnungen</h2>
<p class="muted no-margin">Standortgenau via DWD-Warncell-IDs (WFS)</p>
</div>
<label class="inline-check"><input type="checkbox" id="warningsLocalOnly" checked /> Nur Standort</label>
</div>
<div id="warncellInfo" class="warncell-info hidden"></div>
<div id="warningsList" class="warnings-list"></div>
</section>
</section>
<section class="view" data-view="hourly">
<section class="glass-card">
<div class="section-head"><h2>Stundenansicht</h2><span class="muted">48 Stunden</span></div>
<div class="table-wrap"><table><thead><tr><th>Zeit</th><th>Wetter</th><th>Temp.</th><th>Regen</th><th>Wolken</th><th>Wind</th><th>Böen</th></tr></thead><tbody id="hourlyTable"></tbody></table></div>
</section>
</section>
<section class="view" data-view="daily">
<section class="glass-card">
<div class="section-head"><h2>7-Tage Forecast</h2><span class="muted" id="dailySource"></span></div>
<div class="days" id="dailyForecast"></div>
</section>
</section>
<section class="view" data-view="raw">
<section class="glass-card">
<div class="section-head">
<div><h2>Radar-Rohdaten</h2><p class="muted no-margin">DWD OpenData · lokaler Renderer</p></div>
<button id="syncRadarBtn" class="btn btn-secondary">Sync starten</button>
</div>
<div class="table-wrap"><table><thead><tr><th>Datei</th><th>Größe</th><th>Geändert</th><th>Download</th></tr></thead><tbody id="radarFiles"></tbody></table></div>
</section>
<section class="glass-card">
<div class="section-head"><h2>Gerenderte Frames</h2><span class="muted" id="renderedCount"></span></div>
<div class="rendered-grid" id="renderedFrames"></div>
</section>
<section class="glass-card">
<div class="section-head"><h2>Datenquellen</h2></div>
<div class="source-list" id="sourceList"></div>
</section>
</section>
<section class="view" data-view="settings">
<section class="glass-card">
<h2>Einstellungen</h2>
<div class="settings-grid">
<label>Temperatureinheit<select id="unitTemp"><option value="celsius">°C</option><option value="fahrenheit">°F</option></select></label>
<label>Windeinheit<select id="unitWind"><option value="kmh">km/h</option><option value="ms">m/s</option><option value="kn">kn</option></select></label>
<label>Radar-Zeitleiste<select id="radarMinutes"><option value="60">60 Min.</option><option value="90">90 Min.</option><option value="120" selected>120 Min.</option></select></label>
<label>Standard-Standort<button id="saveDefaultLocationBtn" class="btn btn-secondary full-width">Aktuellen Ort speichern</button></label>
</div>
</section>
</section>
</div>
</main>
<footer class="site-footer">
<div class="container footer-grid">
<div>
<strong>HexaWetter</strong>
<p>Selfhosted Wetterportal von HexaHost. Keine amtliche Wetterberatung.</p>
<p class="muted">Daten: DWD Open Data · Open-Meteo · Bright Sky · OpenStreetMap</p>
</div>
<div>
<strong>Rechtliches</strong>
<ul>
<li><a href="impressum.html">Impressum</a></li>
<li><a href="datenschutz.html">Datenschutz</a></li>
</ul>
</div>
<div>
<strong>System</strong>
<ul>
<li><a href="admin.html">Admin-Bereich</a></li>
<li><a href="/api/health" target="_blank" rel="noopener">API Health</a></li>
</ul>
</div>
</div>
</footer>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="app.js"></script>
</body>
</html>