diff --git a/lib/documents.js b/lib/documents.js index d6eb681..eaf7242 100644 --- a/lib/documents.js +++ b/lib/documents.js @@ -195,6 +195,11 @@ async function generateTailoredTexts({ job, basisDokumente, settings, llmNotizen const bewerber = [ settings && settings.name ? `Name: ${settings.name}` : 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'; 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 ` + `Tätigkeitsbezeichnung wählen (z. B. "Softwareentwickler", "IT-Fachkraft", "Anwendungsentwickler") ` + `statt eines geschützten Titels.\n` + - `- kontakt: E-Mail, Telefon, Ort, Webseite, Geburtsdatum NUR übernehmen, wenn in den ` + - `Basis-Unterlagen vorhanden; sonst leerer String. Nichts erfinden. Geburtsdatum als ` + - `DD.MM.YYYY (z. B. "03.04.1990"). Keine anderen persönlichen Angaben (Führerschein, ` + - `Familienstand, Nationalität o. Ä.) - diese Felder bleiben leer.\n` + + `- kontakt: E-Mail, Telefon, Ort (Wohnort), Webseite und Geburtsdatum werden DIR oben ` + + `unter "Bewerber" als feste Angaben geliefert. Übernimm sie WORTWÖRTLICH (Geburtsdatum ` + + `als DD.MM.YYYY, z. B. "03.04.1990"). Fehlt eine Angabe dort, übernimm sie nur, wenn sie ` + + `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. ` + `(nur falls aus Stellenanzeige oder Notizen bekannt, sonst leer); ort = "PLZ Ort"; ` + `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) { + 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 { - name: (settings && settings.name) || '', - adresse: (settings && settings.adresse) || '', + name: s.name || '', + adresse: s.adresse || '', 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, }; } diff --git a/public/js/main.js b/public/js/main.js index a3fb27a..c5f3279 100644 --- a/public/js/main.js +++ b/public/js/main.js @@ -9,29 +9,24 @@ const sunIcon = document.getElementById('sunIcon'); const moonIcon = document.getElementById('moonIcon'); // Modal Elements -const settingsModal = document.getElementById('settingsModal'); const applicationModal = document.getElementById('applicationModal'); const deleteModal = document.getElementById('deleteModal'); const pdfExportModal = document.getElementById('pdfExportModal'); // Form Elements -const settingsForm = document.getElementById('settingsForm'); const applicationForm = document.getElementById('applicationForm'); const pdfExportForm = document.getElementById('pdfExportForm'); const filterForm = document.getElementById('filterForm'); // Button Elements -const settingsBtn = document.getElementById('settingsBtn'); const addApplicationBtn = document.getElementById('addApplicationBtn'); const exportPdfBtn = document.getElementById('exportPdfBtn'); // Close Modal Buttons -const closeSettingsModal = document.getElementById('closeSettingsModal'); const closeApplicationModal = document.getElementById('closeApplicationModal'); const closeDeleteModal = document.getElementById('closeDeleteModal'); const closePdfModal = document.getElementById('closePdfModal'); -const cancelSettings = document.getElementById('cancelSettings'); const cancelApplication = document.getElementById('cancelApplication'); const cancelDelete = document.getElementById('cancelDelete'); const cancelPdfExport = document.getElementById('cancelPdfExport'); @@ -96,51 +91,6 @@ function resetApplicationForm() { 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) // ============================================ @@ -723,15 +673,6 @@ function loadPdfLibraries() { // Dark Mode Toggle 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 addApplicationBtn.addEventListener('click', openAddApplicationModal); closeApplicationModal.addEventListener('click', () => { @@ -779,7 +720,6 @@ document.addEventListener('click', (e) => { // Close modals when clicking outside document.addEventListener('click', (e) => { - if (e.target === settingsModal) hideModal(settingsModal); if (e.target === applicationModal) { hideModal(applicationModal); resetApplicationForm(); @@ -791,7 +731,6 @@ document.addEventListener('click', (e) => { // Close modals with Escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape') { - if (!settingsModal.classList.contains('hidden')) hideModal(settingsModal); if (!applicationModal.classList.contains('hidden')) { hideModal(applicationModal); resetApplicationForm(); diff --git a/server.js b/server.js index 879b792..edb05f0 100644 --- a/server.js +++ b/server.js @@ -1009,19 +1009,32 @@ function initializeDatabase() { id INTEGER PRIMARY KEY CHECK (id = 1), name TEXT, adresse TEXT, - kundennummer TEXT + kundennummer TEXT, + email TEXT, + telefon TEXT, + ort TEXT, + webseite TEXT, + geburtsdatum TEXT ) `, (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 db.get('SELECT COUNT(*) as count FROM settings WHERE id = 1', (err, result) => { if (err) return reject(err); if (result && result.count === 0) { db.run( - 'INSERT INTO settings (id, name, adresse, kundennummer) VALUES (1, ?, ?, ?)', - ['Max Mustermann', 'Musterstraße 1, 12345 Musterstadt', ''], + 'INSERT INTO settings (id, name, adresse, kundennummer, email, telefon, ort, webseite, geburtsdatum) VALUES (1, ?, ?, ?, ?, ?, ?, ?, ?)', + ['Max Mustermann', 'Musterstraße 1, 12345 Musterstadt', '', '', '', '', '', '', ''], (err) => { if (err) return reject(err); resolve(); @@ -1074,7 +1087,6 @@ initializeDatabase().then(() => { const applications = await dbAll(query, params); await attachVerlauf(applications); 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. const kommendeTermine = await upcomingTermine(6); @@ -1118,7 +1130,6 @@ initializeDatabase().then(() => { res.render('index', { applications, - settings, statistics: { total: totalCount ? totalCount.count : 0, byArt, @@ -1170,13 +1181,17 @@ initializeDatabase().then(() => { // Save settings app.post('/api/settings', async (req, res) => { try { - const { name, adresse, kundennummer } = req.body; - + const { name, adresse, kundennummer, email, telefon, ort, webseite, geburtsdatum } = req.body; + await dbRun( - 'UPDATE settings SET name = ?, adresse = ?, kundennummer = ? WHERE id = 1', - [sanitizeInput(name), sanitizeInput(adresse), sanitizeInput(kundennummer)] + '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.json({ success: true }); } catch (error) { console.error('Error saving settings:', error); @@ -1953,6 +1968,7 @@ initializeDatabase().then(() => { res.render('vorlagen', { basisDokumente, basisAnhaenge, + settings: await dbGet('SELECT * FROM settings WHERE id = 1'), prompts: promptStore.list(await loadPrompts()), designFelder: designStore.list(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 ----- // 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 diff --git a/views/index.ejs b/views/index.ejs index 5220c0f..41038b2 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -474,78 +474,6 @@ <%- include('partials/footer') %> - -
-