feat: Anzeigename im Web (Profil bearbeiten + Anzeige)

- Profil: Anzeigename + Login-Name editierbar (vorher Benutzername read-only).
  Login-Namenwechsel speichert den frisch zurueckgegebenen Token.
- Menue/Dropdown und "Erstellt von"/Picker zeigen den Anzeigenamen.
- localStorage-User um display_name ergaenzt. Version v32.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-05-31 17:49:19 +02:00
parent f9923b022e
commit 2033cf99d4
5 changed files with 41 additions and 10 deletions

View File

@@ -858,16 +858,21 @@
<!-- Account Info -->
<div class="settings-section">
<h4>Konto</h4>
<h4 data-i18n="profile_account">Konto</h4>
<div class="form-group">
<label>Benutzername</label>
<input type="text" id="profile-username" disabled class="input-disabled" />
<label data-i18n="profile_display_name">Anzeigename</label>
<input type="text" id="profile-display-name-input" data-i18n-placeholder="profile_display_name_ph" placeholder="Anzeigename" />
</div>
<div class="form-group">
<label data-i18n="profile_login_name">Login-Name</label>
<input type="text" id="profile-username" spellcheck="false" autocapitalize="none" />
<p class="panel-desc" data-i18n="profile_login_name_desc">Klein geschrieben, fürs Anmelden. Groß-/Kleinschreibung egal.</p>
</div>
<div class="form-group">
<label>E-Mail</label>
<input type="email" id="profile-email" placeholder="Keine E-Mail hinterlegt" />
</div>
<button class="btn btn-primary btn-sm" id="profile-save-info">Speichern</button>
<button class="btn btn-primary btn-sm" id="profile-save-info" data-i18n="save">Speichern</button>
</div>
<!-- Password -->

View File

@@ -60,7 +60,7 @@ async function launchApp() {
// User dropdown menu
const dropdown = document.getElementById('user-dropdown');
document.getElementById('dropdown-username').textContent = user.username || 'Benutzer';
document.getElementById('dropdown-username').textContent = user.display_name || user.username || 'Benutzer';
avatar.addEventListener('click', e => {
e.stopPropagation();

View File

@@ -2951,9 +2951,10 @@ function bindSettingsModal() {
export function openProfileModal() {
const user = JSON.parse(localStorage.getItem('user') || '{}');
// Username & email
// Names & email
document.getElementById('profile-username').value = user.username || '';
document.getElementById('profile-display-name').textContent = user.username || '';
document.getElementById('profile-display-name-input').value = user.display_name || user.username || '';
document.getElementById('profile-display-name').textContent = user.display_name || user.username || '';
// Load fresh profile data
api.get('/profile/').then(profile => {
@@ -3021,11 +3022,26 @@ function renderProfileCalendars() {
}
function bindProfileModal() {
// Save profile info (email)
// Save profile info (display name, login name, email)
document.getElementById('profile-save-info').onclick = async () => {
const email = document.getElementById('profile-email').value.trim();
const displayName = document.getElementById('profile-display-name-input').value.trim();
const loginName = document.getElementById('profile-username').value.trim();
const user = JSON.parse(localStorage.getItem('user') || '{}');
const body = { email: email || null };
if (displayName) body.display_name = displayName;
if (loginName && loginName.toLowerCase() !== (user.username || '')) body.username = loginName;
try {
await api.put('/profile/', { email: email || null });
const res = await api.put('/profile/', body);
// A login-name change returns a fresh token (the old one is now invalid).
if (res && res.access_token) localStorage.setItem('token', res.access_token);
// Keep the cached user (menu, "created by", etc.) in sync.
const updated = { ...user };
if (displayName) updated.display_name = displayName;
if (body.username) updated.username = body.username.toLowerCase();
localStorage.setItem('user', JSON.stringify(updated));
const dd = document.getElementById('dropdown-username');
if (dd) dd.textContent = updated.display_name || updated.username || 'Benutzer';
showToast(t('profile_saved'));
} catch (e) { showToast(e.message, true); }
};

View File

@@ -128,6 +128,11 @@ const translations = {
settings_group_visible_desc: 'Wähle, welcher deiner Kalender für deine Gruppenmitglieder sichtbar ist',
group_visible_none: 'Keiner',
drag_reorder: 'Zum Sortieren ziehen',
profile_account: 'Konto',
profile_display_name: 'Anzeigename',
profile_display_name_ph: 'Anzeigename',
profile_login_name: 'Login-Name',
profile_login_name_desc: 'Klein geschrieben, fürs Anmelden. Groß-/Kleinschreibung egal.',
settings_hour_height: 'Stundenhöhe (Wochen- & Tagesansicht)',
settings_hour_height_desc: 'Wie viel Platz eine Stunde in der Zeitrasteransicht einnimmt',
hour_compact: 'Kompakt', hour_normal: 'Normal',
@@ -387,6 +392,11 @@ const translations = {
settings_group_visible_desc: 'Choose which of your calendars your group members can see',
group_visible_none: 'None',
drag_reorder: 'Drag to reorder',
profile_account: 'Account',
profile_display_name: 'Display name',
profile_display_name_ph: 'Display name',
profile_login_name: 'Login name',
profile_login_name_desc: 'Lowercase, used to sign in. Case-insensitive.',
settings_hour_height: 'Hour height (week & day view)',
settings_hour_height_desc: 'How much space one hour takes in the time grid',
hour_compact: 'Compact', hour_normal: 'Normal',

View File

@@ -1,2 +1,2 @@
// Increment APP_VERSION with every code change
export const APP_VERSION = 'v31';
export const APP_VERSION = 'v32';