feat: connect settings live flow to auth api

This commit is contained in:
a.tolmachev
2026-03-31 02:21:31 +03:00
parent 61718bce5a
commit 44363c0b1c
13 changed files with 502 additions and 102 deletions
+17
View File
@@ -145,6 +145,23 @@
getSession: function() {
return request(AUTH_BASE + '/session');
},
getProfile: function() {
return request(AUTH_BASE + '/profile');
},
updateProfile: function(payload) {
return request(AUTH_BASE + '/profile', {
method: 'PATCH',
headers: headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify(payload),
});
},
changePassword: function(payload) {
return request(AUTH_BASE + '/password', {
method: 'POST',
headers: headers({ 'Content-Type': 'application/json' }),
body: JSON.stringify(payload),
});
},
listWorkspaces: function() {
return get('/workspaces');
},
+9 -5
View File
@@ -56,6 +56,12 @@
} catch (_error) {}
}
function replaceSession(session) {
sessionCache = session;
persistUserMirror(session);
return session;
}
async function fetchSession(force) {
if (!force && sessionCache) {
return sessionCache;
@@ -66,9 +72,7 @@
sessionPromise = window.CrankApi.getSession()
.then(function(session) {
sessionCache = session;
persistUserMirror(session);
return session;
return replaceSession(session);
})
.catch(function(error) {
clearUserMirror();
@@ -109,8 +113,7 @@
email: email,
password: password,
});
sessionCache = session;
persistUserMirror(session);
replaceSession(session);
window.location.href = homeUrl();
}
@@ -134,6 +137,7 @@
window.CrankAuth = {
fetchSession: fetchSession,
replaceSession: replaceSession,
guardProtectedPage: guardProtectedPage,
guardLoginPage: guardLoginPage,
login: login,
+127 -17
View File
@@ -1,24 +1,132 @@
function populateProfile() {
try {
var user = JSON.parse(localStorage.getItem('crank_user'));
if (!user) {
var settingsSession = null;
function initials(displayName, email) {
var source = displayName || email || 'Crank';
return source
.split(/\s+/)
.filter(Boolean)
.slice(0, 2)
.map(function(part) { return part.charAt(0).toUpperCase(); })
.join('') || 'CR';
}
function setStatus(id, text, isError) {
var node = document.getElementById(id);
if (!node) return;
node.textContent = text || '';
node.style.color = text
? (isError ? 'var(--red)' : 'var(--green)')
: 'var(--text-muted)';
}
function populateProfile(session) {
if (!session || !session.user) {
return;
}
var user = session.user;
document.getElementById('profile-avatar').textContent = initials(user.display_name, user.email);
document.getElementById('profile-display-name').textContent = user.display_name || 'Operator';
document.getElementById('profile-email').textContent = user.email || '';
var firstName = document.getElementById('field-firstname');
var email = document.getElementById('field-email');
if (firstName) {
firstName.value = user.display_name || '';
}
if (email) {
email.value = user.email || '';
}
}
async function loadProfile() {
if (!window.CrankAuth || !window.CrankApi) {
return;
}
settingsSession = await window.CrankAuth.fetchSession(true);
populateProfile(settingsSession);
}
function bindProfileSave() {
var button = document.getElementById('settings-profile-save-btn');
if (!button || button.dataset.bound === 'true') {
return;
}
button.dataset.bound = 'true';
button.addEventListener('click', async function() {
var original = button.textContent;
button.disabled = true;
button.textContent = 'Saving…';
setStatus('settings-profile-status', '', false);
try {
var session = await window.CrankApi.updateProfile({
display_name: document.getElementById('field-firstname').value.trim(),
email: document.getElementById('field-email').value.trim(),
});
settingsSession = session;
if (window.CrankAuth && typeof window.CrankAuth.replaceSession === 'function') {
window.CrankAuth.replaceSession(session);
}
populateProfile(session);
setStatus('settings-profile-status', 'Profile saved.', false);
button.textContent = 'Saved ✓';
} catch (error) {
setStatus('settings-profile-status', error.message || 'Failed to save profile', true);
button.textContent = original;
} finally {
setTimeout(function() {
button.disabled = false;
button.textContent = original;
}, 1200);
}
});
}
function bindPasswordSave() {
var button = document.getElementById('settings-password-save-btn');
if (!button || button.dataset.bound === 'true') {
return;
}
button.dataset.bound = 'true';
button.addEventListener('click', async function() {
var currentPassword = document.getElementById('security-current-password').value;
var newPassword = document.getElementById('security-new-password').value;
var confirmPassword = document.getElementById('security-confirm-password').value;
if (newPassword !== confirmPassword) {
setStatus('settings-password-status', 'New password and confirmation do not match.', true);
return;
}
document.getElementById('profile-avatar').textContent = user.initials || 'AT';
document.getElementById('profile-display-name').textContent = user.name || 'Operator';
document.getElementById('profile-email').textContent = user.email || '';
var original = button.textContent;
button.disabled = true;
button.textContent = 'Saving…';
setStatus('settings-password-status', '', false);
var firstName = document.getElementById('field-firstname');
var email = document.getElementById('field-email');
if (firstName && user.name) {
firstName.value = user.name;
try {
await window.CrankApi.changePassword({
current_password: currentPassword,
new_password: newPassword,
});
document.getElementById('security-current-password').value = '';
document.getElementById('security-new-password').value = '';
document.getElementById('security-confirm-password').value = '';
setStatus('settings-password-status', 'Password updated.', false);
button.textContent = 'Saved ✓';
} catch (error) {
setStatus('settings-password-status', error.message || 'Failed to change password', true);
button.textContent = original;
} finally {
setTimeout(function() {
button.disabled = false;
button.textContent = original;
}, 1200);
}
if (email && user.email) {
email.value = user.email;
}
} catch (_error) {
}
});
}
function injectLanguageSwitcher() {
@@ -161,8 +269,10 @@ async function loadWorkspaceSettings() {
}
document.addEventListener('DOMContentLoaded', function () {
populateProfile();
injectLanguageSwitcher();
bindSectionNavigation();
loadProfile();
bindProfileSave();
bindPasswordSave();
loadWorkspaceSettings();
});