Guard against duplicate applications + capture jobs on any website
Duplicate safeguard: before creating an application (manual add and browser import) the server checks for an existing one for the same job — matched on a normalised source URL (job-id params like Indeed's jk pin the posting across paths/tracking) or an identical company + role (case/umlaut/whitespace-insensitive). On a match it returns 409 with the matches; the web form and the extension show the existing entry and re-submit with force=true only if the user confirms. Not a hard block, so legitimate re-applications stay possible. Universal capture: the extension popup becomes an editable capture form that works on any site. It extracts the active page on demand (schema.org JobPosting JSON-LD -> OpenGraph/meta -> h1/title/selection -> canonical URL), lets the user review/correct, and sends. The import route is now source-agnostic and derives the application source (art) from the URL instead of hardcoding Indeed; the Indeed on-page button remains as a fast path. Adds scripting/activeTab permissions. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+73
-16
@@ -5,48 +5,105 @@
|
||||
<style>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
width: 320px;
|
||||
width: 360px;
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
color: #1f2937;
|
||||
background: #fff;
|
||||
}
|
||||
h1 { font-size: 15px; margin: 0 0 4px; }
|
||||
h1 { font-size: 15px; margin: 0 0 2px; }
|
||||
p.hint { font-size: 12px; color: #6b7280; margin: 0 0 12px; }
|
||||
label { display: block; font-size: 12px; font-weight: 600; margin-bottom: 4px; }
|
||||
input {
|
||||
label { display: block; font-size: 12px; font-weight: 600; margin: 10px 0 4px; }
|
||||
input, textarea, select {
|
||||
width: 100%; box-sizing: border-box;
|
||||
padding: 8px 10px; font-size: 13px;
|
||||
border: 1px solid #d1d5db; border-radius: 6px;
|
||||
font-family: inherit;
|
||||
}
|
||||
.row { display: flex; gap: 8px; margin-top: 12px; }
|
||||
textarea { resize: vertical; min-height: 84px; }
|
||||
.grid2 { display: flex; gap: 8px; }
|
||||
.grid2 > div { flex: 1; }
|
||||
.row { display: flex; gap: 8px; margin-top: 14px; }
|
||||
button {
|
||||
flex: 1; padding: 8px 10px; font-size: 13px; font-weight: 600;
|
||||
flex: 1; padding: 9px 10px; font-size: 13px; font-weight: 600;
|
||||
border: none; border-radius: 6px; cursor: pointer;
|
||||
}
|
||||
.save { background: #2563eb; color: #fff; }
|
||||
.save:hover { background: #1d4ed8; }
|
||||
.test { background: #e5e7eb; color: #1f2937; }
|
||||
.test:hover { background: #d1d5db; }
|
||||
button:disabled { opacity: .6; cursor: default; }
|
||||
.primary { background: #1f4068; color: #fff; }
|
||||
.primary:hover:not(:disabled) { background: #17324f; }
|
||||
.secondary { background: #e5e7eb; color: #1f2937; }
|
||||
.secondary:hover:not(:disabled) { background: #d1d5db; }
|
||||
#status { font-size: 12px; margin-top: 10px; min-height: 16px; }
|
||||
#status a { color: #1f4068; }
|
||||
.ok { color: #16a34a; }
|
||||
.err { color: #dc2626; }
|
||||
details { margin-top: 16px; border-top: 1px solid #e5e7eb; padding-top: 10px; }
|
||||
summary { font-size: 12px; font-weight: 600; color: #6b7280; cursor: pointer; }
|
||||
details .row { margin-top: 10px; }
|
||||
details label { margin-top: 8px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Bewerbungs-Tracker</h1>
|
||||
<p class="hint">Adresse deines Bewerbungs-Trackers. Von hier werden importierte Indeed-Stellen verarbeitet.</p>
|
||||
<h1>Stelle erfassen</h1>
|
||||
<p class="hint">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.</p>
|
||||
|
||||
<label for="trackerUrl">Tracker-Adresse</label>
|
||||
<input type="url" id="trackerUrl" placeholder="http://localhost:3000" />
|
||||
<div class="grid2">
|
||||
<div>
|
||||
<label for="firma">Firma</label>
|
||||
<input type="text" id="firma" placeholder="Unternehmen" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="art">Quelle</label>
|
||||
<select id="art">
|
||||
<option>E-Mail</option>
|
||||
<option>Online-Portal</option>
|
||||
<option>Indeed</option>
|
||||
<option>StepStone</option>
|
||||
<option>Firmenwebsite</option>
|
||||
<option>Post</option>
|
||||
<option>Initiativbewerbung</option>
|
||||
<option>Arbeitsagentur</option>
|
||||
<option>Sonstiges</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="stelle">Stelle</label>
|
||||
<input type="text" id="stelle" placeholder="Stellenbezeichnung" />
|
||||
|
||||
<div class="grid2">
|
||||
<div>
|
||||
<label for="ort">Ort</label>
|
||||
<input type="text" id="ort" placeholder="Ort" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="gehalt">Gehalt (optional)</label>
|
||||
<input type="text" id="gehalt" placeholder="z. B. 55.000 EUR" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label for="stellenbeschreibung">Stellenbeschreibung</label>
|
||||
<textarea id="stellenbeschreibung" placeholder="Beschreibung der Stelle …"></textarea>
|
||||
|
||||
<input type="hidden" id="quelle_url" />
|
||||
|
||||
<div class="row">
|
||||
<button class="save" id="saveBtn">Speichern</button>
|
||||
<button class="test" id="testBtn">Verbindung testen</button>
|
||||
<button class="secondary" id="rescanBtn">Neu auslesen</button>
|
||||
<button class="primary" id="sendBtn">An Tracker senden</button>
|
||||
</div>
|
||||
|
||||
<div id="status"></div>
|
||||
|
||||
<details id="settings">
|
||||
<summary>Einstellungen</summary>
|
||||
<label for="trackerUrl">Tracker-Adresse</label>
|
||||
<input type="url" id="trackerUrl" placeholder="http://localhost:3000" />
|
||||
<div class="row">
|
||||
<button class="primary" id="saveBtn">Speichern</button>
|
||||
<button class="secondary" id="testBtn">Verbindung testen</button>
|
||||
</div>
|
||||
</details>
|
||||
|
||||
<script src="popup.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user