fix(ui): wire AdminRolesScript and clean unused imports

- Replace inline script in AdminRolesPage with extracted AdminRolesScript component
- Remove unused JSX import aliases and format AdminUserDetailsPage
This commit is contained in:
Tyler Gillispie 2026-08-26 11:43:52 -07:00
parent fa2778d4fe
commit 58d8e54ae5
2 changed files with 1 additions and 149 deletions

View File

@ -1,16 +1,7 @@
import { AdminLayout } from "./AdminLayout.tsx";
import type { AdminTable as _AdminTable } from "./admin/AdminTable.tsx";
import { AdminModal } from "./admin/AdminModal.tsx";
import { RoleEditorDrawer } from "./admin/drawers/RoleEditorDrawer.tsx";
import { AdminRolesScript } from "./admin/AdminRolesScript.tsx";
// We need to use these imports, they are falsely flagged by lint because they are only used in JSX.
// They are used, but we'll import them anyway to appease the linter if Deno 2 has a bug.
// In Hono JSX, imports might not be strictly recognized.
const _AdminModal = AdminModal;
const _AdminRolesScript = AdminRolesScript;
const _RoleEditorDrawer = RoleEditorDrawer;
export const AdminRolesPage = ({
roles,
apps,
@ -287,140 +278,7 @@ export const AdminRolesPage = ({
}
`}
</style>
<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 = '';
document.getElementById('roleFormTitle').textContent = 'Create New Role';
document.getElementById('scopeSelectContainer').style.display = 'grid';
document.getElementById('roleName').value = '';
document.getElementById('roleDescription').value = '';
document.getElementById('roleFormCard').style.display = 'block';
document.getElementById('roleFormCard').scrollIntoView({ behavior: 'smooth' });
}
function openEditRoleDrawer(roleJson) {
const role = JSON.parse(roleJson);
document.getElementById('editRoleId').value = role.id;
document.getElementById('roleFormTitle').textContent = 'Edit Role: ' + role.name;
document.getElementById('scopeSelectContainer').style.display = 'none';
document.getElementById('roleName').value = role.name || '';
document.getElementById('roleDescription').value = role.description || '';
document.getElementById('roleFormCard').style.display = 'block';
document.getElementById('roleFormCard').scrollIntoView({ behavior: 'smooth' });
}
function closeRoleDrawer() {
document.getElementById('roleFormModal').style.display = 'none';
}
function handleScopeChange() {
const scope = document.getElementById('roleScope').value;
const appContainer = document.getElementById('appSelectContainer');
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);
}
}
`,
}}
/>
<AdminRolesScript />
</AdminLayout>
);
};

View File

@ -1,13 +1,7 @@
import { AdminLayout } from "./AdminLayout.tsx";
import { AdminTable } from "./admin/AdminTable.tsx";
import { GrantDrawer } from "./admin/drawers/GrantDrawer.tsx";
import type { GrantDrawer as _GrantDrawer } from "./admin/drawers/GrantDrawer.tsx";
import { AdminUserDetailsScript } from "./admin/AdminUserDetailsScript.tsx";
import type { AdminUserDetailsScript as _AdminUserDetailsScript } from "./admin/AdminUserDetailsScript.tsx";
// In Hono JSX, imports might not be strictly recognized.
export const AdminUserDetailsPage = ({
user,