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>
179 lines
7.1 KiB
TypeScript
179 lines
7.1 KiB
TypeScript
export const AdminUserDetailsScript = ({ allRoles }: { allRoles: any[] }) => {
|
|
return (
|
|
<script
|
|
dangerouslySetInnerHTML={{
|
|
__html: `
|
|
const ROLES_CATALOG = ${JSON.stringify(allRoles)};
|
|
|
|
function updateRoleOptions() {
|
|
const grantAppIdEl = document.getElementById('grantAppId');
|
|
if(!grantAppIdEl) return;
|
|
const appId = grantAppIdEl.value;
|
|
const roleSelect = document.getElementById('grantRole');
|
|
if(!roleSelect) return;
|
|
roleSelect.innerHTML = '';
|
|
|
|
const available = ROLES_CATALOG.filter(r => !r.app_id || r.app_id === appId);
|
|
if (available.length === 0) {
|
|
const opt = document.createElement('option');
|
|
opt.value = 'user';
|
|
opt.textContent = 'user';
|
|
roleSelect.appendChild(opt);
|
|
return;
|
|
}
|
|
|
|
available.forEach(r => {
|
|
const opt = document.createElement('option');
|
|
opt.value = r.name;
|
|
opt.textContent = r.name + (r.app_id ? ' (App Custom)' : ' (Global)');
|
|
roleSelect.appendChild(opt);
|
|
});
|
|
}
|
|
|
|
if (document.getElementById('grantAppId')) {
|
|
updateRoleOptions();
|
|
}
|
|
|
|
function showNotice(msg, isError) {
|
|
const banner = document.getElementById('status-banner');
|
|
if(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);
|
|
}
|
|
}
|
|
|
|
async function handleUpdateProfile(e, userId) {
|
|
e.preventDefault();
|
|
const displayName = document.getElementById('displayNameInput').value.trim();
|
|
|
|
try {
|
|
const res = await fetch('/api/admin/users/' + userId + '/profile', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ displayName }),
|
|
});
|
|
const data = await res.json();
|
|
if (res.ok) {
|
|
showNotice('User display name saved successfully!', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
showNotice(data.error || 'Failed to update display name', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error updating display name', true);
|
|
}
|
|
}
|
|
|
|
async function handleGrantAccess(e, userId) {
|
|
e.preventDefault();
|
|
const appId = document.getElementById('grantAppId').value;
|
|
const role = document.getElementById('grantRole').value;
|
|
|
|
try {
|
|
const res = await fetch('/api/admin/users/' + userId + '/grants', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({ appId, role }),
|
|
});
|
|
const data = await res.json();
|
|
if (res.ok) {
|
|
showNotice('Application access granted successfully!', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
showNotice(data.error || 'Failed to update application grant', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error updating grant', true);
|
|
}
|
|
}
|
|
|
|
async function revokeGrant(userId, appId, appName) {
|
|
if (!confirm('Revoke access to "' + appName + '" for this user?')) return;
|
|
try {
|
|
const res = await fetch('/api/admin/users/' + userId + '/grants/' + appId, {
|
|
method: 'DELETE',
|
|
});
|
|
if (res.ok) {
|
|
showNotice('Access revoked', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
const data = await res.json();
|
|
showNotice(data.error || 'Failed to revoke grant', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error revoking grant', true);
|
|
}
|
|
}
|
|
|
|
async function generateRecoveryLink(userId) {
|
|
try {
|
|
const res = await fetch('/api/admin/users/' + userId + '/recovery', { method: 'POST' });
|
|
const data = await res.json();
|
|
if (res.ok) {
|
|
const link = window.location.origin + '/recovery?code=' + data.recoveryCode;
|
|
document.getElementById('recovery-link-text').textContent = link;
|
|
document.getElementById('recovery-link-container').style.display = 'block';
|
|
showNotice('Recovery link generated!', false);
|
|
} else {
|
|
showNotice(data.error || 'Failed to generate link', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error', true);
|
|
}
|
|
}
|
|
|
|
async function revokeSession(sessionId) {
|
|
if (!confirm('Revoke this session?')) return;
|
|
try {
|
|
const res = await fetch('/api/admin/sessions/' + sessionId, { method: 'DELETE' });
|
|
if (res.ok) {
|
|
showNotice('Session revoked', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
showNotice('Failed to revoke session', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error', true);
|
|
}
|
|
}
|
|
|
|
async function revokeAllSessions(userId) {
|
|
if (!confirm('Revoke ALL sessions for this user? They will be immediately logged out.')) return;
|
|
try {
|
|
const res = await fetch('/api/admin/users/' + userId + '/sessions', { method: 'DELETE' });
|
|
if (res.ok) {
|
|
showNotice('All sessions revoked', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
showNotice('Failed to revoke all sessions', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error', true);
|
|
}
|
|
}
|
|
|
|
async function deletePasskey(userId, passkeyId) {
|
|
if (!confirm('Permanently delete this device? The user will no longer be able to log in with it.')) return;
|
|
try {
|
|
const res = await fetch('/api/admin/users/' + userId + '/passkeys/' + passkeyId, { method: 'DELETE' });
|
|
const data = await res.json();
|
|
if (res.ok) {
|
|
showNotice('Passkey deleted', false);
|
|
setTimeout(() => window.location.reload(), 600);
|
|
} else {
|
|
showNotice(data.error || 'Failed to delete passkey', true);
|
|
}
|
|
} catch (err) {
|
|
showNotice('Network error', true);
|
|
}
|
|
}
|
|
`,
|
|
}}
|
|
/>
|
|
);
|
|
};
|