diff --git a/app/flask-postgres/app/app.py b/app/flask-postgres/app/app.py index d799707..0d3fac0 100644 --- a/app/flask-postgres/app/app.py +++ b/app/flask-postgres/app/app.py @@ -1198,11 +1198,19 @@ def dokumente(): cur.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( "dokumente.html", page_title="Dokumente", active_page="dokumente", items=items, + progress_percent=progress_percent, + completed_items=completed_items, + total_items=total_items, **get_current_user() ) @@ -1352,4 +1360,131 @@ def dokument_file(item_id): def format_datetime(value): if not value: return "-" - return value.strftime("%d.%m.%Y %H:%M") \ No newline at end of file + 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() + ) \ No newline at end of file diff --git a/app/flask-postgres/app/templates/admin_checklist.html b/app/flask-postgres/app/templates/admin_checklist.html new file mode 100644 index 0000000..e477b2e --- /dev/null +++ b/app/flask-postgres/app/templates/admin_checklist.html @@ -0,0 +1,124 @@ +{% extends "base.html" %} + +{% block content %} + + + +
+
+
+

Neues Checklist-Item anlegen

+
+ + {% if form_error %} +
{{ form_error }}
+ {% endif %} + +
+ + +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ +
+
+
+
+ +
+
+
+

Bestehende Checklist-Items

+
+ +
+ + + + + + + + + + + + + {% for item in items %} + + + + + + + + + + + + + + + + + + {% endfor %} + +
IDLevelTitelKurzbeschreibungDefault FilenameAktionen
{{ item.id }} + + + + + + + + +
+ + +
+
+
+
+
+ +{% endblock %} \ No newline at end of file diff --git a/app/flask-postgres/app/templates/base.html b/app/flask-postgres/app/templates/base.html index 2747553..43eb4f7 100644 --- a/app/flask-postgres/app/templates/base.html +++ b/app/flask-postgres/app/templates/base.html @@ -35,7 +35,8 @@
Profil {% if is_admin %} - Admin + Mandanten + Checklist {% endif %} {% if is_user_admin %} Useradministration diff --git a/app/flask-postgres/app/templates/dokumente.html b/app/flask-postgres/app/templates/dokumente.html index 03bec61..0141110 100644 --- a/app/flask-postgres/app/templates/dokumente.html +++ b/app/flask-postgres/app/templates/dokumente.html @@ -7,6 +7,21 @@

Checkliste der hochzuladenden Zertifizierungsdokumente.

+
+
+ Fortschritt + {{ completed_items }} / {{ total_items }} Dokumente +
+ +
+
+
+
@@ -31,7 +46,7 @@ {% if item.stored_filename %}
- + {{ item.original_filename or item.stored_filename }} diff --git a/app/flask-postgres/styles/site.css b/app/flask-postgres/styles/site.css index e1cdd26..f8f290b 100644 --- a/app/flask-postgres/styles/site.css +++ b/app/flask-postgres/styles/site.css @@ -760,4 +760,60 @@ button { font-size: 12px; color: #6b7280; 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; } \ No newline at end of file