322 lines
13 KiB
HTML
322 lines
13 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block content %}
|
||
<div class="container">
|
||
|
||
{% if success %}
|
||
<div class="alert-success" style="margin-bottom: 20px;">
|
||
✓ {{ success }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if error %}
|
||
<div class="alert-error" style="margin-bottom: 20px;">
|
||
⚠️ {{ error }}
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Multi-Event Picker (wenn mehrere anstehende Runden offen sind) -->
|
||
{% if open_events|length > 1 %}
|
||
<div class="multi-event-banner">
|
||
<span>📅 Weitere offene Termine:</span>
|
||
<div class="multi-event-chips">
|
||
{% for ev in open_events %}
|
||
<a href="/?event_id={{ ev.id }}{% if is_valid_guest %}&token={{ guest_token }}{% endif %}" class="mini-chip {% if event and event.id == ev.id %}active{% endif %}">
|
||
{{ ev.event_date.strftime('%d.%m.') }} – {{ ev.title }}
|
||
</a>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
<!-- Header Banner -->
|
||
{% if event %}
|
||
<div class="banner {% if is_closed %}banner-closed{% endif %}">
|
||
<div class="banner-top-row">
|
||
<div class="banner-badge {% if is_closed %}closed{% endif %}">
|
||
{% if is_closed %}🔴 Runde geschlossen{% else %}🟢 Runde geöffnet{% endif %}
|
||
</div>
|
||
<div class="countdown-container" id="countdownContainer">
|
||
<span class="countdown-label">Frist:</span>
|
||
<span class="countdown-timer" id="countdownTimer">Berechne...</span>
|
||
</div>
|
||
</div>
|
||
|
||
<h1>{{ event.title }}</h1>
|
||
<p class="banner-sub">
|
||
Brotzeit-Tag: <strong>{{ event.event_date.strftime('%d.%m.%Y') }}</strong> |
|
||
Bestellschluss: <strong>{{ event.deadline.strftime('%d.%m.%Y um %H:%M Uhr') }}</strong>
|
||
</p>
|
||
|
||
{% if event.sponsor_name %}
|
||
<div class="sponsor-badge-banner">
|
||
🎉 <strong>Runde gesponsert:</strong> Die Brotzeit geht heute auf <strong>{{ event.sponsor_name }}</strong>!
|
||
</div>
|
||
{% endif %}
|
||
|
||
<div class="banner-stats">
|
||
<span>👥 Bereits <strong>{{ order_count }}</strong> Bestellungen von Kollegen eingegangen</span>
|
||
</div>
|
||
</div>
|
||
|
||
{% if is_closed %}
|
||
<div class="closed-notice-bar">
|
||
🔒 <strong>Bestellschluss erreicht:</strong> Diese Bestellrunde ist geschlossen. Änderungen oder neue Bestellungen sind nicht mehr möglich. Wende dich bei Notfällen direkt an den Organisator.
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% set is_locked = is_closed or (existing_order and existing_order.is_paid) %}
|
||
|
||
<!-- Order Form -->
|
||
<form action="/order" method="POST" class="order-form" id="orderForm">
|
||
<input type="hidden" name="event_id" value="{{ event.id }}">
|
||
<input type="hidden" name="guest_token" value="{{ guest_token }}">
|
||
|
||
<div class="form-grid">
|
||
<!-- Left: Products -->
|
||
<div class="products-section">
|
||
<h2>1. Brotzeit auswählen</h2>
|
||
<div class="product-list">
|
||
{% for product in products %}
|
||
{% set current_qty = user_items.get(product.id, 0) %}
|
||
<div class="product-card {% if is_locked %}disabled{% endif %}" data-price="{{ product.price }}" data-id="{{ product.id }}">
|
||
<div class="product-info">
|
||
<h3>{{ product.name }}</h3>
|
||
<p class="product-desc">{{ product.description }}</p>
|
||
<div class="product-price">
|
||
{% if event.sponsor_name %}
|
||
<span style="text-decoration: line-through; opacity: 0.6; font-size: 0.9rem;">{{ "%.2f"|format(product.price) }} €</span>
|
||
<span style="color: var(--accent-gold); font-weight: 700;"> 0,00 € (Gesponsert)</span>
|
||
{% else %}
|
||
{{ "%.2f"|format(product.price) }} € <small>/ {{ product.unit }}</small>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="quantity-control">
|
||
<button type="button" class="qty-btn" onclick="changeQty({{ product.id }}, -1)" {% if is_locked %}disabled{% endif %}>-</button>
|
||
<input type="number" id="qty_{{ product.id }}" name="qty_{{ product.id }}" value="{{ current_qty }}" min="0" max="50" class="qty-input" onchange="updateTotal()" {% if is_locked %}disabled{% endif %}>
|
||
<button type="button" class="qty-btn" onclick="changeQty({{ product.id }}, 1)" {% if is_locked %}disabled{% endif %}>+</button>
|
||
</div>
|
||
</div>
|
||
{% endfor %}
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Right: User Details & Summary -->
|
||
<div class="summary-section">
|
||
<div class="summary-card">
|
||
<h2>2. Angaben & Übersicht</h2>
|
||
|
||
{% if current_user %}
|
||
<div class="logged-in-badge">
|
||
<div class="user-avatar">👤</div>
|
||
<div>
|
||
<strong>{{ current_user.first_name }} {{ current_user.last_name }}</strong>
|
||
{% if current_user.role == 'guest' %}<small>(Gast)</small>{% endif %}
|
||
<br>
|
||
<small class="text-muted">{{ current_user.email }}</small>
|
||
</div>
|
||
</div>
|
||
{% else %}
|
||
<!-- Gast Angaben -->
|
||
<div class="guest-order-box">
|
||
<div class="privacy-note" style="margin-bottom: 12px;">
|
||
👋 <strong>Gast- & Dienstleister-Bestellung:</strong><br>
|
||
Du bestellst ohne dauerhaftes Konto für diesen Termin ({{ event.event_date.strftime('%d.%m.') }}) mit.
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="g_first_name">Vorname / Name *</label>
|
||
<input type="text" id="g_first_name" name="guest_first_name" required placeholder="z. B. Tobias">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="g_last_name">Firma / Abteilung (optional)</label>
|
||
<input type="text" id="g_last_name" name="guest_last_name" placeholder="z. B. IT-Service Müller">
|
||
</div>
|
||
<div class="input-group">
|
||
<label for="g_email">E-Mail (optional)</label>
|
||
<input type="email" id="g_email" name="guest_email" placeholder="name@firma.de">
|
||
</div>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% set is_locked = is_closed or (existing_order and existing_order.is_paid) %}
|
||
|
||
{% if existing_order %}
|
||
{% if existing_order.is_paid %}
|
||
<div class="alert-success" style="margin-bottom: 16px; border-left: 4px solid var(--success); font-size: 0.88rem; line-height: 1.4;">
|
||
🔒 <strong>Bestellung bereits bezahlt ({{ "%.2f"|format(existing_order.total_price) }} €)</strong><br>
|
||
Deine Bestellung wurde vom Organisator bereits als bezahlt verbucht
|
||
({% if existing_order.payment_method == 'balance' %}Guthaben abgebucht{% else %}Barzahlung vermerkt{% endif %}).
|
||
Änderungen sind gesperrt. Wende dich an deinen Organisator für Anpassungen.
|
||
</div>
|
||
{% else %}
|
||
<div class="notice-box" style="margin-bottom: 14px;">
|
||
ℹ️ Du hast bereits für diese Runde bestellt (Status: <strong>Noch offen</strong>).
|
||
{% if not is_closed %}Du kannst deine Mengen bis zum Fristende anpassen oder die Bestellung stornieren.{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
{% endif %}
|
||
|
||
<hr class="divider">
|
||
|
||
<div class="order-total-box">
|
||
<div class="total-label">Gesamtsumme:</div>
|
||
<div class="total-amount" id="totalAmount">0,00 €</div>
|
||
</div>
|
||
|
||
{% if event.sponsor_name %}
|
||
<div style="font-size: 0.85rem; color: var(--accent-gold); margin-bottom: 14px; text-align: center;">
|
||
🎁 Die Rechnung wird von <strong>{{ event.sponsor_name }}</strong> übernommen!
|
||
</div>
|
||
{% endif %}
|
||
|
||
<button type="submit" class="btn-submit" id="submitBtn" {% if is_locked %}disabled{% endif %}>
|
||
{% if existing_order and existing_order.is_paid %}
|
||
🔒 Bestellung bereits bezahlt & gesperrt
|
||
{% elif is_closed %}
|
||
🔒 Bestellschluss vorüber
|
||
{% elif existing_order %}
|
||
🥨 Bestellung aktualisieren
|
||
{% else %}
|
||
🥨 Bestellung verbindlich abschicken
|
||
{% endif %}
|
||
</button>
|
||
|
||
{% if existing_order and not existing_order.is_paid and not is_closed %}
|
||
<div style="margin-top: 10px;">
|
||
<button type="button" onclick="cancelMyOrder()" class="btn-secondary" style="width: 100%; border-color: var(--danger); color: var(--danger); font-size: 0.85rem; padding: 8px 12px; cursor: pointer;">
|
||
✕ Bestellung komplett stornieren
|
||
</button>
|
||
</div>
|
||
{% endif %}
|
||
|
||
{% if not is_closed and not (existing_order and existing_order.is_paid) %}
|
||
<small class="form-hint">Du kannst deine Bestellung bis zum Bestellschluss jederzeit hier anpassen.</small>
|
||
{% elif existing_order and existing_order.is_paid %}
|
||
<small class="form-hint">Bereits abgerechnet – Änderungen nur über den Organisator.</small>
|
||
{% else %}
|
||
<small class="form-hint text-warning">Die Frist ist abgelaufen.</small>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
|
||
{% else %}
|
||
<div class="empty-state">
|
||
<div class="empty-icon">🏖️</div>
|
||
<h2>Aktuell keine offene Bestellrunde</h2>
|
||
<p>Für dieses Team ist im Moment keine Brotzeit geplant.</p>
|
||
{% if current_user and current_user.role == 'admin' %}
|
||
<a href="/admin?tab=event" class="btn-primary" style="margin-top: 1rem;">Neue Bestellrunde im Cockpit anlegen</a>
|
||
{% endif %}
|
||
</div>
|
||
{% endif %}
|
||
|
||
</div>
|
||
|
||
<script>
|
||
function changeQty(productId, delta) {
|
||
const input = document.getElementById('qty_' + productId);
|
||
if (!input || input.disabled) return;
|
||
let val = parseInt(input.value) || 0;
|
||
val = Math.max(0, val + delta);
|
||
input.value = val;
|
||
updateTotal();
|
||
}
|
||
|
||
function updateTotal() {
|
||
let total = 0;
|
||
const cards = document.querySelectorAll('.product-card');
|
||
cards.forEach(card => {
|
||
const price = parseFloat(card.dataset.price) || 0;
|
||
const id = card.dataset.id;
|
||
const qtyInput = document.getElementById('qty_' + id);
|
||
const qty = parseInt(qtyInput ? qtyInput.value : 0) || 0;
|
||
total += price * qty;
|
||
});
|
||
|
||
const totalEl = document.getElementById('totalAmount');
|
||
if (totalEl) {
|
||
totalEl.innerText = total.toFixed(2).replace('.', ',') + ' €';
|
||
}
|
||
}
|
||
|
||
// --- LIVE COUNTDOWN TIMER ---
|
||
{% if event and not is_closed %}
|
||
const deadlineMs = new Date("{{ event.deadline.isoformat() }}").getTime();
|
||
|
||
function updateCountdown() {
|
||
const nowMs = new Date().getTime();
|
||
const diffMs = deadlineMs - nowMs;
|
||
const timerEl = document.getElementById('countdownTimer');
|
||
const containerEl = document.getElementById('countdownContainer');
|
||
const submitBtn = document.getElementById('submitBtn');
|
||
|
||
if (diffMs <= 0) {
|
||
if (timerEl) timerEl.innerText = "Abgelaufen (geschlossen)";
|
||
if (containerEl) {
|
||
containerEl.classList.add('expired');
|
||
}
|
||
if (submitBtn) {
|
||
submitBtn.disabled = true;
|
||
submitBtn.innerText = "🔒 Bestellschluss vorüber";
|
||
}
|
||
document.querySelectorAll('.qty-btn').forEach(btn => btn.disabled = true);
|
||
document.querySelectorAll('.qty-input').forEach(inp => inp.disabled = true);
|
||
return;
|
||
}
|
||
|
||
const totalSeconds = Math.floor(diffMs / 1000);
|
||
const days = Math.floor(totalSeconds / 86400);
|
||
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||
const seconds = totalSeconds % 60;
|
||
|
||
let text = "";
|
||
if (days > 0) {
|
||
text = `${days}T ${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||
} else {
|
||
text = `${pad(hours)}:${pad(minutes)}:${pad(seconds)}`;
|
||
}
|
||
|
||
if (timerEl) timerEl.innerText = `⏳ Noch ${text}`;
|
||
|
||
if (totalSeconds < 3600 && containerEl) {
|
||
containerEl.classList.add('urgent');
|
||
}
|
||
}
|
||
|
||
function pad(num) {
|
||
return num.toString().padStart(2, '0');
|
||
}
|
||
|
||
setInterval(updateCountdown, 1000);
|
||
updateCountdown();
|
||
{% elif is_closed %}
|
||
const timerEl = document.getElementById('countdownTimer');
|
||
if (timerEl) timerEl.innerText = "Frist beendet";
|
||
{% endif %}
|
||
|
||
function cancelMyOrder() {
|
||
if (!confirm('Möchtest du deine Bestellung für diese Runde wirklich komplett stornieren?')) {
|
||
return;
|
||
}
|
||
const form = document.createElement('form');
|
||
form.method = 'POST';
|
||
form.action = '/order/cancel';
|
||
|
||
const evInput = document.createElement('input');
|
||
evInput.type = 'hidden';
|
||
evInput.name = 'event_id';
|
||
evInput.value = '{{ event.id if event else "" }}';
|
||
form.appendChild(evInput);
|
||
|
||
document.body.appendChild(form);
|
||
form.submit();
|
||
}
|
||
|
||
document.addEventListener('DOMContentLoaded', updateTotal);
|
||
</script>
|
||
{% endblock %}
|