Phase 2 Admin Drawers & Scripts execution: - Extract `AppDrawer`, `InviteDrawer`, `RoleEditorDrawer`, and `GrantDrawer`. - Extract `AdminAppsScript`, `AdminInvitesScript`, `AdminRolesScript`, and `AdminUserDetailsScript`. - Hook extracted components into their respective pages. - Format `AdminRolesPage.tsx` and all modified files using `deno fmt`. Co-authored-by: mrteye <1945243+mrteye@users.noreply.github.com>
155 lines
6.4 KiB
TypeScript
155 lines
6.4 KiB
TypeScript
export const AdminRolesScript = () => {
|
|
return (
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
function showNotice(msg, isError) {
|
|
const banner = document.getElementById('status-banner');
|
|
banner.textContent = msg;
|
|
banner.style.display = 'block';
|
|
banner.style.background = isError ? 'var(--danger-bg)' : 'var(--success-bg)';
|
|
banner.style.color = isError ? 'var(--danger-text)' : 'var(--success-text)';
|
|
banner.style.border = isError ? '1px solid var(--danger-border)' : '1px solid var(--success-border)';
|
|
setTimeout(() => { banner.style.display = 'none'; }, 5000);
|
|
}
|
|
|
|
function openCreateRoleDrawer() {
|
|
document.getElementById('editRoleId').value = '';
|
|
const titleEl = document.getElementById('roleFormTitle');
|
|
if(titleEl) titleEl.textContent = 'Create New Role';
|
|
document.getElementById('scopeSelectContainer').style.display = 'grid';
|
|
document.getElementById('roleName').value = '';
|
|
document.getElementById('roleDescription').value = '';
|
|
|
|
const modal = document.getElementById('roleFormModal');
|
|
if (modal) modal.style.display = 'flex';
|
|
|
|
const card = document.getElementById('roleFormCard');
|
|
if (card) {
|
|
card.style.display = 'block';
|
|
card.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
}
|
|
|
|
function openEditRoleDrawer(roleJson) {
|
|
const role = JSON.parse(roleJson);
|
|
document.getElementById('editRoleId').value = role.id;
|
|
const titleEl = document.getElementById('roleFormTitle');
|
|
if(titleEl) titleEl.textContent = 'Edit Role: ' + role.name;
|
|
document.getElementById('scopeSelectContainer').style.display = 'none';
|
|
document.getElementById('roleName').value = role.name || '';
|
|
document.getElementById('roleDescription').value = role.description || '';
|
|
|
|
const modal = document.getElementById('roleFormModal');
|
|
if (modal) modal.style.display = 'flex';
|
|
|
|
const card = document.getElementById('roleFormCard');
|
|
if (card) {
|
|
card.style.display = 'block';
|
|
card.scrollIntoView({ behavior: 'smooth' });
|
|
}
|
|
}
|
|
|
|
function closeRoleDrawer() {
|
|
const modal = document.getElementById('roleFormModal');
|
|
if (modal) modal.style.display = 'none';
|
|
}
|
|
|
|
function handleScopeChange() {
|
|
const scope = document.getElementById('roleScope').value;
|
|
const appContainer = document.getElementById('appSelectContainer');
|
|
if (appContainer) appContainer.style.display = scope === 'app_specific' ? 'block' : 'none';
|
|
}
|
|
|
|
function filterRoles() {
|
|
const query = (document.getElementById('roleSearchInput')?.value || '').toLowerCase().trim();
|
|
const selectedScope = document.getElementById('filterScopeSelect')?.value || 'all';
|
|
|
|
const rows = document.querySelectorAll('.role-row');
|
|
const cards = document.querySelectorAll('.role-card');
|
|
let visibleCount = 0;
|
|
|
|
const checkMatch = (appId, searchText) => {
|
|
const scopeMatch = selectedScope === 'all' || (selectedScope === 'global' && appId === 'global') || (appId === selectedScope);
|
|
const textMatch = !query || searchText.includes(query);
|
|
return scopeMatch && textMatch;
|
|
};
|
|
|
|
rows.forEach(r => {
|
|
const appId = r.getAttribute('data-app-id');
|
|
const search = r.getAttribute('data-search') || '';
|
|
const match = checkMatch(appId, search);
|
|
r.style.display = match ? '' : 'none';
|
|
if (match) visibleCount++;
|
|
});
|
|
|
|
cards.forEach(c => {
|
|
const appId = c.getAttribute('data-app-id');
|
|
const search = c.getAttribute('data-search') || '';
|
|
const match = checkMatch(appId, search);
|
|
c.style.display = match ? '' : 'none';
|
|
});
|
|
|
|
document.getElementById('roleCountDisplay').textContent = 'Showing ' + visibleCount + ' roles';
|
|
}
|
|
|
|
async function handleSaveRole(e) {
|
|
e.preventDefault();
|
|
const editId = document.getElementById('editRoleId').value;
|
|
const scope = document.getElementById('roleScope')?.value;
|
|
const name = document.getElementById('roleName').value.trim();
|
|
const description = document.getElementById('roleDescription').value.trim();
|
|
let appId = null;
|
|
|
|
if (!editId && scope === 'app_specific') {
|
|
appId = document.getElementById('roleAppId').value;
|
|
}
|
|
|
|
if (!name) {
|
|
showNotice('Role identifier is required', true);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const url = editId ? ('/api/admin/roles/' + editId) : '/api/admin/roles';
|
|
const method = editId ? 'PUT' : 'POST';
|
|
const res = await fetch(url, {
|
|
method,
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ name, description, appId }),
|
|
});
|
|
const data = await res.json();
|
|
if (res.ok) {
|
|
showNotice(editId ? 'Role updated successfully!' : 'Role created successfully!', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
showNotice(data.error || 'Failed to save role', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error saving role', true);
|
|
}
|
|
}
|
|
|
|
async function deleteRole(roleId, roleName) {
|
|
if (!confirm('Are you sure you want to delete role "' + roleName + '"?')) return;
|
|
try {
|
|
const res = await fetch('/api/admin/roles/' + roleId, {
|
|
method: 'DELETE',
|
|
});
|
|
if (res.ok) {
|
|
showNotice('Role deleted', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
const data = await res.json();
|
|
showNotice(data.error || 'Failed to delete role', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error', true);
|
|
}
|
|
}
|
|
`,
|
|
}}
|
|
/>
|
|
);
|
|
};
|