Files
jobbi-bewerbung/views/vorlagen.ejs
T
thomasandClaude 97b48f9841 Einstellungen-Seite: .env-Werte in DB (app_state), keine .env mehr
Neue /einstellungen-Seite (Zahnrad im Header) mit allen bisherigen .env-Werten
(Ollama, E-Mail, CalDAV, REST-API), gespeichert in SQLite (app_state, cfg:-Prefix).
Libs lesen per lib/config.js zur Laufzeit statt beim Start -> Aenderungen
wirken sofort, kein Neustart. Bestehende .env wird beim ersten Start einmalig
migriert.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-13 20:57:31 +02:00

495 lines
33 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="de">
<head>
<%- include('partials/head') %>
</head>
<body class="min-h-screen flex flex-col transition-colors duration-300 bg-gray-50 dark:bg-gray-900 text-gray-900 dark:text-gray-100" id="body">
<%- include('partials/header', { hideSettings: true }) %>
<main class="flex-1 container mx-auto px-4 py-8 max-w-3xl">
<!-- Back link -->
<a href="/" class="inline-flex items-center gap-2 text-sm text-blue-600 dark:text-blue-400 hover:underline mb-6">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
</svg>
Zurück zur Übersicht
</a>
<h1 class="text-2xl font-bold text-gray-800 dark:text-white mb-2">Basis-Unterlagen (Vorlagen)</h1>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Diese Unterlagen dienen der KI als Faktengrundlage. Beim Import einer Stelle über die Browser-Erweiterung
werden daraus automatisch zugeschnittene Bewerbungsunterlagen erstellt es werden ausschließlich die
hier hinterlegten Informationen verwendet, es wird nichts hinzuerfunden. Füge z. B. dein
Standard-Anschreiben und deinen Lebenslauf als Text ein.
</p>
<!-- API key warning -->
<% if (!hasApiKey) { %>
<div class="rounded-lg bg-amber-50 dark:bg-amber-900/30 border border-amber-200 dark:border-amber-800 px-4 py-3 mb-6">
<div class="flex items-start gap-2">
<svg class="w-5 h-5 text-amber-600 dark:text-amber-400 shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"></path>
</svg>
<div class="text-sm text-amber-800 dark:text-amber-200">
<p class="font-semibold">Kein API-Schlüssel konfiguriert</p>
<p>Trage den Ollama-API-Schlüssel unter
<a href="/einstellungen" class="underline font-medium hover:no-underline">Einstellungen</a>
ein, damit Bewerbungsunterlagen automatisch generiert werden können.</p>
</div>
</div>
</div>
<% } %>
<!-- Persönliche Angaben (formerly the index-page settings modal) -->
<h2 id="persoenlich" class="text-xl font-bold text-gray-800 dark:text-white mt-10 mb-2 scroll-mt-4">Persönliche Angaben</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Deine Kontaktdaten fließen in jede Bewerbung ein oben in Lebenslauf und Anschreiben
sowie in die Kontaktleiste. Die KI übernimmt sie unverändert und erfindet nichts mehr;
sie müssen dafür nicht länger in den Basis-Unterlagen stehen.
</p>
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8">
<form action="/vorlagen/persoenlich" method="POST" class="space-y-4">
<div>
<label for="name" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Vollständiger Name *
</label>
<input type="text" id="name" name="name" required
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="Max Mustermann" value="<%= settings.name || '' %>">
</div>
<div>
<label for="adresse" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
Adresse
</label>
<textarea id="adresse" name="adresse" rows="2"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="Musterstraße 1, 12345 Musterstadt"><%= settings.adresse || '' %></textarea>
</div>
<div class="grid gap-4 sm:grid-cols-2">
<div>
<label for="email" class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">E-Mail</label>
<input type="email" id="email" name="email"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="name@example.com" value="<%= settings.email || '' %>">
</div>
<div>
<label for="telefon" class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Telefon</label>
<input type="text" id="telefon" name="telefon"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="z. B. 02043 123456" value="<%= settings.telefon || '' %>">
</div>
<div>
<label for="ort" class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Wohnort</label>
<input type="text" id="ort" name="ort"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="z. B. Gladbeck" value="<%= settings.ort || '' %>">
</div>
<div>
<label for="webseite" class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Webseite</label>
<input type="text" id="webseite" name="webseite"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="z. B. hackner.dev" value="<%= settings.webseite || '' %>">
</div>
<div>
<label for="geburtsdatum" class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Geburtsdatum</label>
<input type="text" id="geburtsdatum" name="geburtsdatum"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="TT.MM.JJJJ" value="<%= settings.geburtsdatum || '' %>">
</div>
<div>
<label for="kundennummer" class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Jobcenter-Kundennummer</label>
<input type="text" id="kundennummer" name="kundennummer"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="z. B. 123456789" value="<%= settings.kundennummer || '' %>">
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
Speichern
</button>
</div>
</form>
</section>
<!-- Existing base documents -->
<section class="space-y-4 mb-8">
<% if (basisDokumente.length === 0) { %>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 text-center text-gray-500 dark:text-gray-400">
Noch keine Basis-Unterlagen hinterlegt. Füge unten dein erstes Dokument hinzu.
</div>
<% } %>
<% basisDokumente.forEach(doc => { %>
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<form action="/vorlagen/<%= doc.id %>" method="POST" class="space-y-3">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Typ</label>
<select name="typ" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
<% basisTypOptions.forEach(opt => { %>
<option value="<%= opt %>" <%= doc.typ === opt ? 'selected' : '' %>><%= opt %></option>
<% }); %>
</select>
</div>
<div class="sm:col-span-2">
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Bezeichnung</label>
<input type="text" name="name" value="<%= doc.name || '' %>"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="z. B. Standard-Anschreiben">
</div>
</div>
<div>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Inhalt (Text)</label>
<textarea name="inhalt" rows="8"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white leading-relaxed font-mono text-sm"><%= doc.inhalt %></textarea>
</div>
<div class="flex justify-end gap-2">
<button type="submit"
class="px-3 py-1.5 text-sm bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors">
Speichern
</button>
</div>
</form>
<form action="/vorlagen/<%= doc.id %>/delete" method="POST" class="mt-1 text-right"
onsubmit="return confirm('Dieses Basis-Dokument löschen?');">
<button type="submit"
class="text-xs text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300 hover:underline">
Dokument löschen
</button>
</form>
</div>
<% }); %>
</section>
<!-- Add new base document -->
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
<h2 class="text-lg font-semibold text-gray-800 dark:text-white mb-4">Basis-Dokument hinzufügen</h2>
<form action="/vorlagen" method="POST" class="space-y-3">
<div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
<div>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Typ</label>
<select name="typ" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
<% basisTypOptions.forEach(opt => { %>
<option value="<%= opt %>"><%= opt %></option>
<% }); %>
</select>
</div>
<div class="sm:col-span-2">
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Bezeichnung</label>
<input type="text" name="name"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="z. B. Standard-Anschreiben">
</div>
</div>
<div>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Inhalt (Text)</label>
<textarea name="inhalt" rows="10" required
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white leading-relaxed font-mono text-sm"
placeholder="Füge hier den vollständigen Text deines Anschreibens oder Lebenslaufs ein…"></textarea>
</div>
<div class="flex justify-end">
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
Hinzufügen
</button>
</div>
</form>
</section>
<!-- Static extra attachments (Zeugnisse etc.) -->
<h2 class="text-xl font-bold text-gray-800 dark:text-white mt-10 mb-2">Zusätzliche Anlagen</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Feste Dateien (z. B. Arbeitszeugnisse, Zertifikate), die jeder generierten Bewerbung
automatisch als Anhang beigelegt und im Anschreiben unter „Anlagen" aufgeführt werden.
Für einzelne Bewerbungen kannst du den Anhang später auf der Bewerbungsseite wieder entfernen.
</p>
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-6">
<% if (basisAnhaenge.length === 0) { %>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">Noch keine zusätzlichen Anlagen hinterlegt.</p>
<% } else { %>
<ul class="space-y-2 mb-2">
<% basisAnhaenge.forEach(a => { %>
<li class="flex items-center justify-between gap-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 px-4 py-3">
<div class="flex items-center gap-3 min-w-0">
<svg class="w-6 h-6 text-red-500 shrink-0" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"></path>
</svg>
<div class="min-w-0">
<p class="text-sm font-medium text-gray-800 dark:text-gray-100 truncate"><%= a.name || a.dateiname %></p>
<p class="text-xs text-gray-500 dark:text-gray-400 truncate"><%= a.dateiname %></p>
</div>
</div>
<div class="flex items-center gap-3 shrink-0">
<a href="/anlagen/<%= a.id %>/download" class="text-sm text-blue-600 hover:text-blue-800 dark:text-blue-400 hover:underline">Download</a>
<form action="/anlagen/<%= a.id %>/delete" method="POST" onsubmit="return confirm('Diese Anlage löschen?');">
<button type="submit" class="text-red-500 hover:text-red-700 dark:text-red-400" aria-label="Anlage löschen">
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
</button>
</form>
</div>
</li>
<% }); %>
</ul>
<% } %>
<form action="/anlagen" method="POST" enctype="multipart/form-data" class="border-t border-gray-200 dark:border-gray-700 pt-4 mt-4 space-y-3">
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
<div>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Datei (PDF)</label>
<input type="file" name="datei" accept="application/pdf,image/*" required
class="w-full text-sm text-gray-700 dark:text-gray-300 file:mr-3 file:py-2 file:px-3 file:rounded-md file:border-0 file:bg-blue-600 file:text-white hover:file:bg-blue-700">
</div>
<div>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Bezeichnung (optional)</label>
<input type="text" name="name"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white"
placeholder="z. B. Arbeitszeugnis">
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
Anlage hochladen
</button>
</div>
</form>
</section>
<!-- Signature -->
<h2 class="text-xl font-bold text-gray-800 dark:text-white mt-10 mb-2">Unterschrift</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Lade dein Unterschriftsbild (PNG/JPG, am besten mit transparentem Hintergrund) hoch. Es wird im
Anschreiben direkt unter „Mit freundlichen Grüßen" anstelle des getippten Namens eingefügt.
</p>
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-6">
<% if (hasSignatur) { %>
<div class="flex items-center justify-between gap-4 mb-4">
<div class="rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 p-3">
<img src="/unterschrift?t=<%= Date.now() %>" alt="Unterschrift" class="max-h-24 max-w-[240px] object-contain">
</div>
<form action="/unterschrift/delete" method="POST" onsubmit="return confirm('Unterschrift entfernen?');">
<button type="submit" class="text-sm text-red-600 hover:text-red-800 dark:text-red-400 hover:underline">Entfernen</button>
</form>
</div>
<% } else { %>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">Noch keine Unterschrift hinterlegt.</p>
<% } %>
<form action="/unterschrift" method="POST" enctype="multipart/form-data"
class="border-t border-gray-200 dark:border-gray-700 pt-4 mt-2 flex flex-wrap items-end gap-3">
<div class="flex-1 min-w-[220px]">
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Unterschrift-Bild (PNG/JPG)</label>
<input type="file" name="signatur" accept="image/png,image/jpeg" required
class="w-full text-sm text-gray-700 dark:text-gray-300 file:mr-3 file:py-2 file:px-3 file:rounded-md file:border-0 file:bg-blue-600 file:text-white hover:file:bg-blue-700">
</div>
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
<%= hasSignatur ? 'Ersetzen' : 'Hochladen' %>
</button>
</form>
</section>
<!-- Applicant photo (Bewerberfoto) -->
<h2 class="text-xl font-bold text-gray-800 dark:text-white mt-10 mb-2">Bewerberfoto</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Lade optional dein Bewerbungsfoto (PNG/JPG, Hochformat) hoch. Es wird oben rechts im
Lebenslauf platziert; ohne Foto bleibt der Lebenslauf textlich wie bisher.
</p>
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-6">
<% if (hasFoto) { %>
<div class="flex items-center justify-between gap-4 mb-4">
<div class="rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 p-3">
<img src="/bewerbungsfoto?t=<%= Date.now() %>" alt="Bewerberfoto" class="max-h-40 max-w-[120px] object-contain">
</div>
<form action="/bewerbungsfoto/delete" method="POST" onsubmit="return confirm('Bewerberfoto entfernen?');">
<button type="submit" class="text-sm text-red-600 hover:text-red-800 dark:text-red-400 hover:underline">Entfernen</button>
</form>
</div>
<% } else { %>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-4">Noch kein Bewerberfoto hinterlegt.</p>
<% } %>
<form action="/bewerbungsfoto" method="POST" enctype="multipart/form-data"
class="border-t border-gray-200 dark:border-gray-700 pt-4 mt-2 flex flex-wrap items-end gap-3">
<div class="flex-1 min-w-[220px]">
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Bewerberfoto (PNG/JPG, Hochformat)</label>
<input type="file" name="foto" accept="image/png,image/jpeg" required
class="w-full text-sm text-gray-700 dark:text-gray-300 file:mr-3 file:py-2 file:px-3 file:rounded-md file:border-0 file:bg-blue-600 file:text-white hover:file:bg-blue-700">
</div>
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
<%= hasFoto ? 'Ersetzen' : 'Hochladen' %>
</button>
</form>
</section>
<!-- Design of the generated PDFs -->
<h2 id="design" class="text-xl font-bold text-gray-800 dark:text-white mt-10 mb-2 scroll-mt-4">Design der Unterlagen</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Aussehen von Anschreiben und Lebenslauf. <strong>Das Anschreiben übernimmt immer das Layout des
Lebenslaufs</strong>, damit beide Dokumente als ein Set ankommen. Der Lebenslauf passt in jedem Layout
garantiert auf eine Seite. Die Vorschau zeigt Musterinhalte mit deinen Kontaktdaten, Foto und deiner
Unterschrift, ohne dass die KI läuft.
</p>
<div class="rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 px-4 py-3 mb-6 text-sm text-blue-900 dark:text-blue-200">
<p><strong>Klassisch mit Sidebar</strong> zurückhaltend, monochrom, für klassische Bewerbungen (Standard).</p>
<p class="mt-1"><strong>Social Media</strong> verspielt: getönte Seite, runde Karten, Herzchen, Schreibschrift.
Bewusst auffällig passend für kreative und Social-Media-Stellen, nicht für die Bank.</p>
</div>
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8">
<form action="/vorlagen/design" method="POST" class="space-y-4" id="designForm">
<div class="grid gap-4 sm:grid-cols-2">
<% designFelder.forEach(f => { %>
<div data-feld="<%= f.key %>" <%- f.nurLayout ? 'data-nur-layout="' + f.nurLayout + '"' : '' %>>
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1"><%= f.label %></label>
<select name="<%= f.key %>"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white">
<% f.optionen.forEach(o => { %>
<option value="<%= o.key %>" <%= o.gewaehlt ? 'selected' : '' %>><%= o.label %></option>
<% }) %>
</select>
</div>
<% }) %>
</div>
<!-- The hidden field must come first: when the box is checked both
values are submitted and the *last* one wins. -->
<input type="hidden" name="foto_anzeigen" value="0">
<label class="flex items-center gap-2 text-sm text-gray-700 dark:text-gray-300">
<input type="checkbox" name="foto_anzeigen" value="1" <%= designFotoAn ? 'checked' : '' %>
class="rounded border-gray-300 dark:border-gray-600">
Bewerberfoto im Lebenslauf anzeigen
<% if (!hasFoto) { %>
<span class="text-xs text-gray-400 dark:text-gray-500">(noch kein Foto hinterlegt)</span>
<% } %>
</label>
<div class="flex flex-wrap items-center gap-3 border-t border-gray-200 dark:border-gray-700 pt-4">
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
Speichern
</button>
<button type="button" id="vorschauAnschreiben"
class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Vorschau Anschreiben
</button>
<button type="button" id="vorschauLebenslauf"
class="px-4 py-2 border border-gray-300 dark:border-gray-600 text-gray-700 dark:text-gray-200 rounded-md hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
Vorschau Lebenslauf
</button>
<% if (designAngepasst) { %>
<button type="submit" formaction="/vorlagen/design/reset" formnovalidate
onclick="return confirm('Design auf die Standardwerte zurücksetzen?');"
class="text-sm text-gray-600 dark:text-gray-400 hover:underline">
Auf Standard zurücksetzen
</button>
<% } %>
</div>
<p class="text-xs text-gray-400 dark:text-gray-500">
Die Vorschau zeigt die aktuell <em>ausgewählten</em> Werte auch ungespeicherte.
</p>
</form>
</section>
<!-- Editable KI prompts -->
<h2 id="prompts" class="text-xl font-bold text-gray-800 dark:text-white mt-10 mb-2 scroll-mt-4">KI-Prompts</h2>
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
Hier steht, <em>wie</em> die KI schreibt: Rolle, Tonfall und Regeln für Unterlagen, Chat und E-Mails.
Du kannst jeden Text anpassen etwa um zu duzen, Formulierungen zu verbieten oder eine andere
Sprache zu verlangen. Der technische Aufbau (Aufbau des Lebenslaufs, Felder der E-Mail) ist fest
verdrahtet und wird davon nicht berührt, damit nichts kaputtgehen kann.
Mit <strong>Zurücksetzen</strong> holst du jederzeit den Standardtext zurück.
</p>
<section class="space-y-4 mb-8">
<% prompts.forEach(p => { %>
<details class="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-hidden" <%= p.angepasst ? 'open' : '' %>>
<summary class="cursor-pointer select-none px-6 py-4 flex items-center gap-3">
<span class="font-semibold text-gray-800 dark:text-white"><%= p.titel %></span>
<% if (p.angepasst) { %>
<span class="text-xs px-2 py-0.5 rounded-full bg-blue-100 text-blue-800 dark:bg-blue-900/50 dark:text-blue-300">angepasst</span>
<% } else { %>
<span class="text-xs px-2 py-0.5 rounded-full bg-gray-100 text-gray-500 dark:bg-gray-700 dark:text-gray-400">Standard</span>
<% } %>
</summary>
<div class="px-6 pb-6 border-t border-gray-200 dark:border-gray-700 pt-4">
<p class="text-sm text-gray-500 dark:text-gray-400 mb-3"><%= p.beschreibung %></p>
<% if (p.platzhalter && p.platzhalter.length) { %>
<p class="text-xs text-gray-500 dark:text-gray-400 mb-3">
Platzhalter:
<% p.platzhalter.forEach(ph => { %>
<code class="px-1 rounded bg-gray-100 dark:bg-gray-700">{{<%= ph %>}}</code>
<% }) %>
wird beim Generieren automatisch ersetzt (Name aus den Einstellungen).
</p>
<% } %>
<form action="/vorlagen/prompts/<%= p.key %>" method="POST" class="space-y-3">
<textarea name="inhalt" rows="9"
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-900 dark:text-white font-mono text-xs leading-relaxed"><%= p.inhalt %></textarea>
<div class="flex items-center gap-3">
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
Speichern
</button>
<% if (p.angepasst) { %>
<button type="submit" formaction="/vorlagen/prompts/<%= p.key %>/reset"
formnovalidate
onclick="return confirm('Diesen Prompt auf den Standardtext zurücksetzen? Deine Änderungen gehen verloren.');"
class="text-sm text-gray-600 dark:text-gray-400 hover:underline">
Auf Standard zurücksetzen
</button>
<% } %>
</div>
</form>
</div>
</details>
<% }) %>
</section>
</main>
<%- include('partials/footer') %>
<script>
(function () {
const cy = document.getElementById('currentYear');
if (cy) cy.textContent = new Date().getFullYear();
})();
// Design preview: open the sample PDF with the values currently selected
// in the form (not just the saved ones), so a choice can be judged before
// it is committed.
(function () {
const form = document.getElementById('designForm');
if (!form) return;
function oeffne(welches) {
const q = new URLSearchParams(new FormData(form)).toString();
window.open('/vorlagen/design/vorschau/' + welches + '.pdf?' + q, '_blank');
}
const a = document.getElementById('vorschauAnschreiben');
const l = document.getElementById('vorschauLebenslauf');
if (a) a.addEventListener('click', () => oeffne('anschreiben'));
if (l) l.addEventListener('click', () => oeffne('lebenslauf'));
// Hide fields that only apply to one layout (e.g. the sidebar tint has
// no meaning in the social layout).
const layoutSel = form.querySelector('[name="layout"]');
function syncFelder() {
const aktiv = layoutSel ? layoutSel.value : 'sidebar';
form.querySelectorAll('[data-nur-layout]').forEach((el) => {
el.style.display = el.dataset.nurLayout === aktiv ? '' : 'none';
});
}
if (layoutSel) layoutSel.addEventListener('change', syncFelder);
syncFelder();
})();
</script>
</body>
</html>