diff --git a/server.js b/server.js index da3dcf8..84e6ffe 100644 --- a/server.js +++ b/server.js @@ -1181,6 +1181,76 @@ initializeDatabase().then(() => { } }); + // --------------------------------------------------------------------------- + // Postfach: incoming e-mails that landed in the inbox but could not be + // matched to an application automatically. The user assigns them by hand. + // --------------------------------------------------------------------------- + + // Page listing all e-mails with no bewerbung_id, plus every application as + // assignment target. + app.get('/postfach', async (req, res) => { + try { + const emails = await dbAll( + `SELECT * FROM emails WHERE bewerbung_id IS NULL ORDER BY datetime(email_date) DESC, id DESC` + ); + if (emails.length) { + const eIds = emails.map((e) => e.id); + const atts = await dbAll( + `SELECT id, email_id, name, mime FROM email_anhaenge WHERE email_id IN (${eIds.map(() => '?').join(',')})`, + eIds + ); + const byEmail = {}; + atts.forEach((a) => { (byEmail[a.email_id] = byEmail[a.email_id] || []).push(a); }); + emails.forEach((e) => { e.anhaenge = byEmail[e.id] || []; }); + } + // Applications the user can assign an e-mail to (newest first). + const bewerbungen = await dbAll( + `SELECT id, firma, stelle, ort, datum FROM bewerbungen ORDER BY datum DESC, created_at DESC` + ); + res.render('postfach', { + emails, + bewerbungen, + mailConfigured: mailer.isConfigured(), + mailError: req.query.mailerror ? String(req.query.mailerror) : '', + mailOk: req.query.mailok ? String(req.query.mailok) : '', + hideSettings: true, + }); + } catch (error) { + console.error('Error loading postfach:', error); + res.status(500).send('Serverfehler'); + } + }); + + // Assign an unlinked e-mail to an existing application. + app.post('/postfach/:emailId/zuweisen', async (req, res) => { + try { + const bewerbungId = Number(req.body.bewerbung_id); + if (!bewerbungId) { + return res.redirect('/postfach?mailerror=' + encodeURIComponent('Bitte eine Bewerbung auswählen.')); + } + // Make sure the target application exists and the e-mail is still unlinked. + const app = await dbGet('SELECT id FROM bewerbungen WHERE id = ?', [bewerbungId]); + if (!app) { + return res.redirect('/postfach?mailerror=' + encodeURIComponent('Ausgewählte Bewerbung existiert nicht.')); + } + await dbRun('UPDATE emails SET bewerbung_id = ? WHERE id = ? AND bewerbung_id IS NULL', [bewerbungId, req.params.emailId]); + res.redirect('/postfach?mailok=' + encodeURIComponent('E-Mail wurde der Bewerbung zugewiesen.')); + } catch (error) { + console.error('Error assigning e-mail:', error); + res.redirect('/postfach?mailerror=' + encodeURIComponent('Zuweisung fehlgeschlagen: ' + (error.message || 'Unbekannter Fehler'))); + } + }); + + // Count of unlinked e-mails — drives the header badge on every page. + app.get('/api/emails/unassigned-count', async (req, res) => { + try { + const row = await dbGet('SELECT COUNT(*) as count FROM emails WHERE bewerbung_id IS NULL'); + res.json({ count: row ? row.count : 0 }); + } catch (error) { + res.status(500).json({ count: 0 }); + } + }); + // Add a timeline entry (status change with date + comment) app.post('/bewerbung/:id/verlauf', async (req, res) => { try { diff --git a/views/partials/header.ejs b/views/partials/header.ejs index 2ae4d1d..e44289e 100644 --- a/views/partials/header.ejs +++ b/views/partials/header.ejs @@ -9,6 +9,17 @@
+ + + + + + + + +
+ diff --git a/views/postfach.ejs b/views/postfach.ejs new file mode 100644 index 0000000..b622df2 --- /dev/null +++ b/views/postfach.ejs @@ -0,0 +1,136 @@ + + + + <%- include('partials/head') %> + + + <%- include('partials/header', { hideSettings: true }) %> + +
+ + + + + + Zurück zur Übersicht + + +
+
+
+

Postfach — unverknüpfte E-Mails

+

+ E-Mails aus dem Postfach, die keiner Bewerbung zugeordnet sind. Hier per Hand einer Bewerbung zuweisen. +

+
+ <% if (mailConfigured) { %> +
+ + +
+ <% } %> +
+ + <% if (!mailConfigured) { %> +
+ + E-Mail ist nicht konfiguriert. Bitte MAIL_HOST, MAIL_USER und MAIL_PASSWORD in der .env setzen. +
+ <% } else { %> + + <% if (mailError) { %> +
<%= mailError %>
+ <% } %> + <% if (mailOk) { %> +
<%= mailOk %>
+ <% } %> + + <% if (emails && emails.length) { %> + + <% } else { %> +
+ +

Keine unverknüpften E-Mails. Alle Nachrichten im Postfach sind bereits einer Bewerbung zugeordnet.

+
+ <% } %> + + <% } %> +
+
+ + <%- include('partials/footer') %> + + + + \ No newline at end of file