feat(stellungnahme): E-Mail, Name in Grußformel, Datum-Fix, UX-Polish
- 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:
@@ -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,12 +169,11 @@
|
||||
else next.add(s);
|
||||
}
|
||||
selectedSlugs = next;
|
||||
// Expand group when selecting so user sees what they picked
|
||||
if (!allSelected) {
|
||||
const nextGroups = new Set(expandedGroups);
|
||||
nextGroups.add(group.slug);
|
||||
expandedGroups = nextGroups;
|
||||
}
|
||||
// Expand on select, collapse on deselect
|
||||
const nextGroups = new Set(expandedGroups);
|
||||
if (allSelected) nextGroups.delete(group.slug);
|
||||
else nextGroups.add(group.slug);
|
||||
expandedGroups = nextGroups;
|
||||
}
|
||||
|
||||
// Combined selection for output — ortskonkrete first, then allgemeine
|
||||
@@ -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`;
|
||||
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";
|
||||
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\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>
|
||||
|
||||
Reference in New Issue
Block a user