337 lines
16 KiB
Plaintext
337 lines
16 KiB
Plaintext
<!DOCTYPE html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>PlusFit24 – Mitglied #<%= member.id %></title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700;800&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="/css/style.css">
|
||
</head>
|
||
<body class="admin-body">
|
||
<div class="admin-layout">
|
||
|
||
<aside class="admin-sidebar">
|
||
<div class="logo admin-logo">Plusfit<span>24</span></div>
|
||
<nav class="admin-nav">
|
||
<a href="/admin" class="nav-link">📋 Tarife</a>
|
||
<a href="/admin#kategorien" class="nav-link">🏷️ Kategorien</a>
|
||
<a href="/admin#mitglieder" class="nav-link active">👥 Mitglieder</a>
|
||
<a href="/admin#einstellungen" class="nav-link">⚙️ Einstellungen</a>
|
||
</nav>
|
||
<div class="sidebar-footer">
|
||
<span>👤 <%= admin %></span>
|
||
<a href="/admin/logout" class="logout-link">Abmelden</a>
|
||
</div>
|
||
</aside>
|
||
|
||
<main class="admin-main">
|
||
|
||
<div class="detail-header">
|
||
<a href="/admin#mitglieder" class="btn btn-outline btn-sm">← Zurück</a>
|
||
<div class="detail-header-title">
|
||
<h1><%= member.first_name %> <%= member.last_name %></h1>
|
||
<span class="member-id-badge">Mitglied #<%= member.id %></span>
|
||
<span class="status-badge <%= member.status === 'active' ? 'active' : (member.status === 'paused' ? 'warning' : 'inactive') %>">
|
||
<%= member.status === 'active' ? '✅ Aktiv' : member.status === 'paused' ? '⏸ Pausiert' : '❌ Inaktiv' %>
|
||
</span>
|
||
<% if (member.is_minor) { %>
|
||
<span class="status-badge warning">⚠️ Minderjährig</span>
|
||
<% } %>
|
||
</div>
|
||
<div class="detail-header-actions">
|
||
<button class="btn btn-primary" id="editBtn" onclick="enableEdit()">✏️ Bearbeiten</button>
|
||
<button class="btn btn-success hidden" id="saveBtn" form="memberForm">💾 Speichern</button>
|
||
<button class="btn btn-outline hidden" id="cancelBtn" onclick="cancelEdit()">✕ Abbrechen</button>
|
||
</div>
|
||
</div>
|
||
|
||
<% if (success) { %><div class="alert alert-success"><%= success %></div><% } %>
|
||
<% if (error) { %><div class="alert alert-error"><%= error %></div><% } %>
|
||
|
||
<form method="POST" action="/admin/members/<%= member.id %>/update" id="memberForm">
|
||
<div class="karteikarte-grid">
|
||
|
||
<!-- ===== KARTE 1: Persönliche Daten ===== -->
|
||
<div class="karte">
|
||
<div class="karte-header"><span class="karte-icon">👤</span><h3>Persönliche Daten</h3></div>
|
||
<div class="karte-body">
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Anrede</label>
|
||
<select name="salutation" disabled class="karte-input">
|
||
<option value="Herr" <%= member.salutation === 'Herr' ? 'selected' : '' %>>Herr</option>
|
||
<option value="Frau" <%= member.salutation === 'Frau' ? 'selected' : '' %>>Frau</option>
|
||
<option value="Keine Angabe"<%= member.salutation === 'Keine Angabe'? 'selected' : '' %>>Keine Angabe</option>
|
||
</select>
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Titel</label>
|
||
<input type="text" name="title" value="<%= member.title || '' %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Vorname</label>
|
||
<input type="text" name="first_name" value="<%= member.first_name %>" disabled class="karte-input">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Nachname</label>
|
||
<input type="text" name="last_name" value="<%= member.last_name %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Geburtsdatum</label>
|
||
<input type="date" name="birth_date" value="<%= member.birth_date ? new Date(member.birth_date).toISOString().split('T')[0] : '' %>" disabled class="karte-input">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Telefon</label>
|
||
<input type="tel" name="phone" value="<%= member.phone || '' %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field karte-field-full">
|
||
<label>E-Mail</label>
|
||
<input type="email" name="email" value="<%= member.email %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== KARTE 2: Adresse ===== -->
|
||
<div class="karte">
|
||
<div class="karte-header"><span class="karte-icon">📍</span><h3>Adresse</h3></div>
|
||
<div class="karte-body">
|
||
<div class="karte-row">
|
||
<div class="karte-field karte-field-full">
|
||
<label>Straße, Hausnummer</label>
|
||
<input type="text" name="street" value="<%= member.street %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field karte-field-full">
|
||
<label>Adresszusatz</label>
|
||
<input type="text" name="address_addition" value="<%= member.address_addition || '' %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field" style="max-width:140px">
|
||
<label>PLZ</label>
|
||
<input type="text" name="zip" value="<%= member.zip %>" disabled class="karte-input">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Ort</label>
|
||
<input type="text" name="city" value="<%= member.city %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== KARTE 3: Vertrag ===== -->
|
||
<div class="karte">
|
||
<div class="karte-header"><span class="karte-icon">📄</span><h3>Vertrag</h3></div>
|
||
<div class="karte-body">
|
||
|
||
<div class="karte-row">
|
||
<div class="karte-field karte-field-full">
|
||
<label>Tarif</label>
|
||
<select name="tariff_id" disabled class="karte-input">
|
||
<% tariffs.forEach(t => { %>
|
||
<option value="<%= t.id %>" <%= member.tariff_id === t.id ? 'selected' : '' %>>
|
||
<%= t.name %> – <%= Number(t.price_monthly).toFixed(2) %>€/Monat
|
||
</option>
|
||
<% }) %>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Abschlussdatum</label>
|
||
<input type="text" value="<%= member.signup_date ? new Date(member.signup_date).toLocaleDateString('de-DE') : '–' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Vertragsbeginn</label>
|
||
<input type="text" value="<%= member.contract_start ? new Date(member.contract_start).toLocaleDateString('de-DE') : '–' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Vertragsende</label>
|
||
<input type="text" value="<%= member.contract_end ? new Date(member.contract_end).toLocaleDateString('de-DE') : '–' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Eff. Ende <small>(inkl. Auszeit)</small></label>
|
||
<input type="text" value="<%= member.effective_end ? new Date(member.effective_end).toLocaleDateString('de-DE') : '–' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
</div>
|
||
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Erste Zahlung am</label>
|
||
<input type="text" value="<%= member.first_payment_date ? new Date(member.first_payment_date).toLocaleDateString('de-DE') : '–' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Erster Betrag</label>
|
||
<input type="text" value="<%= member.first_payment_amt ? Number(member.first_payment_amt).toFixed(2) + ' €' : '–' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Status</label>
|
||
<select name="status" disabled class="karte-input">
|
||
<option value="active" <%= member.status === 'active' ? 'selected' : '' %>>✅ Aktiv</option>
|
||
<option value="inactive" <%= member.status === 'inactive' ? 'selected' : '' %>>❌ Inaktiv</option>
|
||
<option value="paused" <%= member.status === 'paused' ? 'selected' : '' %>>⏸ Pausiert</option>
|
||
</select>
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Auszeit gesamt</label>
|
||
<input type="text" value="<%= member.pause_months_total || 0 %> Monat(e)" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Auszeiten Tabelle -->
|
||
<div class="auszeit-section">
|
||
<div class="auszeit-header">
|
||
<span class="auszeit-title">⏸ Auszeiten</span>
|
||
</div>
|
||
|
||
<% if (pauses.length === 0) { %>
|
||
<p class="karte-empty" id="noPausesMsg">Keine Auszeiten eingetragen.</p>
|
||
<% } else { %>
|
||
<table class="pause-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Von</th>
|
||
<th>Bis</th>
|
||
<th>Monate</th>
|
||
<th>Grund</th>
|
||
<th>Eingetragen am</th>
|
||
<th class="edit-only hidden">Aktion</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<% pauses.forEach(p => { %>
|
||
<tr>
|
||
<td><%= new Date(p.pause_start).toLocaleDateString('de-DE') %></td>
|
||
<td><%= new Date(p.pause_end).toLocaleDateString('de-DE') %></td>
|
||
<td><strong><%= p.pause_months %></strong></td>
|
||
<td><%= p.reason || '–' %></td>
|
||
<td><%= new Date(p.created_at).toLocaleDateString('de-DE') %></td>
|
||
<td class="edit-only hidden">
|
||
<form method="POST" action="/admin/members/<%= member.id %>/pauses/<%= p.id %>/delete"
|
||
onsubmit="return confirm('Auszeit wirklich löschen?')">
|
||
<button type="submit" class="btn btn-sm btn-danger">🗑</button>
|
||
</form>
|
||
</td>
|
||
</tr>
|
||
<% }) %>
|
||
</tbody>
|
||
</table>
|
||
<% } %>
|
||
|
||
<!-- Neue Auszeit – nur im Bearbeiten-Modus sichtbar -->
|
||
<div class="neue-auszeit hidden" id="neueAuszeit">
|
||
<div class="neue-auszeit-title">+ Neue Auszeit eintragen</div>
|
||
<div class="auszeit-form-row">
|
||
<div class="karte-field">
|
||
<label>Von *</label>
|
||
<input type="date" name="pause_start" form="auszeitForm" required class="karte-input">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Bis *</label>
|
||
<input type="date" name="pause_end" form="auszeitForm" required class="karte-input">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Grund</label>
|
||
<input type="text" name="reason" form="auszeitForm" class="karte-input" placeholder="z.B. Urlaub, Verletzung...">
|
||
</div>
|
||
<div class="karte-field" style="max-width:120px; justify-content:flex-end; padding-top:22px">
|
||
<button type="submit" form="auszeitForm" class="btn btn-primary">Speichern</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ===== KARTE 4: Bankdaten ===== -->
|
||
<div class="karte">
|
||
<div class="karte-header"><span class="karte-icon">🏦</span><h3>Bankdaten / SEPA</h3></div>
|
||
<div class="karte-body">
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>Geldinstitut</label>
|
||
<input type="text" name="bank_name" value="<%= member.bank_name || '' %>" disabled class="karte-input">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>Kontoinhaber</label>
|
||
<input type="text" name="account_holder" value="<%= member.account_holder || '' %>" disabled class="karte-input">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field">
|
||
<label>SEPA akzeptiert</label>
|
||
<input type="text" value="<%= member.sepa_accepted ? '✅ Ja' : '❌ Nein' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
<div class="karte-field">
|
||
<label>AGB akzeptiert</label>
|
||
<input type="text" value="<%= member.agb_accepted ? '✅ Ja' : '❌ Nein' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
</div>
|
||
<div class="karte-row">
|
||
<div class="karte-field karte-field-full">
|
||
<label>IBAN</label>
|
||
<input type="text" name="iban" id="ibanInput"
|
||
value="<%= member.iban ? member.iban.replace(/(.{4})/g, '$1 ').trim() : '' %>"
|
||
disabled class="karte-input karte-iban" maxlength="34" autocomplete="off">
|
||
<div class="iban-message" id="ibanMessage"></div>
|
||
</div>
|
||
</div>
|
||
<% if (member.is_minor) { %>
|
||
<div class="karte-row">
|
||
<div class="karte-field karte-field-full">
|
||
<label>Einverständniserklärung (Erziehungsberechtigte)</label>
|
||
<input type="text" value="<%= member.guardian_consent ? '✅ Vorhanden' : '❌ Fehlt' %>" disabled class="karte-input karte-readonly">
|
||
</div>
|
||
</div>
|
||
<% } %>
|
||
</div>
|
||
</div>
|
||
|
||
</div><!-- end karteikarte-grid -->
|
||
</form>
|
||
|
||
<!-- Auszeit Form (außerhalb von memberForm!) -->
|
||
<form id="auszeitForm" method="POST" action="/admin/members/<%= member.id %>/pauses/add"></form>
|
||
|
||
</main>
|
||
</div>
|
||
|
||
<script src="/js/iban.js"></script>
|
||
<script>
|
||
const editableFields = document.querySelectorAll('.karte-input:not(.karte-readonly)');
|
||
|
||
function enableEdit() {
|
||
editableFields.forEach(f => f.removeAttribute('disabled'));
|
||
document.getElementById('editBtn').classList.add('hidden');
|
||
document.getElementById('saveBtn').classList.remove('hidden');
|
||
document.getElementById('cancelBtn').classList.remove('hidden');
|
||
document.querySelectorAll('.karte').forEach(k => k.classList.add('edit-mode'));
|
||
// Auszeit-Bereich einblenden
|
||
document.getElementById('neueAuszeit').classList.remove('hidden');
|
||
document.querySelectorAll('.edit-only').forEach(el => el.classList.remove('hidden'));
|
||
// IBAN Validierung
|
||
const ibanInput = document.getElementById('ibanInput');
|
||
if (ibanInput) attachIBANValidation(ibanInput, null, document.getElementById('ibanMessage'));
|
||
}
|
||
|
||
function cancelEdit() {
|
||
window.location.reload();
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|