Persoenliche Angaben unter Vorlagen statt hardcoded/Settings-Modal
- settings-Tabelle um email/telefon/ort/webseite/geburtsdatum erweitert (CREATE + ALTER-Migration + Default-INSERT) - Vorlagen-Seite: neuer Bereich "Persoenliche Angaben" (8 Felder), POST /vorlagen/persoenlich; Settings-Modal + Zahnrad-Button entfernt - Generierung: Kontakt kommt verbindlich aus den Settings (buildHeader laesst gespeicherte Werte ueber die KI-Extraktion gewinnen), bewerber- Fakten + Prompt angepasst; Design-Vorschau zeigt echte Kontaktdaten - main.js: Settings-Modal-Handling entfernt; /api/settings bleibt bestehen Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
+23
-7
@@ -195,6 +195,11 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen
|
|||||||
const bewerber = [
|
const bewerber = [
|
||||||
settings && settings.name ? `Name: ${settings.name}` : null,
|
settings && settings.name ? `Name: ${settings.name}` : null,
|
||||||
settings && settings.adresse ? `Adresse: ${settings.adresse}` : null,
|
settings && settings.adresse ? `Adresse: ${settings.adresse}` : null,
|
||||||
|
settings && settings.email ? `E-Mail: ${settings.email}` : null,
|
||||||
|
settings && settings.telefon ? `Telefon: ${settings.telefon}` : null,
|
||||||
|
settings && settings.ort ? `Wohnort: ${settings.ort}` : null,
|
||||||
|
settings && settings.webseite ? `Webseite: ${settings.webseite}` : null,
|
||||||
|
settings && settings.geburtsdatum ? `Geburtsdatum: ${settings.geburtsdatum}` : null,
|
||||||
].filter(Boolean).join('\n') || 'Keine Angaben';
|
].filter(Boolean).join('\n') || 'Keine Angaben';
|
||||||
|
|
||||||
const basisText = basisDokumente
|
const basisText = basisDokumente
|
||||||
@@ -327,10 +332,12 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen
|
|||||||
`"Facharzt", "Bachelor/Master", "Dipl.-..." u. Ä.). Im Zweifel eine neutrale, nicht geschützte ` +
|
`"Facharzt", "Bachelor/Master", "Dipl.-..." u. Ä.). Im Zweifel eine neutrale, nicht geschützte ` +
|
||||||
`Tätigkeitsbezeichnung wählen (z. B. "Softwareentwickler", "IT-Fachkraft", "Anwendungsentwickler") ` +
|
`Tätigkeitsbezeichnung wählen (z. B. "Softwareentwickler", "IT-Fachkraft", "Anwendungsentwickler") ` +
|
||||||
`statt eines geschützten Titels.\n` +
|
`statt eines geschützten Titels.\n` +
|
||||||
`- kontakt: E-Mail, Telefon, Ort, Webseite, Geburtsdatum NUR übernehmen, wenn in den ` +
|
`- kontakt: E-Mail, Telefon, Ort (Wohnort), Webseite und Geburtsdatum werden DIR oben ` +
|
||||||
`Basis-Unterlagen vorhanden; sonst leerer String. Nichts erfinden. Geburtsdatum als ` +
|
`unter "Bewerber" als feste Angaben geliefert. Übernimm sie WORTWÖRTLICH (Geburtsdatum ` +
|
||||||
`DD.MM.YYYY (z. B. "03.04.1990"). Keine anderen persönlichen Angaben (Führerschein, ` +
|
`als DD.MM.YYYY, z. B. "03.04.1990"). Fehlt eine Angabe dort, übernimm sie nur, wenn sie ` +
|
||||||
`Familienstand, Nationalität o. Ä.) - diese Felder bleiben leer.\n` +
|
`in den Basis-Unterlagen steht, sonst leerer String. NIEMALS erfinden. Keine anderen ` +
|
||||||
|
`persönlichen Angaben (Führerschein, Familienstand, Nationalität o. Ä.) - diese Felder ` +
|
||||||
|
`bleiben leer.\n` +
|
||||||
`- anschreiben.empfaenger: Empfängerblock. firma = Unternehmen; adresse = Straße + Hausnr. ` +
|
`- anschreiben.empfaenger: Empfängerblock. firma = Unternehmen; adresse = Straße + Hausnr. ` +
|
||||||
`(nur falls aus Stellenanzeige oder Notizen bekannt, sonst leer); ort = "PLZ Ort"; ` +
|
`(nur falls aus Stellenanzeige oder Notizen bekannt, sonst leer); ort = "PLZ Ort"; ` +
|
||||||
`ansprechpartner = konkrete Kontaktperson (nur falls bekannt, sonst leer). Nutze eine in den ` +
|
`ansprechpartner = konkrete Kontaktperson (nur falls bekannt, sonst leer). Nutze eine in den ` +
|
||||||
@@ -1834,11 +1841,20 @@ function renderSingleColumn(compose, t) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildHeader(settings, kontakt, headline) {
|
function buildHeader(settings, kontakt, headline) {
|
||||||
|
const s = settings || {};
|
||||||
|
// Persönliche Kontaktfelder kommen verbindlich aus den Settings (unter "Vorlagen"
|
||||||
|
// gepflegt) – sie gewinnen gegenüber dem vom Modell extrahierten Wert. Ein leeres
|
||||||
|
// Settings-Feld fällt auf den KI-Wert zurück, damit nichts verloren geht.
|
||||||
return {
|
return {
|
||||||
name: (settings && settings.name) || '',
|
name: s.name || '',
|
||||||
adresse: (settings && settings.adresse) || '',
|
adresse: s.adresse || '',
|
||||||
headline,
|
headline,
|
||||||
...kontakt,
|
email: s.email || (kontakt && kontakt.email) || '',
|
||||||
|
telefon: s.telefon || (kontakt && kontakt.telefon) || '',
|
||||||
|
ort: s.ort || (kontakt && kontakt.ort) || '',
|
||||||
|
webseite: s.webseite || (kontakt && kontakt.webseite) || '',
|
||||||
|
geburtsdatum: s.geburtsdatum || (kontakt && kontakt.geburtsdatum) || '',
|
||||||
|
fuehrerschein: kontakt && kontakt.fuehrerschein,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,29 +9,24 @@ const sunIcon = document.getElementById('sunIcon');
|
|||||||
const moonIcon = document.getElementById('moonIcon');
|
const moonIcon = document.getElementById('moonIcon');
|
||||||
|
|
||||||
// Modal Elements
|
// Modal Elements
|
||||||
const settingsModal = document.getElementById('settingsModal');
|
|
||||||
const applicationModal = document.getElementById('applicationModal');
|
const applicationModal = document.getElementById('applicationModal');
|
||||||
const deleteModal = document.getElementById('deleteModal');
|
const deleteModal = document.getElementById('deleteModal');
|
||||||
const pdfExportModal = document.getElementById('pdfExportModal');
|
const pdfExportModal = document.getElementById('pdfExportModal');
|
||||||
|
|
||||||
// Form Elements
|
// Form Elements
|
||||||
const settingsForm = document.getElementById('settingsForm');
|
|
||||||
const applicationForm = document.getElementById('applicationForm');
|
const applicationForm = document.getElementById('applicationForm');
|
||||||
const pdfExportForm = document.getElementById('pdfExportForm');
|
const pdfExportForm = document.getElementById('pdfExportForm');
|
||||||
const filterForm = document.getElementById('filterForm');
|
const filterForm = document.getElementById('filterForm');
|
||||||
|
|
||||||
// Button Elements
|
// Button Elements
|
||||||
const settingsBtn = document.getElementById('settingsBtn');
|
|
||||||
const addApplicationBtn = document.getElementById('addApplicationBtn');
|
const addApplicationBtn = document.getElementById('addApplicationBtn');
|
||||||
const exportPdfBtn = document.getElementById('exportPdfBtn');
|
const exportPdfBtn = document.getElementById('exportPdfBtn');
|
||||||
|
|
||||||
// Close Modal Buttons
|
// Close Modal Buttons
|
||||||
const closeSettingsModal = document.getElementById('closeSettingsModal');
|
|
||||||
const closeApplicationModal = document.getElementById('closeApplicationModal');
|
const closeApplicationModal = document.getElementById('closeApplicationModal');
|
||||||
const closeDeleteModal = document.getElementById('closeDeleteModal');
|
const closeDeleteModal = document.getElementById('closeDeleteModal');
|
||||||
const closePdfModal = document.getElementById('closePdfModal');
|
const closePdfModal = document.getElementById('closePdfModal');
|
||||||
|
|
||||||
const cancelSettings = document.getElementById('cancelSettings');
|
|
||||||
const cancelApplication = document.getElementById('cancelApplication');
|
const cancelApplication = document.getElementById('cancelApplication');
|
||||||
const cancelDelete = document.getElementById('cancelDelete');
|
const cancelDelete = document.getElementById('cancelDelete');
|
||||||
const cancelPdfExport = document.getElementById('cancelPdfExport');
|
const cancelPdfExport = document.getElementById('cancelPdfExport');
|
||||||
@@ -96,51 +91,6 @@ function resetApplicationForm() {
|
|||||||
currentApplicationId = null;
|
currentApplicationId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================
|
|
||||||
// Settings Management
|
|
||||||
// ============================================
|
|
||||||
|
|
||||||
function loadSettings() {
|
|
||||||
fetch('/api/settings')
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(settings => {
|
|
||||||
if (settings) {
|
|
||||||
document.getElementById('userName').value = settings.name || '';
|
|
||||||
document.getElementById('userAddress').value = settings.adresse || '';
|
|
||||||
document.getElementById('customerNumber').value = settings.kundennummer || '';
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error loading settings:', error));
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveSettings(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const formData = new FormData(settingsForm);
|
|
||||||
const settings = {
|
|
||||||
name: formData.get('name'),
|
|
||||||
adresse: formData.get('adresse'),
|
|
||||||
kundennummer: formData.get('kundennummer')
|
|
||||||
};
|
|
||||||
|
|
||||||
fetch('/api/settings', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify(settings)
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
hideModal(settingsModal);
|
|
||||||
// Refresh page to update settings in header
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => console.error('Error saving settings:', error));
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// Application Management (CRUD)
|
// Application Management (CRUD)
|
||||||
// ============================================
|
// ============================================
|
||||||
@@ -723,15 +673,6 @@ function loadPdfLibraries() {
|
|||||||
// Dark Mode Toggle
|
// Dark Mode Toggle
|
||||||
darkModeToggle.addEventListener('click', toggleDarkMode);
|
darkModeToggle.addEventListener('click', toggleDarkMode);
|
||||||
|
|
||||||
// Settings Modal
|
|
||||||
settingsBtn.addEventListener('click', () => {
|
|
||||||
loadSettings();
|
|
||||||
showModal(settingsModal);
|
|
||||||
});
|
|
||||||
closeSettingsModal.addEventListener('click', () => hideModal(settingsModal));
|
|
||||||
cancelSettings.addEventListener('click', () => hideModal(settingsModal));
|
|
||||||
settingsForm.addEventListener('submit', saveSettings);
|
|
||||||
|
|
||||||
// Application Modal
|
// Application Modal
|
||||||
addApplicationBtn.addEventListener('click', openAddApplicationModal);
|
addApplicationBtn.addEventListener('click', openAddApplicationModal);
|
||||||
closeApplicationModal.addEventListener('click', () => {
|
closeApplicationModal.addEventListener('click', () => {
|
||||||
@@ -779,7 +720,6 @@ document.addEventListener('click', (e) => {
|
|||||||
|
|
||||||
// Close modals when clicking outside
|
// Close modals when clicking outside
|
||||||
document.addEventListener('click', (e) => {
|
document.addEventListener('click', (e) => {
|
||||||
if (e.target === settingsModal) hideModal(settingsModal);
|
|
||||||
if (e.target === applicationModal) {
|
if (e.target === applicationModal) {
|
||||||
hideModal(applicationModal);
|
hideModal(applicationModal);
|
||||||
resetApplicationForm();
|
resetApplicationForm();
|
||||||
@@ -791,7 +731,6 @@ document.addEventListener('click', (e) => {
|
|||||||
// Close modals with Escape key
|
// Close modals with Escape key
|
||||||
document.addEventListener('keydown', (e) => {
|
document.addEventListener('keydown', (e) => {
|
||||||
if (e.key === 'Escape') {
|
if (e.key === 'Escape') {
|
||||||
if (!settingsModal.classList.contains('hidden')) hideModal(settingsModal);
|
|
||||||
if (!applicationModal.classList.contains('hidden')) {
|
if (!applicationModal.classList.contains('hidden')) {
|
||||||
hideModal(applicationModal);
|
hideModal(applicationModal);
|
||||||
resetApplicationForm();
|
resetApplicationForm();
|
||||||
|
|||||||
@@ -1009,19 +1009,32 @@ function initializeDatabase() {
|
|||||||
id INTEGER PRIMARY KEY CHECK (id = 1),
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
name TEXT,
|
name TEXT,
|
||||||
adresse TEXT,
|
adresse TEXT,
|
||||||
kundennummer TEXT
|
kundennummer TEXT,
|
||||||
|
email TEXT,
|
||||||
|
telefon TEXT,
|
||||||
|
ort TEXT,
|
||||||
|
webseite TEXT,
|
||||||
|
geburtsdatum TEXT
|
||||||
)
|
)
|
||||||
`, (err) => {
|
`, (err) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
|
||||||
|
// Migration: persönliche Kontaktfelder nachträglich anlegen, falls die
|
||||||
|
// Tabelle aus einer älteren Version stammt (CREATE TABLE IF NOT EXISTS
|
||||||
|
// ergänzt fehlende Spalten nicht). Fehler "duplicate column" ignorieren.
|
||||||
|
const kontaktSpalten = ['email', 'telefon', 'ort', 'webseite', 'geburtsdatum'];
|
||||||
|
kontaktSpalten.forEach((spalte) => {
|
||||||
|
db.run(`ALTER TABLE settings ADD COLUMN ${spalte} TEXT`, () => {});
|
||||||
|
});
|
||||||
|
|
||||||
// Insert default settings if not exists
|
// Insert default settings if not exists
|
||||||
db.get('SELECT COUNT(*) as count FROM settings WHERE id = 1', (err, result) => {
|
db.get('SELECT COUNT(*) as count FROM settings WHERE id = 1', (err, result) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
|
|
||||||
if (result && result.count === 0) {
|
if (result && result.count === 0) {
|
||||||
db.run(
|
db.run(
|
||||||
'INSERT INTO settings (id, name, adresse, kundennummer) VALUES (1, ?, ?, ?)',
|
'INSERT INTO settings (id, name, adresse, kundennummer, email, telefon, ort, webseite, geburtsdatum) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?)',
|
||||||
['Max Mustermann', 'Musterstraße 1, 12345 Musterstadt', ''],
|
['Max Mustermann', 'Musterstraße 1, 12345 Musterstadt', '', '', '', '', '', '', ''],
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err) return reject(err);
|
if (err) return reject(err);
|
||||||
resolve();
|
resolve();
|
||||||
@@ -1074,7 +1087,6 @@ initializeDatabase().then(() => {
|
|||||||
const applications = await dbAll(query, params);
|
const applications = await dbAll(query, params);
|
||||||
await attachVerlauf(applications);
|
await attachVerlauf(applications);
|
||||||
applications.forEach((a) => { a.labelsArr = parseLabels(a.labels); });
|
applications.forEach((a) => { a.labelsArr = parseLabels(a.labels); });
|
||||||
const settings = await dbGet('SELECT * FROM settings WHERE id = 1');
|
|
||||||
// Upcoming calendar appointments for the dashboard widget.
|
// Upcoming calendar appointments for the dashboard widget.
|
||||||
const kommendeTermine = await upcomingTermine(6);
|
const kommendeTermine = await upcomingTermine(6);
|
||||||
|
|
||||||
@@ -1118,7 +1130,6 @@ initializeDatabase().then(() => {
|
|||||||
|
|
||||||
res.render('index', {
|
res.render('index', {
|
||||||
applications,
|
applications,
|
||||||
settings,
|
|
||||||
statistics: {
|
statistics: {
|
||||||
total: totalCount ? totalCount.count : 0,
|
total: totalCount ? totalCount.count : 0,
|
||||||
byArt,
|
byArt,
|
||||||
@@ -1170,11 +1181,15 @@ initializeDatabase().then(() => {
|
|||||||
// Save settings
|
// Save settings
|
||||||
app.post('/api/settings', async (req, res) => {
|
app.post('/api/settings', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { name, adresse, kundennummer } = req.body;
|
const { name, adresse, kundennummer, email, telefon, ort, webseite, geburtsdatum } = req.body;
|
||||||
|
|
||||||
await dbRun(
|
await dbRun(
|
||||||
'UPDATE settings SET name = ?, adresse = ?, kundennummer = ? WHERE id = 1',
|
'UPDATE settings SET name = ?, adresse = ?, kundennummer = ?, email = ?, telefon = ?, ort = ?, webseite = ?, geburtsdatum = ? WHERE id = 1',
|
||||||
[sanitizeInput(name), sanitizeInput(adresse), sanitizeInput(kundennummer)]
|
[
|
||||||
|
sanitizeInput(name), sanitizeInput(adresse), sanitizeInput(kundennummer),
|
||||||
|
sanitizeInput(email), sanitizeInput(telefon), sanitizeInput(ort),
|
||||||
|
sanitizeInput(webseite), sanitizeInput(geburtsdatum)
|
||||||
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
res.json({ success: true });
|
res.json({ success: true });
|
||||||
@@ -1953,6 +1968,7 @@ initializeDatabase().then(() => {
|
|||||||
res.render('vorlagen', {
|
res.render('vorlagen', {
|
||||||
basisDokumente,
|
basisDokumente,
|
||||||
basisAnhaenge,
|
basisAnhaenge,
|
||||||
|
settings: await dbGet('SELECT * FROM settings WHERE id = 1'),
|
||||||
prompts: promptStore.list(await loadPrompts()),
|
prompts: promptStore.list(await loadPrompts()),
|
||||||
designFelder: designStore.list(design),
|
designFelder: designStore.list(design),
|
||||||
designAngepasst: designStore.isAngepasst(design),
|
designAngepasst: designStore.isAngepasst(design),
|
||||||
@@ -1969,6 +1985,28 @@ initializeDatabase().then(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ----- Persönliche Angaben (under Vorlagen, formerly the index-page modal) -----
|
||||||
|
// Registered before /vorlagen/:id so the generic base-document handler doesn't
|
||||||
|
// swallow this path. Stores name/address/contact data that flows into every
|
||||||
|
// generated document (replacing the previous KI extraction from basis texts).
|
||||||
|
app.post('/vorlagen/persoenlich', async (req, res) => {
|
||||||
|
try {
|
||||||
|
const { name, adresse, kundennummer, email, telefon, ort, webseite, geburtsdatum } = req.body;
|
||||||
|
await dbRun(
|
||||||
|
'UPDATE settings SET name = ?, adresse = ?, kundennummer = ?, email = ?, telefon = ?, ort = ?, webseite = ?, geburtsdatum = ? WHERE id = 1',
|
||||||
|
[
|
||||||
|
sanitizeInput(name), sanitizeInput(adresse), sanitizeInput(kundennummer),
|
||||||
|
sanitizeInput(email), sanitizeInput(telefon), sanitizeInput(ort),
|
||||||
|
sanitizeInput(webseite), sanitizeInput(geburtsdatum)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
res.redirect('/vorlagen#persoenlich');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error saving personal data:', error);
|
||||||
|
res.status(500).send('Serverfehler');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// ----- Editable KI prompts -----
|
// ----- Editable KI prompts -----
|
||||||
// Registered before /vorlagen/:id so the generic base-document handlers don't
|
// Registered before /vorlagen/:id so the generic base-document handlers don't
|
||||||
// swallow these paths. The prompt text is stored raw (no HTML escaping): it is
|
// swallow these paths. The prompt text is stored raw (no HTML escaping): it is
|
||||||
|
|||||||
@@ -474,78 +474,6 @@
|
|||||||
|
|
||||||
<%- include('partials/footer') %>
|
<%- include('partials/footer') %>
|
||||||
|
|
||||||
<!-- Settings Modal -->
|
|
||||||
<div id="settingsModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
|
|
||||||
<div class="flex items-center justify-center min-h-screen p-4">
|
|
||||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-xl w-full max-w-md p-6">
|
|
||||||
<div class="flex justify-between items-center mb-6">
|
|
||||||
<h2 class="text-xl font-bold text-gray-800 dark:text-white">Benutzereinstellungen</h2>
|
|
||||||
<button id="closeSettingsModal" class="text-gray-400 hover:text-gray-600 dark:hover:text-gray-300">
|
|
||||||
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<form id="settingsForm" class="space-y-4">
|
|
||||||
<div>
|
|
||||||
<label for="userName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
||||||
Vollständiger Name *
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="userName"
|
|
||||||
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="userAddress" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
||||||
Adresse *
|
|
||||||
</label>
|
|
||||||
<textarea
|
|
||||||
id="userAddress"
|
|
||||||
name="adresse"
|
|
||||||
required
|
|
||||||
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>
|
|
||||||
<label for="customerNumber" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">
|
|
||||||
Jobcenter Kundennummer
|
|
||||||
</label>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
id="customerNumber"
|
|
||||||
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 class="flex justify-end space-x-3 pt-4">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="cancelSettings"
|
|
||||||
class="px-4 py-2 border border-gray-300 dark:border-gray-600 rounded-md text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
|
||||||
Abbrechen
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors">
|
|
||||||
Speichern
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Add/Edit Application Modal -->
|
<!-- Add/Edit Application Modal -->
|
||||||
<div id="applicationModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
|
<div id="applicationModal" class="fixed inset-0 bg-black bg-opacity-50 z-50 hidden">
|
||||||
<div class="flex items-center justify-center min-h-screen p-4">
|
<div class="flex items-center justify-center min-h-screen p-4">
|
||||||
|
|||||||
@@ -66,20 +66,6 @@
|
|||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- Settings button -->
|
|
||||||
<% if (typeof hideSettings === 'undefined' || !hideSettings) { %>
|
|
||||||
<button
|
|
||||||
id="settingsBtn"
|
|
||||||
class="p-2 rounded-full bg-white/20 hover:bg-white/30 transition-colors text-white"
|
|
||||||
aria-label="Einstellungen"
|
|
||||||
>
|
|
||||||
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"></path>
|
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"></path>
|
|
||||||
</svg>
|
|
||||||
</button>
|
|
||||||
<% } %>
|
|
||||||
|
|
||||||
<!-- Notification bell: unread received e-mails (incl. auto-assigned replies) -->
|
<!-- Notification bell: unread received e-mails (incl. auto-assigned replies) -->
|
||||||
<div class="relative" id="notifWrap">
|
<div class="relative" id="notifWrap">
|
||||||
<button id="notifBtn" type="button"
|
<button id="notifBtn" type="button"
|
||||||
|
|||||||
+76
-1
@@ -40,6 +40,81 @@
|
|||||||
</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 -->
|
<!-- Existing base documents -->
|
||||||
<section class="space-y-4 mb-8">
|
<section class="space-y-4 mb-8">
|
||||||
<% if (basisDokumente.length === 0) { %>
|
<% if (basisDokumente.length === 0) { %>
|
||||||
@@ -257,7 +332,7 @@
|
|||||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
|
<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
|
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
|
Lebenslaufs</strong>, damit beide Dokumente als ein Set ankommen. Der Lebenslauf passt in jedem Layout
|
||||||
garantiert auf eine Seite. Die Vorschau zeigt Musterinhalte mit deinem Namen, Foto und deiner
|
garantiert auf eine Seite. Die Vorschau zeigt Musterinhalte mit deinen Kontaktdaten, Foto und deiner
|
||||||
Unterschrift, ohne dass die KI läuft.
|
Unterschrift, ohne dass die KI läuft.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user