feat: add RBAC management UI with dashboard, users, groups, and roles tabs
Tab-based admin page at /admin/rbac with split-pane entity views, inheritance visualization, OIDC badges, and role/group management. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
66
ui/src/pages/admin/rbac/RbacPage.tsx
Normal file
66
ui/src/pages/admin/rbac/RbacPage.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { useSearchParams } from 'react-router';
|
||||
import { useAuthStore } from '../../../auth/auth-store';
|
||||
import { DashboardTab } from './DashboardTab';
|
||||
import { UsersTab } from './UsersTab';
|
||||
import { GroupsTab } from './GroupsTab';
|
||||
import { RolesTab } from './RolesTab';
|
||||
import styles from './RbacPage.module.css';
|
||||
|
||||
const TABS = ['dashboard', 'users', 'groups', 'roles'] as const;
|
||||
type TabKey = (typeof TABS)[number];
|
||||
|
||||
const TAB_LABELS: Record<TabKey, string> = {
|
||||
dashboard: 'Dashboard',
|
||||
users: 'Users',
|
||||
groups: 'Groups',
|
||||
roles: 'Roles',
|
||||
};
|
||||
|
||||
export function RbacPage() {
|
||||
const roles = useAuthStore((s) => s.roles);
|
||||
|
||||
if (!roles.includes('ADMIN')) {
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<div className={styles.accessDenied}>
|
||||
Access Denied — this page requires the ADMIN role.
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <RbacContent />;
|
||||
}
|
||||
|
||||
function RbacContent() {
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const rawTab = searchParams.get('tab');
|
||||
const activeTab: TabKey = TABS.includes(rawTab as TabKey) ? (rawTab as TabKey) : 'dashboard';
|
||||
|
||||
function setTab(tab: TabKey) {
|
||||
setSearchParams({ tab }, { replace: true });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.page}>
|
||||
<div className={styles.tabs}>
|
||||
{TABS.map((tab) => (
|
||||
<button
|
||||
key={tab}
|
||||
type="button"
|
||||
className={`${styles.tab} ${activeTab === tab ? styles.tabActive : ''}`}
|
||||
onClick={() => setTab(tab)}
|
||||
>
|
||||
{TAB_LABELS[tab]}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<div className={styles.tabContent}>
|
||||
{activeTab === 'dashboard' && <DashboardTab />}
|
||||
{activeTab === 'users' && <UsersTab />}
|
||||
{activeTab === 'groups' && <GroupsTab />}
|
||||
{activeTab === 'roles' && <RolesTab />}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user