Allow editing non-Administrators group access from the Groups tab.
Load a group into the form to change scope and permissions; keep Administrators locked like delete.
This commit is contained in:
+99
-32
@@ -208,12 +208,15 @@ function GroupsConfigTab() {
|
||||
const [isLoadingGroups, setIsLoadingGroups] = useState(true)
|
||||
const [groupsLoadError, setGroupsLoadError] = useState<string | null>(null)
|
||||
|
||||
const [editingGroupName, setEditingGroupName] = useState<string | null>(null)
|
||||
const [draftGroupName, setDraftGroupName] = useState('')
|
||||
const [draftScopeKind, setDraftScopeKind] = useState<GroupScopeKind>('node')
|
||||
const [draftScopeName, setDraftScopeName] = useState('')
|
||||
const [draftPermissions, setDraftPermissions] = useState<string[]>([])
|
||||
const [submitError, setSubmitError] = useState<string | null>(null)
|
||||
|
||||
const isEditing = editingGroupName !== null
|
||||
|
||||
useEffect(() => {
|
||||
let isCancelled = false
|
||||
|
||||
@@ -245,6 +248,27 @@ function GroupsConfigTab() {
|
||||
}
|
||||
}, [])
|
||||
|
||||
function clearGroupForm() {
|
||||
setEditingGroupName(null)
|
||||
setDraftGroupName('')
|
||||
setDraftScopeKind('node')
|
||||
setDraftScopeName('')
|
||||
setDraftPermissions([])
|
||||
setSubmitError(null)
|
||||
}
|
||||
|
||||
function handleEditGroup(group: Group) {
|
||||
if (group.name === ADMINISTRATORS_GROUP_NAME) {
|
||||
return
|
||||
}
|
||||
setEditingGroupName(group.name)
|
||||
setDraftGroupName(group.name)
|
||||
setDraftScopeKind(group.scope_kind)
|
||||
setDraftScopeName(group.scope_name)
|
||||
setDraftPermissions([...group.permissions])
|
||||
setSubmitError(null)
|
||||
}
|
||||
|
||||
function togglePermission(permission: string, checked: boolean) {
|
||||
setDraftPermissions((prev) => {
|
||||
if (checked) {
|
||||
@@ -261,6 +285,11 @@ function GroupsConfigTab() {
|
||||
async function handleSaveGroup() {
|
||||
setSubmitError(null)
|
||||
|
||||
if (draftGroupName.trim() === ADMINISTRATORS_GROUP_NAME) {
|
||||
setSubmitError('Administrators group cannot be created or modified here')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await upsertGroup({
|
||||
name: draftGroupName,
|
||||
@@ -270,10 +299,7 @@ function GroupsConfigTab() {
|
||||
})
|
||||
|
||||
setGroups(response.groups)
|
||||
setDraftGroupName('')
|
||||
setDraftScopeKind('node')
|
||||
setDraftScopeName('')
|
||||
setDraftPermissions([])
|
||||
clearGroupForm()
|
||||
} catch (err) {
|
||||
setSubmitError(
|
||||
err instanceof Error ? err.message : 'Failed to save group',
|
||||
@@ -287,6 +313,9 @@ function GroupsConfigTab() {
|
||||
try {
|
||||
const response = await deleteGroup(groupName)
|
||||
setGroups(response.groups)
|
||||
if (editingGroupName === groupName) {
|
||||
clearGroupForm()
|
||||
}
|
||||
} catch (err) {
|
||||
setSubmitError(
|
||||
err instanceof Error ? err.message : 'Failed to delete group',
|
||||
@@ -308,38 +337,60 @@ function GroupsConfigTab() {
|
||||
<p className="config-empty">No groups configured yet.</p>
|
||||
) : (
|
||||
<ul className="groups-ul">
|
||||
{groups.map((group) => (
|
||||
<li key={group.name} className="groups-li">
|
||||
<div className="groups-li-content">
|
||||
<div className="groups-li-title">{group.name}</div>
|
||||
<div className="groups-li-meta">
|
||||
Scope: {group.scope_kind}: {group.scope_name}
|
||||
</div>
|
||||
<div className="groups-li-meta">
|
||||
Permissions: {group.permissions.join(', ')}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="groups-delete"
|
||||
disabled={group.name === ADMINISTRATORS_GROUP_NAME}
|
||||
title={
|
||||
group.name === ADMINISTRATORS_GROUP_NAME
|
||||
? 'Administrators group cannot be deleted'
|
||||
: undefined
|
||||
{groups.map((group) => {
|
||||
const isAdministrators = group.name === ADMINISTRATORS_GROUP_NAME
|
||||
const isSelected = editingGroupName === group.name
|
||||
return (
|
||||
<li
|
||||
key={group.name}
|
||||
className={
|
||||
isSelected ? 'groups-li groups-li-selected' : 'groups-li'
|
||||
}
|
||||
onClick={() => void handleDeleteGroup(group.name)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
<div className="groups-li-content">
|
||||
<div className="groups-li-title">{group.name}</div>
|
||||
<div className="groups-li-meta">
|
||||
Scope: {group.scope_kind}: {group.scope_name}
|
||||
</div>
|
||||
<div className="groups-li-meta">
|
||||
Permissions: {group.permissions.join(', ')}
|
||||
</div>
|
||||
</div>
|
||||
<div className="groups-li-actions">
|
||||
{!isAdministrators ? (
|
||||
<button
|
||||
type="button"
|
||||
className="groups-edit"
|
||||
onClick={() => handleEditGroup(group)}
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className="groups-delete"
|
||||
disabled={isAdministrators}
|
||||
title={
|
||||
isAdministrators
|
||||
? 'Administrators group cannot be deleted'
|
||||
: undefined
|
||||
}
|
||||
onClick={() => void handleDeleteGroup(group.name)}
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
)
|
||||
})}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="groups-form">
|
||||
<p className="config-hint">Create or update a group</p>
|
||||
<p className="config-hint">
|
||||
{isEditing ? 'Edit group' : 'Create group'}
|
||||
</p>
|
||||
|
||||
{submitError ? (
|
||||
<p className="config-empty groups-error" role="alert">
|
||||
@@ -353,6 +404,7 @@ function GroupsConfigTab() {
|
||||
id="group-name"
|
||||
className="config-input"
|
||||
value={draftGroupName}
|
||||
readOnly={isEditing}
|
||||
onChange={(event) => {
|
||||
setDraftGroupName(event.target.value)
|
||||
}}
|
||||
@@ -408,9 +460,24 @@ function GroupsConfigTab() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" className="groups-save" onClick={() => void handleSaveGroup()}>
|
||||
Save group
|
||||
</button>
|
||||
<div className="groups-form-actions">
|
||||
{isEditing ? (
|
||||
<button
|
||||
type="button"
|
||||
className="groups-new"
|
||||
onClick={() => clearGroupForm()}
|
||||
>
|
||||
New group
|
||||
</button>
|
||||
) : null}
|
||||
<button
|
||||
type="button"
|
||||
className="groups-save"
|
||||
onClick={() => void handleSaveGroup()}
|
||||
>
|
||||
{isEditing ? 'Save changes' : 'Save group'}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user