Initial commit

This commit is contained in:
Bjoern Welker
2026-01-30 08:55:14 +01:00
commit 81a1ed7eef
17 changed files with 2824 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
{% extends "base.html" %}
{% block content %}
<div class="card form-card">
<h2>Ausbuchen: {{ item.artikel }} ({{ item.groesse }})</h2>
<div class="note">Aktueller Bestand: <strong>{{ item.gezaehlt }}</strong></div>
<form method="post" onsubmit="return confirm('Wirklich ausbuchen?');">
<div class="form-grid">
<label>
Menge
<input type="number" name="menge" min="1" required />
</label>
<label>
Grund (optional)
<input type="text" name="grund" placeholder="z. B. Verkauf, Defekt, Muster" />
</label>
</div>
<div class="form-actions">
<button class="btn btn-accent" type="submit">Ausbuchen</button>
<a class="btn ghost" href="{{ url_for('bp.index') }}">Abbrechen</a>
</div>
</form>
</div>
{% endblock %}

37
wawi/templates/base.html Normal file
View File

@@ -0,0 +1,37 @@
<!doctype html>
<html lang="de">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>{{ title or "Hellas Artikelverwaltung" }}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
</head>
<body>
<header>
<div class="wrap top">
<div class="brand">
<img src="{{ url_for('static', filename='logo.png') }}" alt="Hellas 1899 Logo" />
<div>
<h1>Hellas 1899 · Artikelverwaltung</h1>
<div class="meta">Bestand &amp; Ausbuchungen</div>
</div>
</div>
<nav class="nav">
{% if logged_in %}
<a class="btn icon" href="{{ url_for('bp.index') }}" title="Übersicht" aria-label="Übersicht"><span></span></a>
<a class="btn icon accent" href="{{ url_for('bp.new_item') }}" title="Neuer Artikel" aria-label="Neuer Artikel"><span></span></a>
<a class="btn icon" href="{{ url_for('bp.users') }}" title="Benutzer" aria-label="Benutzer"><span>☺︎</span></a>
<a class="btn icon" href="{{ url_for('bp.orders') }}" title="Bestellungen" aria-label="Bestellungen"><span>☑︎</span></a>
<a class="btn icon ghost" href="{{ url_for('bp.logout') }}" title="Logout" aria-label="Logout"><span></span></a>
{% endif %}
</nav>
</div>
</header>
<main>
<div class="wrap">
{% block content %}{% endblock %}
</div>
</main>
<a href="#" class="to-top" title="Nach oben" aria-label="Nach oben"></a>
</body>
</html>

46
wawi/templates/edit.html Normal file
View File

@@ -0,0 +1,46 @@
{% extends "base.html" %}
{% block content %}
<div class="card form-card">
<h2>{{ "Artikel bearbeiten" if item else "Neuen Artikel anlegen" }}</h2>
<form method="post" enctype="multipart/form-data">
<div class="form-grid">
<label>
Artikel
<input type="text" name="artikel" required value="{{ item.artikel if item else '' }}" />
</label>
<label>
Größe
<input type="text" name="groesse" required value="{{ item.groesse if item else '' }}" />
</label>
<label>
Preis (EUR)
<input type="number" name="preis" step="0.01" min="0" value="{{ item.preis if item else 0 }}" />
</label>
<label>
BildURL (optional)
<input type="text" name="bild_url" placeholder="/images/artikel.jpg" value="{{ item.bild_url if item else '' }}" />
</label>
<label>
Bild hochladen (optional)
<input type="file" name="bild_file" accept="image/*" />
</label>
<label>
Soll
<input type="number" name="soll" min="0" value="{{ item.soll if item else 0 }}" />
</label>
<label>
Bestand
<input type="number" name="gezaehlt" min="0" value="{{ item.gezaehlt if item else 0 }}" />
</label>
<label>
Verkäufe
<input type="number" name="verkaeufe" min="0" value="{{ item.verkaeufe if item else 0 }}" />
</label>
</div>
<div class="form-actions">
<button class="btn btn-accent" type="submit">Speichern</button>
<a class="btn ghost" href="{{ url_for('bp.index') }}">Abbrechen</a>
</div>
</form>
</div>
{% endblock %}

88
wawi/templates/index.html Normal file
View File

