update
This commit is contained in:
@@ -183,6 +183,19 @@ header h1{font-size:1.2rem;font-weight:600;letter-spacing:.3px;white-space:nowra
|
||||
.login-switch{text-align:center;margin-top:14px;font-size:.82rem;color:#6a8a9a}
|
||||
.login-switch a{color:#5B8DB3;cursor:pointer;text-decoration:underline}
|
||||
|
||||
/* Registrierung: Überschrift, Fließtext, Labels, Felder, Button, Link – alles 9pt */
|
||||
.login-box.login-register,
|
||||
.login-box.login-register h2,
|
||||
.login-box.login-register p,
|
||||
.login-box.login-register label,
|
||||
.login-box.login-register input,
|
||||
.login-box.login-register select,
|
||||
.login-box.login-register button,
|
||||
.login-box.login-register .login-error,
|
||||
.login-box.login-register .login-switch,
|
||||
.login-box.login-register .login-switch a{font-size:9pt!important}
|
||||
.login-box.login-register h2{font-weight:600}
|
||||
|
||||
/* === Admin Panel === */
|
||||
.admin-overlay{display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.3);z-index:150;align-items:flex-start;justify-content:center;padding-top:40px;overflow-y:auto}
|
||||
.admin-overlay.open{display:flex}
|
||||
@@ -386,7 +399,7 @@ header h1{font-size:1.2rem;font-weight:600;letter-spacing:.3px;white-space:nowra
|
||||
|
||||
<div class="status-bar">
|
||||
<span>Aktualisiert alle 10 Sek.</span>
|
||||
<span style="opacity:.5" id="ui-version">v2026.04.18b</span>
|
||||
<span style="opacity:.5" id="ui-version">v2026.04.20</span>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
@@ -394,6 +407,9 @@ header h1{font-size:1.2rem;font-weight:600;letter-spacing:.3px;white-space:nowra
|
||||
STATE
|
||||
=================================================================== */
|
||||
var API_BASE = window.location.origin + '/empfang';
|
||||
function getPracticeIdOrEmpty() {
|
||||
try { return localStorage.getItem('aza_practice_id') || ''; } catch (e) { return ''; }
|
||||
}
|
||||
var currentSession = null;
|
||||
var practiceUsers = [];
|
||||
var serverTasks = [];
|
||||
@@ -443,7 +459,9 @@ var currentToneIdx = parseInt(localStorage.getItem('empfang_tone_idx') || '0', 1
|
||||
API WRAPPER (global 401 handling)
|
||||
=================================================================== */
|
||||
async function apiFetch(url, opts) {
|
||||
var r = await fetch(url, opts || {});
|
||||
opts = opts || {};
|
||||
if (!opts.credentials) opts.credentials = 'include';
|
||||
var r = await fetch(url, opts);
|
||||
if (r.status === 401) {
|
||||
currentSession = null;
|
||||
stopPolling();
|
||||
@@ -458,7 +476,7 @@ async function apiFetch(url, opts) {
|
||||
=================================================================== */
|
||||
async function checkAuth() {
|
||||
try {
|
||||
var r = await fetch(API_BASE + '/auth/me');
|
||||
var r = await fetch(API_BASE + '/auth/me', {credentials: 'include'});
|
||||
if (r.status === 401) return null;
|
||||
var d = await r.json();
|
||||
if (d.authenticated) return d;
|
||||
@@ -471,7 +489,7 @@ async function showLoginOverlay() {
|
||||
var overlay = document.getElementById('login-overlay');
|
||||
overlay.classList.remove('hidden');
|
||||
try {
|
||||
var r = await fetch(API_BASE + '/auth/needs_setup');
|
||||
var r = await fetch(API_BASE + '/auth/needs_setup', {credentials: 'include'});
|
||||
var d = await r.json();
|
||||
if (d.needs_setup) { renderSetupForm(); return; }
|
||||
} catch(e) {}
|
||||
@@ -484,6 +502,7 @@ function hideLoginOverlay() {
|
||||
|
||||
function renderSetupForm() {
|
||||
var box = document.getElementById('login-box');
|
||||
box.className = 'login-box';
|
||||
box.innerHTML =
|
||||
'<h2>AZA Praxis-Chat einrichten</h2>' +
|
||||
'<p>Willkommen! Richten Sie Ihre Praxis und den ersten Administrator ein.</p>' +
|
||||
@@ -498,6 +517,7 @@ function renderSetupForm() {
|
||||
|
||||
function renderLoginForm() {
|
||||
var box = document.getElementById('login-box');
|
||||
box.className = 'login-box';
|
||||
var lastUser = localStorage.getItem('aza_last_login_user') || '';
|
||||
box.innerHTML =
|
||||
'<h2>Anmelden</h2>' +
|
||||
@@ -520,39 +540,159 @@ function renderLoginForm() {
|
||||
|
||||
function renderForgotPasswordForm() {
|
||||
var box = document.getElementById('login-box');
|
||||
box.className = 'login-box';
|
||||
var pid = getPracticeIdOrEmpty();
|
||||
box.innerHTML =
|
||||
'<h2>Passwort vergessen</h2>' +
|
||||
'<p>Geben Sie Ihre E-Mail-Adresse ein. Sie erhalten einen Link zum Zur\u00fccksetzen.</p>' +
|
||||
'<div class="login-field"><label>E-Mail</label><input type="email" id="forgot-email" autocomplete="email" placeholder="praxis@beispiel.ch"></div>' +
|
||||
'<p>Geben Sie Ihren <strong>Benutzernamen</strong> (wie in der Praxis angezeigt) oder Ihre <strong>E-Mail-Adresse</strong> ein.</p>' +
|
||||
'<p style="font-size:.82rem;color:#6a8a9a">Nach einer erfolgreichen Anmeldung speichert der Browser die Praxiszuordnung — dann ist der Ablauf einfacher.</p>' +
|
||||
(pid ? '' : '<p style="font-size:.78rem;color:#a67c00">Hinweis: Auf diesem Ger\u00e4t ist noch keine Praxis gespeichert. Benutzername oder E-Mail trotzdem m\u00f6glich; bei mehreren Konten ggf. zus\u00e4tzliche Auswahl.</p>') +
|
||||
'<div class="login-field"><label>Benutzername oder E-Mail</label><input type="text" id="forgot-login" autocomplete="username" placeholder="z. B. Suro oder name@praxis.ch"></div>' +
|
||||
'<button class="login-btn" onclick="doForgotPassword()">Reset-Link senden</button>' +
|
||||
'<div class="login-error" id="login-error"></div>' +
|
||||
'<div class="login-switch"><a onclick="renderLoginForm()">← Zur\u00fcck zur Anmeldung</a></div>';
|
||||
document.getElementById('forgot-email').focus();
|
||||
document.getElementById('forgot-login').focus();
|
||||
}
|
||||
|
||||
function renderForgotPickUser(loginEmail, candidates) {
|
||||
var box = document.getElementById('login-box');
|
||||
box.className = 'login-box';
|
||||
var buttons = (candidates || []).map(function(c, i) {
|
||||
var lab = esc(c.display_name || '') + (c.practice_name ? ' \u2013 ' + esc(c.practice_name) : '');
|
||||
return '<button type="button" class="login-btn" style="margin-bottom:8px;width:100%;text-align:left" onclick="doForgotPickCandidate(' + i + ')">' + lab + '</button>';
|
||||
}).join('');
|
||||
box.innerHTML =
|
||||
'<h2>Passwort vergessen</h2>' +
|
||||
'<p>Diese E-Mail-Adresse ist <strong>mehreren Benutzerkonten</strong> zugeordnet. Bitte w\u00e4hlen Sie Ihr Konto oder geben Sie Ihren Benutzernamen ein.</p>' +
|
||||
'<div class="login-field" style="margin-top:10px">' + buttons + '</div>' +
|
||||
'<div class="login-field"><label>Benutzername (exakt)</label><input type="text" id="forgot-pick-manual" autocomplete="username" placeholder="Wie in der Praxis angezeigt"></div>' +
|
||||
'<button class="login-btn" onclick="doForgotPickManual()">Auswahl best\u00e4tigen</button>' +
|
||||
'<div class="login-error" id="login-error"></div>' +
|
||||
'<div class="login-switch"><a onclick="renderForgotPasswordForm()">← Zur\u00fcck</a></div>';
|
||||
window._forgotCandidates = candidates;
|
||||
window._forgotLoginEmail = loginEmail;
|
||||
document.getElementById('forgot-pick-manual').focus();
|
||||
}
|
||||
|
||||
async function doForgotPickCandidate(i) {
|
||||
var c = (window._forgotCandidates || [])[i];
|
||||
if (!c) return;
|
||||
await _postForgotSecondStep(window._forgotLoginEmail, c.display_name, c.practice_id);
|
||||
}
|
||||
|
||||
async function doForgotPickManual() {
|
||||
var v = (document.getElementById('forgot-pick-manual').value || '').trim();
|
||||
var errEl = document.getElementById('login-error');
|
||||
var cand = window._forgotCandidates || [];
|
||||
var hit = null;
|
||||
for (var j = 0; j < cand.length; j++) {
|
||||
if ((cand[j].display_name || '') === v) { hit = cand[j]; break; }
|
||||
}
|
||||
if (!hit) {
|
||||
errEl.style.color = '#842029';
|
||||
errEl.textContent = 'Kein passender Benutzername. Bitte exakt wie in der Praxis w\u00e4hlen oder eingeben.';
|
||||
return;
|
||||
}
|
||||
await _postForgotSecondStep(window._forgotLoginEmail, hit.display_name, hit.practice_id);
|
||||
}
|
||||
|
||||
async function _postForgotSecondStep(loginEmail, displayName, practiceId) {
|
||||
var errEl = document.getElementById('login-error');
|
||||
errEl.style.color = '';
|
||||
errEl.textContent = '';
|
||||
try {
|
||||
var r = await fetch(API_BASE + '/auth/forgot_password', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
login: loginEmail,
|
||||
display_name: displayName,
|
||||
practice_id: practiceId || getPracticeIdOrEmpty()
|
||||
})
|
||||
});
|
||||
var d = await r.json().catch(function() { return {}; });
|
||||
if (d.step === 'no_email' || (d.success === false && d.step === 'no_email')) {
|
||||
errEl.style.color = '#842029';
|
||||
errEl.textContent = d.message || 'Keine E-Mail hinterlegt.';
|
||||
return;
|
||||
}
|
||||
if (d.success === false && d.message) {
|
||||
errEl.style.color = '#842029';
|
||||
errEl.textContent = d.message;
|
||||
return;
|
||||
}
|
||||
errEl.style.color = '#155724';
|
||||
errEl.textContent = d.message || 'Link wurde gesendet.';
|
||||
} catch (e) {
|
||||
errEl.style.color = '#842029';
|
||||
errEl.textContent = 'Fehler beim Senden.';
|
||||
}
|
||||
}
|
||||
|
||||
async function doForgotPassword() {
|
||||
var email = (document.getElementById('forgot-email').value || '').trim();
|
||||
var raw = (document.getElementById('forgot-login').value || '').trim();
|
||||
var errEl = document.getElementById('login-error');
|
||||
if (!email) { errEl.textContent = 'Bitte E-Mail-Adresse eingeben.'; return; }
|
||||
errEl.style.color = '';
|
||||
if (!raw) { errEl.style.color = '#842029'; errEl.textContent = 'Bitte Benutzername oder E-Mail eingeben.'; return; }
|
||||
try {
|
||||
var r = await fetch(API_BASE + '/auth/forgot_password', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({email: email})
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({login: raw, practice_id: getPracticeIdOrEmpty()})
|
||||
});
|
||||
var d = await r.json();
|
||||
var d = await r.json().catch(function() { return {}; });
|
||||
if (d.step === 'pick_user' && d.candidates && d.candidates.length) {
|
||||
renderForgotPickUser(d.login || raw, d.candidates);
|
||||
return;
|
||||
}
|
||||
if (d.step === 'no_email' || (d.success === false && d.step === 'no_email')) {
|
||||
errEl.style.color = '#842029';
|
||||
errEl.textContent = d.message || 'Keine E-Mail hinterlegt.';
|
||||
return;
|
||||
}
|
||||
if (d.success === false && d.message) {
|
||||
errEl.style.color = '#842029';
|
||||
errEl.textContent = d.message;
|
||||
return;
|
||||
}
|
||||
errEl.style.color = '#155724';
|
||||
errEl.textContent = d.message || 'Reset-Link wurde gesendet.';
|
||||
} catch(e) { errEl.textContent = 'Fehler beim Senden.'; }
|
||||
errEl.textContent = d.message || 'Wenn ein passendes Konto existiert, wurde ein Link gesendet.';
|
||||
} catch(e) {
|
||||
errEl.style.color = '#842029';
|
||||
errEl.textContent = 'Fehler beim Senden.';
|
||||
}
|
||||
}
|
||||
|
||||
function stripResetTokenFromUrl() {
|
||||
try {
|
||||
var p = window.location.pathname || '';
|
||||
var sp = new URLSearchParams(window.location.search);
|
||||
sp.delete('reset_token');
|
||||
var q = sp.toString();
|
||||
history.replaceState(null, '', p + (q ? '?' + q : ''));
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
function renderResetPasswordInvalid(msg) {
|
||||
stripResetTokenFromUrl();
|
||||
var box = document.getElementById('login-box');
|
||||
box.className = 'login-box';
|
||||
box.innerHTML =
|
||||
'<h2>Link ung\u00fcltig</h2>' +
|
||||
'<p class="login-error" id="login-error" style="display:block;color:#842029">' + esc(msg || 'Ung\u00fcltiger oder abgelaufener Link.') + '</p>' +
|
||||
'<div class="login-switch"><a onclick="renderLoginForm()">Zur Anmeldung</a></div>';
|
||||
}
|
||||
|
||||
function renderResetPasswordForm(resetToken) {
|
||||
var box = document.getElementById('login-box');
|
||||
box.className = 'login-box';
|
||||
box.innerHTML =
|
||||
'<h2>Neues Passwort setzen</h2>' +
|
||||
'<h2>Passwort zur\u00fccksetzen</h2>' +
|
||||
'<p>Sie haben eine Passwort-Zur\u00fccksetzung angefordert.</p>' +
|
||||
'<p>Bitte w\u00e4hlen Sie ein neues Passwort (min. 4 Zeichen).</p>' +
|
||||
'<div class="login-field"><label>Neues Passwort</label><input type="password" id="reset-pass" autocomplete="new-password"></div>' +
|
||||
'<div class="login-field"><label>Passwort best\u00e4tigen</label><input type="password" id="reset-pass2" autocomplete="new-password"></div>' +
|
||||
'<button class="login-btn" onclick="doResetPassword(\'' + resetToken + '\')">Passwort speichern</button>' +
|
||||
'<div class="login-field"><label>Neues Passwort best\u00e4tigen</label><input type="password" id="reset-pass2" autocomplete="new-password"></div>' +
|
||||
'<button class="login-btn" onclick="doResetPassword(\'' + resetToken + '\')">Neues Passwort speichern</button>' +
|
||||
'<div class="login-error" id="login-error"></div>';
|
||||
document.getElementById('reset-pass').focus();
|
||||
}
|
||||
@@ -568,23 +708,34 @@ async function doResetPassword(resetToken) {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({reset_token: resetToken, password: pass1})
|
||||
});
|
||||
var d = await r.json();
|
||||
var d = await r.json().catch(function() { return {}; });
|
||||
if (r.ok && d.success) {
|
||||
stripResetTokenFromUrl();
|
||||
var pref = (d.display_name || d.email || '').trim();
|
||||
if (pref) { try { localStorage.setItem('aza_last_login_user', pref); } catch (e2) {} }
|
||||
errEl.style.color = '#155724';
|
||||
errEl.textContent = 'Passwort ge\u00e4ndert. Sie k\u00f6nnen sich jetzt anmelden.';
|
||||
errEl.textContent = 'Passwort wurde erfolgreich ge\u00e4ndert. Sie k\u00f6nnen sich jetzt anmelden.';
|
||||
setTimeout(renderLoginForm, 2000);
|
||||
} else {
|
||||
errEl.textContent = d.detail || d.message || 'Fehler beim Zur\u00fccksetzen.';
|
||||
var fail = d.detail;
|
||||
if (typeof fail === 'string') {
|
||||
errEl.textContent = fail;
|
||||
} else if (Array.isArray(fail) && fail[0] && fail[0].msg) {
|
||||
errEl.textContent = fail.map(function(x) { return x.msg; }).join(' ');
|
||||
} else {
|
||||
errEl.textContent = d.message || 'Passwort konnte nicht zur\u00fcckgesetzt werden.';
|
||||
}
|
||||
}
|
||||
} catch(e) { errEl.textContent = 'Fehler beim Zur\u00fccksetzen.'; }
|
||||
} catch(e) { errEl.textContent = 'Passwort konnte nicht zur\u00fcckgesetzt werden.'; }
|
||||
}
|
||||
|
||||
function renderRegisterForm() {
|
||||
var box = document.getElementById('login-box');
|
||||
box.className = 'login-box login-register';
|
||||
box.innerHTML =
|
||||
'<h2>Bei Ihrer Praxis registrieren</h2>' +
|
||||
'<h2>registrieren Sie sich f\u00fcr den Chat</h2>' +
|
||||
'<p>Ihr Administrator hat Ihnen einen Einladungscode gegeben.</p>' +
|
||||
'<div class="login-field"><label>Einladungscode</label><input type="text" id="reg-code" placeholder="z.B. Xk7m-9pQr"></div>' +
|
||||
'<div class="login-field"><label>Einladungscode</label><input type="text" id="reg-code" placeholder="z.B. CHAT-AB12-CD34"></div>' +
|
||||
'<div class="login-field"><label>Ihr Name</label><input type="text" id="reg-name" autocomplete="username" placeholder="z.B. Sandra M\u00fcller"></div>' +
|
||||
'<div class="login-field"><label>Passwort (min. 4 Zeichen)</label><input type="password" id="reg-pass" autocomplete="new-password"></div>' +
|
||||
'<div class="login-field"><label>Ihre Rolle</label>' +
|
||||
@@ -625,10 +776,15 @@ async function doSetup() {
|
||||
if (!pass || pass.length < 4) { errEl.textContent = 'Passwort (min. 4 Zeichen) erforderlich.'; return; }
|
||||
try {
|
||||
var r = await fetch(API_BASE + '/auth/setup', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
method: 'POST', credentials: 'include',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({name: name, password: pass, practice_name: practiceName, email: email})
|
||||
});
|
||||
if (r.ok) {
|
||||
try {
|
||||
var sd = await r.json();
|
||||
if (sd.practice_id) localStorage.setItem('aza_practice_id', sd.practice_id);
|
||||
} catch(e) {}
|
||||
if (!await onAuthSuccess()) errEl.textContent = 'Einrichtung fehlgeschlagen.';
|
||||
} else {
|
||||
var d = await r.json().catch(function(){ return {}; });
|
||||
@@ -641,20 +797,39 @@ async function doLogin() {
|
||||
var name = (document.getElementById('login-name').value || '').trim();
|
||||
var pass = document.getElementById('login-pass').value || '';
|
||||
var errEl = document.getElementById('login-error');
|
||||
if (!name || !pass) { errEl.textContent = 'Name/E-Mail und Passwort erforderlich.'; return; }
|
||||
errEl.style.color = '';
|
||||
if (!name || !pass) { errEl.textContent = 'Benutzername/E-Mail und Passwort erforderlich.'; return; }
|
||||
try {
|
||||
var r = await fetch(API_BASE + '/auth/login', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({name: name, password: pass})
|
||||
method: 'POST', credentials: 'include',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
name: name,
|
||||
password: pass,
|
||||
practice_id: getPracticeIdOrEmpty()
|
||||
})
|
||||
});
|
||||
if (r.ok) {
|
||||
localStorage.setItem('aza_last_login_user', name);
|
||||
try {
|
||||
var ld = await r.json();
|
||||
if (ld.practice_id) localStorage.setItem('aza_practice_id', ld.practice_id);
|
||||
var saveUser = (ld.display_name || name).trim();
|
||||
localStorage.setItem('aza_last_login_user', saveUser);
|
||||
} catch(e) {}
|
||||
if (!await onAuthSuccess()) errEl.textContent = 'Anmeldung fehlgeschlagen.';
|
||||
} else {
|
||||
var d = await r.json().catch(function(){ return {}; });
|
||||
errEl.textContent = d.detail || d.error || 'Anmeldung fehlgeschlagen.';
|
||||
errEl.style.color = '#842029';
|
||||
var det = d.detail;
|
||||
if (typeof det === 'object' && det !== null && det.message) {
|
||||
errEl.textContent = det.message;
|
||||
} else if (typeof det === 'string') {
|
||||
errEl.textContent = det;
|
||||
} else {
|
||||
errEl.textContent = d.error || 'Anmeldung fehlgeschlagen.';
|
||||
}
|
||||
}
|
||||
} catch(e) { errEl.textContent = 'Verbindungsfehler.'; }
|
||||
} catch(e) { errEl.style.color = '#842029'; errEl.textContent = 'Verbindungsfehler.'; }
|
||||
}
|
||||
|
||||
async function doRegister() {
|
||||
@@ -666,10 +841,15 @@ async function doRegister() {
|
||||
if (!code || !name || !pass) { errEl.textContent = 'Alle Felder erforderlich.'; return; }
|
||||
try {
|
||||
var r = await fetch(API_BASE + '/auth/register', {
|
||||
method: 'POST', headers: {'Content-Type': 'application/json'},
|
||||
method: 'POST', credentials: 'include',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({invite_code: code, name: name, password: pass, role: role})
|
||||
});
|
||||
if (r.ok) {
|
||||
try {
|
||||
var rd = await r.json();
|
||||
if (rd.practice_id) localStorage.setItem('aza_practice_id', rd.practice_id);
|
||||
} catch(e) {}
|
||||
if (!await onAuthSuccess()) errEl.textContent = 'Registrierung fehlgeschlagen.';
|
||||
} else {
|
||||
var d = await r.json().catch(function(){ return {}; });
|
||||
@@ -679,7 +859,7 @@ async function doRegister() {
|
||||
}
|
||||
|
||||
async function doLogout() {
|
||||
try { await fetch(API_BASE + '/auth/logout', {method: 'POST'}); } catch(e) {}
|
||||
try { await fetch(API_BASE + '/auth/logout', {method: 'POST', credentials: 'include'}); } catch(e) {}
|
||||
currentSession = null;
|
||||
practiceUsers = [];
|
||||
serverTasks = [];
|
||||
@@ -721,11 +901,26 @@ async function doLogout() {
|
||||
if (resetToken) {
|
||||
stopPolling();
|
||||
document.getElementById('login-overlay').classList.remove('hidden');
|
||||
renderResetPasswordForm(resetToken);
|
||||
try {
|
||||
var vr = await fetch(
|
||||
API_BASE + '/auth/reset_verify?reset_token=' + encodeURIComponent(resetToken)
|
||||
);
|
||||
var vd = await vr.json().catch(function() { return {valid: false}; });
|
||||
if (!vd.valid) {
|
||||
renderResetPasswordInvalid(vd.detail || 'Ung\u00fcltiger oder abgelaufener Link.');
|
||||
return;
|
||||
}
|
||||
renderResetPasswordForm(resetToken);
|
||||
} catch (e) {
|
||||
renderResetPasswordInvalid('Verbindungsfehler. Bitte sp\u00e4ter erneut versuchen.');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var me = await checkAuth();
|
||||
if (me && me.practice_id) {
|
||||
try { localStorage.setItem('aza_practice_id', me.practice_id); } catch(e) {}
|
||||
}
|
||||
if (!me) {
|
||||
var inviteParam = urlParams.get('invite');
|
||||
stopPolling();
|
||||
@@ -1629,7 +1824,7 @@ async function activateUser(userId) {
|
||||
}
|
||||
|
||||
async function resetPassword(userId, userName) {
|
||||
if (!confirm('Passwort von "' + userName + '" zuruecksetzen?')) return;
|
||||
if (!confirm('Passwort von "' + userName + '" zur\u00fccksetzen?')) return;
|
||||
try {
|
||||
var r = await apiFetch(API_BASE + '/admin/users/' + userId + '/reset_password', {method:'POST'});
|
||||
var d = await r.json();
|
||||
|
||||
Reference in New Issue
Block a user