snapshot: preserve account security and profile UI cleanup

This commit is contained in:
Md Bayazid Bostame
2026-03-27 03:04:02 +01:00
parent c679488437
commit f2c9b3b65d
12 changed files with 699 additions and 370 deletions

View File

@@ -20,10 +20,6 @@
<h1>{% trans "Profil" %}</h1>
<p>{% trans "Ihre aktuelle Workdock-Kontoübersicht und wichtige Sicherheitsaktionen." %}</p>
</div>
<div class="account-hero-badges">
<span class="account-chip">{{ role_label }}</span>
<span class="account-chip account-chip-muted">{{ account_user.username }}</span>
</div>
</section>
<div class="account-layout">
@@ -58,14 +54,6 @@
<p>{{ account_user.email|default:account_user.username }}</p>
</div>
<div class="account-profile-meta">
<div>
<span>{% trans "Rolle" %}</span>
<strong>{{ role_label }}</strong>
</div>
<div>
<span>{% trans "Benutzername" %}</span>
<strong>{{ account_user.username }}</strong>
</div>
<div>
<span>{% trans "Position" %}</span>
<strong>{{ account_user_profile.job_title|default:"-" }}</strong>
@@ -171,29 +159,36 @@
<section class="account-panel">
<div class="account-panel-head">
<h2>{% trans "Sicherheit & Aktionen" %}</h2>
<p>{% trans "Direkte Aktionen für Ihr Workdock-Konto." %}</p>
</div>
<div class="account-action-grid">
<a class="account-action-card" href="{% url 'password_change' %}">
<strong>{% trans "Passwort ändern" %}</strong>
<span>{% trans "Aktualisieren Sie Ihr Passwort direkt im Konto." %}</span>
</a>
<div class="account-action-card{% if account_user_profile.totp_enabled %}{% else %} account-action-card-muted{% endif %}">
<strong>{% trans "TOTP" %}</strong>
<span>
<div class="account-security-overview">
<div class="account-security-item">
<span>{% trans "TOTP" %}</span>
<strong>{% if account_user_profile.totp_enabled %}{% trans "Aktiv" %}{% else %}{% trans "Aus" %}{% endif %}</strong>
<p>
{% if account_user_profile.totp_enabled %}
{% trans "Zweiter Faktor ist aktiv und wird bei der Anmeldung geprüft." %}
{% trans "Anmeldung wird zusätzlich mit einem zweiten Faktor geschützt." %}
{% else %}
{% trans "Standardmäßig deaktiviert. Kann hier jederzeit aktiviert werden." %}
{% trans "Optional. Kann bei Bedarf direkt unten aktiviert werden." %}
{% endif %}
</span>
</p>
</div>
<div class="account-action-card account-action-card-muted">
<strong>{% trans "Sitzung" %}</strong>
<span>{% trans "Sie können sich jederzeit sicher vom aktuellen Gerät abmelden." %}</span>
<div class="account-security-item">
<span>{% trans "Recovery-Codes" %}</span>
<strong>
{% if account_user_profile.totp_enabled %}
{{ account_user_profile.totp_recovery_codes|length }}
{% else %}
-
{% endif %}
</strong>
<p>
{% if account_user_profile.totp_enabled %}
{% trans "Einmal-Codes für Notfälle oder verlorene Authenticator-Geräte." %}
{% else %}
{% trans "Werden automatisch erzeugt, sobald TOTP aktiviert wird." %}
{% endif %}
</p>
</div>
</div>
@@ -241,18 +236,52 @@
<button class="btn btn-secondary" type="submit">{% trans "TOTP deaktivieren" %}</button>
</div>
</form>
{% else %}
<div class="account-detail-grid">
<div class="account-detail">
<span>{% trans "Manueller Schlüssel" %}</span>
<strong class="account-secret">{{ totp_pending_secret }}</strong>
<form class="account-totp-form" method="post">
{% csrf_token %}
<input type="hidden" name="account_form" value="totp_regenerate_codes" />
<div class="account-form-grid">
{% for field in totp_regenerate_form %}
<div class="account-form-field{% if field.errors %} has-error{% endif %}">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.errors %}
<div class="account-form-error">{{ field.errors|join:", " }}</div>
{% endif %}
</div>
{% endfor %}
</div>
<div class="account-detail">
<span>{% trans "Setup-Link" %}</span>
<strong class="account-secret">{{ totp_otpauth_uri }}</strong>
<div class="account-inline-actions">
<button class="btn btn-primary" type="submit">{% trans "Recovery-Codes neu erzeugen" %}</button>
</div>
</form>
{% else %}
<div class="account-qr-card">
{% if totp_qr_svg %}
{{ totp_qr_svg|safe }}
{% endif %}
<div class="account-secret-panel">
<div class="account-secret-head">
<div>
<span>{% trans "Manueller Schlüssel" %}</span>
<strong>{% trans "Nur bei Bedarf anzeigen" %}</strong>
</div>
<button
class="btn btn-secondary account-secret-toggle"
type="button"
data-secret-toggle
aria-expanded="false"
aria-controls="totp-manual-secret"
title="{% trans 'Manuellen Schlüssel anzeigen oder ausblenden' %}"
>
<span data-secret-toggle-icon></span>
</button>
</div>
<div class="account-secret-body is-hidden" id="totp-manual-secret" data-secret-body>
<strong class="account-secret">{{ totp_pending_secret }}</strong>
</div>
</div>
</div>
<p class="mini">{% trans "Wenn Ihre App keinen QR-Code scannen kann, tragen Sie den Schlüssel oder den otpauth-Link manuell ein." %}</p>
<p class="mini">{% trans "Scannen Sie den QR-Code mit Ihrer Authenticator-App. Den manuellen Schlüssel können Sie bei Bedarf einblenden." %}</p>
<form class="account-totp-form" method="post">
{% csrf_token %}
<input type="hidden" name="account_form" value="totp_enable" />
@@ -272,14 +301,22 @@
</div>
</form>
{% endif %}
</div>
<div class="account-actions">
<a class="btn btn-primary" href="{% url 'password_change' %}">{% trans "Passwort ändern" %}</a>
<form method="post" action="{% url 'logout' %}">
{% csrf_token %}
<button class="btn btn-secondary" type="submit">{% trans "Abmelden" %}</button>
</form>
{% if totp_recovery_codes %}
<div class="account-recovery-card">
<div class="account-panel-head">
<div>
<h3>{% trans "Recovery-Codes" %}</h3>
<p>{% trans "Diese Codes werden nur jetzt im Klartext angezeigt. Jeden Code können Sie genau einmal verwenden." %}</p>
</div>
</div>
<div class="account-recovery-grid">
{% for code in totp_recovery_codes %}
<div class="account-recovery-code">{{ code }}</div>
{% endfor %}
</div>
</div>
{% endif %}
</div>
</section>
</div>
@@ -295,6 +332,9 @@
var cancel = document.querySelector('[data-account-edit-cancel="details"]');
var view = document.querySelector('[data-account-edit-view="details"]');
var form = document.querySelector('[data-account-edit-form="details"]');
var secretToggle = document.querySelector('[data-secret-toggle]');
var secretBody = document.querySelector('[data-secret-body]');
var secretIcon = document.querySelector('[data-secret-toggle-icon]');
if (!toggle || !cancel || !view || !form) return;
function setMode(editing) {
@@ -310,6 +350,17 @@
cancel.addEventListener('click', function () {
setMode(false);
});
if (secretToggle && secretBody) {
secretToggle.addEventListener('click', function () {
var isOpen = secretToggle.getAttribute('aria-expanded') === 'true';
secretToggle.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
secretBody.classList.toggle('is-hidden', isOpen);
if (secretIcon) {
secretIcon.textContent = isOpen ? '◐' : '◑';
}
});
}
}());
</script>
{% endblock %}

View File

@@ -36,6 +36,10 @@
{{ form.otp_code.label_tag }}{{ form.otp_code }}
<div class="mini">{% trans "Nur erforderlich, wenn TOTP für Ihr Konto aktiviert ist." %}</div>
</div>
<div class="field{% if form.recovery_code.errors %} has-error{% endif %}">
{{ form.recovery_code.label_tag }}{{ form.recovery_code }}
<div class="mini">{% trans "Alternativ können Sie einen einmaligen Recovery-Code verwenden." %}</div>
</div>
<button class="btn btn-primary" type="submit">{% trans "Anmelden" %}</button>
</form>
</div>