Jobangebote: Gemerkte nicht mehr unter Offen anzeigen
Favorisierte Angebote erscheinen ausschließlich im Gemerkt-Tab und werden aus der Offen-Liste, dem Offen-Zähler (Header-Badge) und dem Tab-Zähler ausgeblendet. Auf der Offen-Seite wird die Zeile beim Merken live entfernt. Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3630,7 +3630,7 @@ initializeDatabase().then(async () => {
|
|||||||
// Count of open offers, consumed by the header badge (no auth — just a number).
|
// Count of open offers, consumed by the header badge (no auth — just a number).
|
||||||
app.get('/jobangebote/anzahl-offen', async (req, res) => {
|
app.get('/jobangebote/anzahl-offen', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const row = await dbGet("SELECT COUNT(*) as count FROM jobangebote WHERE user_id = ? AND status = 'offen'", [uid()]);
|
const row = await dbGet("SELECT COUNT(*) as count FROM jobangebote WHERE user_id = ? AND status = 'offen' AND favorit = 0", [uid()]);
|
||||||
res.json({ count: row ? row.count : 0 });
|
res.json({ count: row ? row.count : 0 });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(500).json({ count: 0 });
|
res.status(500).json({ count: 0 });
|
||||||
@@ -3642,7 +3642,7 @@ initializeDatabase().then(async () => {
|
|||||||
async function jobangeboteZaehler() {
|
async function jobangeboteZaehler() {
|
||||||
const row = await dbGet(
|
const row = await dbGet(
|
||||||
`SELECT
|
`SELECT
|
||||||
COUNT(*) FILTER (WHERE status = 'offen') AS offen,
|
COUNT(*) FILTER (WHERE status = 'offen' AND favorit = 0) AS offen,
|
||||||
COUNT(*) FILTER (WHERE status = 'uebernommen') AS uebernommen,
|
COUNT(*) FILTER (WHERE status = 'uebernommen') AS uebernommen,
|
||||||
COUNT(*) FILTER (WHERE favorit = 1) AS favoriten
|
COUNT(*) FILTER (WHERE favorit = 1) AS favoriten
|
||||||
FROM jobangebote WHERE user_id = ?`,
|
FROM jobangebote WHERE user_id = ?`,
|
||||||
@@ -3656,16 +3656,17 @@ initializeDatabase().then(async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Main list: only OPEN offers (still to be decided on). Taken-over offers
|
// Main list: only OPEN offers (still to be decided on). Taken-over offers
|
||||||
// live on their own page (/jobangebote/uebernommen). Gemerkte Angebote stehen
|
// live on their own page (/jobangebote/uebernommen). Gemerkte (favorisierte)
|
||||||
// hier oben und haben zusätzlich einen eigenen Tab (/jobangebote/favoriten).
|
// Angebote erscheinen ausschließlich im eigenen Tab (/jobangebote/favoriten)
|
||||||
|
// und werden hier ausgeblendet.
|
||||||
app.get('/jobangebote', async (req, res) => {
|
app.get('/jobangebote', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const jobangebote = await dbAll(
|
const jobangebote = await dbAll(
|
||||||
`SELECT j.*, b.datum AS bewerbung_datum
|
`SELECT j.*, b.datum AS bewerbung_datum
|
||||||
FROM jobangebote j
|
FROM jobangebote j
|
||||||
LEFT JOIN bewerbungen b ON b.id = j.verknuepfte_bewerbung_id AND b.user_id = j.user_id
|
LEFT JOIN bewerbungen b ON b.id = j.verknuepfte_bewerbung_id AND b.user_id = j.user_id
|
||||||
WHERE j.user_id = ? AND j.status = 'offen'
|
WHERE j.user_id = ? AND j.status = 'offen' AND j.favorit = 0
|
||||||
ORDER BY j.favorit DESC, j.created_at DESC, j.id DESC`,
|
ORDER BY j.created_at DESC, j.id DESC`,
|
||||||
[uid()]
|
[uid()]
|
||||||
);
|
);
|
||||||
jobangebote.forEach((j) => { j.labelsArr = parseLabels(j.labels); });
|
jobangebote.forEach((j) => { j.labelsArr = parseLabels(j.labels); });
|
||||||
|
|||||||
@@ -26,7 +26,7 @@
|
|||||||
|
|
||||||
<%- include('partials/favorit-script') %>
|
<%- 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="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8" data-fav-card data-fav-view="<%= favAnsicht ? 'favoriten' : 'offen' %>">
|
||||||
<div class="flex flex-wrap items-start justify-between gap-3 mb-2">
|
<div class="flex flex-wrap items-start justify-between gap-3 mb-2">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-white"><%= favAnsicht ? 'Gemerkte Jobangebote' : 'Jobangebote' %></h2>
|
<h2 class="text-lg font-semibold text-gray-800 dark:text-white"><%= favAnsicht ? 'Gemerkte Jobangebote' : 'Jobangebote' %></h2>
|
||||||
@@ -41,8 +41,8 @@
|
|||||||
<code class="px-1 py-0.5 rounded bg-gray-100 dark:bg-gray-700">POST /api/v1/joboffers</code>
|
<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
|
REST-Schnittstelle eingespielt werden. Ein Angebot lässt sich per Klick
|
||||||
als Bewerbung-Entwurf übernehmen. Hier siehst du nur <strong>offene</strong>
|
als Bewerbung-Entwurf übernehmen. Hier siehst du nur <strong>offene</strong>
|
||||||
Angebote — bereits übernommene findest du im Tab „Übernommen“.
|
Angebote, die du dir <em>nicht</em> gemerkt hast — bereits übernommene
|
||||||
Mit dem Stern gemerkte Angebote stehen oben.
|
findest du im Tab „Übernommen“, gemerkte im Tab „Gemerkt“.
|
||||||
<% } %>
|
<% } %>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
<% if (jobangebote && jobangebote.length) { %>
|
<% if (jobangebote && jobangebote.length) { %>
|
||||||
<ul class="mt-4 space-y-4">
|
<ul class="mt-4 space-y-4">
|
||||||
<% jobangebote.forEach(function(j){ %>
|
<% jobangebote.forEach(function(j){ %>
|
||||||
<li <%= favAnsicht ? 'data-fav-item' : '' %> class="rounded-lg border px-4 py-4 <%= j.favorit
|
<li 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-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' %>">
|
: '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="flex flex-wrap items-start justify-between gap-3">
|
||||||
@@ -249,7 +249,8 @@
|
|||||||
</svg>
|
</svg>
|
||||||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-1">Noch keine Jobangebote gemerkt.</p>
|
<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">
|
<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.
|
Klicke bei einem Angebot auf <strong>Merken</strong> — es erscheint dann hier
|
||||||
|
und wird aus der offenen Liste ausgeblendet.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<% } else { %>
|
<% } else { %>
|
||||||
|
|||||||
@@ -40,9 +40,10 @@
|
|||||||
.then(function (r) {
|
.then(function (r) {
|
||||||
if (r.ok && r.json && r.json.success) {
|
if (r.ok && r.json && r.json.success) {
|
||||||
setzeBtn(btn, !!r.json.favorit);
|
setzeBtn(btn, !!r.json.favorit);
|
||||||
// Auf der Startseite: beim Entfernen die Zeile ausblenden
|
// Zeile ausblenden, sobald sie nicht mehr in die aktuelle
|
||||||
// und die ganze Sektion einräumen, wenn sie leer wird.
|
// Ansicht gehört: auf der Offen-Seite beim Merken (favorit=1),
|
||||||
if (!r.json.favorit) entferneFavoritZeile(btn);
|
// auf der Gemerkt-Seite / Startseite beim Entfernen (favorit=0).
|
||||||
|
entferneFavoritZeile(btn, !!r.json.favorit);
|
||||||
} else {
|
} else {
|
||||||
setzeBtn(btn, aktiv); // Server hat abgelehnt -> zurückrollen
|
setzeBtn(btn, aktiv); // Server hat abgelehnt -> zurückrollen
|
||||||
if (r.json && r.json.error) console.warn('Favorit:', r.json.error);
|
if (r.json && r.json.error) console.warn('Favorit:', r.json.error);
|
||||||
@@ -62,11 +63,20 @@
|
|||||||
if (lbl) lbl.textContent = aktiv ? 'Gemerkt' : 'Merken';
|
if (lbl) lbl.textContent = aktiv ? 'Gemerkt' : 'Merken';
|
||||||
}
|
}
|
||||||
|
|
||||||
function entferneFavoritZeile(btn) {
|
function entferneFavoritZeile(btn, istFavorit) {
|
||||||
var item = btn.closest('[data-fav-item]');
|
var item = btn.closest('[data-fav-item]');
|
||||||
if (!item) return; // nicht auf der Startseite -> nur optisch umschalten
|
if (!item) return; // keine ausblendbare Zeile -> nur optisch umschalten
|
||||||
var card = btn.closest('[data-fav-card]') || document.getElementById('favoritenCard');
|
var card = btn.closest('[data-fav-card]') || document.getElementById('favoritenCard');
|
||||||
|
// Offen-Seite: Zeile verschwindet beim Merken (favorit=1).
|
||||||
|
// Gemerkt-Seite / Startseite: Zeile verschwindet beim Entfernen (favorit=0).
|
||||||
|
var view = card && card.getAttribute('data-fav-view');
|
||||||
|
var entfernenBeiFavorit = (view === 'offen');
|
||||||
|
if (istFavorit !== entfernenBeiFavorit) return;
|
||||||
item.remove();
|
item.remove();
|
||||||
|
// Auf der Offen-Seite bleibt die Sektion samt Überschrift stehen —
|
||||||
|
// dort wird nur die einzelne Zeile entfernt. Auf der Gemerkt-Seite
|
||||||
|
// und Startseite räumen wir die leere Sektion ganz ein.
|
||||||
|
if (view === 'offen') return;
|
||||||
if (card && !card.querySelectorAll('[data-fav-item]').length) {
|
if (card && !card.querySelectorAll('[data-fav-item]').length) {
|
||||||
card.style.transition = 'opacity 0.2s ease';
|
card.style.transition = 'opacity 0.2s ease';
|
||||||
card.style.opacity = '0';
|
card.style.opacity = '0';
|
||||||
|
|||||||
Reference in New Issue
Block a user