|
|
|
|
@@ -41,6 +41,7 @@ export default function AdminUserGroupsPage() {
|
|
|
|
|
const [members, setMembers] = useState<UserGroupMember[]>([])
|
|
|
|
|
const [subjectPermissions, setSubjectPermissions] = useState<SubjectPermission[]>([])
|
|
|
|
|
const [selectedGroupID, setSelectedGroupID] = useState('')
|
|
|
|
|
const [groupQuery, setGroupQuery] = useState('')
|
|
|
|
|
const [newMemberUserID, setNewMemberUserID] = useState('')
|
|
|
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
|
const [busy, setBusy] = useState(false)
|
|
|
|
|
@@ -60,6 +61,21 @@ export default function AdminUserGroupsPage() {
|
|
|
|
|
return groups.find((item) => item.id === selectedGroupID) || null
|
|
|
|
|
}, [groups, selectedGroupID])
|
|
|
|
|
|
|
|
|
|
const filteredGroups = useMemo(() => {
|
|
|
|
|
const normalized = groupQuery.trim().toLowerCase()
|
|
|
|
|
if (!normalized) {
|
|
|
|
|
return groups
|
|
|
|
|
}
|
|
|
|
|
return groups.filter((item) => {
|
|
|
|
|
const descriptionText = item.description || ''
|
|
|
|
|
return (
|
|
|
|
|
item.name.toLowerCase().includes(normalized) ||
|
|
|
|
|
item.id.toLowerCase().includes(normalized) ||
|
|
|
|
|
descriptionText.toLowerCase().includes(normalized)
|
|
|
|
|
)
|
|
|
|
|
})
|
|
|
|
|
}, [groupQuery, groups])
|
|
|
|
|
|
|
|
|
|
const loadGroups = async () => {
|
|
|
|
|
let list: UserGroup[]
|
|
|
|
|
let permissions: SubjectPermission[]
|
|
|
|
|
@@ -259,7 +275,7 @@ export default function AdminUserGroupsPage() {
|
|
|
|
|
<Button variant="outlined" startIcon={<AddIcon />} onClick={openCreate}>New Group</Button>
|
|
|
|
|
</Box>
|
|
|
|
|
{error ? <Alert severity="error" sx={{ mb: 1 }}>{error}</Alert> : null}
|
|
|
|
|
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '360px minmax(0, 1fr)' }, gap: 1 }}>
|
|
|
|
|
<Box sx={{ display: 'grid', gridTemplateColumns: { xs: '1fr', md: '360px minmax(0, 1fr)' }, gap: 1, alignItems: 'start' }}>
|
|
|
|
|
<Paper
|
|
|
|
|
sx={{
|
|
|
|
|
p: 2,
|
|
|
|
|
@@ -267,10 +283,22 @@ export default function AdminUserGroupsPage() {
|
|
|
|
|
theme.palette.mode === 'dark' ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.015)'
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<TextField
|
|
|
|
|
size="small"
|
|
|
|
|
label="Search"
|
|
|
|
|
placeholder="Name, id, or description"
|
|
|
|
|
value={groupQuery}
|
|
|
|
|
onChange={(event) => setGroupQuery(event.target.value)}
|
|
|
|
|
fullWidth
|
|
|
|
|
sx={{ mb: 1 }}
|
|
|
|
|
/>
|
|
|
|
|
{loading ? <Typography variant="body2" color="text.secondary">Loading groups...</Typography> : null}
|
|
|
|
|
{!loading && groups.length === 0 ? <Typography variant="body2" color="text.secondary">No groups.</Typography> : null}
|
|
|
|
|
{!loading && groups.length > 0 && filteredGroups.length === 0 ? (
|
|
|
|
|
<Typography variant="body2" color="text.secondary">No matching groups.</Typography>
|
|
|
|
|
) : null}
|
|
|
|
|
<List>
|
|
|
|
|
{groups.map((group) => (
|
|
|
|
|
{filteredGroups.map((group) => (
|
|
|
|
|
<ListItem
|
|
|
|
|
key={group.id}
|
|
|
|
|
divider
|
|
|
|
|
|