| <script> |
| import { toast } from 'svelte-sonner'; |
| import dayjs from 'dayjs'; |
| import relativeTime from 'dayjs/plugin/relativeTime'; |
| dayjs.extend(relativeTime); |
| |
| import { onMount, getContext } from 'svelte'; |
| import { goto } from '$app/navigation'; |
| |
| import { WEBUI_NAME, config, user, showSidebar, knowledge } from '$lib/stores'; |
| import { WEBUI_BASE_URL } from '$lib/constants'; |
| |
| import Tooltip from '$lib/components/common/Tooltip.svelte'; |
| import Plus from '$lib/components/icons/Plus.svelte'; |
| import Badge from '$lib/components/common/Badge.svelte'; |
| import UsersSolid from '$lib/components/icons/UsersSolid.svelte'; |
| import ChevronRight from '$lib/components/icons/ChevronRight.svelte'; |
| import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte'; |
| import Search from '$lib/components/icons/Search.svelte'; |
| import User from '$lib/components/icons/User.svelte'; |
| import UserCircleSolid from '$lib/components/icons/UserCircleSolid.svelte'; |
| import EditGroupModal from './Groups/EditGroupModal.svelte'; |
| import Pencil from '$lib/components/icons/Pencil.svelte'; |
| import GroupItem from './Groups/GroupItem.svelte'; |
| import { createNewGroup, getGroups } from '$lib/apis/groups'; |
| import { |
| getUserDefaultPermissions, |
| getAllUsers, |
| updateUserDefaultPermissions |
| } from '$lib/apis/users'; |
| |
| const i18n = getContext('i18n'); |
| |
| let loaded = false; |
| |
| let groups = []; |
| let filteredGroups; |
| |
| $: filteredGroups = groups.filter((user) => { |
| if (search === '') { |
| return true; |
| } else { |
| let name = user.name.toLowerCase(); |
| const query = search.toLowerCase(); |
| return name.includes(query); |
| } |
| }); |
| |
| let search = ''; |
| let defaultPermissions = {}; |
| |
| let showAddGroupModal = false; |
| let showDefaultPermissionsModal = false; |
| |
| const setGroups = async () => { |
| groups = await getGroups(localStorage.token); |
| }; |
| |
| const addGroupHandler = async (group) => { |
| const res = await createNewGroup(localStorage.token, group).catch((error) => { |
| toast.error(`${error}`); |
| return null; |
| }); |
| |
| if (res) { |
| toast.success($i18n.t('Group created successfully')); |
| groups = await getGroups(localStorage.token); |
| } |
| }; |
| |
| const updateDefaultPermissionsHandler = async (group) => { |
| console.debug(group.permissions); |
| |
| const res = await updateUserDefaultPermissions(localStorage.token, group.permissions).catch( |
| (error) => { |
| toast.error(`${error}`); |
| return null; |
| } |
| ); |
| |
| if (res) { |
| toast.success($i18n.t('Default permissions updated successfully')); |
| defaultPermissions = await getUserDefaultPermissions(localStorage.token); |
| } |
| }; |
| |
| onMount(async () => { |
| if ($user?.role !== 'admin') { |
| await goto('/'); |
| return; |
| } |
| |
| defaultPermissions = await getUserDefaultPermissions(localStorage.token); |
| await setGroups(); |
| loaded = true; |
| }); |
| </script> |
|
|
| {#if loaded} |
| <EditGroupModal |
| bind:show={showAddGroupModal} |
| edit={false} |
| tabs={['general', 'permissions']} |
| permissions={defaultPermissions} |
| onSubmit={addGroupHandler} |
| /> |
|
|
| <div class="mt-0.5 mb-2 gap-1 flex flex-col md:flex-row justify-between"> |
| <div class="flex items-center md:self-center text-xl font-medium px-0.5 gap-2 shrink-0"> |
| <div> |
| {$i18n.t('Groups')} |
| </div> |
| |
| <div class="text-lg font-medium text-gray-500 dark:text-gray-500"> |
| {groups.length} |
| </div> |
| </div> |
| |
| <div class="flex gap-1"> |
| <div class=" flex w-full space-x-2"> |
| <div class="flex flex-1"> |
| <div class=" self-center ml-1 mr-3"> |
| <Search /> |
| </div> |
| <input |
| class=" w-full text-sm pr-4 py-1 rounded-r-xl outline-hidden bg-transparent" |
| bind:value={search} |
| placeholder={$i18n.t('Search')} |
| /> |
| </div> |
| |
| <div> |
| <Tooltip content={$i18n.t('Create Group')}> |
| <button |
| class=" p-2 rounded-xl hover:bg-gray-100 dark:bg-gray-900 dark:hover:bg-gray-850 transition font-medium text-sm flex items-center space-x-1" |
| on:click={() => { |
| showAddGroupModal = !showAddGroupModal; |
| }} |
| > |
| <Plus className="size-3.5" /> |
| </button> |
| </Tooltip> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <div> |
| {#if filteredGroups.length === 0} |
| <div class="flex flex-col items-center justify-center h-40"> |
| <div class=" text-xl font-medium"> |
| {$i18n.t('Organize your users')} |
| </div> |
| |
| <div class="mt-1 text-sm dark:text-gray-300"> |
| {$i18n.t('Use groups to group your users and assign permissions.')} |
| </div> |
| |
| <div class="mt-3"> |
| <button |
| class=" px-4 py-1.5 text-sm rounded-full bg-black hover:bg-gray-800 text-white dark:bg-white dark:text-black dark:hover:bg-gray-100 transition font-medium flex items-center space-x-1" |
| aria-label={$i18n.t('Create Group')} |
| on:click={() => { |
| showAddGroupModal = true; |
| }} |
| > |
| {$i18n.t('Create Group')} |
| </button> |
| </div> |
| </div> |
| {:else} |
| <div> |
| <div class=" flex items-center gap-3 justify-between text-xs uppercase px-1 font-medium"> |
| <div class="w-full basis-3/5">{$i18n.t('Group')}</div> |
| |
| <div class="w-full basis-2/5 text-right">{$i18n.t('Users')}</div> |
| </div> |
| |
| <hr class="mt-1.5 border-gray-100/30 dark:border-gray-850/30" /> |
| |
| {#each filteredGroups as group} |
| <div class="my-2"> |
| <GroupItem {group} {setGroups} {defaultPermissions} /> |
| </div> |
| {/each} |
| </div> |
| {/if} |
|
|
| <hr class="mb-2 border-gray-100/30 dark:border-gray-850/30" /> |
|
|
| <EditGroupModal |
| bind:show={showDefaultPermissionsModal} |
| tabs={['permissions']} |
| bind:permissions={defaultPermissions} |
| custom={false} |
| onSubmit={updateDefaultPermissionsHandler} |
| /> |
|
|
| <button |
| class="flex items-center justify-between rounded-lg w-full transition pt-1" |
| on:click={() => { |
| showDefaultPermissionsModal = true; |
| }} |
| > |
| <div class="flex items-center gap-2.5"> |
| <div class="p-1.5 bg-black/5 dark:bg-white/10 rounded-full"> |
| <UsersSolid className="size-4" /> |
| </div> |
| |
| <div class="text-left"> |
| <div class=" text-sm font-medium">{$i18n.t('Default permissions')}</div> |
| |
| <div class="flex text-xs mt-0.5"> |
| {$i18n.t('applies to all users with the "user" role')} |
| </div> |
| </div> |
| </div> |
|
|
| <div> |
| <ChevronRight strokeWidth="2.5" /> |
| </div> |
| </button> |
| </div> |
| {/if} |
|
|