Changeset 0.22.10 (#152)
This commit is contained in:
@@ -17,38 +17,37 @@ async function toggleUserActive(id, active) {
|
||||
}
|
||||
|
||||
async function showApproveForm(userId, username) {
|
||||
const form = document.getElementById(`approveForm-${userId}`);
|
||||
const teamsEl = document.getElementById(`approveTeams-${userId}`);
|
||||
if (!form || !teamsEl) return;
|
||||
|
||||
// Load teams for checkboxes
|
||||
document.getElementById('approveUserId').value = userId;
|
||||
document.getElementById('approveUserTitle').textContent = `Approve "${username}"`;
|
||||
const teamsEl = document.getElementById('approveTeamsList');
|
||||
teamsEl.innerHTML = '<span class="text-muted">Loading teams...</span>';
|
||||
form.style.display = '';
|
||||
openModal('approveUserModal');
|
||||
|
||||
try {
|
||||
const resp = await API.adminListTeams();
|
||||
const teams = (resp.data || []).filter(t => t.is_active);
|
||||
if (teams.length === 0) {
|
||||
teamsEl.innerHTML = '<span class="text-muted">No teams — user will be activated without team assignment</span>';
|
||||
} else {
|
||||
teamsEl.innerHTML = '<label class="form-label" style="font-size:12px;margin-bottom:4px">Assign to teams:</label>' +
|
||||
teams.map(t => `<label class="checkbox-label" style="font-size:13px"><input type="checkbox" value="${t.id}" class="approve-team-cb"> ${esc(t.name)}</label>`).join('');
|
||||
teamsEl.innerHTML = '<label class="form-label" style="font-size:12px;margin-bottom:8px;display:block">Assign to teams:</label>' +
|
||||
teams.map(t => `<label class="toggle-label" style="margin-bottom:6px"><input type="checkbox" value="${t.id}" class="approve-team-cb"> <span>${esc(t.name)}</span></label>`).join('');
|
||||
}
|
||||
} catch (e) { teamsEl.innerHTML = `<span class="text-muted">Could not load teams</span>`; }
|
||||
}
|
||||
|
||||
function hideApproveForm(userId) {
|
||||
const form = document.getElementById(`approveForm-${userId}`);
|
||||
if (form) form.style.display = 'none';
|
||||
closeModal('approveUserModal');
|
||||
}
|
||||
|
||||
async function submitApproval(userId) {
|
||||
const form = document.getElementById(`approveForm-${userId}`);
|
||||
const teamIds = [...(form?.querySelectorAll('.approve-team-cb:checked') || [])].map(cb => cb.value);
|
||||
if (!userId) userId = document.getElementById('approveUserId').value;
|
||||
const teamIds = [...(document.querySelectorAll('#approveTeamsList .approve-team-cb:checked') || [])].map(cb => cb.value);
|
||||
try {
|
||||
const el = _adminScroll(), pos = el?.scrollTop || 0;
|
||||
await API.adminToggleActive(userId, true, teamIds, 'member');
|
||||
const msg = teamIds.length ? `User activated & assigned to ${teamIds.length} team(s)` : 'User activated';
|
||||
UI.toast(msg, 'success');
|
||||
closeModal('approveUserModal');
|
||||
await UI.loadAdminUsers();
|
||||
_restoreScroll(el, pos);
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
@@ -73,27 +72,38 @@ async function createAdminUser() {
|
||||
const r = document.getElementById('adminNewRole').value;
|
||||
if (!u || !e || !p) { UI.toast('All fields required', 'warning'); return; }
|
||||
await API.adminCreateUser(u, e, p, r);
|
||||
document.getElementById('adminAddUserForm').style.display = 'none';
|
||||
closeModal('createUserModal');
|
||||
// Clear form fields
|
||||
document.getElementById('adminNewUsername').value = '';
|
||||
document.getElementById('adminNewEmail').value = '';
|
||||
document.getElementById('adminNewPassword').value = '';
|
||||
document.getElementById('adminNewRole').value = 'user';
|
||||
UI.toast('User created', 'success');
|
||||
await UI.loadAdminUsers();
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
}
|
||||
|
||||
async function adminResetUserPassword(id, username) {
|
||||
const pw = prompt(
|
||||
`Reset password for "${username}"?\n\n` +
|
||||
`⚠️ WARNING: This will DESTROY the user's personal vault.\n` +
|
||||
`All personal API keys (BYOK) will be permanently deleted.\n` +
|
||||
`The user will need to re-add any personal provider keys.\n\n` +
|
||||
`Enter new password (min 8 chars):`
|
||||
);
|
||||
if (!pw) return;
|
||||
if (pw.length < 8) { UI.toast('Password must be at least 8 characters', 'warning'); return; }
|
||||
if (!await showConfirm(`Final confirmation: Reset password AND destroy personal vault for "${username}"?`)) return;
|
||||
try {
|
||||
await API.adminResetPassword(id, pw);
|
||||
UI.toast(`Password reset for ${username}. Vault destroyed.`, 'success');
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
// Show the reset password modal
|
||||
document.getElementById('resetPwUser').textContent = username;
|
||||
document.getElementById('resetPwInput').value = '';
|
||||
openModal('resetPwModal');
|
||||
document.getElementById('resetPwInput').focus();
|
||||
|
||||
// Wire up the confirm button (remove old listener, add new)
|
||||
const btn = document.getElementById('resetPwConfirmBtn');
|
||||
const newBtn = btn.cloneNode(true);
|
||||
btn.parentNode.replaceChild(newBtn, btn);
|
||||
newBtn.addEventListener('click', async () => {
|
||||
const pw = document.getElementById('resetPwInput').value;
|
||||
if (!pw || pw.length < 8) { UI.toast('Password must be at least 8 characters', 'warning'); return; }
|
||||
if (!await showConfirm(`Final confirmation: Reset password AND destroy personal vault for "${username}"?`)) return;
|
||||
try {
|
||||
await API.adminResetPassword(id, pw);
|
||||
closeModal('resetPwModal');
|
||||
UI.toast(`Password reset for ${username}. Vault destroyed.`, 'success');
|
||||
} catch (e) { UI.toast(e.message, 'error'); }
|
||||
});
|
||||
}
|
||||
|
||||
// ── Admin Provider / Role handlers — now managed by UI primitives in ui-admin.js ──
|
||||
@@ -197,7 +207,7 @@ function ensureAdminPersonaForm() {
|
||||
kbScope: 'admin',
|
||||
onSubmit: (vals) => createAdminPersona(vals),
|
||||
onCancel: () => {
|
||||
container.style.display = 'none';
|
||||
closeModal('personaFormModal');
|
||||
_editingPersonaId = null;
|
||||
_adminPersonaForm.setSubmitLabel('Create');
|
||||
_adminPersonaForm.clearForm();
|
||||
@@ -262,7 +272,8 @@ function editAdminPersona(id) {
|
||||
ensureAdminPersonaForm();
|
||||
const form = _adminPersonaForm;
|
||||
if (!form) return;
|
||||
document.getElementById('adminAddPersonaForm').style.display = '';
|
||||
document.getElementById('personaFormTitle').textContent = 'Edit Persona';
|
||||
openModal('personaFormModal');
|
||||
form.setValues(p);
|
||||
form.setSubmitLabel('Update');
|
||||
// Load KB bindings (v0.17.0)
|
||||
@@ -312,7 +323,7 @@ async function createAdminPersona(vals) {
|
||||
}
|
||||
|
||||
_editingPersonaId = null;
|
||||
document.getElementById('adminAddPersonaForm').style.display = 'none';
|
||||
closeModal('personaFormModal');
|
||||
if (_adminPersonaForm) {
|
||||
_adminPersonaForm.setSubmitLabel('Create');
|
||||
_adminPersonaForm.clearForm();
|
||||
@@ -467,17 +478,18 @@ function _initAdminListeners() {
|
||||
|
||||
// Admin — users
|
||||
document.getElementById('adminAddUserBtn')?.addEventListener('click', () => {
|
||||
const f = document.getElementById('adminAddUserForm');
|
||||
f.style.display = f.style.display === 'none' ? '' : 'none';
|
||||
openModal('createUserModal');
|
||||
});
|
||||
document.getElementById('adminCancelUserBtn')?.addEventListener('click', () => { document.getElementById('adminAddUserForm').style.display = 'none'; });
|
||||
document.getElementById('adminCreateUserBtn')?.addEventListener('click', createAdminUser);
|
||||
|
||||
// Admin — approve user modal
|
||||
document.getElementById('approveUserSubmitBtn')?.addEventListener('click', () => submitApproval());
|
||||
|
||||
// Admin — providers (form managed by primitives in loadAdminProviders)
|
||||
document.getElementById('adminAddProviderBtn')?.addEventListener('click', () => {
|
||||
const f = document.getElementById('adminAddProviderForm');
|
||||
if (UI._adminProvForm) UI._adminProvForm.setCreateMode();
|
||||
f.style.display = f.style.display === 'none' ? '' : 'none';
|
||||
document.getElementById('providerFormTitle').textContent = 'Add Provider';
|
||||
openModal('providerFormModal');
|
||||
});
|
||||
|
||||
// Admin — models
|
||||
@@ -490,19 +502,17 @@ function _initAdminListeners() {
|
||||
_editingPersonaId = null;
|
||||
form.setSubmitLabel('Create');
|
||||
form.clearForm();
|
||||
const f = document.getElementById('adminAddPersonaForm');
|
||||
f.style.display = f.style.display === 'none' ? '' : 'none';
|
||||
document.getElementById('personaFormTitle').textContent = 'Create Persona';
|
||||
openModal('personaFormModal');
|
||||
});
|
||||
|
||||
// Admin — Teams
|
||||
document.getElementById('adminAddTeamBtn')?.addEventListener('click', () => {
|
||||
document.getElementById('adminAddTeamForm').style.display = '';
|
||||
openModal('createTeamModal');
|
||||
document.getElementById('adminTeamName').focus();
|
||||
});
|
||||
document.getElementById('adminCancelTeamBtn')?.addEventListener('click', () => {
|
||||
document.getElementById('adminAddTeamForm').style.display = 'none';
|
||||
document.getElementById('adminTeamName').value = '';
|
||||
document.getElementById('adminTeamDesc').value = '';
|
||||
closeModal('createTeamModal');
|
||||
});
|
||||
document.getElementById('adminCreateTeamBtn')?.addEventListener('click', async () => {
|
||||
const name = document.getElementById('adminTeamName').value.trim();
|
||||
@@ -510,7 +520,7 @@ function _initAdminListeners() {
|
||||
if (!name) return UI.toast('Team name required', 'error');
|
||||
try {
|
||||
await API.adminCreateTeam(name, desc);
|
||||
document.getElementById('adminAddTeamForm').style.display = 'none';
|
||||
closeModal('createTeamModal');
|
||||
document.getElementById('adminTeamName').value = '';
|
||||
document.getElementById('adminTeamDesc').value = '';
|
||||
UI.toast('Team created');
|
||||
@@ -559,7 +569,7 @@ function _initAdminListeners() {
|
||||
|
||||
// ── Group management ────────────────────────
|
||||
document.getElementById('adminAddGroupBtn')?.addEventListener('click', async () => {
|
||||
document.getElementById('adminAddGroupForm').style.display = '';
|
||||
openModal('createGroupModal');
|
||||
document.getElementById('adminGroupName').focus();
|
||||
// Populate team dropdown
|
||||
try {
|
||||
@@ -574,9 +584,7 @@ function _initAdminListeners() {
|
||||
document.getElementById('adminGroupTeamRow').style.display = this.value === 'team' ? '' : 'none';
|
||||
});
|
||||
document.getElementById('adminCancelGroupBtn')?.addEventListener('click', () => {
|
||||
document.getElementById('adminAddGroupForm').style.display = 'none';
|
||||
document.getElementById('adminGroupName').value = '';
|
||||
document.getElementById('adminGroupDesc').value = '';
|
||||
closeModal('createGroupModal');
|
||||
});
|
||||
document.getElementById('adminCreateGroupBtn')?.addEventListener('click', async () => {
|
||||
const name = document.getElementById('adminGroupName').value.trim();
|
||||
@@ -587,7 +595,7 @@ function _initAdminListeners() {
|
||||
if (scope === 'team' && !teamId) return UI.toast('Select a team for team-scoped groups', 'error');
|
||||
try {
|
||||
await API.adminCreateGroup(name, desc, scope, teamId);
|
||||
document.getElementById('adminAddGroupForm').style.display = 'none';
|
||||
closeModal('createGroupModal');
|
||||
document.getElementById('adminGroupName').value = '';
|
||||
document.getElementById('adminGroupDesc').value = '';
|
||||
UI.toast('Group created');
|
||||
@@ -783,7 +791,7 @@ function editAdminExtension(id) {
|
||||
`;
|
||||
|
||||
// Find the extension row and insert the form after it
|
||||
const rows = document.querySelectorAll('#adminExtensionsList .admin-user-row');
|
||||
const rows = document.querySelectorAll('#adminExtensionsList tbody tr');
|
||||
for (const row of rows) {
|
||||
if (row.querySelector(`[onclick*="editAdminExtension('${id}')"]`)) {
|
||||
row.insertAdjacentHTML('afterend', formHTML);
|
||||
|
||||
Reference in New Issue
Block a user