feat: 4-tier role concept, personal balance & cash register system, left off-canvas drawer navigation

This commit is contained in:
2026-09-22 09:05:14 +02:00
parent 0bdcbe7976
commit 896f1d7d08
8 changed files with 1350 additions and 257 deletions
+23 -3
View File
@@ -701,6 +701,7 @@
<tr>
<th>Name & E-Mail</th>
<th>Rolle</th>
<th style="text-align: right;">Guthaben</th>
<th>Aktionen</th>
</tr>
</thead>
@@ -712,20 +713,39 @@
<small class="text-muted">{{ member.email }}</small>
</td>
<td>
{% if member.role == 'admin' %}
<span class="badge-role admin">Admin</span>
{% if current_user.role == 'admin' and member.id != current_user.id %}
<form action="/admin/users/{{ member.id }}/role" method="POST" style="margin: 0;">
<select name="role" onchange="this.form.submit()" style="padding: 4px 8px; font-size: 0.8rem; border-radius: 6px; background: var(--bg-input); color: var(--text-main); border: 1px solid var(--border-color);">
<option value="user" {% if member.role == 'user' %}selected{% endif %}>👤 Mitarbeiter</option>
<option value="organizer" {% if member.role == 'organizer' %}selected{% endif %}>📋 Organisator</option>
<option value="admin" {% if member.role == 'admin' %}selected{% endif %}>👑 Admin</option>
</select>
</form>
{% else %}
<span class="badge-role">Mitarbeiter</span>
{% if member.role == 'admin' %}
<span class="badge-role admin">👑 Admin</span>
{% elif member.role == 'organizer' %}
<span class="badge-role organizer">📋 Organisator</span>
{% else %}
<span class="badge-role">👤 Mitarbeiter</span>
{% endif %}
{% endif %}
</td>
<td style="text-align: right; font-weight: 700;">
<a href="/kasse" style="text-decoration: none; color: {% if member.balance > 0 %}var(--success){% elif member.balance < 0 %}var(--danger){% else %}var(--text-muted){% endif %};">
{{ "%.2f"|format(member.balance) }} €
</a>
</td>
<td>
<div style="display: flex; gap: 6px;">
{% if current_user.role == 'admin' %}
<button type="button" class="btn-copy-small" onclick="openResetPassword('{{ member.id }}', '{{ member.first_name|escape }} {{ member.last_name|escape }}')">🔑 Passwort</button>
{% if member.id != current_user.id %}
<form action="/admin/users/{{ member.id }}/delete" method="POST" style="margin: 0;" onsubmit="return confirm('Möchtest du {{ member.first_name }} {{ member.last_name }} wirklich unwiderruflich löschen?');">
<button type="submit" class="btn-revoke-small" title="Benutzer löschen">🗑️</button>
</form>
{% endif %}
{% endif %}
</div>
</td>
</tr>
+195 -47
View File
@@ -16,52 +16,184 @@
</head>
<body>
<!-- ================= TOP NAVBAR ================= -->
<header class="navbar">
<div class="container nav-content">
<a href="/" class="brand">
🥨 <span>Schmankerl</span> <small class="tenant-badge">{{ tenant.name if tenant else 'Portal' }}</small>
</a>
<!-- LEFT: Menu Toggle + Brand -->
<div class="nav-left">
<button class="menu-toggle-btn" id="drawerToggleBtn" onclick="openDrawer()" aria-label="Menü öffnen" aria-expanded="false">
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
<span class="menu-btn-text">Menü</span>
</button>
<div class="nav-right-cluster">
<!-- Theme Switcher -->
<a href="/" class="brand">
🥨 <span>Schmankerl</span>
{% if tenant and tenant.name %}
<small class="tenant-badge">{{ tenant.name.replace('Schmankerl ', '') }}</small>
{% endif %}
</a>
</div>
<!-- RIGHT: Balance (if logged in) + Theme Toggle + User Chip -->
<div class="nav-right">
{% if current_user and current_user.role != 'guest' %}
<a href="/kasse" class="user-balance-pill" title="Aktuelles Guthaben – Klick für Kassendetails">
<span class="balance-icon">💰</span>
<span class="balance-amount">{{ "%.2f"|format(current_user.balance) }} €</span>
</a>
{% endif %}
<!-- Theme Toggle -->
<button type="button" class="theme-toggle-btn" id="themeToggleBtn" onclick="toggleTheme()" title="Design umschalten (Hell / Dunkel)">
<span id="themeToggleIcon">🌙</span> <span id="themeToggleLabel" class="hide-mobile">Dunkel</span>
</button>
<!-- Mobile Hamburger Toggle -->
<button class="mobile-toggle" aria-label="Menü öffnen" id="mobileNavToggle" aria-expanded="false">
<span class="bar"></span>
<span class="bar"></span>
<span class="bar"></span>
{% if current_user %}
<button type="button" class="user-chip-btn" id="userChipBtn" onclick="openDrawer()" title="Profil & Menü öffnen">
<span class="user-avatar-circle">{{ current_user.first_name[0] }}</span>
<span class="user-chip-name hide-mobile">{{ current_user.first_name }}</span>
</button>
{% else %}
<a href="/login" class="btn-primary btn-login-top">Anmelden</a>
{% endif %}
</div>
<nav class="nav-links" id="navLinks">
{% if current_user %}
<a href="/" class="nav-item">🥨 Bestellen</a>
<a href="/einkauf" class="nav-item nav-einkauf-link">🛒 Einkaufsliste & Kasse</a>
{% if current_user.role == 'admin' %}
<a href="/admin" class="nav-item nav-admin">⚙️ Orga & Admin</a>
{% endif %}
<span class="user-greeting">👤 {{ current_user.first_name }}</span>
<a href="/logout" class="nav-item nav-logout">Abmelden</a>
{% else %}
<a href="/login" class="nav-item nav-login">Anmelden</a>
{% endif %}
<a href="https://new.dev.waiswuaschd.de" target="_blank" class="nav-item nav-home">Zur Plattform</a>
</nav>
</div>
</header>
<!-- ================= LEFT NAVIGATION DRAWER ================= -->
<div class="drawer-backdrop" id="drawerBackdrop" onclick="closeDrawer()"></div>
<aside class="nav-drawer" id="navDrawer" aria-hidden="true">
<div class="drawer-header">
<div class="drawer-brand">
<span class="drawer-logo">🥨</span>
<div>
<strong style="font-size: 1.1rem; color: var(--text-main);">Schmankerl</strong>
<div class="drawer-tenant">{{ tenant.name if tenant else 'Brotzeit-Portal' }}</div>
</div>
</div>
<button type="button" class="drawer-close-btn" onclick="closeDrawer()" aria-label="Menü schließen">✕</button>
</div>
<!-- User Profile Card in Drawer -->
{% if current_user %}
<div class="drawer-user-card">
<div class="user-card-top">
<div class="user-card-avatar">{{ current_user.first_name[0] }}{{ current_user.last_name[0] if current_user.last_name else '' }}</div>
<div class="user-card-meta">
<div class="user-card-name">{{ current_user.first_name }} {{ current_user.last_name }}</div>
<div class="user-card-email">{{ current_user.email }}</div>
<div class="user-card-role">
{% if current_user.role == 'admin' %}
<span class="badge-role admin">👑 Administrator</span>
{% elif current_user.role == 'organizer' %}
<span class="badge-role organizer">📋 Organisator</span>
{% elif current_user.role == 'guest' %}
<span class="badge-role guest">🥨 Gast</span>
{% else %}
<span class="badge-role employee">👤 Mitarbeiter</span>
{% endif %}
</div>
</div>
</div>
{% if current_user.role != 'guest' %}
<div class="drawer-balance-box">
<div>
<div class="balance-box-label">Guthaben-Stand</div>
<div class="balance-box-value">{{ "%.2f"|format(current_user.balance) }} €</div>
</div>
<a href="/kasse" class="balance-box-link" onclick="closeDrawer()">Kasse & Verlauf →</a>
</div>
{% endif %}
</div>
{% endif %}
<!-- Navigation Items -->
<nav class="drawer-nav">
<div class="nav-group-title">BROTZEIT</div>
<a href="/" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">🥨</span>
<span class="nav-item-text">Brotzeit bestellen</span>
</a>
{% if current_user and (current_user.role in ['admin', 'organizer']) %}
<a href="/einkauf" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">🛒</span>
<span class="nav-item-text">Einkaufsliste & Kasse</span>
<span class="nav-badge-pill">Einkauf</span>
</a>
{% endif %}
{% if current_user and current_user.role != 'guest' %}
<div class="nav-group-title">KASSE & GUTHABEN</div>
<a href="/kasse" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">💰</span>
<span class="nav-item-text">Kassensystem & Guthaben</span>
</a>
{% endif %}
{% if current_user and current_user.role in ['admin', 'organizer'] %}
<div class="nav-group-title">ORGANISATION</div>
<a href="/admin?tab=event" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">📅</span>
<span class="nav-item-text">Termine & Runden</span>
</a>
<a href="/admin?tab=products" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">🥨</span>
<span class="nav-item-text">Sortiment & Preise</span>
</a>
<a href="/admin?tab=team" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">✉️</span>
<span class="nav-item-text">Einladungen & Gast-Links</span>
</a>
{% endif %}
{% if current_user and current_user.role == 'admin' %}
<div class="nav-group-title">SYSTEM-ADMINISTRATION</div>
<a href="/admin?tab=team" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">👥</span>
<span class="nav-item-text">Team- & Rollenverwaltung</span>
<span class="nav-badge-pill admin">Admin</span>
</a>
{% endif %}
<div class="nav-group-title">KONTO</div>
{% if current_user %}
<a href="/logout" class="drawer-nav-item nav-logout" onclick="closeDrawer()">
<span class="nav-item-icon">🚪</span>
<span class="nav-item-text">Abmelden</span>
</a>
{% else %}
<a href="/login" class="drawer-nav-item" onclick="closeDrawer()">
<span class="nav-item-icon">🔑</span>
<span class="nav-item-text">Anmelden</span>
</a>
{% endif %}
</nav>
<div class="drawer-footer">
<button type="button" class="drawer-theme-btn" onclick="toggleTheme()">
<span id="drawerThemeIcon">🌙</span> <span id="drawerThemeLabel">Dunkelmodus</span>
</button>
</div>
</aside>
<!-- ================= MAIN CONTENT ================= -->
<main class="main-content">
{% block content %}{% endblock %}
</main>
<!-- ================= FOOTER ================= -->
<footer class="footer">
<div class="container footer-inner">
<p>&copy; 2026 Schmankerl by <a href="https://new.dev.waiswuaschd.de">Waiswuaschd.de</a></p>
<p>&copy; 2026 Schmankerl – Das moderne Brotzeit-Portal</p>
<div class="footer-tenant-info">
Mandant: <strong>{{ tenant.slug if tenant else 'demo' }}</strong> ({{ tenant.subdomain if tenant else '' }})
Mandant: <strong>{{ tenant.name if tenant else 'demo' }}</strong>
</div>
</div>
</footer>
@@ -71,12 +203,18 @@
function updateThemeUI(theme) {
const icon = document.getElementById('themeToggleIcon');
const label = document.getElementById('themeToggleLabel');
const dIcon = document.getElementById('drawerThemeIcon');
const dLabel = document.getElementById('drawerThemeLabel');
if (theme === 'dark') {
if (icon) icon.innerText = '☀️';
if (label) label.innerText = 'Hell';
if (dIcon) dIcon.innerText = '☀️';
if (dLabel) dLabel.innerText = 'Hellmodus umschalten';
} else {
if (icon) icon.innerText = '🌙';
if (label) label.innerText = 'Dunkel';
if (dIcon) dIcon.innerText = '🌙';
if (dLabel) dLabel.innerText = 'Dunkelmodus umschalten';
}
}
@@ -88,30 +226,40 @@
updateThemeUI(next);
}
// Initialize Theme UI on load
// Drawer Open / Close Logic
function openDrawer() {
const drawer = document.getElementById('navDrawer');
const backdrop = document.getElementById('drawerBackdrop');
const btn = document.getElementById('drawerToggleBtn');
if (drawer && backdrop) {
drawer.classList.add('open');
backdrop.classList.add('open');
drawer.setAttribute('aria-hidden', 'false');
if (btn) btn.setAttribute('aria-expanded', 'true');
document.body.style.overflow = 'hidden';
}
}
function closeDrawer() {
const drawer = document.getElementById('navDrawer');
const backdrop = document.getElementById('drawerBackdrop');
const btn = document.getElementById('drawerToggleBtn');
if (drawer && backdrop) {
drawer.classList.remove('open');
backdrop.classList.remove('open');
drawer.setAttribute('aria-hidden', 'true');
if (btn) btn.setAttribute('aria-expanded', 'false');
document.body.style.overflow = '';
}
}
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeDrawer();
});
document.addEventListener('DOMContentLoaded', () => {
const current = document.documentElement.getAttribute('data-theme') || 'light';
updateThemeUI(current);
// Mobile Nav Drawer Toggle
const navToggle = document.getElementById('mobileNavToggle');
const navLinks = document.getElementById('navLinks');
if (navToggle && navLinks) {
navToggle.addEventListener('click', () => {
const isOpen = navLinks.classList.toggle('open');
navToggle.classList.toggle('open', isOpen);
navToggle.setAttribute('aria-expanded', isOpen);
document.body.style.overflow = isOpen ? 'hidden' : '';
});
navLinks.querySelectorAll('a').forEach(link => {
link.addEventListener('click', () => {
navLinks.classList.remove('open');
navToggle.classList.remove('open');
navToggle.setAttribute('aria-expanded', 'false');
document.body.style.overflow = '';
});
});
}
});
</script>
</body>
+36 -18
View File
@@ -106,9 +106,6 @@
<button onclick="window.print()" class="btn-secondary">🖨️ Liste drucken (A4)</button>
<a href="/events/{{ event.id }}/calendar.ics" class="btn-secondary" style="text-decoration: none; display: inline-flex; align-items: center;">📅 Outlook (.ics)</a>
{% endif %}
{% if current_user.role == 'admin' %}
<a href="/admin" class="btn-outline" style="text-decoration: none;">⚙️ Zur Verwaltung</a>
{% endif %}
</div>
</div>
@@ -148,13 +145,13 @@
<div class="sponsor-highlight-card">
<div class="sponsor-icon">🎁</div>
<div>
<h3>Diese Runde wird gesponsert!</h3>
<h3>🎉 Diese Runde wird gesponsert!</h3>
<p>Sponsor / Anlass: <strong>{{ event.sponsor_name }}</strong> übernimmt die Gesamtrechnung von <strong>{{ "%.2f"|format(total_revenue) }} €</strong> für das gesamte Team.</p>
</div>
</div>
{% endif %}
<!-- Stats Cards -->
<!-- 4 Quick Stats -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Gesamtsumme Einkauf</div>
@@ -173,20 +170,20 @@
</div>
</div>
<div class="stat-card">
<div class="stat-label">Teilnehmer</div>
<div class="stat-label">Bestellungen</div>
<div class="stat-value">{{ orders|length }} Personen</div>
</div>
</div>
<!-- 2-Column Responsive Layout -->
<div class="admin-columns">
<!-- Grid: Links Sammelliste, Rechts Wer bekommt was -->
<div class="buyer-grid">
<!-- Sammelliste für Metzger & Bäcker -->
<div class="admin-card">
<h2>🛒 Sammelliste für Metzger & Bäcker</h2>
<p class="card-hint">Das musst du für die gesamte Runde besorgen:</p>
<div class="shopping-table-wrapper">
<h2>🥖 Sammelliste für Bäcker & Metzger</h2>
<p class="card-hint">Mengen gebündelt für deinen Einkauf an der Theke:</p>
<div class="orders-table-wrapper">
<table class="data-table">
<thead>
<tr>
@@ -213,7 +210,7 @@
<!-- Einzelübersicht / Strichliste zum Abhaken -->
<div class="admin-card">
<h2>👥 Wer bekommt was? (Kasse & Abhaken)</h2>
<p class="card-hint">Tippe auf den Status, um bezahlt/offen umzuschalten:</p>
<p class="card-hint">Tippe auf den Status zum Abbuchen oder Abhaken:</p>
<div class="orders-table-wrapper">
<table class="data-table">
@@ -222,7 +219,7 @@
<th>Kollege / Gast</th>
<th>Bestellung</th>
<th>Betrag</th>
<th>Status</th>
<th>Status / Kasse</th>
</tr>
</thead>
<tbody>
@@ -234,6 +231,8 @@
<span class="badge-role guest">Gast</span>
{% elif not order.user and order.guest_display_name %}
<span class="badge-role" style="background: rgba(148, 163, 184, 0.15); color: #cbd5e1;">Gast</span>
{% elif order.user %}
<div><small class="text-muted">Guthaben: <strong>{{ "%.2f"|format(order.user.balance) }} €</strong></small></div>
{% endif %}
</td>
<td>
@@ -250,12 +249,31 @@
<td>
{% if event.sponsor_name %}
<span class="badge-btn paid" style="cursor: default;">✓ Gesponsert</span>
{% else %}
<form action="/admin/toggle-paid/{{ order.id }}" method="POST" style="margin: 0;">
<button type="submit" class="badge-btn {% if order.is_paid %}paid{% else %}unpaid{% endif %}">
{% if order.is_paid %}✓ Bezahlt{% else %}⏳ Offen{% endif %}
{% elif order.is_paid %}
<form action="/admin/toggle-paid/{{ order.id }}" method="POST" style="margin: 0; display: inline;">
<button type="submit" class="badge-btn paid" title="Klick um Bezahlung zurückzusetzen">
{% if order.payment_method == 'balance' %}
✓ Bezahlt (Guthaben)
{% else %}
✓ Bezahlt (Bar)
{% endif %}
</button>
</form>
{% else %}
<div style="display: flex; gap: 6px; flex-wrap: wrap;">
{% if order.user and order.user.balance >= order.total_price %}
<form action="/kasse/charge-order/{{ order.id }}?method=balance" method="POST" style="margin: 0;">
<button type="submit" class="badge-btn paid" style="background: rgba(34, 197, 94, 0.15); color: var(--success);" title="Vom Guthaben des Mitarbeiters abbuchen">
💰 Abbuchen
</button>
</form>
{% endif %}
<form action="/kasse/charge-order/{{ order.id }}?method=cash" method="POST" style="margin: 0;">
<button type="submit" class="badge-btn unpaid" title="Barzahlung annehmen">
💵 Bar bezahlt
</button>
</form>
</div>
{% endif %}
</td>
</tr>
+297
View File
@@ -0,0 +1,297 @@
{% extends "base.html" %}
{% block title %}Kassensystem & Guthaben – {{ tenant.name }}{% endblock %}
{% block content %}
<div class="container">
<!-- Header -->
<div class="kasse-header">
<div>
<span class="badge-role" style="margin-bottom: 6px;">💰 Kasse & Finanzen</span>
<h1>Brotzeit-Kasse & Guthaben</h1>
<p class="admin-sub">
Transparente Abrechnung, bargeldloses Bestellen und Übersicht aller Buchungen.
</p>
</div>
{% if current_user.role in ['admin', 'organizer'] %}
<div class="kasse-actions">
<a href="/einkauf" class="btn-secondary" style="text-decoration: none;">🛒 Zur Einkaufsliste</a>
<a href="/admin" class="btn-secondary" style="text-decoration: none;">⚙️ Zum Orga-Cockpit</a>
</div>
{% endif %}
</div>
{% if success %}
<div class="alert-success" style="margin-bottom: 24px;">
✓ {{ success }}
</div>
{% endif %}
{% if error %}
<div class="alert-error" style="margin-bottom: 24px;">
✕ {{ error }}
</div>
{% endif %}
<!-- ================= PERSÖNLICHE GUTHABEN-KARTE ================= -->
<div class="user-balance-hero">
<div class="balance-hero-left">
<div class="balance-hero-label">Dein persönliches Guthaben:</div>
<div class="balance-hero-amount {% if current_user.balance > 0 %}positive{% elif current_user.balance < 0 %}negative{% endif %}">
{{ "%.2f"|format(current_user.balance) }} €
</div>
<p class="balance-hero-hint">
{% if current_user.balance > 0 %}
Du hast ausreichend Guthaben für kommende Brotzeitrunden. Bei der Abholung kannst du einfach per 1-Klick abbuchen lassen!
{% elif current_user.balance == 0 %}
Dein Guthaben ist aktuell ausgeglichen (0,00 €). Du kannst Bar zahlen oder dein Konto beim Kassenwart aufladen.
{% else %}
Dein Konto ist im Minus. Bitte gleiche den Betrag beim Kassenwart oder Organisator aus.
{% endif %}
</p>
</div>
<div class="balance-hero-right">
<div class="info-bubble">
<span class="bubble-icon">💡</span>
<div>
<strong>Wie funktioniert das Guthaben?</strong>
<p>Gib deinem Kassenwart oder Organisator einfach deinen gewünschten Betrag (z. B. 20 € bar oder per PayPal). Dieser wird deinem Konto gutgeschrieben und bei jeder Brotzeit automatisch abgezogen.</p>
</div>
</div>
</div>
</div>
<!-- ================= KASSENWART / ORGA BEREICH ================= -->
{% if current_user.role in ['admin', 'organizer'] %}
<div class="kassenwart-section">
<div class="section-badge-row">
<h2>👑 Kassenwart-Cockpit (Team-Kasse)</h2>
<span class="badge-role admin">Admin & Orga Ansicht</span>
</div>
<!-- Stats Grid -->
<div class="stats-grid">
<div class="stat-card">
<div class="stat-label">Gesamter Kassenbestand (Team)</div>
<div class="stat-value success">{{ "%.2f"|format(total_team_balance) }} €</div>
</div>
<div class="stat-card">
<div class="stat-label">Mitarbeiter mit Guthaben</div>
<div class="stat-value">{{ positive_users_count }} Personen</div>
</div>
<div class="stat-card">
<div class="stat-label">Gesamtzahl Buchungen</div>
<div class="stat-value">{{ all_transactions|length }} Vorgänge</div>
</div>
</div>
<div class="kasse-columns">
<!-- Formular: Einzahlung buchen -->
<div class="admin-card">
<h2>➕ Guthaben für Mitarbeiter einzahlen</h2>
<p class="card-hint">Verbuche Barzahlungen, PayPal oder Überweisungen direkt auf das Mitarbeiter-Konto:</p>
<form action="/kasse/deposit" method="POST" class="admin-form">
<div class="input-group">
<label for="deposit_user_id">Mitarbeiter auswählen</label>
<select id="deposit_user_id" name="user_id" required>
<option value="">-- Kollege auswählen --</option>
{% for u in team_members %}
<option value="{{ u.id }}">
{{ u.first_name }} {{ u.last_name }} (Aktuell: {{ "%.2f"|format(u.balance) }} €)
</option>
{% endfor %}
</select>
</div>
<div class="input-group">
<label for="deposit_amount">Betrag in Euro (€)</label>
<div class="quick-amounts">
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('10.00')">+10 €</button>
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('20.00')">+20 €</button>
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('30.00')">+30 €</button>
<button type="button" class="quick-amt-btn" onclick="setDepositAmount('50.00')">+50 €</button>
</div>
<input type="number" id="deposit_amount" name="amount" step="0.50" min="0.50" max="500.00" placeholder="20.00" required>
</div>
<div class="input-group">
<label for="deposit_note">Zahlungsart / Bemerkung</label>
<input type="text" id="deposit_note" name="description" placeholder="z. B. Barzahlung an Kasse oder PayPal" value="Barzahlung">
</div>
<button type="submit" class="btn-primary" style="width: 100%; margin-top: 10px;">
💰 Guthaben jetzt gutschreiben
</button>
</form>
</div>
<!-- Tabelle: Mitarbeiter-Salden -->
<div class="admin-card">
<h2>👥 Team-Guthaben im Überblick</h2>
<p class="card-hint">Aktuelle Salden aller registrierten Kollegen:</p>
<div class="orders-table-wrapper" style="max-height: 400px; overflow-y: auto;">
<table class="data-table">
<thead>
<tr>
<th>Kollege</th>
<th>Rolle</th>
<th style="text-align: right;">Guthaben</th>
<th style="text-align: center;">Aktion</th>
</tr>
</thead>
<tbody>
{% for u in team_members %}
<tr>
<td>
<strong>{{ u.first_name }} {{ u.last_name }}</strong>
<br><small class="text-muted">{{ u.email }}</small>
</td>
<td>
{% if u.role == 'admin' %}
<span class="badge-role admin">Admin</span>
{% elif u.role == 'organizer' %}
<span class="badge-role organizer">Orga</span>
{% else %}
<span class="badge-role employee">Mitarbeiter</span>
{% endif %}
</td>
<td style="text-align: right; font-weight: 800; font-size: 1rem; {% if u.balance > 0 %}color: var(--success);{% elif u.balance < 0 %}color: var(--danger);{% endif %}">
{{ "%.2f"|format(u.balance) }} €
</td>
<td style="text-align: center;">
<button type="button" class="btn-fill-small" onclick="quickSelectUser({{ u.id }}, '{{ u.first_name }} {{ u.last_name }}')">
+ Aufladen
</button>
</td>
</tr>
{% else %}
<tr><td colspan="4" class="empty-cell">Keine Teammitglieder vorhanden.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Vollständiges Buchungsjournal (Für Kassenwart) -->
<div class="admin-card" style="margin-top: 28px;">
<h2>📜 Kassenbuch / Transaktions-Journal</h2>
<p class="card-hint">Revisionssichere Historie aller Einzahlungen und Abbuchungen:</p>
<div class="orders-table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Datum & Zeit</th>
<th>Mitarbeiter</th>
<th>Typ</th>
<th>Beschreibung / Anlass</th>
<th>Verbucht durch</th>
<th style="text-align: right;">Betrag</th>
</tr>
</thead>
<tbody>
{% for t in all_transactions %}
<tr>
<td>{{ t.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
<td>
<strong>{{ t.user.first_name }} {{ t.user.last_name }}</strong>
</td>
<td>
{% if t.transaction_type == 'deposit' %}
<span class="badge-role" style="background: rgba(34, 197, 94, 0.15); color: var(--success);">+ Einzahlung</span>
{% elif t.transaction_type == 'order_charge' %}
<span class="badge-role" style="background: rgba(56, 189, 248, 0.15); color: var(--accent);">- Brotzeit</span>
{% else %}
<span class="badge-role">{{ t.transaction_type }}</span>
{% endif %}
</td>
<td>{{ t.description or '—' }}</td>
<td>
{% if t.created_by %}
{{ t.created_by.first_name }} {{ t.created_by.last_name }}
{% else %}
System
{% endif %}
</td>
<td style="text-align: right; font-weight: 800; {% if t.amount > 0 %}color: var(--success);{% else %}color: var(--text-main);{% endif %}">
{% if t.amount > 0 %}+{% endif %}{{ "%.2f"|format(t.amount) }} €
</td>
</tr>
{% else %}
<tr><td colspan="6" class="empty-cell">Noch keine Buchungen im Journal vorhanden.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
<!-- ================= PERSÖNLICHE BUCHUNGSHISTORIE ================= -->
<div class="admin-card" style="margin-top: 28px;">
<h2>📑 Deine persönlichen Buchungen</h2>
<p class="card-hint">Hier siehst du transparent alle Aufladungen und Abbuchungen deines Kontos:</p>
<div class="orders-table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Datum</th>
<th>Vorgang</th>
<th>Notiz / Anlass</th>
<th style="text-align: right;">Betrag</th>
</tr>
</thead>
<tbody>
{% for t in user_transactions %}
<tr>
<td>{{ t.created_at.strftime('%d.%m.%Y %H:%M') }}</td>
<td>
{% if t.transaction_type == 'deposit' %}
<span class="badge-role" style="background: rgba(34, 197, 94, 0.15); color: var(--success);">+ Einzahlung</span>
{% elif t.transaction_type == 'order_charge' %}
<span class="badge-role" style="background: rgba(56, 189, 248, 0.15); color: var(--accent);">- Brotzeit</span>
{% else %}
<span class="badge-role">{{ t.transaction_type }}</span>
{% endif %}
</td>
<td>{{ t.description or 'Brotzeit-Abrechnung' }}</td>
<td style="text-align: right; font-weight: 800; {% if t.amount > 0 %}color: var(--success);{% else %}color: var(--text-main);{% endif %}">
{% if t.amount > 0 %}+{% endif %}{{ "%.2f"|format(t.amount) }} €
</td>
</tr>
{% else %}
<tr><td colspan="4" class="empty-cell">Bisher sind noch keine Buchungen für dein Konto vorhanden.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<script>
function setDepositAmount(val) {
document.getElementById('deposit_amount').value = val;
}
function quickSelectUser(userId, name) {
const select = document.getElementById('deposit_user_id');
if (select) {
select.value = userId;
select.focus();
document.getElementById('deposit_amount').focus();
}
}
</script>
{% endblock %}
+11
View File
@@ -51,6 +51,17 @@
</button>
</div>
<div class="demo-cred-item">
<div>
<span class="badge-role organizer">📋 Organisator</span>
<code>orga@waiswuaschd.de</code>
<span class="text-muted" style="font-size: 0.8rem;">(PW: <code>demo123</code>)</span>
</div>
<button type="button" class="btn-fill-small" onclick="fillCredentials('orga@waiswuaschd.de', 'demo123')">
✍️ Ausfüllen
</button>
</div>
<div class="demo-cred-item">
<div>
<span class="badge-role">👤 Mitarbeiter</span>