feat(windkarte): Leaflet-Block für Windvorranggebiete SWT
Neuer CMS-Block `wind_map` — platzierbar auf jeder Seite per Row-Content. Fetcht wind_area-Einträge + GeoJSON-Asset client-side, rendert Leaflet-Karte (CartoDB Light) mit farbcodierten Polygonen nach Planungsstatus. Klick auf Gebiet öffnet Slide-in-Panel mit Kenndaten, Gemeinden, Stellungnahme-Link und Puffer-Ringen (200/600/1000 m via Turf). Erweiterbar für 2. Entwurf 2026. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
<script lang="ts">
|
||||
import Icon from "@iconify/svelte";
|
||||
import "$lib/iconify-offline";
|
||||
import type { WindArea } from "$lib/windkarte";
|
||||
|
||||
let {
|
||||
area,
|
||||
onclose,
|
||||
}: {
|
||||
area: WindArea | null;
|
||||
onclose: () => void;
|
||||
} = $props();
|
||||
|
||||
const STATUS_LABEL: Record<string, string> = {
|
||||
rechtskraeftig: "Rechtskräftig",
|
||||
entwurf_2: "2. Entwurf 2026",
|
||||
entwurf_3: "3. Entwurf",
|
||||
};
|
||||
const STATUS_CLASS: Record<string, string> = {
|
||||
rechtskraeftig: "bg-wald-100 text-wald-800",
|
||||
entwurf_2: "bg-erde-100 text-erde-800",
|
||||
entwurf_3: "bg-himmel-100 text-himmel-800",
|
||||
};
|
||||
|
||||
function stellungnahmeHref(s: WindArea["stellungnahme"]): string | null {
|
||||
if (!s) return null;
|
||||
if (typeof s === "string") return `/post/${s}`;
|
||||
if (s._slug) return `/post/${s._slug}`;
|
||||
return null;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if area}
|
||||
<div class="pointer-events-auto flex h-full flex-col overflow-y-auto bg-stein-0 shadow-xl">
|
||||
<!-- Header -->
|
||||
<div class="flex items-start justify-between gap-3 border-b border-stein-200 px-5 py-4">
|
||||
<div class="min-w-0">
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span class="text-xl font-semibold text-stein-800">{area.name}</span>
|
||||
{#if area.status}
|
||||
<span class="rounded-full px-2 py-0.5 text-xs font-medium {STATUS_CLASS[area.status] ?? 'bg-stein-100 text-stein-600'}">
|
||||
{STATUS_LABEL[area.status] ?? area.status}
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
{#if area.bezeichnung}
|
||||
<p class="mt-0.5 text-sm text-stein-500">{area.bezeichnung}</p>
|
||||
{/if}
|
||||
</div>
|
||||
<button
|
||||
onclick={onclose}
|
||||
class="shrink-0 rounded-md p-1.5 text-stein-400 hover:bg-stein-100 hover:text-stein-700 transition-colors"
|
||||
aria-label="Panel schließen"
|
||||
>
|
||||
<Icon icon="mdi:close" class="size-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div class="flex-1 space-y-5 px-5 py-4 text-sm">
|
||||
<!-- Kenndaten -->
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
{#if area.flaeche_ha != null}
|
||||
<div class="rounded-lg bg-stein-50 p-3">
|
||||
<p class="text-xs text-stein-500">Fläche</p>
|
||||
<p class="mt-0.5 font-semibold text-stein-800">{area.flaeche_ha.toFixed(1)} ha</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if area.anlagen_geplant != null}
|
||||
<div class="rounded-lg bg-stein-50 p-3">
|
||||
<p class="text-xs text-stein-500">Geplante Anlagen</p>
|
||||
<p class="mt-0.5 font-semibold text-stein-800">{area.anlagen_geplant}</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if area.hoehe_max_m != null}
|
||||
<div class="rounded-lg bg-stein-50 p-3">
|
||||
<p class="text-xs text-stein-500">Max. Nabenhöhe</p>
|
||||
<p class="mt-0.5 font-semibold text-stein-800">{area.hoehe_max_m} m</p>
|
||||
</div>
|
||||
{/if}
|
||||
{#if area.investor}
|
||||
<div class="rounded-lg bg-stein-50 p-3 col-span-2">
|
||||
<p class="text-xs text-stein-500">Investor / Betreiber</p>
|
||||
<p class="mt-0.5 font-semibold text-stein-800">{area.investor}</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- Gemeinden -->
|
||||
{#if area.gemeinden && area.gemeinden.length > 0}
|
||||
<div>
|
||||
<p class="mb-2 text-xs font-medium uppercase tracking-wide text-stein-500">Betroffene Gemeinden</p>
|
||||
<div class="flex flex-wrap gap-1.5">
|
||||
{#each area.gemeinden as g}
|
||||
<span class="rounded-full bg-himmel-100 px-2.5 py-0.5 text-xs font-medium text-himmel-800">{g}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Notizen -->
|
||||
{#if area.notizen}
|
||||
<div>
|
||||
<p class="mb-1.5 text-xs font-medium uppercase tracking-wide text-stein-500">Hinweise</p>
|
||||
<p class="text-stein-700 leading-relaxed">{area.notizen}</p>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Stellungnahme -->
|
||||
{#if stellungnahmeHref(area.stellungnahme)}
|
||||
<a
|
||||
href={stellungnahmeHref(area.stellungnahme)}
|
||||
class="flex items-center gap-2 rounded-lg border border-wald-300 bg-wald-50 px-4 py-3 text-sm font-medium text-wald-700 hover:bg-wald-100 transition-colors"
|
||||
>
|
||||
<Icon icon="mdi:file-document-edit-outline" class="size-4 shrink-0" />
|
||||
Zur Stellungnahme-Vorlage
|
||||
</a>
|
||||
{:else}
|
||||
<div class="rounded-lg border border-dashed border-stein-200 px-4 py-3 text-xs text-stein-400">
|
||||
<Icon icon="mdi:file-document-outline" class="inline size-3.5 mr-1" />
|
||||
Stellungnahme-Vorlage noch nicht verknüpft
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
Reference in New Issue
Block a user