Jobangebote: Favoriten-Funktion mit eigenem "Gemerkt"-Tab
Jobangebote lassen sich per Stern merken — eigene Spalte `favorit`, getrennt von den Bewerbungs-Favoriten. Gemerkte Angebote stehen oben in der offenen Liste und haben einen eigenen Tab, der Favoriten quer über alle Status zeigt und ausgeblendet bleibt, solange es keine gibt. Der Stern wird nur in der Weboberfläche gesetzt; ein erneutes Einspielen desselben Angebots über die REST-API überschreibt ihn nicht. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+41
-17
@@ -15,26 +15,35 @@
|
||||
Zurück zur Übersicht
|
||||
</a>
|
||||
|
||||
<!-- Tabs: open offers vs. taken-over offers -->
|
||||
<div class="flex flex-wrap items-center gap-2 mb-6">
|
||||
<a href="/jobangebote" class="px-3 py-1.5 rounded-md text-sm font-medium bg-blue-600 text-white shadow-sm">
|
||||
Offen (<%= jobangebote.length %>)
|
||||
</a>
|
||||
<a href="/jobangebote/uebernommen" class="px-3 py-1.5 rounded-md text-sm font-medium bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||||
Übernommen (<%= uebernommenCount %>)
|
||||
</a>
|
||||
</div>
|
||||
<!-- Tabs: open offers · favorites · taken-over offers -->
|
||||
<% var favAnsicht = (typeof ansicht !== 'undefined' && ansicht === 'favoriten'); %>
|
||||
<%- include('partials/jobangebote-tabs', {
|
||||
aktiv: favAnsicht ? 'favoriten' : 'offen',
|
||||
offenCount: offenCount,
|
||||
favoritenCount: favoritenCount,
|
||||
uebernommenCount: uebernommenCount,
|
||||
}) %>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8">
|
||||
<%- include('partials/favorit-script') %>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8"<%= favAnsicht ? ' data-fav-card' : '' %>>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3 mb-2">
|
||||
<div>
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-white">Jobangebote</h2>
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-white"><%= favAnsicht ? 'Gemerkte Jobangebote' : 'Jobangebote' %></h2>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
Hier landen Stellen, die von Drittanbietersoftware über die
|
||||
<code class="px-1 py-0.5 rounded bg-gray-100 dark:bg-gray-700">POST /api/v1/joboffers</code>
|
||||
REST-Schnittstelle eingespielt werden. Ein Angebot lässt sich per Klick
|
||||
als Bewerbung-Entwurf übernehmen. Hier siehst du nur <strong>offene</strong>
|
||||
Angebote — bereits übernommene findest du im Tab „Übernommen“.
|
||||
<% if (favAnsicht) { %>
|
||||
Angebote, die du dir mit dem Stern gemerkt hast — unabhängig davon, ob sie
|
||||
noch offen oder bereits übernommen sind. Ein erneuter Klick auf den Stern
|
||||
entfernt das Angebot wieder aus dieser Liste. Die Merkliste gilt nur für
|
||||
Jobangebote und ist von den gemerkten Bewerbungen getrennt.
|
||||
<% } else { %>
|
||||
Hier landen Stellen, die von Drittanbietersoftware über die
|
||||
<code class="px-1 py-0.5 rounded bg-gray-100 dark:bg-gray-700">POST /api/v1/joboffers</code>
|
||||
REST-Schnittstelle eingespielt werden. Ein Angebot lässt sich per Klick
|
||||
als Bewerbung-Entwurf übernehmen. Hier siehst du nur <strong>offene</strong>
|
||||
Angebote — bereits übernommene findest du im Tab „Übernommen“.
|
||||
Mit dem Stern gemerkte Angebote stehen oben.
|
||||
<% } %>
|
||||
</p>
|
||||
</div>
|
||||
<div class="shrink-0 flex items-center gap-2">
|
||||
@@ -52,7 +61,9 @@
|
||||
<% if (jobangebote && jobangebote.length) { %>
|
||||
<ul class="mt-4 space-y-4">
|
||||
<% jobangebote.forEach(function(j){ %>
|
||||
<li class="rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 px-4 py-4">
|
||||
<li <%= favAnsicht ? 'data-fav-item' : '' %> class="rounded-lg border px-4 py-4 <%= j.favorit
|
||||
? 'border-amber-300 dark:border-amber-400/40 bg-amber-50/60 dark:bg-amber-500/5'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40' %>">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2 mb-1">
|
||||
@@ -139,6 +150,7 @@
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="shrink-0 flex flex-col gap-2 self-start">
|
||||
<%- include('partials/favorit-button', { j: j }) %>
|
||||
<% if (j.verknuepfte_bewerbung_id) { %>
|
||||
<a href="/bewerbung/<%= j.verknuepfte_bewerbung_id %>"
|
||||
class="inline-flex items-center justify-center gap-1.5 px-3 py-1.5 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors">
|
||||
@@ -157,6 +169,7 @@
|
||||
<% } %>
|
||||
<form action="/jobangebote/<%= j.id %>/delete" method="POST"
|
||||
onsubmit="return confirm('Jobangebot „<%= j.firma %> · <%= j.stelle %>“ löschen? Es kommt auf die Blacklist und wird nicht erneut eingespielt.');">
|
||||
<% if (favAnsicht) { %><input type="hidden" name="zurueck" value="favoriten"><% } %>
|
||||
<button type="submit"
|
||||
class="inline-flex items-center justify-center gap-1.5 px-3 py-1.5 text-xs border border-gray-300 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-md transition-colors w-full">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path></svg>
|
||||
@@ -173,6 +186,7 @@
|
||||
Beschreibung / Details bearbeiten
|
||||
</summary>
|
||||
<form action="/jobangebote/<%= j.id %>/bearbeiten" method="POST" class="mt-3 space-y-3">
|
||||
<% if (favAnsicht) { %><input type="hidden" name="zurueck" value="favoriten"><% } %>
|
||||
<div class="grid gap-3 sm:grid-cols-2">
|
||||
<label class="block text-xs">
|
||||
<span class="text-gray-500 dark:text-gray-400">Firma</span>
|
||||
@@ -228,6 +242,16 @@
|
||||
</li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
<% } else if (favAnsicht) { %>
|
||||
<div class="text-center py-12">
|
||||
<svg class="w-14 h-14 mx-auto text-gray-300 dark:text-gray-600 mb-3" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.5l2.6 5.27 5.82.85-4.21 4.1.99 5.79-5.2-2.73-5.2 2.73.99-5.79-4.21-4.1 5.82-.85 2.6-5.27z"></path>
|
||||
</svg>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-1">Noch keine Jobangebote gemerkt.</p>
|
||||
<p class="text-xs text-gray-400 dark:text-gray-500">
|
||||
Klicke bei einem Angebot auf <strong>Merken</strong> — es erscheint dann hier und steht in der offenen Liste ganz oben.
|
||||
</p>
|
||||
</div>
|
||||
<% } else { %>
|
||||
<div class="text-center py-12">
|
||||
<svg class="w-14 h-14 mx-auto text-gray-300 dark:text-gray-600 mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
|
||||
@@ -15,15 +15,15 @@
|
||||
Zurück zu den Jobangeboten
|
||||
</a>
|
||||
|
||||
<!-- Tabs: open offers vs. taken-over offers -->
|
||||
<div class="flex flex-wrap items-center gap-2 mb-6">
|
||||
<a href="/jobangebote" class="px-3 py-1.5 rounded-md text-sm font-medium bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors">
|
||||
Offen (<%= offenCount %>)
|
||||
</a>
|
||||
<a href="/jobangebote/uebernommen" class="px-3 py-1.5 rounded-md text-sm font-medium bg-blue-600 text-white shadow-sm">
|
||||
Übernommen (<%= uebernommen.length %>)
|
||||
</a>
|
||||
</div>
|
||||
<!-- Tabs: open offers · favorites · taken-over offers -->
|
||||
<%- include('partials/jobangebote-tabs', {
|
||||
aktiv: 'uebernommen',
|
||||
offenCount: offenCount,
|
||||
favoritenCount: favoritenCount,
|
||||
uebernommenCount: uebernommenCount,
|
||||
}) %>
|
||||
|
||||
<%- include('partials/favorit-script') %>
|
||||
|
||||
<div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3 mb-2">
|
||||
@@ -43,7 +43,9 @@
|
||||
<% if (uebernommen && uebernommen.length) { %>
|
||||
<ul class="mt-4 space-y-4">
|
||||
<% uebernommen.forEach(function(j){ %>
|
||||
<li class="rounded-lg border border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40 px-4 py-4">
|
||||
<li class="rounded-lg border px-4 py-4 <%= j.favorit
|
||||
? 'border-amber-300 dark:border-amber-400/40 bg-amber-50/60 dark:bg-amber-500/5'
|
||||
: 'border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-700/40' %>">
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="min-w-0 flex-1">
|
||||
<div class="flex flex-wrap items-center gap-2 mb-1">
|
||||
@@ -100,6 +102,7 @@
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="shrink-0 flex flex-col gap-2 self-start">
|
||||
<%- include('partials/favorit-button', { j: j }) %>
|
||||
<% if (j.verknuepfte_bewerbung_id) { %>
|
||||
<a href="/bewerbung/<%= j.verknuepfte_bewerbung_id %>"
|
||||
class="inline-flex items-center justify-center gap-1.5 px-3 py-1.5 text-xs bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors">
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<%
|
||||
// Tab-Leiste der Jobangebote-Seiten. `aktiv` ist einer von 'offen',
|
||||
// 'favoriten', 'uebernommen'. Der Favoriten-Tab zeigt gemerkte Angebote quer
|
||||
// über alle Status und wird nur eingeblendet, wenn es Favoriten gibt (oder er
|
||||
// gerade offen ist) — leere Tabs braucht niemand.
|
||||
var tabs = [
|
||||
{ key: 'offen', href: '/jobangebote', label: 'Offen', count: offenCount },
|
||||
{ key: 'favoriten', href: '/jobangebote/favoriten', label: 'Gemerkt', count: favoritenCount, stern: true },
|
||||
{ key: 'uebernommen', href: '/jobangebote/uebernommen', label: 'Übernommen', count: uebernommenCount },
|
||||
].filter(function (t) { return t.key !== 'favoriten' || t.count > 0 || aktiv === 'favoriten'; });
|
||||
%>
|
||||
<div class="flex flex-wrap items-center gap-2 mb-6">
|
||||
<% tabs.forEach(function (t) { %>
|
||||
<a href="<%= t.href %>"
|
||||
<%= aktiv === t.key ? 'aria-current="page"' : '' %>
|
||||
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-md text-sm font-medium transition-colors <%= aktiv === t.key
|
||||
? 'bg-blue-600 text-white shadow-sm'
|
||||
: 'bg-white dark:bg-gray-800 text-gray-700 dark:text-gray-200 border border-gray-200 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700' %>">
|
||||
<% if (t.stern) { %>
|
||||
<svg class="h-4 w-4 <%= aktiv === t.key ? 'text-white' : 'text-amber-500' %>" viewBox="0 0 24 24" fill="currentColor"><path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.5l2.6 5.27 5.82.85-4.21 4.1.99 5.79-5.2-2.73-5.2 2.73.99-5.79-4.21-4.1 5.82-.85 2.6-5.27z"/></svg>
|
||||
<% } %>
|
||||
<%= t.label %> (<%= t.count %>)
|
||||
</a>
|
||||
<% }); %>
|
||||
</div>
|
||||
Reference in New Issue
Block a user