diff --git a/app/flask-postgres/app/app.py b/app/flask-postgres/app/app.py index 8191fff..dc4074b 100644 --- a/app/flask-postgres/app/app.py +++ b/app/flask-postgres/app/app.py @@ -490,10 +490,9 @@ def profil(): return render_template( "profil.html", - page_title="Profil", - active_page="profil", profile=profile, - gruppen=gruppen, + groups=gruppen, + mandant_level_label=format_level(profile["mandant_level"]), **get_current_user() ) @@ -1930,10 +1929,12 @@ def useradmin_user_upload(): if not uploaded_file or uploaded_file.filename == "": form_error = "Bitte eine CSV-Datei auswählen." else: + file_bytes = uploaded_file.read() + try: - content = uploaded_file.read().decode("utf-8-sig") - except Exception: - form_error = "Die Datei konnte nicht gelesen werden. Bitte UTF-8 CSV verwenden." + content = file_bytes.decode("utf-8-sig") + except UnicodeDecodeError: + form_error = "Die Datei ist nicht im UTF-8 Format. Bitte als UTF-8 CSV speichern." if not form_error: reader = csv.reader(io.StringIO(content), delimiter=";") @@ -1942,13 +1943,22 @@ def useradmin_user_upload(): if not rows: form_error = "Die Datei ist leer." + # Nur echte Datenzeilen zählen (keine leeren) + data_rows = [ + row for row in rows + if row and any(str(col).strip() for col in row) + ] + + if len(data_rows) > 50: + form_error = f"Maximal 50 Benutzer erlaubt. Datei enthält {len(data_rows)} Zeilen." + parsed_users = [] if not form_error: email_pattern = r"^[^@\s]+@[^@\s]+\.[^@\s]+$" seen_emails_in_file = set() - for line_no, row in enumerate(rows, start=1): + for line_no, row in enumerate(data_rows, start=1): if not row or all(not str(col).strip() for col in row): continue @@ -2059,4 +2069,61 @@ def useradmin_user_delete(user_id): cur.close() conn.close() - return redirect(url_for("useradmin_mandant")) \ No newline at end of file + return redirect(url_for("useradmin_mandant")) + +@app.route("/pwdchange", methods=["GET", "POST"]) +@login_required +def pwdchange(): + user_id = session.get("user_id") + + form_error = None + success_message = None + + if request.method == "POST": + old_password = request.form.get("old_password", "") + new_password = request.form.get("new_password", "") + new_password2 = request.form.get("new_password2", "") + + if not old_password or not new_password or not new_password2: + form_error = "Alle Felder sind erforderlich." + elif new_password != new_password2: + form_error = "Neue Passwörter stimmen nicht überein." + elif len(new_password) < 8: + form_error = "Das Passwort muss mindestens 8 Zeichen lang sein." + else: + conn = get_connection() + cur = conn.cursor() + + cur.execute(""" + SELECT password_hash + FROM app_user + WHERE id = %s + """, (user_id,)) + row = cur.fetchone() + + if not row: + form_error = "User nicht gefunden." + elif not check_password_hash(row[0], old_password): + form_error = "Altes Passwort ist falsch." + else: + new_hash = generate_password_hash(new_password) + + cur.execute(""" + UPDATE app_user + SET password_hash = %s + WHERE id = %s + """, (new_hash, user_id)) + + conn.commit() + success_message = "Passwort erfolgreich geändert." + + cur.close() + conn.close() + + return render_template( + "pwdchange.html", + page_title="Passwort ändern", + form_error=form_error, + success_message=success_message, + **get_current_user() + ) \ No newline at end of file diff --git a/app/flask-postgres/app/templates/profil.html b/app/flask-postgres/app/templates/profil.html index 471ca5b..8a5a4a6 100644 --- a/app/flask-postgres/app/templates/profil.html +++ b/app/flask-postgres/app/templates/profil.html @@ -2,35 +2,47 @@ {% block content %} -

Profil

- - - - - - - - - - - - - - -
ID{{ profile.id }}
Name{{ profile.name }}
E-Mail{{ profile.email }}
Mandant{{ profile.mandant_name }} ({{ profile.mandant_kuerzel }})
Mandant E-Mail{{ profile.mandant_email or '-' }}
Mandant Level{{ profile.mandant_level_label }}
Gruppen im Mandanten - {% if gruppen %} -
- {% for gruppe in gruppen %} - {{ gruppe }} - {% endfor %} -
- {% else %} - - - {% endif %} -
- -
- Passwort ändern + +
+
+ +
+ +
ID
+
{{ profile.id }}
+ +
Name
+
{{ profile.name }}
+ +
E-Mail
+
{{ profile.email }}
+ +
Mandant
+
{{ profile.mandant_name }}
+ +
Mandant E-Mail
+
{{ profile.mandant_email }}
+ +
Mandant Level
+
{{ mandant_level_label }}
+ +
Gruppen
+
+ {% for g in groups %} + {{ g }} + {% endfor %} +
+ +
+ + + +
+
+ {% endblock %} \ No newline at end of file diff --git a/app/flask-postgres/styles/site.css b/app/flask-postgres/styles/site.css index 0b87bde..baa1801 100644 --- a/app/flask-postgres/styles/site.css +++ b/app/flask-postgres/styles/site.css @@ -1214,4 +1214,59 @@ button { .admin-subtitle { margin: 6px 0 0; color: #526172; +} + +/* =============================== + Profil Layout + =============================== */ + +.profile-grid { + display: grid; + grid-template-columns: 220px 1fr; + row-gap: 14px; + column-gap: 20px; + align-items: center; + margin-top: 20px; +} + +/* Labels linksbündig */ +.profile-label { + text-align: left; + font-weight: 700; + color: #1e3a5f; +} + +/* Werte */ +.profile-value { + color: #1f2937; +} + +/* Gruppen-Badges */ +.badge { + display: inline-block; + padding: 6px 14px; + margin-right: 8px; + border-radius: 999px; + background: #e7edf5; + color: #1e3a5f; + font-weight: 600; + font-size: 14px; +} + +@media (max-width: 700px) { + .profile-grid { + grid-template-columns: 1fr; + } + + .profile-label { + margin-top: 10px; + } +} + +.profile-label { + color: #64748b; +} + +.profile-value { + font-weight: 500; } \ No newline at end of file diff --git a/app/flask-postgres/tests/userliste2.csv b/app/flask-postgres/tests/userliste2.csv new file mode 100644 index 0000000..ac83815 --- /dev/null +++ b/app/flask-postgres/tests/userliste2.csv @@ -0,0 +1,3 @@ +Max Mustermann;mm@kolb.cc;1;Geheimnis +Susi Wunderschön;sw@kolb.cc;1;Geheimnis +Petra Sauberfrau;ps@kolb.cc;1;Geheimnis \ No newline at end of file