feat(wind): taktische Live-Windanzeige in der Kopfzeile (v1.6.2)
All checks were successful
Build and Push Docker Image / build-and-push (push) Successful in 32m13s

- Wind-Badge oben in der Topbar: Pfeil zeigt die Ausbreitungsrichtung
  (wohin es treibt, Nord oben) + Himmelsrichtung „aus NW", mit Live-Punkt
- Aktualisiert sich in Echtzeit: solange im Live-Modus, alle 10 Min. frische
  Windrichtung von Open-Meteo für den Einsatzort
- Manuelles Setzen (Windrose) schaltet Live-Modus ab; „Live vom Standort"
  schaltet ihn wieder an
- Tooltip: „Wind aus NW (315°) — treibt nach SO · live"

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-22 21:11:42 +02:00
parent 997e200d68
commit d758c8c744
3 changed files with 47 additions and 5 deletions

View File

@@ -49,6 +49,7 @@ import {
Loader2,
} from 'lucide-react'
import { HoseSettingsDialog } from '@/components/dialogs/hose-settings-dialog'
import { degToCompass } from '@/components/map/map-compass'
import type { Project, DrawFeature, ProjectMode } from '@/types'
import { formatDateTime } from '@/lib/utils'
import { Logo } from '@/components/ui/logo'
@@ -77,6 +78,8 @@ interface TopbarProps {
onStartTour?: () => void
presentationLocked?: boolean
onTogglePresentationLock?: () => void
/** Windrichtung wird live aktualisiert (für die Live-Anzeige oben) */
windLive?: boolean
}
export function Topbar({
@@ -103,6 +106,7 @@ export function Topbar({
onStartTour,
presentationLocked,
onTogglePresentationLock,
windLive,
}: TopbarProps) {
const [isLoadDialogOpen, setIsLoadDialogOpen] = useState(false)
const [isHoseSettingsOpen, setIsHoseSettingsOpen] = useState(false)
@@ -198,6 +202,22 @@ export function Topbar({
{/* Zone 3 — Uhr + Aktionen */}
<div className="flex items-center gap-1 md:gap-1.5 shrink-0">
{/* Wind — taktische Live-Anzeige (Pfeil zeigt Ausbreitungsrichtung, N oben) */}
{typeof project?.windDirection === 'number' && (
<div
className="hidden sm:flex items-center gap-1.5 rounded-md bg-blue-600 text-white pl-1.5 pr-2 py-1 shrink-0"
title={`Wind aus ${degToCompass(project.windDirection)} (${project.windDirection}°) — treibt nach ${degToCompass(project.windDirection + 180)}${windLive ? ' · live' : ''}`}
>
<svg viewBox="0 0 24 24" className="w-4 h-4 shrink-0" style={{ transform: `rotate(${project.windDirection + 180}deg)` }} aria-hidden>
<path d="M12 3 L18 16 L12 12.5 L6 16 Z" fill="currentColor" />
</svg>
<span className="text-xs font-bold leading-none whitespace-nowrap">
<span className="hidden lg:inline opacity-80 font-medium">Wind </span>aus {degToCompass(project.windDirection)}
</span>
{windLive && <span className="w-1.5 h-1.5 rounded-full bg-green-300 animate-pulse shrink-0" title="Live" />}
</div>
)}
{/* Uhr — Ops-Rooms leben nach der Uhr */}
<span className="hidden sm:inline text-base md:text-lg font-bold tabular-nums mr-0.5 md:mr-1.5">
{now.toLocaleTimeString('de-CH', { hour: '2-digit', minute: '2-digit' })}