chore: publish clean community baseline
This commit is contained in:
@@ -0,0 +1,447 @@
|
||||
var settingsSession = null;
|
||||
var settingsCapabilities = null;
|
||||
function tKey(key) {
|
||||
return typeof t === 'function' ? t(key) : key;
|
||||
}
|
||||
function tfKey(key, vars) {
|
||||
return typeof tf === 'function' ? tf(key, vars) : key;
|
||||
}
|
||||
|
||||
function settingsWorkspaceId() {
|
||||
var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
|
||||
return workspace ? workspace.id : 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 titleCaseRole(role) {
|
||||
var normalized = String(role || 'viewer').toLowerCase();
|
||||
if (normalized === 'operator') return tKey('workspace_setup.role.operator');
|
||||
if (normalized === 'owner') return tKey('workspace_setup.role.owner');
|
||||
if (normalized === 'admin') return tKey('workspace_setup.role.admin');
|
||||
if (normalized === 'viewer') return tKey('workspace_setup.role.viewer');
|
||||
return normalized.replace(/^\w/, function(character) {
|
||||
return character.toUpperCase();
|
||||
});
|
||||
}
|
||||
|
||||
function populateCurrentSession(session) {
|
||||
var summary = document.getElementById('settings-session-summary');
|
||||
if (!summary) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(session && session.user)) {
|
||||
summary.textContent = tKey('settings.session.unavailable');
|
||||
return;
|
||||
}
|
||||
|
||||
var membership = null;
|
||||
if (Array.isArray(session.memberships)) {
|
||||
membership = session.memberships.find(function(item) {
|
||||
return item.workspace && item.workspace.id === session.current_workspace_id;
|
||||
}) || session.memberships[0] || null;
|
||||
}
|
||||
|
||||
var workspaceLabel = membership && membership.workspace
|
||||
? (membership.workspace.display_name || membership.workspace.slug || membership.workspace.id)
|
||||
: tKey('settings.session.current_workspace');
|
||||
var roleLabel = membership ? titleCaseRole(membership.role) : tKey('workspace_setup.role.viewer');
|
||||
summary.textContent = tfKey('settings.session.summary', {
|
||||
email: session.user.email,
|
||||
role: roleLabel,
|
||||
workspace: workspaceLabel,
|
||||
});
|
||||
}
|
||||
|
||||
function capabilityLabel(key) {
|
||||
return tKey('settings.capability.' + key);
|
||||
}
|
||||
|
||||
function renderWorkspaceProtocolOptions(capabilities, selectedProtocol) {
|
||||
var select = document.getElementById('settings-ws-protocol');
|
||||
if (!select) {
|
||||
return;
|
||||
}
|
||||
|
||||
var supportedProtocols = capabilities && Array.isArray(capabilities.supported_protocols) && capabilities.supported_protocols.length
|
||||
? capabilities.supported_protocols
|
||||
: ['rest'];
|
||||
var fallbackProtocol = supportedProtocols.indexOf('rest') !== -1 ? 'rest' : supportedProtocols[0];
|
||||
var effectiveSelection = supportedProtocols.indexOf(selectedProtocol) !== -1
|
||||
? selectedProtocol
|
||||
: fallbackProtocol;
|
||||
|
||||
select.innerHTML = '';
|
||||
supportedProtocols.forEach(function (protocol) {
|
||||
var option = document.createElement('option');
|
||||
option.value = protocol;
|
||||
option.textContent = capabilityLabel(protocol);
|
||||
option.selected = protocol === effectiveSelection;
|
||||
select.appendChild(option);
|
||||
});
|
||||
}
|
||||
|
||||
function populateWorkspaceCapabilityHint(capabilities) {
|
||||
var body = document.getElementById('settings-ws-protocol-hint');
|
||||
if (!body || !capabilities) {
|
||||
return;
|
||||
}
|
||||
|
||||
var protocols = (capabilities.supported_protocols || []).map(capabilityLabel).join(', ');
|
||||
body.textContent = tfKey('settings.ws.protocol_hint_capabilities', {
|
||||
protocols: protocols || capabilityLabel('none'),
|
||||
});
|
||||
}
|
||||
|
||||
function populateNotificationsCapabilityHint(capabilities) {
|
||||
var title = document.getElementById('settings-notifications-capability-title');
|
||||
var body = document.getElementById('settings-notifications-capability-body');
|
||||
if (!title || !body || !capabilities) {
|
||||
return;
|
||||
}
|
||||
|
||||
title.textContent = tKey('settings.notifications.capability_title_' + capabilities.edition);
|
||||
body.textContent = tfKey('settings.notifications.capability_body_' + capabilities.edition, {
|
||||
edition: capabilities.edition,
|
||||
});
|
||||
}
|
||||
|
||||
function populateCapabilities(capabilities) {
|
||||
var title = document.getElementById('settings-security-capability-title');
|
||||
var body = document.getElementById('settings-security-capability-body');
|
||||
if (!title || !body || !capabilities) {
|
||||
return;
|
||||
}
|
||||
|
||||
var protocols = (capabilities.supported_protocols || []).map(capabilityLabel).join(', ');
|
||||
var accessModes = (capabilities.machine_access_modes || []).map(capabilityLabel).join(', ');
|
||||
var securityLevels = (capabilities.supported_security_levels || []).map(capabilityLabel).join(', ');
|
||||
var limits = capabilities.limits || {};
|
||||
|
||||
title.textContent = tKey('settings.security.capability_title_' + capabilities.edition);
|
||||
body.textContent = tfKey('settings.security.capability_summary', {
|
||||
protocols: protocols || capabilityLabel('none'),
|
||||
access_modes: accessModes || capabilityLabel('none'),
|
||||
security_levels: securityLevels || capabilityLabel('none'),
|
||||
max_workspaces: limits.max_workspaces == null ? capabilityLabel('unlimited') : String(limits.max_workspaces),
|
||||
max_users: limits.max_users_per_workspace == null ? capabilityLabel('unlimited') : String(limits.max_users_per_workspace),
|
||||
max_agents: limits.max_agents_per_workspace == null ? capabilityLabel('unlimited') : String(limits.max_agents_per_workspace),
|
||||
});
|
||||
populateWorkspaceCapabilityHint(capabilities);
|
||||
populateNotificationsCapabilityHint(capabilities);
|
||||
renderWorkspaceProtocolOptions(capabilities, document.getElementById('settings-ws-protocol').value || 'rest');
|
||||
}
|
||||
|
||||
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 || 'Crank';
|
||||
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 || '';
|
||||
}
|
||||
|
||||
populateCurrentSession(session);
|
||||
}
|
||||
|
||||
async function loadProfile() {
|
||||
if (!window.CrankAuth || !window.CrankApi) {
|
||||
return;
|
||||
}
|
||||
|
||||
settingsSession = await window.CrankAuth.fetchSession(true);
|
||||
populateProfile(settingsSession);
|
||||
}
|
||||
|
||||
async function loadCapabilities() {
|
||||
if (!window.CrankApi || typeof window.CrankApi.getCapabilities !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
settingsCapabilities = await window.CrankApi.getCapabilities();
|
||||
populateCapabilities(settingsCapabilities);
|
||||
} catch (_error) {
|
||||
}
|
||||
}
|
||||
|
||||
async function renderOverlaySlots() {
|
||||
if (!window.CrankOverlay || typeof window.CrankOverlay.render !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
await (window.whenWorkspacesReady ? window.whenWorkspacesReady() : Promise.resolve());
|
||||
await window.CrankOverlay.render(document, {
|
||||
workspace: window.getCurrentWorkspace ? window.getCurrentWorkspace() : null,
|
||||
capabilities: settingsCapabilities,
|
||||
locale: localStorage.getItem('crank_lang') || 'en',
|
||||
});
|
||||
}
|
||||
|
||||
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 = tKey('settings.profile.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', tKey('settings.profile.saved_status'), false);
|
||||
button.textContent = tKey('settings.profile.saved');
|
||||
} catch (error) {
|
||||
setStatus('settings-profile-status', error.message || tKey('settings.profile.save_error'), 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', tKey('settings.security.mismatch'), true);
|
||||
return;
|
||||
}
|
||||
|
||||
var original = button.textContent;
|
||||
button.disabled = true;
|
||||
button.textContent = tKey('settings.profile.saving');
|
||||
setStatus('settings-password-status', '', false);
|
||||
|
||||
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', tKey('settings.security.saved'), false);
|
||||
button.textContent = tKey('settings.profile.saved');
|
||||
} catch (error) {
|
||||
setStatus('settings-password-status', error.message || tKey('settings.security.save_error'), true);
|
||||
button.textContent = original;
|
||||
} finally {
|
||||
setTimeout(function() {
|
||||
button.disabled = false;
|
||||
button.textContent = original;
|
||||
}, 1200);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function injectLanguageSwitcher() {
|
||||
var profileSection = document.getElementById('section-profile');
|
||||
if (!profileSection) {
|
||||
return;
|
||||
}
|
||||
|
||||
var cardBody = profileSection.querySelector('.section-card-body');
|
||||
if (!cardBody) {
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('html/fragments/lang-switcher.html')
|
||||
.then(function(response) { return response.text(); })
|
||||
.then(function(html) {
|
||||
var template = document.createElement('template');
|
||||
template.innerHTML = html;
|
||||
var fragment = template.content.cloneNode(true);
|
||||
var saveRow = cardBody.querySelector('div[style*="justify-content:flex-end"]');
|
||||
if (saveRow) {
|
||||
saveRow.parentNode.insertBefore(fragment, saveRow);
|
||||
} else {
|
||||
cardBody.appendChild(fragment);
|
||||
}
|
||||
if (typeof applyLang === 'function') {
|
||||
applyLang();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function bindSectionNavigation() {
|
||||
var navItems = document.querySelectorAll('.settings-nav-item[data-section]');
|
||||
navItems.forEach(function (button) {
|
||||
button.addEventListener('click', function () {
|
||||
navItems.forEach(function (item) { item.classList.remove('active'); });
|
||||
this.classList.add('active');
|
||||
var target = document.getElementById('section-' + this.dataset.section);
|
||||
if (target) {
|
||||
target.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
var initialSection = window.location.hash ? window.location.hash.slice(1) : 'profile';
|
||||
var initialButton = document.querySelector('.settings-nav-item[data-section="' + initialSection + '"]')
|
||||
|| document.querySelector('.settings-nav-item[data-section="profile"]');
|
||||
if (initialButton) {
|
||||
setTimeout(function () { initialButton.click(); }, 50);
|
||||
}
|
||||
|
||||
var observer = new IntersectionObserver(function (entries) {
|
||||
entries.forEach(function (entry) {
|
||||
if (!entry.isIntersecting) {
|
||||
return;
|
||||
}
|
||||
var section = entry.target.id.replace('section-', '');
|
||||
document.querySelectorAll('.settings-nav-item[data-section]').forEach(function (button) {
|
||||
button.classList.toggle('active', button.dataset.section === section);
|
||||
});
|
||||
});
|
||||
}, { threshold: 0.3 });
|
||||
|
||||
document.querySelectorAll('[id^="section-"]').forEach(function (section) {
|
||||
if (!section.hidden) {
|
||||
observer.observe(section);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function loadWorkspaceSettings() {
|
||||
if (!window.CrankApi || !window.whenWorkspacesReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
await window.whenWorkspacesReady();
|
||||
var workspace = window.getCurrentWorkspace ? window.getCurrentWorkspace() : null;
|
||||
if (!workspace) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var record = await window.CrankApi.getWorkspace(workspace.id);
|
||||
var item = record.workspace;
|
||||
var settings = item.settings || {};
|
||||
|
||||
document.getElementById('settings-ws-slug').value = item.slug || '';
|
||||
document.getElementById('settings-ws-display-name').value = item.display_name || '';
|
||||
document.getElementById('settings-ws-description').value = settings.description || '';
|
||||
document.getElementById('settings-ws-timezone').value = settings.timezone || 'UTC';
|
||||
renderWorkspaceProtocolOptions(settingsCapabilities, settings.default_protocol || 'rest');
|
||||
|
||||
document.querySelectorAll('#section-workspace .lang-btn').forEach(function (button) {
|
||||
button.classList.toggle('active', button.dataset.lang === (settings.language || (localStorage.getItem('crank_lang') || 'en')));
|
||||
});
|
||||
|
||||
var saveButton = document.getElementById('settings-ws-save-btn');
|
||||
if (saveButton.dataset.bound === 'true') {
|
||||
return;
|
||||
}
|
||||
saveButton.dataset.bound = 'true';
|
||||
|
||||
saveButton.addEventListener('click', async function () {
|
||||
var saveButton = this;
|
||||
var original = saveButton.textContent;
|
||||
saveButton.disabled = true;
|
||||
saveButton.textContent = tKey('settings.workspace.saving');
|
||||
|
||||
try {
|
||||
await window.CrankApi.updateWorkspace(item.id, {
|
||||
slug: document.getElementById('settings-ws-slug').value.trim(),
|
||||
display_name: document.getElementById('settings-ws-display-name').value.trim(),
|
||||
settings: {
|
||||
description: document.getElementById('settings-ws-description').value.trim(),
|
||||
timezone: document.getElementById('settings-ws-timezone').value,
|
||||
default_protocol: document.getElementById('settings-ws-protocol').value,
|
||||
language: localStorage.getItem('crank_lang') || 'en',
|
||||
color: (workspace.settings && workspace.settings.color) || '#0d9488',
|
||||
},
|
||||
});
|
||||
|
||||
if (window.refreshWorkspaces) {
|
||||
var workspaces = await window.refreshWorkspaces();
|
||||
var updated = workspaces.find(function (entry) { return entry.id === item.id; });
|
||||
if (updated && window.setCurrentWorkspace) {
|
||||
await window.setCurrentWorkspace(updated);
|
||||
}
|
||||
}
|
||||
|
||||
saveButton.textContent = tKey('settings.workspace.saved');
|
||||
} catch (error) {
|
||||
if (window.CrankUi) {
|
||||
window.CrankUi.error(error.message || tKey('settings.workspace.save_error'), tKey('settings.workspace.save_error_title'));
|
||||
}
|
||||
saveButton.textContent = original;
|
||||
} finally {
|
||||
setTimeout(function () {
|
||||
saveButton.disabled = false;
|
||||
saveButton.textContent = original;
|
||||
}, 1200);
|
||||
}
|
||||
});
|
||||
} catch (_error) {
|
||||
}
|
||||
}
|
||||
|
||||
async function initSettingsPage() {
|
||||
injectLanguageSwitcher();
|
||||
bindSectionNavigation();
|
||||
await loadProfile();
|
||||
await loadCapabilities();
|
||||
bindProfileSave();
|
||||
bindPasswordSave();
|
||||
await loadWorkspaceSettings();
|
||||
await renderOverlaySlots();
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
void initSettingsPage();
|
||||
});
|
||||
Reference in New Issue
Block a user