mirror of
https://github.com/open-webui/open-webui
synced 2024-12-28 06:42:47 +00:00
enh: update channel
This commit is contained in:
parent
e9194d9524
commit
7ad8918cd9
@ -78,6 +78,31 @@ async def get_channel_by_id(id: str, user=Depends(get_verified_user)):
|
||||
return ChannelModel(**channel.model_dump())
|
||||
|
||||
|
||||
############################
|
||||
# UpdateChannelById
|
||||
############################
|
||||
|
||||
|
||||
@router.post("/{id}/update", response_model=Optional[ChannelModel])
|
||||
async def update_channel_by_id(
|
||||
id: str, form_data: ChannelForm, user=Depends(get_admin_user)
|
||||
):
|
||||
channel = Channels.get_channel_by_id(id)
|
||||
if not channel:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND
|
||||
)
|
||||
|
||||
try:
|
||||
channel = Channels.update_channel_by_id(id, form_data)
|
||||
return ChannelModel(**channel.model_dump())
|
||||
except Exception as e:
|
||||
log.exception(e)
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_400_BAD_REQUEST, detail=ERROR_MESSAGES.DEFAULT()
|
||||
)
|
||||
|
||||
|
||||
############################
|
||||
# GetChannelMessages
|
||||
############################
|
||||
|
@ -102,6 +102,38 @@ export const getChannelById = async (token: string = '', channel_id: string) =>
|
||||
return res;
|
||||
}
|
||||
|
||||
export const updateChannelById = async (token: string = '', channel_id: string, channel: ChannelForm) => {
|
||||
let error = null;
|
||||
|
||||
const res = await fetch(`${WEBUI_API_BASE_URL}/channels/${channel_id}/update`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
authorization: `Bearer ${token}`
|
||||
},
|
||||
body: JSON.stringify({ ...channel })
|
||||
})
|
||||
.then(async (res) => {
|
||||
if (!res.ok) throw await res.json();
|
||||
return res.json();
|
||||
})
|
||||
.then((json) => {
|
||||
return json;
|
||||
})
|
||||
.catch((err) => {
|
||||
error = err.detail;
|
||||
console.log(err);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (error) {
|
||||
throw error;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
export const getChannelMessages = async (token: string = '', channel_id: string, page: number = 1) => {
|
||||
let error = null;
|
||||
|
@ -65,7 +65,7 @@
|
||||
{($settings?.widescreenMode ?? null) ? 'max-w-full' : 'max-w-5xl'} mx-auto"
|
||||
>
|
||||
{#if channel}
|
||||
<div class="flex flex-col py-1 gap-1.5 py-5">
|
||||
<div class="flex flex-col gap-1.5 py-5">
|
||||
<div class="text-2xl font-medium capitalize">{channel.name}</div>
|
||||
|
||||
<div class=" text-gray-500">
|
||||
@ -76,7 +76,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex justify-center py-1 text-xs items-center gap-2 py-5">
|
||||
<div class="flex justify-center text-xs items-center gap-2 py-5">
|
||||
<div class=" ">Start of the channel</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
@ -70,7 +70,7 @@
|
||||
|
||||
{#if message.created_at}
|
||||
<span
|
||||
class=" self-center invisible group-hover:visible text-gray-400 text-xs font-medium capitalize ml-0.5 -mt-0.5"
|
||||
class=" self-center invisible group-hover:visible text-gray-400 text-xs font-medium first-letter:capitalize ml-0.5 -mt-0.5"
|
||||
>
|
||||
{formatDate(message.created_at / 1000000)}
|
||||
</span>
|
||||
|
@ -37,7 +37,6 @@
|
||||
|
||||
const confirmHandler = async () => {
|
||||
show = false;
|
||||
|
||||
await onConfirm();
|
||||
dispatch('confirm', inputValue);
|
||||
};
|
||||
@ -47,11 +46,15 @@
|
||||
});
|
||||
|
||||
$: if (mounted) {
|
||||
if (show) {
|
||||
if (show && modalElement) {
|
||||
document.body.appendChild(modalElement);
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
document.body.style.overflow = 'hidden';
|
||||
} else {
|
||||
} else if (modalElement) {
|
||||
window.removeEventListener('keydown', handleKeyDown);
|
||||
document.body.removeChild(modalElement);
|
||||
|
||||
document.body.style.overflow = 'unset';
|
||||
}
|
||||
}
|
||||
@ -62,7 +65,7 @@
|
||||
<!-- svelte-ignore a11y-no-static-element-interactions -->
|
||||
<div
|
||||
bind:this={modalElement}
|
||||
class=" fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] flex justify-center z-[99999] overflow-hidden overscroll-contain"
|
||||
class=" fixed top-0 right-0 left-0 bottom-0 bg-black/60 w-full h-screen max-h-[100dvh] flex justify-center z-[99999999] overflow-hidden overscroll-contain"
|
||||
in:fade={{ duration: 10 }}
|
||||
on:mousedown={() => {
|
||||
show = false;
|
||||
|
12
src/lib/components/icons/Cog6Solid.svelte
Normal file
12
src/lib/components/icons/Cog6Solid.svelte
Normal file
@ -0,0 +1,12 @@
|
||||
<script lang="ts">
|
||||
export let className = 'w-4 h-4';
|
||||
export let strokeWidth = '1.5';
|
||||
</script>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class={className}>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M6.455 1.45A.5.5 0 0 1 6.952 1h2.096a.5.5 0 0 1 .497.45l.186 1.858a4.996 4.996 0 0 1 1.466.848l1.703-.769a.5.5 0 0 1 .639.206l1.047 1.814a.5.5 0 0 1-.14.656l-1.517 1.09a5.026 5.026 0 0 1 0 1.694l1.516 1.09a.5.5 0 0 1 .141.656l-1.047 1.814a.5.5 0 0 1-.639.206l-1.703-.768c-.433.36-.928.649-1.466.847l-.186 1.858a.5.5 0 0 1-.497.45H6.952a.5.5 0 0 1-.497-.45l-.186-1.858a4.993 4.993 0 0 1-1.466-.848l-1.703.769a.5.5 0 0 1-.639-.206l-1.047-1.814a.5.5 0 0 1 .14-.656l1.517-1.09a5.033 5.033 0 0 1 0-1.694l-1.516-1.09a.5.5 0 0 1-.141-.656L2.46 3.593a.5.5 0 0 1 .639-.206l1.703.769c.433-.36.928-.65 1.466-.848l.186-1.858Zm-.177 7.567-.022-.037a2 2 0 0 1 3.466-1.997l.022.037a2 2 0 0 1-3.466 1.997Z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
@ -53,7 +53,7 @@
|
||||
import Tooltip from '../common/Tooltip.svelte';
|
||||
import Folders from './Sidebar/Folders.svelte';
|
||||
import { getChannels, createNewChannel } from '$lib/apis/channels';
|
||||
import CreateChannelModal from './Sidebar/CreateChannelModal.svelte';
|
||||
import ChannelModal from './Sidebar/ChannelModal.svelte';
|
||||
import ChannelItem from './Sidebar/ChannelItem.svelte';
|
||||
import PencilSquare from '../icons/PencilSquare.svelte';
|
||||
|
||||
@ -403,10 +403,21 @@
|
||||
}}
|
||||
/>
|
||||
|
||||
<CreateChannelModal
|
||||
<ChannelModal
|
||||
bind:show={showCreateChannel}
|
||||
onChange={async () => {
|
||||
await initChannels();
|
||||
onSubmit={async ({ name, access_control }) => {
|
||||
const res = await createNewChannel(localStorage.token, {
|
||||
name: name,
|
||||
access_control: access_control
|
||||
}).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
if (res) {
|
||||
await initChannels();
|
||||
showCreateChannel = false;
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@ -642,7 +653,12 @@
|
||||
onAddLabel={$i18n.t('Create Channel')}
|
||||
>
|
||||
{#each $channels as channel}
|
||||
<ChannelItem id={channel.id} name={channel.name} />
|
||||
<ChannelItem
|
||||
{channel}
|
||||
onUpdate={async () => {
|
||||
await initChannels();
|
||||
}}
|
||||
/>
|
||||
{/each}
|
||||
</Folder>
|
||||
{/if}
|
||||
|
@ -1,33 +1,55 @@
|
||||
<script lang="ts">
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { onMount, getContext, createEventDispatcher, tick, onDestroy } from 'svelte';
|
||||
import { onMount, getContext, tick, onDestroy } from 'svelte';
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
import { page } from '$app/stores';
|
||||
|
||||
import { mobile, showSidebar, user } from '$lib/stores';
|
||||
import EllipsisHorizontal from '$lib/components/icons/EllipsisHorizontal.svelte';
|
||||
import { updateChannelById } from '$lib/apis/channels';
|
||||
|
||||
import Cog6 from '$lib/components/icons/Cog6.svelte';
|
||||
import ChannelModal from './ChannelModal.svelte';
|
||||
|
||||
export let onUpdate: Function = () => {};
|
||||
|
||||
export let className = '';
|
||||
export let channel;
|
||||
|
||||
export let id;
|
||||
export let name;
|
||||
let showEditChannelModal = false;
|
||||
|
||||
let itemElement;
|
||||
</script>
|
||||
|
||||
<ChannelModal
|
||||
bind:show={showEditChannelModal}
|
||||
{channel}
|
||||
edit={true}
|
||||
onSubmit={async ({ name, access_control }) => {
|
||||
const res = await updateChannelById(localStorage.token, channel.id, {
|
||||
name,
|
||||
access_control
|
||||
}).catch((error) => {
|
||||
toast.error(error.message);
|
||||
});
|
||||
|
||||
if (res) {
|
||||
toast.success('Channel updated successfully');
|
||||
}
|
||||
|
||||
onUpdate();
|
||||
}}
|
||||
/>
|
||||
|
||||
<div
|
||||
bind:this={itemElement}
|
||||
class=" w-full {className} rounded-lg flex relative group hover:bg-gray-100 dark:hover:bg-gray-900 {$page
|
||||
.url.pathname === `/channels/${id}`
|
||||
.url.pathname === `/channels/${channel.id}`
|
||||
? 'bg-gray-100 dark:bg-gray-900'
|
||||
: ''} px-2.5 py-1"
|
||||
>
|
||||
<a
|
||||
class=" w-full flex justify-between"
|
||||
href="/channels/{id}"
|
||||
href="/channels/{channel.id}"
|
||||
on:click={() => {
|
||||
if ($mobile) {
|
||||
showSidebar.set(false);
|
||||
@ -50,7 +72,7 @@
|
||||
</svg>
|
||||
|
||||
<div class=" text-left self-center overflow-hidden w-full line-clamp-1">
|
||||
{name}
|
||||
{channel.name}
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
@ -60,10 +82,12 @@
|
||||
class="absolute z-10 right-2 invisible group-hover:visible self-center flex items-center dark:text-gray-300"
|
||||
on:pointerup={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
showEditChannelModal = true;
|
||||
}}
|
||||
>
|
||||
<button class="p-0.5 dark:hover:bg-gray-850 rounded-lg touch-auto" on:click={(e) => {}}>
|
||||
<EllipsisHorizontal className="size-4" strokeWidth="2.5" />
|
||||
<Cog6 className="size-3.5" />
|
||||
</button>
|
||||
</button>
|
||||
{/if}
|
||||
|
@ -1,15 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { getContext, createEventDispatcher } from 'svelte';
|
||||
|
||||
import { getContext, createEventDispatcher, onMount } from 'svelte';
|
||||
import { createNewChannel } from '$lib/apis/channels';
|
||||
|
||||
import Modal from '$lib/components/common/Modal.svelte';
|
||||
import AccessControl from '$lib/components/workspace/common/AccessControl.svelte';
|
||||
import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte';
|
||||
|
||||
import { toast } from 'svelte-sonner';
|
||||
const i18n = getContext('i18n');
|
||||
|
||||
export let show = false;
|
||||
export let onChange: Function = () => {};
|
||||
export let onSubmit: Function = () => {};
|
||||
|
||||
export let channel = null;
|
||||
export let edit = false;
|
||||
|
||||
let name = '';
|
||||
let accessControl = null;
|
||||
@ -22,25 +26,41 @@
|
||||
|
||||
const submitHandler = async () => {
|
||||
loading = true;
|
||||
const res = await createNewChannel(localStorage.token, {
|
||||
await onSubmit({
|
||||
name: name.replace(/\s/g, '-'),
|
||||
access_control: accessControl
|
||||
}).catch((error) => {
|
||||
toast.error(error);
|
||||
return null;
|
||||
});
|
||||
|
||||
onChange();
|
||||
show = false;
|
||||
|
||||
loading = false;
|
||||
};
|
||||
|
||||
const init = () => {
|
||||
name = channel.name;
|
||||
accessControl = channel.access_control;
|
||||
};
|
||||
|
||||
$: if (channel) {
|
||||
init();
|
||||
}
|
||||
|
||||
let showDeleteConfirmDialog = false;
|
||||
|
||||
const deleteHandler = async () => {
|
||||
showDeleteConfirmDialog = false;
|
||||
show = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<Modal size="sm" bind:show>
|
||||
<div>
|
||||
<div class=" flex justify-between dark:text-gray-300 px-5 pt-4 pb-1">
|
||||
<div class=" text-lg font-medium self-center">{$i18n.t('Create Channel')}</div>
|
||||
<div class=" text-lg font-medium self-center">
|
||||
{#if edit}
|
||||
{$i18n.t('Edit Channel')}
|
||||
{:else}
|
||||
{$i18n.t('Create Channel')}
|
||||
{/if}
|
||||
</div>
|
||||
<button
|
||||
class="self-center"
|
||||
on:click={() => {
|
||||
@ -93,6 +113,18 @@
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end pt-3 text-sm font-medium gap-1.5">
|
||||
{#if edit}
|
||||
<button
|
||||
class="px-3.5 py-1.5 text-sm font-medium dark:bg-black dark:hover:bg-black/90 dark:text-white bg-white text-black hover:bg-gray-100 transition rounded-full flex flex-row space-x-1 items-center"
|
||||
type="button"
|
||||
on:click={() => {
|
||||
showDeleteConfirmDialog = true;
|
||||
}}
|
||||
>
|
||||
{$i18n.t('Delete')}
|
||||
</button>
|
||||
{/if}
|
||||
|
||||
<button
|
||||
class="px-3.5 py-1.5 text-sm font-medium bg-black hover:bg-gray-950 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
|
||||
? ' cursor-not-allowed'
|
||||
@ -100,7 +132,11 @@
|
||||
type="submit"
|
||||
disabled={loading}
|
||||
>
|
||||
{$i18n.t('Create')}
|
||||
{#if edit}
|
||||
{$i18n.t('Update')}
|
||||
{:else}
|
||||
{$i18n.t('Create')}
|
||||
{/if}
|
||||
|
||||
{#if loading}
|
||||
<div class="ml-2 self-center">
|
||||
@ -136,3 +172,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<DeleteConfirmDialog
|
||||
bind:show={showDeleteConfirmDialog}
|
||||
message={$i18n.t('Are you sure you want to delete this channel?')}
|
||||
confirmLabel={$i18n.t('Delete')}
|
||||
on:confirm={() => {
|
||||
deleteHandler();
|
||||
}}
|
||||
/>
|
Loading…
Reference in New Issue
Block a user