import { useState, useMemo } from 'react' import { Avatar } from '../../../design-system/primitives/Avatar/Avatar' import { Badge } from '../../../design-system/primitives/Badge/Badge' import { Button } from '../../../design-system/primitives/Button/Button' import { Input } from '../../../design-system/primitives/Input/Input' import { MonoText } from '../../../design-system/primitives/MonoText/MonoText' import { SectionHeader } from '../../../design-system/primitives/SectionHeader/SectionHeader' import { Tag } from '../../../design-system/primitives/Tag/Tag' import { InlineEdit } from '../../../design-system/primitives/InlineEdit/InlineEdit' import { MultiSelect } from '../../../design-system/composites/MultiSelect/MultiSelect' import { ConfirmDialog } from '../../../design-system/composites/ConfirmDialog/ConfirmDialog' import { MOCK_USERS, MOCK_GROUPS, MOCK_ROLES, getEffectiveRoles, type MockUser } from './rbacMocks' import styles from './UserManagement.module.css' export function UsersTab() { const [users, setUsers] = useState(MOCK_USERS) const [search, setSearch] = useState('') const [selectedId, setSelectedId] = useState(null) const [creating, setCreating] = useState(false) const [deleteTarget, setDeleteTarget] = useState(null) // Create form state const [newUsername, setNewUsername] = useState('') const [newDisplay, setNewDisplay] = useState('') const [newEmail, setNewEmail] = useState('') const [newPassword, setNewPassword] = useState('') const filtered = useMemo(() => { if (!search) return users const q = search.toLowerCase() return users.filter((u) => u.displayName.toLowerCase().includes(q) || u.email.toLowerCase().includes(q) || u.username.toLowerCase().includes(q) ) }, [users, search]) const selected = users.find((u) => u.id === selectedId) ?? null function handleCreate() { if (!newUsername.trim()) return const newUser: MockUser = { id: `usr-${Date.now()}`, username: newUsername.trim(), displayName: newDisplay.trim() || newUsername.trim(), email: newEmail.trim(), provider: 'local', createdAt: new Date().toISOString(), directRoles: [], directGroups: [], } setUsers((prev) => [...prev, newUser]) setCreating(false) setNewUsername(''); setNewDisplay(''); setNewEmail(''); setNewPassword('') setSelectedId(newUser.id) } function handleDelete() { if (!deleteTarget) return setUsers((prev) => prev.filter((u) => u.id !== deleteTarget.id)) if (selectedId === deleteTarget.id) setSelectedId(null) setDeleteTarget(null) } function updateUser(id: string, patch: Partial) { setUsers((prev) => prev.map((u) => u.id === id ? { ...u, ...patch } : u)) } const effectiveRoles = selected ? getEffectiveRoles(selected) : [] const availableGroups = MOCK_GROUPS.filter((g) => !selected?.directGroups.includes(g.id)) .map((g) => ({ value: g.id, label: g.name })) const availableRoles = MOCK_ROLES.filter((r) => !selected?.directRoles.includes(r.name)) .map((r) => ({ value: r.name, label: r.name })) function getUserGroupPath(user: MockUser): string { if (user.directGroups.length === 0) return 'no groups' const group = MOCK_GROUPS.find((g) => g.id === user.directGroups[0]) if (!group) return 'no groups' const parent = group.parentId ? MOCK_GROUPS.find((g) => g.id === group.parentId) : null return parent ? `${parent.name} > ${group.name}` : group.name } return ( <>
setSearch(e.target.value)} onClear={() => setSearch('')} className={styles.listHeaderSearch} />
{creating && (
setNewUsername(e.target.value)} /> setNewDisplay(e.target.value)} />
setNewEmail(e.target.value)} /> setNewPassword(e.target.value)} />
)}
{filtered.map((user) => (
setSelectedId(user.id)} >
{user.displayName} {user.provider !== 'local' && ( )}
{user.email} · {getUserGroupPath(user)}
{user.directRoles.map((r) => )} {user.directGroups.map((gId) => { const g = MOCK_GROUPS.find((gr) => gr.id === gId) return g ? : null })}
))}
{selected ? ( <>
updateUser(selected.id, { displayName: v })} />
{selected.email}
Status ID {selected.id} Created {new Date(selected.createdAt).toLocaleDateString()} Provider {selected.provider}
Group membership (direct only)
{selected.directGroups.map((gId) => { const g = MOCK_GROUPS.find((gr) => gr.id === gId) return g ? ( updateUser(selected.id, { directGroups: selected.directGroups.filter((id) => id !== gId), })} /> ) : null })} {selected.directGroups.length === 0 && ( (no groups) )}
updateUser(selected.id, { directGroups: [...selected.directGroups, ...ids], })} placeholder="Add groups..." />
Effective roles (direct + inherited)
{effectiveRoles.map(({ role, source }) => ( updateUser(selected.id, { directRoles: selected.directRoles.filter((r) => r !== role), }) : undefined} /> ))} {effectiveRoles.length === 0 && ( (no roles) )}
{effectiveRoles.some((r) => r.source !== 'direct') && ( Roles with ↑ are inherited through group membership )}
updateUser(selected.id, { directRoles: [...selected.directRoles, ...roles], })} placeholder="Add roles..." />
) : (
Select a user to view details
)}
setDeleteTarget(null)} onConfirm={handleDelete} message={`Delete user "${deleteTarget?.username}"? This cannot be undone.`} confirmText={deleteTarget?.username ?? ''} /> ) }