mirror of
https://github.com/open-webui/open-webui.git
synced 2025-12-12 12:25:20 +00:00
feat: Display assigned user groups in Admin Panel
Description:
This PR adds the ability to view a user’s assigned groups in the Admin Panel when editing a user.
Backend Changes:
Added a new endpoint:
GET /api/v1/users/{user_id}/groups
Returns the list of groups assigned to a specific user.
Requires admin privileges.
Frontend Changes:
Implemented getUserGroupsById API function to call the new backend endpoint, in lib/apis/users.
Updated EditUserModal.svelte to:
Load user groups asynchronously when the modal is opened.
Display the groups inline in the form before the Save button.
Show a loading state while fetching, and a “No groups assigned” message if none exist.
Result:
Admins can now see which groups a user belongs to directly from the edit user modal,
improving visibility and reducing the need to navigate away for group membership checks.
This commit is contained in:
parent
397c1e7684
commit
dc453efa5c
3 changed files with 68 additions and 1 deletions
|
|
@ -501,3 +501,13 @@ async def delete_user_by_id(user_id: str, user=Depends(get_admin_user)):
|
||||||
status_code=status.HTTP_403_FORBIDDEN,
|
status_code=status.HTTP_403_FORBIDDEN,
|
||||||
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
|
detail=ERROR_MESSAGES.ACTION_PROHIBITED,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
############################
|
||||||
|
# GetUserGroupsById
|
||||||
|
############################
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/{user_id}/groups")
|
||||||
|
async def get_user_groups_by_id(user_id: str, user=Depends(get_admin_user)):
|
||||||
|
return Groups.get_groups_by_member_id(user_id)
|
||||||
|
|
@ -443,3 +443,30 @@ export const updateUserById = async (token: string, userId: string, user: UserUp
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getUserGroupsById = async (token: string, userId: string) => {
|
||||||
|
let error = null;
|
||||||
|
|
||||||
|
const res = await fetch(`${WEBUI_API_BASE_URL}/users/${userId}/groups`, {
|
||||||
|
method: 'GET',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(async (res) => {
|
||||||
|
if (!res.ok) throw await res.json();
|
||||||
|
return res.json();
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
error = err.detail;
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
import { createEventDispatcher } from 'svelte';
|
import { createEventDispatcher } from 'svelte';
|
||||||
import { onMount, getContext } from 'svelte';
|
import { onMount, getContext } from 'svelte';
|
||||||
|
|
||||||
import { updateUserById } from '$lib/apis/users';
|
import { updateUserById, getUserGroupsById } from '$lib/apis/users';
|
||||||
|
|
||||||
import Modal from '$lib/components/common/Modal.svelte';
|
import Modal from '$lib/components/common/Modal.svelte';
|
||||||
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
import localizedFormat from 'dayjs/plugin/localizedFormat';
|
||||||
|
|
@ -27,6 +27,9 @@
|
||||||
password: ''
|
password: ''
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let _user_groups: any[] = [];
|
||||||
|
let loadingGroups = false;
|
||||||
|
|
||||||
const submitHandler = async () => {
|
const submitHandler = async () => {
|
||||||
const res = await updateUserById(localStorage.token, selectedUser.id, _user).catch((error) => {
|
const res = await updateUserById(localStorage.token, selectedUser.id, _user).catch((error) => {
|
||||||
toast.error(`${error}`);
|
toast.error(`${error}`);
|
||||||
|
|
@ -38,10 +41,23 @@
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const loadUserGroups = async () => {
|
||||||
|
if (!selectedUser?.id) return;
|
||||||
|
loadingGroups = true;
|
||||||
|
try {
|
||||||
|
_user_groups = await getUserGroupsById(localStorage.token, selectedUser.id);
|
||||||
|
} catch (error) {
|
||||||
|
toast.error(`${error}`);
|
||||||
|
} finally {
|
||||||
|
loadingGroups = false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
if (selectedUser) {
|
if (selectedUser) {
|
||||||
_user = selectedUser;
|
_user = selectedUser;
|
||||||
_user.password = '';
|
_user.password = '';
|
||||||
|
loadUserGroups();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
@ -152,6 +168,20 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col w-full">
|
||||||
|
<div class="mb-1 text-xs text-gray-500">{$i18n.t('Groups')}</div>
|
||||||
|
|
||||||
|
{#if loadingGroups}
|
||||||
|
<div class="text-sm font-medium text-white">{$i18n.t('Loading groups...')}</div>
|
||||||
|
{:else if _user_groups.length === 0}
|
||||||
|
<div class="text-sm font-medium text-white">{$i18n.t('No groups assigned')}</div>
|
||||||
|
{:else}
|
||||||
|
<div class="text-sm font-medium text-white">
|
||||||
|
{_user_groups.map(g => g.name).join(', ')}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="flex justify-end pt-3 text-sm font-medium">
|
<div class="flex justify-end pt-3 text-sm font-medium">
|
||||||
<button
|
<button
|
||||||
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
|
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-900 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue