Compare commits

..

2 Commits

Author SHA1 Message Date
9e7d24aeb8 Tippfehler korrigiert 2026-04-03 19:46:29 +02:00
355da43d6a progress bar für Checklist Items 2026-04-03 19:43:11 +02:00
5 changed files with 334 additions and 3 deletions

View File

@ -1198,11 +1198,19 @@ def dokumente():
cur.close() cur.close()
conn.close() conn.close()
total_items = len(items)
completed_items = sum(1 for i in items if i.get("stored_filename"))
progress_percent = int((completed_items / total_items) * 100) if total_items > 0 else 0
return render_template( return render_template(
"dokumente.html", "dokumente.html",
page_title="Dokumente", page_title="Dokumente",
active_page="dokumente", active_page="dokumente",
items=items, items=items,
progress_percent=progress_percent,
completed_items=completed_items,
total_items=total_items,
**get_current_user() **get_current_user()
) )
@ -1352,4 +1360,131 @@ def dokument_file(item_id):
def format_datetime(value): def format_datetime(value):
if not value: if not value:
return "-" return "-"
return value.strftime("%d.%m.%Y %H:%M") return value.strftime("%d.%m.%Y %H:%M")
@app.route("/admin/checklist", methods=["GET", "POST"])
@admin_required
def admin_checklist():
conn = get_connection()
cur = conn.cursor()
form_error = None
form_values = None
if request.method == "POST":
action = request.form.get("action")
if action == "create":
level = request.form.get("level", "").strip()
title = request.form.get("title", "").strip()
short_description = request.form.get("short_description", "").strip()
default_filename = request.form.get("default_filename", "").strip()
form_values = {
"level": level,
"title": title,
"short_description": short_description,
"default_filename": default_filename,
}
if not level:
form_error = "Level ist ein Pflichtfeld."
elif level not in ("1", "2", "3"):
form_error = "Level muss 1, 2 oder 3 sein."
elif not title:
form_error = "Titel ist ein Pflichtfeld."
else:
cur.execute("""
INSERT INTO certification_checklist (
level,
title,
short_description,
default_filename
)
VALUES (%s, %s, %s, %s)
""", (
int(level),
title,
short_description or None,
default_filename or None
))
conn.commit()
elif action == "update":
item_id = request.form.get("id")
level = request.form.get("level", "").strip()
title = request.form.get("title", "").strip()
short_description = request.form.get("short_description", "").strip()
default_filename = request.form.get("default_filename", "").strip()
if not level:
form_error = "Level ist ein Pflichtfeld."
elif level not in ("1", "2", "3"):
form_error = "Level muss 1, 2 oder 3 sein."
elif not title:
form_error = "Titel ist ein Pflichtfeld."
else:
cur.execute("""
UPDATE certification_checklist
SET level = %s,
title = %s,
short_description = %s,
default_filename = %s
WHERE id = %s
""", (
int(level),
title,
short_description or None,
default_filename or None,
int(item_id)
))
conn.commit()
elif action == "delete":
item_id = request.form.get("id")
cur.execute("""
DELETE FROM certification_checklist
WHERE id = %s
""", (int(item_id),))
conn.commit()
if form_error:
cur.execute("""
SELECT id, level, title, short_description, default_filename
FROM certification_checklist
ORDER BY level, id
""")
items = fetchall_dict(cur)
cur.close()
conn.close()
return render_template(
"admin_checklist.html",
page_title="Checklist-Verwaltung",
active_page="admin_checklist",
items=items,
form_error=form_error,
form_values=form_values,
**get_current_user()
)
cur.execute("""
SELECT id, level, title, short_description, default_filename
FROM certification_checklist
ORDER BY level, id
""")
items = fetchall_dict(cur)
cur.close()
conn.close()
return render_template(
"admin_checklist.html",
page_title="Checklist-Verwaltung",
active_page="admin_checklist",
items=items,
form_error=form_error,
form_values=form_values,
**get_current_user()
)

View File

@ -0,0 +1,124 @@
{% extends "base.html" %}
{% block content %}
<div class="page-header">
<h1>Checklist-Verwaltung</h1>
<p class="intro-text">Globale Pflege der Zertifizierungs-Checklistenpunkte.</p>
</div>
<section class="admin-section">
<div class="admin-panel">
<div class="admin-panel-header">
<h2>Neues Checklist-Item anlegen</h2>
</div>
{% if form_error %}
<div class="error-box">{{ form_error }}</div>
{% endif %}
<form method="post" class="admin-grid-form">
<input type="hidden" name="action" value="create">
<div class="form-row">
<label for="level">Level</label>
<select id="level" name="level" required>
<option value="">Bitte wählen</option>
<option value="1" {% if form_values and form_values.level == '1' %}selected{% endif %}>1 - Gold</option>
<option value="2" {% if form_values and form_values.level == '2' %}selected{% endif %}>2 - Silber</option>
<option value="3" {% if form_values and form_values.level == '3' %}selected{% endif %}>3 - Bronze</option>
</select>
</div>
<div class="form-row">
<label for="title">Titel</label>
<input type="text" id="title" name="title"
value="{{ form_values.title if form_values else '' }}" required>
</div>
<div class="form-row form-row-full">
<label for="short_description">Kurzbeschreibung</label>
<input type="text" id="short_description" name="short_description"
value="{{ form_values.short_description if form_values else '' }}">
</div>
<div class="form-row form-row-full">
<label for="default_filename">Default Filename</label>
<input type="text" id="default_filename" name="default_filename"
value="{{ form_values.default_filename if form_values else '' }}">
</div>
<div class="form-row form-row-full">
<button type="submit" class="btn-primary">Checklist-Item anlegen</button>
</div>
</form>
</div>
</section>
<section class="admin-section">
<div class="admin-panel">
<div class="admin-panel-header">
<h2>Bestehende Checklist-Items</h2>
</div>
<div class="table-wrap">
<table class="mandanten-table">
<thead>
<tr>
<th>ID</th>
<th>Level</th>
<th>Titel</th>
<th>Kurzbeschreibung</th>
<th>Default Filename</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{% for item in items %}
<tr>
<form method="post">
<input type="hidden" name="id" value="{{ item.id }}">
<td class="col-id">{{ item.id }}</td>
<td class="col-level">
<select name="level" required>
<option value="1" {% if item.level == 1 %}selected{% endif %}>1 - Gold</option>
<option value="2" {% if item.level == 2 %}selected{% endif %}>2 - Silber</option>
<option value="3" {% if item.level == 3 %}selected{% endif %}>3 - Bronze</option>
</select>
</td>
<td>
<input type="text" name="title" value="{{ item.title }}" required>
</td>
<td>
<input type="text" name="short_description" value="{{ item.short_description or '' }}">
</td>
<td>
<input type="text" name="default_filename" value="{{ item.default_filename or '' }}">
</td>
<td class="col-actions">
<div class="table-actions">
<button type="submit" name="action" value="update" class="btn-primary btn-small">
Speichern
</button>
<button type="submit" name="action" value="delete" class="btn-danger btn-small"
onclick="return confirm('Checklist-Item wirklich löschen?')">
Löschen
</button>
</div>
</td>
</form>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</section>
{% endblock %}

View File

@ -35,7 +35,8 @@
<div class="user-menu-dropdown"> <div class="user-menu-dropdown">
<a href="/profil">Profil</a> <a href="/profil">Profil</a>
{% if is_admin %} {% if is_admin %}
<a href="/admin/mandanten">Admin</a> <a href="/admin/mandanten">Mandanten</a>
<a href="/admin/checklist">Checklist</a>
{% endif %} {% endif %}
{% if is_user_admin %} {% if is_user_admin %}
<a href="/useradmin/mandant">Useradministration</a> <a href="/useradmin/mandant">Useradministration</a>

View File

@ -7,6 +7,21 @@
<p class="intro-text">Checkliste der hochzuladenden Zertifizierungsdokumente.</p> <p class="intro-text">Checkliste der hochzuladenden Zertifizierungsdokumente.</p>
</div> </div>
<div class="progress-box">
<div class="progress-header">
<strong>Fortschritt</strong>
<span>{{ completed_items }} / {{ total_items }} Dokumenten</span>
</div>
<div class="progress-fill
{% if progress_percent < 40 %}progress-red
{% elif progress_percent < 80 %}progress-yellow
{% else %}progress-green
{% endif %}"
style="width: {{ progress_percent }}%;">
</div>
</div>
<section class="admin-section"> <section class="admin-section">
<div class="admin-panel"> <div class="admin-panel">
<div class="table-wrap"> <div class="table-wrap">
@ -31,7 +46,7 @@
<td> <td>
{% if item.stored_filename %} {% if item.stored_filename %}
<div class="file-block"> <div class="file-block">
<a href="/dokumente/file/{{ item.id }}" class="file-name"> <a href="/dokumente/file/{{ item.id }}" class="file-name" target="_new">
{{ item.original_filename or item.stored_filename }} {{ item.original_filename or item.stored_filename }}
</a> </a>

View File

@ -760,4 +760,60 @@ button {
font-size: 12px; font-size: 12px;
color: #6b7280; color: #6b7280;
margin-top: 4px; margin-top: 4px;
}
.admin-grid-form select,
.mandanten-table select {
width: 100%;
min-height: 46px;
padding: 10px 12px;
border: 1px solid #cfd8e3;
border-radius: 10px;
font-size: 16px;
box-sizing: border-box;
background: #fff;
}
/* ===============================
Progress Bar Dokumente
=============================== */
.progress-box {
margin: 20px 0 30px;
}
.progress-header {
display: flex;
justify-content: space-between;
font-size: 14px;
margin-bottom: 6px;
color: #334155;
}
.progress-bar {
width: 100%;
height: 12px;
background: #e5eaf0;
border-radius: 8px;
overflow: hidden;
}
.progress-fill {
height: 100%;
background: linear-gradient(90deg, #4caf50, #2e7d32);
border-radius: 8px;
transition: width 0.3s ease;
}
.progress-red {
background: #d32f2f;
}
.progress-yellow {
background: #f9a825;
}
.progress-green {
background: #2e7d32;
} }