Startseite: Letzte Aktivitaeten und Gesamtzahl der Bewerbungen
Unter der Liste stehen jetzt die acht juengsten Statusaenderungen quer ueber alle Bewerbungen - mit Firma, Stelle, Status, Datum und Kommentar, verlinkt auf die Bewerbung. Sortiert nach Erfassungszeit statt nach fachlichem Datum, damit ein nachgetragener alter Eintrag nicht oben steht. Der Kopf der Listen-Karte nennt die Gesamtzahl. Er sitzt bewusst ausserhalb des md:min-w-Rahmens, damit er auf dem Handy nicht horizontal mitscrollt. Beides verschwindet waehrend einer Suche, wie Liste und Termine auch. Die Status-Farbkarte ist eine Ebene hoeher gewandert, weil Liste und Aktivitaeten sie jetzt beide brauchen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+85
-20
@@ -130,7 +130,39 @@
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
</style>
|
||||
<%
|
||||
// Status-Reihenfolge synchron zu public/js/main.js halten. Steht hier oben,
|
||||
// weil sowohl die Liste als auch die Aktivitäten weiter unten sie brauchen.
|
||||
const statusOrder = ['Entwurf', 'Gesendet', 'Eingangsbestätigung',
|
||||
'In Bearbeitung', 'Interessiert', 'Warten auf Rückmeldung', 'Warten auf meine Antwort',
|
||||
'Vorstellungsgespräch', 'Vertragsverhandlung', 'Einstellung', 'Absage', 'Absage von meiner Seite', 'Keine Rückmeldung'];
|
||||
const statusDot = {
|
||||
'Entwurf': 'bg-purple-500',
|
||||
'Gesendet': 'bg-indigo-500',
|
||||
'Eingangsbestätigung': 'bg-blue-500',
|
||||
'In Bearbeitung': 'bg-teal-500',
|
||||
'Interessiert': 'bg-pink-500',
|
||||
'Warten auf Rückmeldung': 'bg-orange-500',
|
||||
'Warten auf meine Antwort': 'bg-fuchsia-500',
|
||||
'Vorstellungsgespräch': 'bg-yellow-500',
|
||||
'Vertragsverhandlung': 'bg-lime-500',
|
||||
'Einstellung': 'bg-green-500',
|
||||
'Absage': 'bg-red-500',
|
||||
'Absage von meiner Seite': 'bg-rose-500',
|
||||
'Keine Rückmeldung': 'bg-gray-400',
|
||||
'Ohne Status': 'bg-gray-300'
|
||||
};
|
||||
%>
|
||||
<div id="listCard" class="bg-white dark:bg-gray-800 rounded-lg shadow-md overflow-x-auto">
|
||||
<!-- Kopf der Karte: Gesamtzahl der Bewerbungen. Bewusst außerhalb des
|
||||
md:min-w-Rahmens unten, damit er auf dem Handy nicht mitscrollt. -->
|
||||
<div class="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1 px-6 py-4">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-white">Bewerbungen</h2>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400">
|
||||
<span class="font-semibold text-gray-800 dark:text-gray-100"><%= statistics.total %></span>
|
||||
<%= statistics.total === 1 ? 'Bewerbung' : 'Bewerbungen' %> gesamt
|
||||
</p>
|
||||
</div>
|
||||
<div class="md:min-w-[760px]">
|
||||
<!-- Column labels (desktop only — on phones the rows become stacked cards) -->
|
||||
<div class="app-head app-grid px-6 py-3 bg-gray-50 dark:bg-gray-700 text-xs font-medium text-gray-500 dark:text-gray-300 uppercase tracking-wider">
|
||||
@@ -144,26 +176,6 @@
|
||||
</div>
|
||||
<% if (applications.length > 0) { %>
|
||||
<%
|
||||
// Status-Reihenfolge synchron zu public/js/main.js halten
|
||||
const statusOrder = ['Entwurf', 'Gesendet', 'Eingangsbestätigung',
|
||||
'In Bearbeitung', 'Interessiert', 'Warten auf Rückmeldung', 'Warten auf meine Antwort',
|
||||
'Vorstellungsgespräch', 'Vertragsverhandlung', 'Einstellung', 'Absage', 'Absage von meiner Seite', 'Keine Rückmeldung'];
|
||||
const statusDot = {
|
||||
'Entwurf': 'bg-purple-500',
|
||||
'Gesendet': 'bg-indigo-500',
|
||||
'Eingangsbestätigung': 'bg-blue-500',
|
||||
'In Bearbeitung': 'bg-teal-500',
|
||||
'Interessiert': 'bg-pink-500',
|
||||
'Warten auf Rückmeldung': 'bg-orange-500',
|
||||
'Warten auf meine Antwort': 'bg-fuchsia-500',
|
||||
'Vorstellungsgespräch': 'bg-yellow-500',
|
||||
'Vertragsverhandlung': 'bg-lime-500',
|
||||
'Einstellung': 'bg-green-500',
|
||||
'Absage': 'bg-red-500',
|
||||
'Absage von meiner Seite': 'bg-rose-500',
|
||||
'Keine Rückmeldung': 'bg-gray-400',
|
||||
'Ohne Status': 'bg-gray-300'
|
||||
};
|
||||
const groups = {};
|
||||
applications.forEach(a => {
|
||||
const key = (a.status && a.status.trim()) ? a.status.trim() : 'Ohne Status';
|
||||
@@ -315,6 +327,55 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Letzte Aktivitäten: die jüngsten Statusänderungen quer über alle
|
||||
Bewerbungen — der schnellste Blick darauf, was zuletzt passiert ist,
|
||||
ohne jede Gruppe aufklappen zu müssen. -->
|
||||
<section id="aktivitaetCard" class="bg-white dark:bg-gray-800 rounded-lg shadow-md mt-8">
|
||||
<div class="flex flex-wrap items-baseline justify-between gap-x-4 gap-y-1 px-6 py-4 border-b border-gray-200 dark:border-gray-700">
|
||||
<h2 class="text-lg font-semibold text-gray-800 dark:text-white">Letzte Aktivitäten</h2>
|
||||
<a href="/statistik#aktivitaet" class="text-sm text-blue-600 dark:text-blue-400 hover:underline">Alle in der Statistik</a>
|
||||
</div>
|
||||
|
||||
<% if (aktivitaet && aktivitaet.length) { %>
|
||||
<ul class="divide-y divide-gray-100 dark:divide-gray-700/60">
|
||||
<% aktivitaet.forEach(function (a) { %>
|
||||
<li>
|
||||
<% var ziel = a.bewerbung_id ? ('/bewerbung/' + a.bewerbung_id) : null; %>
|
||||
<<%= ziel ? 'a href="' + ziel + '"' : 'div' %> class="flex items-start gap-3 px-6 py-3 <%= ziel ? 'hover:bg-gray-50 dark:hover:bg-gray-700/50 transition-colors' : '' %>">
|
||||
<span class="mt-1.5 inline-block w-2.5 h-2.5 rounded-full shrink-0 <%= statusDot[a.status] || 'bg-gray-300' %>"></span>
|
||||
<span class="min-w-0 flex-1">
|
||||
<span class="flex flex-wrap items-baseline gap-x-2 gap-y-0.5">
|
||||
<% if (a.bewerbung_id) { %>
|
||||
<span class="text-sm font-medium text-gray-900 dark:text-white break-words"><%= a.firma || '(ohne Firma)' %></span>
|
||||
<% if (a.stelle) { %>
|
||||
<span class="text-sm text-gray-500 dark:text-gray-400 break-words">· <%= a.stelle %></span>
|
||||
<% } %>
|
||||
<% } else { %>
|
||||
<span class="text-sm font-medium text-gray-500 dark:text-gray-400">Gelöschte Bewerbung</span>
|
||||
<% } %>
|
||||
</span>
|
||||
<!-- Status, Datum und Kommentar in einer Zeile: ein rechtsbündiges
|
||||
Datum würde auf dem Handy in eine eigene Zeile umbrechen und
|
||||
dort ohne Bezug stehen. -->
|
||||
<span class="mt-0.5 flex flex-wrap items-baseline gap-x-2 text-sm">
|
||||
<span class="font-medium text-gray-700 dark:text-gray-200"><%= a.status %></span>
|
||||
<span class="text-gray-400 dark:text-gray-500 whitespace-nowrap">· <%= new Date(a.datum).toLocaleDateString('de-DE') %></span>
|
||||
<% if (a.kommentar && a.kommentar.trim()) { %>
|
||||
<span class="text-gray-500 dark:text-gray-400 break-words">· <%= a.kommentar %></span>
|
||||
<% } %>
|
||||
</span>
|
||||
</span>
|
||||
</<%= ziel ? 'a' : 'div' %>>
|
||||
</li>
|
||||
<% }); %>
|
||||
</ul>
|
||||
<% } else { %>
|
||||
<div class="px-6 py-10 text-center text-sm text-gray-500 dark:text-gray-400">
|
||||
Noch keine Statusänderungen protokolliert.
|
||||
</div>
|
||||
<% } %>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
function qsa(sel, ctx) {
|
||||
@@ -364,6 +425,7 @@
|
||||
var fehler = document.getElementById('searchError');
|
||||
var listCard = document.getElementById('listCard');
|
||||
var termineCard = document.getElementById('termineCard');
|
||||
var aktivitaetCard = document.getElementById('aktivitaetCard');
|
||||
|
||||
var MIN = 2; // kürzere Eingaben liefern nur Rauschen
|
||||
var MARK_START = '\u0002'; // Fundstellen-Marker aus lib/suche.js
|
||||
@@ -479,6 +541,7 @@
|
||||
panel.classList.add('hidden');
|
||||
if (listCard) listCard.classList.remove('hidden');
|
||||
if (termineCard) termineCard.classList.remove('hidden');
|
||||
if (aktivitaetCard) aktivitaetCard.classList.remove('hidden');
|
||||
input.setAttribute('aria-expanded', 'false');
|
||||
aktiv = -1;
|
||||
}
|
||||
@@ -498,6 +561,7 @@
|
||||
panel.classList.remove('hidden');
|
||||
if (listCard) listCard.classList.add('hidden');
|
||||
if (termineCard) termineCard.classList.add('hidden');
|
||||
if (aktivitaetCard) aktivitaetCard.classList.add('hidden');
|
||||
input.setAttribute('aria-expanded', 'true');
|
||||
aktiv = -1;
|
||||
}
|
||||
@@ -511,6 +575,7 @@
|
||||
panel.classList.remove('hidden');
|
||||
if (listCard) listCard.classList.add('hidden');
|
||||
if (termineCard) termineCard.classList.add('hidden');
|
||||
if (aktivitaetCard) aktivitaetCard.classList.add('hidden');
|
||||
}
|
||||
|
||||
async function suchen(q) {
|
||||
|
||||
Reference in New Issue
Block a user