@@ -0,0 +1,88 @@
{% extends "base.html" %}
{% block content %}
<div class="toolbar">
<form class="search" method="get" action="{{ url_for('bp.index') }}">
<input type="search" name="q" placeholder="Artikel oder Größe suchen…" value="{{ q }}" />
<select name="sort">
<option value="gezaehlt" {% if sort == "gezaehlt" %}selected{% endif %}>Bestand</option>
<option value="soll" {% if sort == "soll" %}selected{% endif %}>Soll</option>
<option value="artikel" {% if sort == "artikel" %}selected{% endif %}>Artikel</option>
<option value="groesse" {% if sort == "groesse" %}selected{% endif %}>Größe</option>
<option value="verkaeufe" {% if sort == "verkaeufe" %}selected{% endif %}>Verkäufe</option>
</select>
<select name="dir">
<option value="desc" {% if direction == "desc" %}selected{% endif %}></option>
<option value="asc" {% if direction == "asc" %}selected{% endif %}></option>
</select>
<button class="btn" type="submit">Filtern</button>
<a class="btn ghost" href="{{ url_for('bp.index') }}">Zurücksetzen</a>
</form>
<div class="stat">
<div class="label">Artikel gesamt</div>
<div class="value">{{ total }}</div>
</div>
<div class="stat">
<div class="label">Bestand gesamt</div>
<div class="value">{{ total_bestand }}</div>
</div>
<a class="stat stat-link" href="{{ url_for('bp.orders') }}" title="Offene Bestellungen anzeigen">
<div class="label">Offene Bestellungen</div>
<div class="value">{{ open_orders }}</div>
</a>
</div>
<div class="card">
<table>
<thead>
<tr>
<th>Artikel</th>
<th>Größe</th>
<th>Preis</th>
<th>Soll</th>
<th>Bestand</th>
<th>Abweichung</th>
<th>Fehlbestand</th>
<th>Verkäufe</th>
<th class="actions">Aktionen</th>
</tr>
</thead>
<tbody>
{% if groups %}
{% for g in groups %}
<tr class="group-row">
<td colspan="9"><strong>{{ g.artikel }}</strong></td>
</tr>
{% for r in g.rows %}
{% set diff = (r.gezaehlt or 0) - (r.soll or 0) %}
{% set fehl = (r.soll or 0) - (r.gezaehlt or 0) %}
<tr>
<td></td>
<td>{{ r.groesse }}</td>
<td>{{ "%.2f"|format(r.preis or 0) }} €</td>
<td>{{ r.soll }}</td>
<td>{{ r.gezaehlt }}</td>
<td class="{{ 'pos' if diff > 0 else 'neg' if diff < 0 else '' }}">{{ diff }}</td>
<td>{{ fehl if fehl > 0 else "" }}</td>
<td>{{ r.verkaeufe }}</td>
<td class="actions">
<a class="btn icon" href="{{ url_for('bp.edit_item', item_id=r.id) }}" title="Bearbeiten" aria-label="Bearbeiten"><span></span></a>
<form method="post" action="{{ url_for('bp.verkauf', item_id=r.id) }}" onsubmit="return confirm('Wirklich 1 Stück als verkauft buchen?');">
<button class="btn icon" type="submit" title="Verkauf +1" aria-label="Verkauf +1"><span>🛒</span></button>
</form>
<a class="btn icon" href="{{ url_for('bp.ausbuchen', item_id=r.id) }}" title="Ausbuchen" aria-label="Ausbuchen"><span></span></a>
<form method="post" action="{{ url_for('bp.delete_item', item_id=r.id) }}" onsubmit="return confirm('Wirklich löschen? Dieser Vorgang kann nicht rückgängig gemacht werden.');">
<button class="btn icon danger" type="submit" title="Löschen" aria-label="Löschen"><span>🗑</span></button>
</form>
</td>
</tr>
{% endfor %}
{% endfor %}
{% else %}
<tr>
<td colspan="9" class="empty">Keine Treffer.</td>
</tr>
{% endif %}
</tbody>
</table>
</div>
{% endblock %}

24
wawi/templates/login.html Normal file
View File

