fix(caldav): move app-password section to Settings → Profile where users look

The app-password UI was in the user-menu profile modal, but users manage CalDAV
in Settings, so it went unnoticed. Move the section into the Settings → Profile
panel (next to account/privacy) and drive it from openSettingsModal via a
top-level initAppPasswords().

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-07-01 14:17:25 +02:00
parent f662163185
commit 9306d638ad
3 changed files with 32 additions and 29 deletions

View File

@@ -716,6 +716,23 @@
</div>
<button class="btn btn-primary btn-sm" id="cfg-profile-save" data-i18n="save">Speichern</button>
<h4 class="panel-title" style="margin-top:24px" data-i18n="app_pw_title">App-Passwörter (CalDAV)</h4>
<p class="panel-desc" data-i18n="app_pw_desc">Eigene Passwörter für CalDAV-Clients. Bei aktivem 2FA nötig, da Apps keinen 2FA-Code eingeben können. Jederzeit widerrufbar.</p>
<div class="form-group app-pw-create">
<input type="text" id="app-pw-label" data-i18n-placeholder="app_pw_label_ph" placeholder="Name (z.B. iPhone)" maxlength="100" />
<button class="btn btn-primary btn-sm" id="app-pw-create-btn" data-i18n="app_pw_create">Erstellen</button>
</div>
<div id="app-pw-new" class="app-pw-new hidden">
<label data-i18n="app_pw_new_label">Neues App-Passwort (nur jetzt sichtbar):</label>
<div class="totp-secret-row">
<code id="app-pw-new-value"></code>
<button class="btn btn-ghost btn-sm" id="app-pw-copy" title="Kopieren">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
</button>
</div>
</div>
<div id="app-pw-list"></div>
<h4 class="panel-title" style="margin-top:24px" data-i18n="settings_privacy">Privatsphäre</h4>
<p class="panel-desc" data-i18n="settings_private_visibility_desc">Wie private Termine für andere Gruppenmitglieder erscheinen</p>
<div class="form-group">
@@ -995,26 +1012,6 @@
</div>
</div>
<!-- App passwords (CalDAV) -->
<div class="settings-section">
<h4 data-i18n="app_pw_title">App-Passwörter (CalDAV)</h4>
<p class="text-muted" data-i18n="app_pw_desc">Eigene Passwörter für CalDAV-Clients. Bei aktivem 2FA nötig, da Apps keinen 2FA-Code eingeben können. Jederzeit widerrufbar.</p>
<div class="form-group app-pw-create">
<input type="text" id="app-pw-label" data-i18n-placeholder="app_pw_label_ph" placeholder="Name (z.B. iPhone)" maxlength="100" />
<button class="btn btn-primary btn-sm" id="app-pw-create-btn" data-i18n="app_pw_create">Erstellen</button>
</div>
<div id="app-pw-new" class="app-pw-new hidden">
<label data-i18n="app_pw_new_label">Neues App-Passwort (nur jetzt sichtbar):</label>
<div class="totp-secret-row">
<code id="app-pw-new-value"></code>
<button class="btn btn-ghost btn-sm" id="app-pw-copy" title="Kopieren">
<svg viewBox="0 0 24 24" fill="currentColor" width="16" height="16"><path d="M16 1H4c-1.1 0-2 .9-2 2v14h2V3h12V1zm3 4H8c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h11c1.1 0 2-.9 2-2V7c0-1.1-.9-2-2-2zm0 16H8V7h11v14z"/></svg>
</button>
</div>
</div>
<div id="app-pw-list"></div>
</div>
<!-- Calendars -->
<div class="settings-section">
<h4>Meine Kalender</h4>

View File

@@ -3110,6 +3110,7 @@ function openSettingsModal() {
api.get('/profile/').then(p => {
document.getElementById('cfg-email').value = p.email || '';
}).catch(() => {});
initAppPasswords();
// Set active contrast/hour-height/duration buttons
[
@@ -4144,12 +4145,17 @@ function bindProfileModal() {
document.getElementById('2fa-disable-pw').value = '';
} catch (e) { showToast(e.message, true); }
};
}
// ── App passwords (CalDAV) ──
// ── App passwords (CalDAV) — lives in Settings → Profile ──
function initAppPasswords() {
const appPwList = document.getElementById('app-pw-list');
document.getElementById('app-pw-new').classList.add('hidden');
if (!appPwList) return;
const newBox = document.getElementById('app-pw-new');
newBox.classList.add('hidden');
document.getElementById('app-pw-new-value').textContent = '';
async function loadAppPasswords() {
const load = async () => {
try {
const rows = await api.get('/profile/app-passwords');
appPwList.innerHTML = rows.length
@@ -4160,17 +4166,17 @@ function bindProfileModal() {
</div>`).join('')
: `<p class="text-muted">${t('app_pw_none')}</p>`;
} catch (e) { /* ignore */ }
}
loadAppPasswords();
};
load();
document.getElementById('app-pw-create-btn').onclick = async () => {
const label = document.getElementById('app-pw-label').value.trim() || 'CalDAV';
try {
const res = await api.post('/profile/app-passwords', { label });
document.getElementById('app-pw-new-value').textContent = res.password;
document.getElementById('app-pw-new').classList.remove('hidden');
newBox.classList.remove('hidden');
document.getElementById('app-pw-label').value = '';
loadAppPasswords();
load();
} catch (e) { showToast(e.message, true); }
};
@@ -4185,7 +4191,7 @@ function bindProfileModal() {
if (!confirm(t('app_pw_revoke_confirm'))) return;
try {
await api.delete(`/profile/app-passwords/${btn.dataset.id}`);
loadAppPasswords();
load();
} catch (err) { showToast(err.message, true); }
};
}

View File

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