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