@@ -0,0 +1,24 @@
{% extends "base.html" %}
{% block content %}
<div class="card form-card">
<h2>Login</h2>
{% if error %}
<div class="note">Benutzername oder Passwort ist falsch.</div>
{% endif %}
<form method="post">
<div class="form-grid">
<label>
Benutzer
<input type="text" name="user" required />
</label>
<label>
Passwort
<input type="password" name="password" required />
</label>
</div>
<div class="form-actions">
<button class="btn btn-accent" type="submit">Anmelden</button>
</div>
</form>
</div>
{% endblock %}

View File

@@ -0,0 +1,79 @@
{% extends "base.html" %}
{% block content %}
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="note">
{% for m in messages %}
<div>{{ m }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<div class="card">
<table>
<thead>
<tr>
<th>Datum</th>
<th>Name</th>
<th>Handy</th>
<th>Mannschaft</th>
<th>Artikel</th>
<th>Größe</th>
<th>Menge</th>
<th>Notiz</th>
<th>Status</th>
<th class="actions">Aktion</th>
</tr>
</thead>
<tbody>
{% for o in rows %}
<tr>
<td>{{ o.created_at }}</td>
<td>{{ o.name }}</td>
<td>{{ o.handy }}</td>
<td>{{ o.mannschaft }}</td>
<td>{{ o.artikel }}</td>
<td>{{ o.groesse }}</td>
<td>{{ o.menge }}</td>
<td>{{ o.notiz or "" }}</td>
<td>
{% if o.canceled %}Storniert
{% elif o.done %}Erledigt
{% else %}Offen
{% endif %}
</td>
<td class="actions">
{% if not o.done and not o.canceled %}
<form method="post" action="{{ url_for('bp.complete_order', order_id=o.id) }}" onsubmit="return confirm('Bestellung als erledigt markieren?');">
<button class="btn small" type="submit">Erledigt</button>
</form>
<form method="post" action="{{ url_for('bp.cancel_order', order_id=o.id) }}" onsubmit="return confirm('Bestellung wirklich stornieren?');">
<button class="btn small danger" type="submit">Stornieren</button>
</form>
{% else %}
{% endif %}
</td>
</tr>
<tr class="order-history">
<td colspan="10">
<details class="inline-details">
<summary>Historie</summary>
<div class="history-grid">
<div><strong>Abgeschlossen von:</strong> {{ o.completed_by or "" }}</div>
<div><strong>Abgeschlossen am:</strong> {{ o.completed_at or "" }}</div>
<div><strong>Storniert von:</strong> {{ o.canceled_by or "" }}</div>
<div><strong>Storniert am:</strong> {{ o.canceled_at or "" }}</div>
</div>
</details>
</td>
</tr>
{% else %}
<tr>
<td colspan="10" class="empty">Keine Bestellungen.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

65
wawi/templates/users.html Normal file
View File

@@ -0,0 +1,65 @@
{% extends "base.html" %}
{% block content %}
<div class="card form-card">
<h2>Benutzer verwalten</h2>
{% if error %}
<div class="note">{{ error }}</div>
{% endif %}
{% with messages = get_flashed_messages() %}
{% if messages %}
<div class="note">
{% for m in messages %}
<div>{{ m }}</div>
{% endfor %}
</div>
{% endif %}
{% endwith %}
<form method="post">
<div class="form-grid">
<label>
Benutzername
<input type="text" name="username" required />
</label>
<label>
Passwort
<input type="password" name="password" required />
</label>
</div>
<div class="form-actions">
<button class="btn btn-accent" type="submit">Anlegen</button>
</div>
</form>
</div>
<div class="card" style="margin-top: 14px;">
<table>
<thead>
<tr>
<th>Benutzer</th>
<th>Erstellt</th>
<th class="actions">Aktion</th>
</tr>
</thead>
<tbody>
{% for u in rows %}
<tr>
<td>{{ u.username }}</td>
<td>{{ u.created_at }}</td>
<td class="actions">
<form method="post" action="{{ url_for('bp.reset_user_password', user_id=u.id) }}" onsubmit="return confirm('Passwort für diesen Benutzer wirklich zurücksetzen?');">
<button class="btn small" type="submit">Passwort neu</button>
</form>
<form method="post" action="{{ url_for('bp.delete_user', user_id=u.id) }}" onsubmit="return confirm('Benutzer wirklich löschen?');">
<button class="btn small danger" type="submit">Löschen</button>
</form>
</td>
</tr>
{% else %}
<tr>
<td colspan="3" class="empty">Keine Benutzer.</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}