Changeset 0.16.0 (#74)

This commit is contained in:
2026-02-27 02:38:35 +00:00
parent 1370d701af
commit 8bb77710b9
28 changed files with 3050 additions and 492 deletions

View File

@@ -400,6 +400,42 @@ async function settingsDeleteTeamPreset(teamId, presetId, name) {
// ── Team Provider handlers — now managed by UI primitives in ui-settings.js ──
// ── Group Actions ───────────────────────────
async function deleteGroup(id, name) {
if (!await showConfirm(`Delete group "${name}"? Members will lose access to any resources granted via this group.`)) return;
try { await API.adminDeleteGroup(id); UI.toast('Group deleted'); await UI.loadAdminGroups(); }
catch (e) { UI.toast(e.message, 'error'); }
}
async function removeGroupMember(groupId, userId, label) {
if (!await showConfirm(`Remove ${label} from group?`)) return;
try {
await API.adminRemoveGroupMember(groupId, userId);
UI.toast('Member removed');
await UI.loadGroupMembers(groupId);
} catch (e) { UI.toast(e.message, 'error'); }
}
async function saveGrant(resourceType, resourceId) {
const scopeSel = document.getElementById(`grantScope_${resourceId}`);
if (!scopeSel) return;
const scope = scopeSel.value;
const groupsDiv = document.getElementById(`grantGroups_${resourceId}`);
const selectedGroups = [...(groupsDiv?.querySelectorAll('.grant-group-cb:checked') || [])].map(cb => cb.value);
if (scope === 'groups' && selectedGroups.length === 0) {
return UI.toast('Select at least one group', 'error');
}
try {
await API.adminSetGrant(resourceType, resourceId, scope, selectedGroups);
// Remove the picker
document.querySelectorAll('.grant-picker-active').forEach(el => el.remove());
UI.toast(scope === 'team_only' ? 'Access reverted to team only' : `Access set to ${scope}`);
} catch (e) { UI.toast(e.message, 'error'); }
}
// ── Admin Listeners (extracted from initListeners) ──
function _initAdminListeners() {
@@ -509,6 +545,66 @@ function _initAdminListeners() {
} catch (e) { UI.toast(e.message, 'error'); }
});
// ── Group management ────────────────────────
document.getElementById('adminAddGroupBtn')?.addEventListener('click', async () => {
document.getElementById('adminAddGroupForm').style.display = '';
document.getElementById('adminGroupName').focus();
// Populate team dropdown
try {
const resp = await API.adminListTeams();
const teams = (resp.data || []).filter(t => t.is_active);
const sel = document.getElementById('adminGroupTeam');
sel.innerHTML = '<option value="">Select team...</option>' +
teams.map(t => `<option value="${t.id}">${esc(t.name)}</option>`).join('');
} catch (e) { /* proceed without teams */ }
});
document.getElementById('adminGroupScope')?.addEventListener('change', function() {
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 = '';
});
document.getElementById('adminCreateGroupBtn')?.addEventListener('click', async () => {
const name = document.getElementById('adminGroupName').value.trim();
const desc = document.getElementById('adminGroupDesc').value.trim();
const scope = document.getElementById('adminGroupScope').value;
const teamId = scope === 'team' ? document.getElementById('adminGroupTeam').value : null;
if (!name) return UI.toast('Group name required', 'error');
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';
document.getElementById('adminGroupName').value = '';
document.getElementById('adminGroupDesc').value = '';
UI.toast('Group created');
await UI.loadAdminGroups(true);
} catch (e) { UI.toast(e.message, 'error'); }
});
document.getElementById('adminGroupBackBtn')?.addEventListener('click', () => {
document.getElementById('adminGroupDetail').style.display = 'none';
document.getElementById('adminGroupList').style.display = '';
UI.loadAdminGroups(true);
});
document.getElementById('adminAddGroupMemberBtn')?.addEventListener('click', async () => {
document.getElementById('adminAddGroupMemberForm').style.display = '';
await UI.loadGroupMemberDropdown(UI._groupEditId);
});
document.getElementById('adminCancelGroupMemberBtn')?.addEventListener('click', () => {
document.getElementById('adminAddGroupMemberForm').style.display = 'none';
});
document.getElementById('adminAddGroupMemberSubmit')?.addEventListener('click', async () => {
const userId = document.getElementById('adminGroupMemberUser').value;
if (!userId) return UI.toast('Select a user', 'error');
try {
await API.adminAddGroupMember(UI._groupEditId, userId);
document.getElementById('adminAddGroupMemberForm').style.display = 'none';
UI.toast('Member added');
await UI.loadGroupMembers(UI._groupEditId);
} catch (e) { UI.toast(e.message, 'error'); }
});
// ── Extension management ────────────────────
document.getElementById('adminInstallExtBtn')?.addEventListener('click', () => {
document.getElementById('adminInstallExtForm').style.display = '';