feat(stellungnahme): E-Mail, Name in Grußformel, Datum-Fix, UX-Polish
Deploy / verify (push) Successful in 57s
Deploy / deploy (push) Successful in 1m9s

- E-Mail-Button: mailto-Link wenn recipientEmail im Block gesetzt
- Name in Grußformel: "Mit freundlichen Grüßen\n\nMax Mustermann"
- Datum-Fix: heute in buildText() berechnet, nicht beim Render
- Disabled-Hint: roter Hinweis wenn Name fehlt
- "Alle ab" klappt Gruppe ein (Symmetrie zu "Alle" öffnet)
- Schritt X von Y unter Step-Indikator
- recipientEmail-Feld in StellingnahmeGeneratorBlockData

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Peter Meier
2026-05-27 16:32:25 +02:00
parent ff383b8b3b
commit 5f5bd06a78
2 changed files with 35 additions and 36 deletions
+2
View File
@@ -470,5 +470,7 @@ export interface StellingnahmeGeneratorBlockData {
allgemeineArgumente?: TextFragment[];
/** ISO-Datum: Einwendungsfrist-Ende (z.B. "2026-07-04"). */
deadline?: string;
/** E-Mail der Planungsstelle — aktiviert Mail-Button. */
recipientEmail?: string;
layout?: BlockLayout;
}
@@ -169,13 +169,12 @@
else next.add(s);
}
selectedSlugs = next;
// Expand group when selecting so user sees what they picked
if (!allSelected) {
// Expand on select, collapse on deselect
const nextGroups = new Set(expandedGroups);
nextGroups.add(group.slug);
if (allSelected) nextGroups.delete(group.slug);
else nextGroups.add(group.slug);
expandedGroups = nextGroups;
}
}
// Combined selection for output — ortskonkrete first, then allgemeine
const selectedFragments = $derived([
@@ -196,46 +195,35 @@ Regionale Planungsstelle
Karl-Liebknecht-Straße 4
98527 Suhl`;
const heute = $derived(
new Date().toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric" }),
);
const gebietsBez = $derived(
windArea
? `${windArea.gebiets_nr}${windArea.bezeichnung ? " " + windArea.bezeichnung : ""}`
: "",
);
const mailtoLink = $derived.by(() => {
if (!block.recipientEmail || !outputText) return "";
const subject = `Einwendung Regionalplan Südwestthüringen Vorranggebiet ${gebietsBez}`;
return `mailto:${block.recipientEmail}?subject=${encodeURIComponent(subject)}&body=${encodeURIComponent(outputText)}`;
});
function buildText(): string {
const heute = new Date().toLocaleDateString("de-DE", { day: "2-digit", month: "2-digit", year: "numeric" });
const sender = [absenderName.trim(), absenderAnschrift.trim(), absenderOrt.trim()]
.filter(Boolean)
.join("\n");
const betreff = `Einwendung zum Entwurf des Regionalplans Südwestthüringen (2. Entwurf)\nWindenergie-Vorranggebiet ${gebietsBez}`;
let body = "";
if (mode === "fertig") {
body += `ich erhebe Einwendung gegen die Ausweisung des Windenergie-Vorranggebiets ${gebietsBez} im 2. Entwurf des Regionalplans Südwestthüringen.\n\n`;
const closing = absenderName.trim()
? `Mit freundlichen Grüßen\n\n${absenderName.trim()}`
: "Mit freundlichen Grüßen";
let body = `ich erhebe Einwendung gegen die Ausweisung des Windenergie-Vorranggebiets ${gebietsBez} im 2. Entwurf des Regionalplans Südwestthüringen.\n\n`;
for (const f of selectedFragments) {
if (f.title) body += `${f.title}\n\n`;
if (f.text) body += `${stripMd(f.text)}\n\n`;
}
if (persoenlicheEingabe.trim())
body += `Persönliche Betroffenheit\n\n${persoenlicheEingabe.trim()}\n\n`;
body +=
"Ich fordere die Regionalplanung auf, das Vorranggebiet aus dem Regionalplan zu streichen oder zumindest einer erneuten, umfassenden Überprüfung zu unterziehen.\n\nMit freundlichen Grüßen";
} else {
body += `Gliederung meiner Einwendung (bitte in eigenen Worten ausformulieren):\n\n`;
let i = 1;
for (const f of selectedFragments) {
body += `${i}. ${f.title ?? f._slug.replace(/_/g, " ")}\n`;
if (f.bulletPoints?.length)
for (const bp of f.bulletPoints) body += ` - ${bp}\n`;
body += "\n";
i++;
}
if (persoenlicheEingabe.trim())
body += `${i}. Persönliche Betroffenheit\n ${persoenlicheEingabe.trim()}\n\n`;
body += "Ich fordere die Regionalplanung auf, das Vorranggebiet aus dem Regionalplan zu streichen oder zumindest einer erneuten, umfassenden Überprüfung zu unterziehen.\n\nMit freundlichen Grüßen";
}
body += `Ich fordere die Regionalplanung auf, das Vorranggebiet aus dem Regionalplan zu streichen oder zumindest einer erneuten, umfassenden Überprüfung zu unterziehen.\n\n${closing}`;
return [sender, "", heute, "", "An:", RECIPIENT, "", "Betreff: " + betreff.split("\n")[0], betreff.split("\n")[1] ?? "", "", "Sehr geehrte Damen und Herren,", "", body.trimEnd()]
.join("\n")
@@ -473,7 +461,7 @@ Karl-Liebknecht-Straße 4
<div class="prose prose-wald mb-6 max-w-none text-stein-700">{@html md(block.intro)}</div>
{/if}
<!-- Step indicator -->
<!-- Step indicator -->
<div class="mb-2 flex items-center gap-1 text-xs font-medium">
{#each Array.from({ length: TOTAL_STEPS }, (_, i) => i) as s}
<button
@@ -492,6 +480,7 @@ Karl-Liebknecht-Straße 4
<div class="h-px flex-1 {s < step ? 'bg-wald-400' : 'bg-stein-200'}"></div>
{/if}
{/each}
</div>
<p class="mb-6 text-right text-xs text-stein-400">Schritt {step + 1} von {TOTAL_STEPS} {STEP_LABELS[step]}</p>
<!-- ── Step 0: Gebietskontext ─────────────────────────────────────────────── -->
@@ -811,7 +800,7 @@ Karl-Liebknecht-Straße 4
<div class="mb-1 font-semibold text-stein-700">Einzureichen bei:</div>
<div class="whitespace-pre">{RECIPIENT}</div>
</div>
<div class="mb-2 flex gap-3">
<button
class="rounded-lg bg-wald-700 px-6 py-2.5 font-semibold text-white transition hover:bg-wald-800 disabled:opacity-40"
@@ -819,6 +808,11 @@ Karl-Liebknecht-Straße 4
onclick={generateOutput}
>Text erstellen</button>
<button class="rounded-lg border border-stein-300 px-4 py-2 text-sm text-stein-600 hover:bg-stein-50" onclick={() => goTo(3)}> Zurück</button>
</div>
{#if !absenderName.trim()}
<p class="mb-4 text-xs text-erde-600">Bitte Namen eintragen, um den Text zu erstellen.</p>
{:else}
<div class="mb-4"></div>
{/if}
{#if showOutput}
@@ -828,6 +822,9 @@ Karl-Liebknecht-Straße 4
<div class="flex gap-2">
<button class="rounded-md border border-stein-300 px-3 py-1.5 text-xs font-medium text-stein-700 hover:bg-stein-50" onclick={copyText}>{copyFeedback || "Kopieren"}</button>
<button class="rounded-md border border-stein-300 px-3 py-1.5 text-xs font-medium text-stein-700 hover:bg-stein-50" onclick={downloadText}>Herunterladen</button>
<button class="rounded-md border border-stein-300 px-3 py-1.5 text-xs font-medium text-stein-700 hover:bg-stein-50" onclick={printText}>Drucken</button>
{#if mailtoLink}
<a href={mailtoLink} class="rounded-md border border-wald-400 bg-wald-50 px-3 py-1.5 text-xs font-medium text-wald-700 hover:bg-wald-100">Per E-Mail senden</a>
{/if}
</div>
</div>