c2a629e2c0
- Views umstrukturiert: einstellungen.ejs -> bewerbung.ejs, neues partials/head.ejs, header/footer/index angepasst - CSS umbenannt: style.css -> styles.css - server.js und public/js/main.js ueberarbeitet - Dockerfile auf schlankes Multi-Stage-Setup umgestellt; docker-compose.yml und .dockerignore entfernt - npm-Scripts docker:build/push/deploy ergaenzt - SQLite-DB und .idea aus Git entfernt und via .gitignore ignoriert Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
219 lines
14 KiB
Plaintext
219 lines
14 KiB
Plaintext
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<%- include('partials/head') %>
|
||
</head>
|
||
<body class="min-h-screen transition-colors duration-300 bg-gray-50 dark:bg-gray-900" id="body">
|
||
<%- include('partials/header', { hideSettings: true }) %>
|
||
|
||
<%
|
||
const statusColor = {
|
||
'Gesendet': 'bg-indigo-100 text-indigo-800 dark:bg-indigo-900 dark:text-indigo-200',
|
||
'Eingangsbestätigung': 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200',
|
||
'Vorstellungsgespräch': 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200',
|
||
'Einstellung': 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200',
|
||
'Absage': 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200',
|
||
'Keine Rückmeldung': 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300'
|
||
};
|
||
const aktuellerStatus = (application.status && application.status.trim()) ? application.status.trim() : 'Ohne Status';
|
||
const today = new Date().toISOString().split('T')[0];
|
||
%>
|
||
|
||
<main class="container mx-auto px-4 py-8 max-w-3xl">
|
||
<!-- Back link -->
|
||
<a href="/" class="inline-flex items-center gap-2 text-sm text-blue-600 dark:text-blue-400 hover:underline mb-6">
|
||
<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="M10 19l-7-7m0 0l7-7m-7 7h18"></path>
|
||
</svg>
|
||
Zurück zur Übersicht
|
||
</a>
|
||
|
||
<!-- Title -->
|
||
<div class="flex flex-wrap items-center gap-3 mb-6">
|
||
<h1 class="text-2xl font-bold text-gray-800 dark:text-white">
|
||
<%= application.firma %> <span class="text-gray-400 dark:text-gray-500">–</span> <%= application.stelle %>
|
||
</h1>
|
||
<span class="px-3 py-1 rounded-full text-sm font-medium <%= statusColor[aktuellerStatus] || 'bg-gray-100 text-gray-800 dark:bg-gray-700 dark:text-gray-300' %>">
|
||
<%= aktuellerStatus %>
|
||
</span>
|
||
</div>
|
||
|
||
<!-- Application data -->
|
||
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8">
|
||
<h2 class="text-lg font-semibold text-gray-800 dark:text-white mb-4">Bewerbungsdaten</h2>
|
||
<form action="/bewerbung/<%= application.id %>" method="POST" class="space-y-4">
|
||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Datum *</label>
|
||
<input type="date" name="datum" required value="<%= application.datum %>"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Art der Bewerbung</label>
|
||
<select name="art" class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
<option value="">-- Bitte wählen --</option>
|
||
<% artOptions.forEach(option => { %>
|
||
<option value="<%= option %>" <%= application.art === option ? 'selected' : '' %>><%= option %></option>
|
||
<% }); %>
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Firma *</label>
|
||
<input type="text" name="firma" required value="<%= application.firma %>"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Stelle *</label>
|
||
<input type="text" name="stelle" required value="<%= application.stelle %>"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-2">Notizen</label>
|
||
<textarea name="notizen" rows="6"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white leading-relaxed"
|
||
placeholder="Allgemeine Notizen zur Bewerbung..."><%= application.notizen || '' %></textarea>
|
||
</div>
|
||
<div>
|
||
<label class="text-sm font-medium text-amber-700 dark:text-amber-400 mb-2 flex items-center gap-1.5">
|
||
<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="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"></path>
|
||
</svg>
|
||
Interne Notizen (nicht im PDF)
|
||
</label>
|
||
<textarea name="interne_notizen" rows="4"
|
||
class="w-full px-3 py-2 border border-amber-300 dark:border-amber-700 rounded-md bg-amber-50 dark:bg-gray-700 text-gray-800 dark:text-white leading-relaxed"
|
||
placeholder="Nur für dich – erscheint nicht im Export..."><%= application.interne_notizen || '' %></textarea>
|
||
</div>
|
||
<div class="flex justify-end">
|
||
<button type="submit" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors">
|
||
Bewerbungsdaten speichern
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</section>
|
||
|
||
<!-- Status timeline -->
|
||
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||
<h2 class="text-lg font-semibold text-gray-800 dark:text-white mb-1">Status-Verlauf</h2>
|
||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-6">
|
||
Jede Statusänderung wird mit Datum dokumentiert. Der jüngste Eintrag bestimmt den aktuellen Status.
|
||
</p>
|
||
|
||
<!-- Existing entries -->
|
||
<% if (verlauf.length > 0) { %>
|
||
<ol class="relative border-l border-gray-200 dark:border-gray-600 ml-2 space-y-6 mb-8">
|
||
<% verlauf.forEach(eintrag => { %>
|
||
<li class="ml-6">
|
||
<span class="absolute -left-1.5 mt-2 w-3 h-3 rounded-full bg-blue-500 ring-4 ring-white dark:ring-gray-800"></span>
|
||
<form action="/bewerbung/<%= application.id %>/verlauf/<%= eintrag.id %>" method="POST"
|
||
class="bg-gray-50 dark:bg-gray-700/40 rounded-lg p-4 border border-gray-200 dark:border-gray-700">
|
||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-3">
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Datum</label>
|
||
<input type="date" name="datum" required value="<%= eintrag.datum %>"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Status</label>
|
||
<select name="status" required
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
<% statusOptions.forEach(option => { %>
|
||
<option value="<%= option %>" <%= eintrag.status === option ? 'selected' : '' %>><%= option %></option>
|
||
<% }); %>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="mb-3">
|
||
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Kommentar</label>
|
||
<textarea name="kommentar" rows="2"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white leading-relaxed"
|
||
placeholder="Was ist passiert?"><%= eintrag.kommentar || '' %></textarea>
|
||
</div>
|
||
<div class="flex justify-end gap-2">
|
||
<button type="submit"
|
||
class="px-3 py-1.5 text-sm bg-blue-600 hover:bg-blue-700 text-white rounded-md transition-colors">
|
||
Speichern
|
||
</button>
|
||
</div>
|
||
</form>
|
||
<form action="/bewerbung/<%= application.id %>/verlauf/<%= eintrag.id %>/delete" method="POST" class="mt-1 text-right"
|
||
onsubmit="return confirm('Diesen Verlaufseintrag löschen?');">
|
||
<button type="submit"
|
||
class="text-xs text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300 hover:underline">
|
||
Eintrag löschen
|
||
</button>
|
||
</form>
|
||
</li>
|
||
<% }); %>
|
||
</ol>
|
||
<% } else { %>
|
||
<p class="text-sm text-gray-500 dark:text-gray-400 mb-8">Noch keine Statusänderungen dokumentiert.</p>
|
||
<% } %>
|
||
|
||
<!-- Add new entry -->
|
||
<div class="border-t border-gray-200 dark:border-gray-700 pt-6">
|
||
<h3 class="text-sm font-semibold text-gray-700 dark:text-gray-300 mb-3">Statusänderung hinzufügen</h3>
|
||
<form action="/bewerbung/<%= application.id %>/verlauf" method="POST" class="space-y-3">
|
||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Datum</label>
|
||
<input type="date" name="datum" required value="<%= today %>"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Status</label>
|
||
<select name="status" required
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white">
|
||
<% statusOptions.forEach(option => { %>
|
||
<option value="<%= option %>"><%= option %></option>
|
||
<% }); %>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div>
|
||
<label class="block text-xs font-medium text-gray-500 dark:text-gray-400 mb-1">Kommentar</label>
|
||
<textarea name="kommentar" rows="2"
|
||
class="w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md bg-white dark:bg-gray-700 text-gray-800 dark:text-white leading-relaxed"
|
||
placeholder="Was ist passiert?"></textarea>
|
||
</div>
|
||
<div class="flex justify-end">
|
||
<button type="submit" class="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors">
|
||
Statusänderung hinzufügen
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</section>
|
||
</main>
|
||
|
||
<%- include('partials/footer') %>
|
||
|
||
<script>
|
||
(function () {
|
||
const body = document.getElementById('body');
|
||
const dm = localStorage.getItem('darkMode');
|
||
if (dm === 'enabled' || (!dm && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
|
||
body.classList.add('dark');
|
||
}
|
||
const toggle = document.getElementById('darkModeToggle');
|
||
const sun = document.getElementById('sunIcon');
|
||
const moon = document.getElementById('moonIcon');
|
||
function sync() {
|
||
const d = body.classList.contains('dark');
|
||
if (sun) sun.classList.toggle('hidden', d);
|
||
if (moon) moon.classList.toggle('hidden', !d);
|
||
}
|
||
sync();
|
||
if (toggle) toggle.addEventListener('click', () => {
|
||
body.classList.toggle('dark');
|
||
localStorage.setItem('darkMode', body.classList.contains('dark') ? 'enabled' : 'disabled');
|
||
sync();
|
||
});
|
||
const cy = document.getElementById('currentYear');
|
||
if (cy) cy.textContent = new Date().getFullYear();
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|