feat: Paket 2 - Product catalog management, guest orders, sponsor rounds, password reset, user deletion, unlimited invite links and setup wizard
This commit is contained in:
+302
-23
@@ -3,7 +3,7 @@
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
|
||||
<!-- Printable distribution sheet for the kitchen (hidden in screen, visible in print) -->
|
||||
<!-- Printable distribution sheet for the kitchen (hidden on screen, visible in print) -->
|
||||
<div class="print-only">
|
||||
<div class="print-header">
|
||||
<h1>🥨 Schmankerl – Brotzeit-Verteilliste</h1>
|
||||
@@ -12,6 +12,11 @@
|
||||
<strong>Datum:</strong> {{ event.event_date.strftime('%d.%m.%Y') if event else '' }} |
|
||||
<strong>Team:</strong> {{ tenant.name }}
|
||||
</p>
|
||||
{% if event and event.sponsor_name %}
|
||||
<div class="print-sponsor-box">
|
||||
🎉 <strong>GESPONSERTE RUNDE:</strong> Ausgegeben von <strong>{{ event.sponsor_name }}</strong> – Gesamtrechnung: <strong>{{ "%.2f"|format(total_revenue) }} €</strong>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="print-section">
|
||||
@@ -50,14 +55,31 @@
|
||||
{% for order in orders %}
|
||||
<tr>
|
||||
<td style="text-align: center; font-size: 1.2rem;">☐</td>
|
||||
<td><strong>{{ order.user.first_name }} {{ order.user.last_name }}</strong></td>
|
||||
<td>
|
||||
<strong>{{ order.user.first_name }} {{ order.user.last_name }}</strong>
|
||||
{% if order.user.role == 'guest' %}<small>(Gast)</small>{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% for item in order.items %}
|
||||
{{ item.quantity }}x {{ item.product.name }}{% if not loop.last %}, {% endif %}
|
||||
{% endfor %}
|
||||
</td>
|
||||
<td style="text-align: right; font-weight: bold;">{{ "%.2f"|format(order.total_price) }} €</td>
|
||||
<td style="text-align: center;">{% if order.is_paid %}Bezahlt ✓{% else %}Offen{% endif %}</td>
|
||||
<td style="text-align: right; font-weight: bold;">
|
||||
{% if event and event.sponsor_name %}
|
||||
<span style="color: #666; font-size: 9pt;">Gesponsert</span>
|
||||
{% else %}
|
||||
{{ "%.2f"|format(order.total_price) }} €
|
||||
{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% if event and event.sponsor_name %}
|
||||
Übernommen ✓
|
||||
{% elif order.is_paid %}
|
||||
Bezahlt ✓
|
||||
{% else %}
|
||||
Offen
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -88,6 +110,9 @@
|
||||
<a href="?tab=event{% if event %}&event_id={{ event.id }}{% endif %}" class="admin-tab-btn {% if active_tab == 'event' %}active{% endif %}">
|
||||
📅 Event- & Frist-Planung
|
||||
</a>
|
||||
<a href="?tab=products{% if event %}&event_id={{ event.id }}{% endif %}" class="admin-tab-btn {% if active_tab == 'products' %}active{% endif %}">
|
||||
🥨 Produkte & Sortiment ({{ products|length }})
|
||||
</a>
|
||||
<a href="?tab=team{% if event %}&event_id={{ event.id }}{% endif %}" class="admin-tab-btn {% if active_tab == 'team' %}active{% endif %}">
|
||||
👥 Team & Einladungen ({{ team_members|length }})
|
||||
</a>
|
||||
@@ -109,6 +134,17 @@
|
||||
{% endif %}
|
||||
|
||||
{% if event %}
|
||||
|
||||
{% if event.sponsor_name %}
|
||||
<div class="sponsor-highlight-card">
|
||||
<div class="sponsor-icon">🎁</div>
|
||||
<div>
|
||||
<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 -->
|
||||
<div class="stats-grid">
|
||||
<div class="stat-card">
|
||||
@@ -117,15 +153,19 @@
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Bereits bezahlt</div>
|
||||
<div class="stat-value success">{{ "%.2f"|format(total_paid) }} €</div>
|
||||
<div class="stat-value success">
|
||||
{% if event.sponsor_name %}Gesponsert{% else %}{{ "%.2f"|format(total_paid) }} €{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Noch offen</div>
|
||||
<div class="stat-value warning">{{ "%.2f"|format(total_revenue - total_paid) }} €</div>
|
||||
<div class="stat-value warning">
|
||||
{% if event.sponsor_name %}0,00 €{% else %}{{ "%.2f"|format(total_revenue - total_paid) }} €{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="stat-card">
|
||||
<div class="stat-label">Teilnehmer</div>
|
||||
<div class="stat-value">{{ orders|length }} Kollegen</div>
|
||||
<div class="stat-value">{{ orders|length }} Personen</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -133,7 +173,7 @@
|
||||
<!-- Aggregated Shopping List -->
|
||||
<div class="admin-card">
|
||||
<h2>🛒 Sammelliste für Metzger & Bäcker</h2>
|
||||
<p class="card-hint">Aufsummierte Mengen für den Einkauf:</p>
|
||||
<p class="card-hint">Aufsummierte Gesamtmengen für den Einkauf:</p>
|
||||
|
||||
<div class="shopping-table-wrapper">
|
||||
<table class="data-table">
|
||||
@@ -162,7 +202,7 @@
|
||||
<!-- Individual Orders & Payment Checklist -->
|
||||
<div class="admin-card">
|
||||
<h2>👥 Wer hat bestellt? (Strichliste)</h2>
|
||||
<p class="card-hint">Klicke auf den Status, um bezahlt/offen umzuschalten:</p>
|
||||
<p class="card-hint">Einzelübersicht mit Artikeln und Kassenstatus:</p>
|
||||
|
||||
<div class="orders-table-wrapper">
|
||||
<table class="data-table">
|
||||
@@ -178,7 +218,11 @@
|
||||
{% for order in orders %}
|
||||
<tr>
|
||||
<td>
|
||||
<strong>{{ order.user.first_name }} {{ order.user.last_name }}</strong><br>
|
||||
<strong>{{ order.user.first_name }} {{ order.user.last_name }}</strong>
|
||||
{% if order.user.role == 'guest' %}
|
||||
<span class="badge-role guest">Gast</span>
|
||||
{% endif %}
|
||||
<br>
|
||||
<small class="text-muted">{{ order.user.email }}</small>
|
||||
</td>
|
||||
<td>
|
||||
@@ -188,13 +232,20 @@
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
<td><strong>{{ "%.2f"|format(order.total_price) }} €</strong></td>
|
||||
<td>
|
||||
<strong>{{ "%.2f"|format(order.total_price) }} €</strong>
|
||||
{% if event.sponsor_name %}<br><small class="text-muted">von Sponsor</small>{% endif %}
|
||||
</td>
|
||||
<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 %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
@@ -220,7 +271,7 @@
|
||||
{% if event %}
|
||||
<div class="admin-card">
|
||||
<h2>✏️ Aktives Event bearbeiten</h2>
|
||||
<p class="card-hint">Passe Titel, Termin und die Bestellfrist sekundengenau an:</p>
|
||||
<p class="card-hint">Passe Titel, Frist, Gast-Erlaubnis und Sponsor an:</p>
|
||||
|
||||
<form action="/admin/events/{{ event.id }}/edit" method="POST" class="admin-form">
|
||||
<div class="input-group">
|
||||
@@ -242,6 +293,17 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="edit_sponsor">🎉 Sponsor / Anlass (Optional)</label>
|
||||
<input type="text" id="edit_sponsor" name="sponsor_name" value="{{ event.sponsor_name or '' }}" placeholder="z. B. Maxi (Geburtstag) oder Firma übernimmt">
|
||||
<small class="form-hint">Falls ausgefüllt, übernimmt der Sponsor die Gesamtrechnung statt Einzelabrechnung.</small>
|
||||
</div>
|
||||
<div class="input-group-checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="allow_guests" value="true" {% if event.allow_guests %}checked{% endif %}>
|
||||
<strong>Gäste-Bestellungen erlauben</strong> (Mitbestellen ohne Registrierung)
|
||||
</label>
|
||||
</div>
|
||||
<div class="input-group" style="margin-top: 14px;">
|
||||
<label for="edit_status">Status der Runde</label>
|
||||
<select id="edit_status" name="status_val">
|
||||
<option value="open" {% if event.status == 'open' %}selected{% endif %}>🟢 Geöffnet (Bestellungen möglich)</option>
|
||||
@@ -277,18 +339,155 @@
|
||||
<input type="time" id="new_deadline_time" name="deadline_time" value="08:30" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="new_sponsor">🎉 Sponsor / Anlass (Optional)</label>
|
||||
<input type="text" id="new_sponsor" name="sponsor_name" placeholder="z. B. Geburtstag von Anna">
|
||||
</div>
|
||||
<div class="input-group-checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="allow_guests" value="true" checked>
|
||||
<strong>Gäste-Bestellungen erlauben</strong>
|
||||
</label>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="margin-top: 1rem;">Runde jetzt starten</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB 3: TEAM & EINLADUNGEN -->
|
||||
<!-- TAB 3: PRODUKTE & SORTIMENT -->
|
||||
{% elif active_tab == 'products' %}
|
||||
<div class="admin-columns">
|
||||
<!-- Product List -->
|
||||
<div class="admin-card" style="grid-column: 1 / -1;">
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
|
||||
<div>
|
||||
<h2>🥨 Sortiment verwalten ({{ products|length }} Artikel)</h2>
|
||||
<p class="card-hint">Artikel für die Bestellkarte anpassen, aktivieren oder deaktivieren:</p>
|
||||
</div>
|
||||
<a href="/setup" class="btn-secondary" style="font-size: 0.85rem;">⚡ Standard-Bayerisch vorladen</a>
|
||||
</div>
|
||||
|
||||
<div class="orders-table-wrapper">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Artikel</th>
|
||||
<th>Beschreibung</th>
|
||||
<th>Preis</th>
|
||||
<th>Einheit</th>
|
||||
<th>Status</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for prod in products %}
|
||||
<tr class="{% if not prod.is_active %}row-inactive{% endif %}">
|
||||
<td><strong>{{ prod.name }}</strong></td>
|
||||
<td><small class="text-muted">{{ prod.description }}</small></td>
|
||||
<td><strong>{{ "%.2f"|format(prod.price) }} €</strong></td>
|
||||
<td>{{ prod.unit }}</td>
|
||||
<td>
|
||||
{% if prod.is_active %}
|
||||
<span class="badge-role" style="background: rgba(34, 197, 94, 0.15); color: #86efac;">Aktiv</span>
|
||||
{% else %}
|
||||
<span class="badge-role" style="background: rgba(239, 68, 68, 0.15); color: #fca5a5;">Inaktiv</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<button type="button" class="btn-copy-small" onclick="openEditProduct('{{ prod.id }}', '{{ prod.name|escape }}', '{{ prod.description|escape }}', '{{ '%.2f'|format(prod.price) }}', '{{ prod.unit }}', '{{ prod.sort_order }}')">✏️ Bearbeiten</button>
|
||||
<form action="/admin/products/{{ prod.id }}/toggle" method="POST" style="margin: 0; display: inline;">
|
||||
<button type="submit" class="btn-revoke-small" style="color: {% if prod.is_active %}#fca5a5{% else %}#86efac{% endif %};">
|
||||
{% if prod.is_active %}Deaktivieren{% else %}Aktivieren{% endif %}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="empty-cell">Noch keine Produkte angelegt. Nutze das Formular unten oder lade das Standard-Sortiment!</td></tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Add new product -->
|
||||
<div class="admin-card" style="margin-top: 2rem; background: rgba(11, 15, 25, 0.6);">
|
||||
<h3>➕ Neues Produkt hinzufügen</h3>
|
||||
<form action="/admin/products/new" method="POST" class="admin-form" style="margin-top: 14px;">
|
||||
<div class="form-row-2">
|
||||
<div class="input-group">
|
||||
<label for="prod_name">Artikelname</label>
|
||||
<input type="text" id="prod_name" name="name" placeholder="z. B. Wiener Würstchen" required>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="prod_desc">Beschreibung (optional)</label>
|
||||
<input type="text" id="prod_desc" name="description" placeholder="z. B. Knackig, vom lokalen Metzger">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row-3">
|
||||
<div class="input-group">
|
||||
<label for="prod_price">Preis (€)</label>
|
||||
<input type="text" id="prod_price" name="price" placeholder="2.50" required>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="prod_unit">Einheit</label>
|
||||
<input type="text" id="prod_unit" name="unit" value="Stück" placeholder="Stück, Paar, Portion">
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="prod_sort">Sortierung</label>
|
||||
<input type="number" id="prod_sort" name="sort_order" value="10">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn-primary" style="margin-top: 10px;">Artikel speichern</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Edit Product Modal (Simple JS) -->
|
||||
<div id="editProductModal" class="modal-backdrop" style="display: none;">
|
||||
<div class="modal-card">
|
||||
<h3>✏️ Produkt bearbeiten</h3>
|
||||
<form id="editProductForm" method="POST" class="admin-form" style="margin-top: 14px;">
|
||||
<div class="input-group">
|
||||
<label for="m_prod_name">Artikelname</label>
|
||||
<input type="text" id="m_prod_name" name="name" required>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="m_prod_desc">Beschreibung</label>
|
||||
<input type="text" id="m_prod_desc" name="description">
|
||||
</div>
|
||||
<div class="form-row-3">
|
||||
<div class="input-group">
|
||||
<label for="m_prod_price">Preis (€)</label>
|
||||
<input type="text" id="m_prod_price" name="price" required>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="m_prod_unit">Einheit</label>
|
||||
<input type="text" id="m_prod_unit" name="unit" required>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<label for="m_prod_sort">Sortierung</label>
|
||||
<input type="number" id="m_prod_sort" name="sort_order">
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; margin-top: 16px;">
|
||||
<button type="submit" class="btn-primary">Änderungen speichern</button>
|
||||
<button type="button" class="btn-secondary" onclick="closeEditProduct()">Abbrechen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TAB 4: TEAM & EINLADUNGEN -->
|
||||
{% elif active_tab == 'team' %}
|
||||
<div class="admin-columns">
|
||||
<!-- Generate invite link -->
|
||||
<div class="admin-card">
|
||||
<h2>✉️ Teammitglied einladen</h2>
|
||||
<p class="card-hint">Erzeuge einen sicheren Einladungslink zum Teilen per Chat (Slack, Teams, WhatsApp):</p>
|
||||
<p class="card-hint">Erzeuge einen Einladungslink (einmalig oder unbegrenzt dauerhaft):</p>
|
||||
|
||||
<form action="/admin/invitations/create" method="POST" class="admin-form">
|
||||
<div class="input-group">
|
||||
@@ -307,8 +506,9 @@
|
||||
<div class="input-group">
|
||||
<label for="inv_valid">Gültigkeit</label>
|
||||
<select id="inv_valid" name="valid_days">
|
||||
<option value="unlimited" selected>∞ Unbegrenzt (Dauerhafter Team-Link)</option>
|
||||
<option value="7">7 Tage</option>
|
||||
<option value="14" selected>14 Tage</option>
|
||||
<option value="14">14 Tage</option>
|
||||
<option value="30">30 Tage</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -317,15 +517,20 @@
|
||||
</form>
|
||||
|
||||
{% if pending_invitations %}
|
||||
<h3 style="margin-top: 2rem;">Offene Einladungslinks</h3>
|
||||
<h3 style="margin-top: 2rem;">Aktive Einladungslinks</h3>
|
||||
<div class="invitations-list">
|
||||
{% for inv in pending_invitations %}
|
||||
<div class="invite-item">
|
||||
<div class="invite-info">
|
||||
<strong>{% if inv.email %}{{ inv.email }}{% else %}Offener Link{% endif %}</strong>
|
||||
<strong>{% if inv.email %}{{ inv.email }}{% else %}Offener Team-Link{% endif %}</strong>
|
||||
<span class="badge-role">{{ inv.role }}</span>
|
||||
{% if inv.is_reusable %}
|
||||
<span class="badge-role" style="background: rgba(34, 197, 94, 0.2); color: #86efac;">Dauerhaft wiederverwendbar</span>
|
||||
{% endif %}
|
||||
<br>
|
||||
<small class="text-muted">Gültig bis {{ inv.expires_at.strftime('%d.%m.%Y') }}</small>
|
||||
<small class="text-muted">
|
||||
{% if inv.expires_at %}Gültig bis {{ inv.expires_at.strftime('%d.%m.%Y') }}{% else %}Unbegrenzt gültig{% endif %}
|
||||
</small>
|
||||
</div>
|
||||
<div class="invite-actions">
|
||||
<button type="button" class="btn-copy-small" onclick="copyInviteUrl('{{ inv.token }}')">📋 Kopieren</button>
|
||||
@@ -348,23 +553,37 @@
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>E-Mail</th>
|
||||
<th>Name & E-Mail</th>
|
||||
<th>Rolle</th>
|
||||
<th>Aktionen</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for member in team_members %}
|
||||
<tr>
|
||||
<td><strong>{{ member.first_name }} {{ member.last_name }}</strong></td>
|
||||
<td><small class="text-muted">{{ member.email }}</small></td>
|
||||
<td>
|
||||
<strong>{{ member.first_name }} {{ member.last_name }}</strong><br>
|
||||
<small class="text-muted">{{ member.email }}</small>
|
||||
</td>
|
||||
<td>
|
||||
{% if member.role == 'admin' %}
|
||||
<span class="badge-role admin">Admin</span>
|
||||
{% elif member.role == 'guest' %}
|
||||
<span class="badge-role guest">Gast</span>
|
||||
{% else %}
|
||||
<span class="badge-role">Mitarbeiter</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div style="display: flex; gap: 6px;">
|
||||
<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 %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
@@ -372,6 +591,27 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Password Reset Modal -->
|
||||
<div id="resetPasswordModal" class="modal-backdrop" style="display: none;">
|
||||
<div class="modal-card">
|
||||
<h3>🔑 Passwort zurücksetzen</h3>
|
||||
<p class="card-hint" id="resetModalUserHint">Neues Passwort für den Benutzer vergeben:</p>
|
||||
<form id="resetPasswordForm" method="POST" class="admin-form">
|
||||
<div class="input-group">
|
||||
<label for="new_pw_input">Neues Passwort</label>
|
||||
<div style="display: flex; gap: 8px;">
|
||||
<input type="text" id="new_pw_input" name="new_password" required minlength="4" placeholder="Mindestens 4 Zeichen">
|
||||
<button type="button" class="btn-secondary" onclick="generateRandomPw()">🎲 Zufall</button>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px; margin-top: 16px;">
|
||||
<button type="submit" class="btn-primary">Passwort jetzt speichern</button>
|
||||
<button type="button" class="btn-secondary" onclick="closeResetPassword()">Abbrechen</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
@@ -394,8 +634,47 @@
|
||||
const origin = window.location.origin;
|
||||
const url = origin + '/join?token=' + token;
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
alert("Einladungslink kopiert!\n\n" + url + "\n\nDu kannst ihn jetzt an den Kollegen senden.");
|
||||
alert("Einladungslink kopiert!\n\n" + url + "\n\nDu kannst ihn jetzt an das Team senden.");
|
||||
});
|
||||
}
|
||||
|
||||
function openResetPassword(userId, userName) {
|
||||
const modal = document.getElementById('resetPasswordModal');
|
||||
const form = document.getElementById('resetPasswordForm');
|
||||
const hint = document.getElementById('resetModalUserHint');
|
||||
form.action = '/admin/users/' + userId + '/reset-password';
|
||||
hint.innerText = 'Neues Passwort für ' + userName + ' vergeben:';
|
||||
document.getElementById('new_pw_input').value = '';
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeResetPassword() {
|
||||
document.getElementById('resetPasswordModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function generateRandomPw() {
|
||||
const chars = 'abcdefghjkmnpqrstuvwxyzABCDEFGHJKMNPQRSTUVWXYZ23456789!#';
|
||||
let pw = '';
|
||||
for (let i = 0; i < 8; i++) {
|
||||
pw += chars.charAt(Math.floor(Math.random() * chars.length));
|
||||
}
|
||||
document.getElementById('new_pw_input').value = pw;
|
||||
}
|
||||
|
||||
function openEditProduct(id, name, desc, price, unit, sort) {
|
||||
const modal = document.getElementById('editProductModal');
|
||||
const form = document.getElementById('editProductForm');
|
||||
form.action = '/admin/products/' + id + '/edit';
|
||||
document.getElementById('m_prod_name').value = name;
|
||||
document.getElementById('m_prod_desc').value = desc;
|
||||
document.getElementById('m_prod_price').value = price;
|
||||
document.getElementById('m_prod_unit').value = unit;
|
||||
document.getElementById('m_prod_sort').value = sort;
|
||||
modal.style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeEditProduct() {
|
||||
document.getElementById('editProductModal').style.display = 'none';
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user