diff --git a/extension/background.js b/extension/background.js index 49a7041..2bbe2d6 100644 --- a/extension/background.js +++ b/extension/background.js @@ -29,6 +29,16 @@ chrome.runtime.onMessage.addListener((message, sender, sendResponse) => { try { data = await res.json(); } catch (_) { /* non-JSON response */ } if (!res.ok) { + // Duplicate guard: surface the existing matches so the user can decide. + if (res.status === 409 && data && data.duplicate) { + sendResponse({ + ok: false, + duplicate: true, + matches: data.matches || [], + error: (data && data.error) || 'Für diese Stelle existiert bereits eine Bewerbung.', + }); + return; + } sendResponse({ ok: false, error: (data && data.error) || `Server antwortete mit ${res.status}`, diff --git a/extension/content.js b/extension/content.js index 512efae..67e2f29 100644 --- a/extension/content.js +++ b/extension/content.js @@ -84,6 +84,7 @@ gehalt: getSalary(root), stellenbeschreibung: getDescription(root), quelle_url: getSourceUrl(), + art: 'Indeed', }; } @@ -119,6 +120,16 @@ status.innerHTML = html; } + function duplicateWarning(matches) { + const lines = (matches || []).slice(0, 5).map(function (m) { + const parts = [m.firma, m.stelle].filter(Boolean).join(' — '); + const meta = [m.datum, m.status].filter(Boolean).join(', '); + return '• ' + parts + (meta ? ' (' + meta + ')' : ''); + }); + return 'Für diese Stelle scheint bereits eine Bewerbung zu existieren:\n\n' + + lines.join('\n') + '\n\nTrotzdem als weitere Bewerbung importieren?'; + } + function onClick(btn, status) { const job = scrapeJob(); @@ -127,37 +138,50 @@ return; } - btn.disabled = true; const label = btn.querySelector('span'); const originalLabel = label ? label.textContent : ''; - btn.innerHTML = 'Wird gesendet…'; - setStatus(status, '', ''); - chrome.runtime.sendMessage({ type: 'IMPORT_JOB', payload: job }, (resp) => { - btn.innerHTML = ICON_SEND + '' + (originalLabel || 'An Bewerbungs-Tracker senden') + ''; + const send = (payload) => { + btn.disabled = true; + btn.innerHTML = 'Wird gesendet…'; + setStatus(status, '', ''); - if (chrome.runtime.lastError) { - btn.disabled = false; - setStatus(status, 'Fehler: ' + chrome.runtime.lastError.message, 'bt-err'); - return; - } - if (!resp || !resp.ok) { - btn.disabled = false; - setStatus(status, (resp && resp.error) || 'Unbekannter Fehler.', 'bt-err'); - return; - } + chrome.runtime.sendMessage({ type: 'IMPORT_JOB', payload: payload }, (resp) => { + btn.innerHTML = ICON_SEND + '' + (originalLabel || 'An Bewerbungs-Tracker senden') + ''; - const link = resp.openUrl - ? ` Entwurf öffnen` - : ''; - setStatus( - status, - '✓ ' + (resp.message || 'Als Entwurf angelegt. Unterlagen werden erstellt.') + link, - 'bt-ok' - ); - // Re-enable after a moment so the user can send again if needed. - setTimeout(() => { btn.disabled = false; }, 1500); - }); + if (chrome.runtime.lastError) { + btn.disabled = false; + setStatus(status, 'Fehler: ' + chrome.runtime.lastError.message, 'bt-err'); + return; + } + if (resp && resp.duplicate) { + btn.disabled = false; + if (confirm(duplicateWarning(resp.matches))) { + send(Object.assign({}, payload, { force: true })); + } else { + setStatus(status, 'Import abgebrochen (mögliches Duplikat).', ''); + } + return; + } + if (!resp || !resp.ok) { + btn.disabled = false; + setStatus(status, (resp && resp.error) || 'Unbekannter Fehler.', 'bt-err'); + return; + } + + const link = resp.openUrl + ? ` Entwurf öffnen` + : ''; + setStatus( + status, + '✓ ' + (resp.message || 'Als Entwurf angelegt. Unterlagen werden erstellt.') + link, + 'bt-ok' + ); + setTimeout(() => { btn.disabled = false; }, 1500); + }); + }; + + send(job); } // ----- Injection + SPA handling ----------------------------------------- diff --git a/extension/manifest.json b/extension/manifest.json index 6dabded..9b4660a 100644 --- a/extension/manifest.json +++ b/extension/manifest.json @@ -1,9 +1,9 @@ { "manifest_version": 3, - "name": "Bewerbungs-Tracker – Indeed Import", - "version": "1.0.0", - "description": "Fügt auf Indeed einen Button neben der Stellenbeschreibung ein und sendet die Stelle an den Bewerbungs-Tracker, der automatisch zugeschnittene Bewerbungsunterlagen erstellt.", - "permissions": ["storage"], + "name": "Bewerbungs-Tracker – Stellen-Import", + "version": "1.1.0", + "description": "Erfasst Stellen von jeder Webseite (nicht nur Indeed) über das Erweiterungs-Popup und sendet sie an den Bewerbungs-Tracker, der automatisch zugeschnittene Bewerbungsunterlagen erstellt.", + "permissions": ["storage", "scripting", "activeTab"], "host_permissions": [ "*://*.indeed.com/*", "http://localhost/*", diff --git a/extension/popup.html b/extension/popup.html index 26be571..626e68a 100644 --- a/extension/popup.html +++ b/extension/popup.html @@ -5,48 +5,105 @@
-Adresse deines Bewerbungs-Trackers. Von hier werden importierte Indeed-Stellen verarbeitet.
+Automatisch von der aktuellen Seite ausgelesen - bitte prüfen, ggf. korrigieren und senden. Tipp: Text auf der Seite markieren, dann wird er als Beschreibung übernommen.
- - +