mirror of
https://github.com/open-webui/open-webui.git
synced 2026-08-28 05:27:35 +00:00
feat(ui): add bulk actions hamburger menu to workspace Models page (#22484)
Add hamburger menu with bulk actions (Enable All, Disable All, Show All, Hide All) to the workspace Models page filter bar, matching the functionality from the admin Settings Models page.
This commit is contained in:
parent
b535275b17
commit
bc9ee7b4d9
1 changed files with 115 additions and 0 deletions
|
|
@ -29,6 +29,8 @@
|
|||
import { capitalizeFirstLetter, copyToClipboard } from '$lib/utils';
|
||||
|
||||
import EllipsisHorizontal from '../icons/EllipsisHorizontal.svelte';
|
||||
import CheckCircle from '../icons/CheckCircle.svelte';
|
||||
import Minus from '../icons/Minus.svelte';
|
||||
import ModelMenu from './Models/ModelMenu.svelte';
|
||||
import ModelDeleteConfirmDialog from '../common/ConfirmDialog.svelte';
|
||||
import Tooltip from '../common/Tooltip.svelte';
|
||||
|
|
@ -41,6 +43,9 @@
|
|||
import XMark from '../icons/XMark.svelte';
|
||||
import EyeSlash from '../icons/EyeSlash.svelte';
|
||||
import Eye from '../icons/Eye.svelte';
|
||||
import { DropdownMenu } from 'bits-ui';
|
||||
import { flyAndScale } from '$lib/utils/transitions';
|
||||
import Dropdown from '$lib/components/common/Dropdown.svelte';
|
||||
import ViewSelector from './common/ViewSelector.svelte';
|
||||
import TagSelector from './common/TagSelector.svelte';
|
||||
import Pagination from '../common/Pagination.svelte';
|
||||
|
|
@ -224,6 +229,50 @@
|
|||
await updateUserSettings(localStorage.token, { ui: $settings });
|
||||
};
|
||||
|
||||
const enableAllHandler = async () => {
|
||||
const modelsToEnable = (models ?? []).filter((m) => !(m.is_active ?? true));
|
||||
// Optimistic UI update
|
||||
modelsToEnable.forEach((m) => (m.is_active = true));
|
||||
models = models;
|
||||
// Sync with server
|
||||
await Promise.all(modelsToEnable.map((model) => toggleModelById(localStorage.token, model.id)));
|
||||
};
|
||||
|
||||
const disableAllHandler = async () => {
|
||||
const modelsToDisable = (models ?? []).filter((m) => m.is_active ?? true);
|
||||
// Optimistic UI update
|
||||
modelsToDisable.forEach((m) => (m.is_active = false));
|
||||
models = models;
|
||||
// Sync with server
|
||||
await Promise.all(
|
||||
modelsToDisable.map((model) => toggleModelById(localStorage.token, model.id))
|
||||
);
|
||||
};
|
||||
|
||||
const showAllHandler = async () => {
|
||||
const modelsToShow = (models ?? []).filter((m) => m?.meta?.hidden === true);
|
||||
// Optimistic UI update
|
||||
modelsToShow.forEach((m) => {
|
||||
m.meta = { ...m.meta, hidden: false };
|
||||
});
|
||||
models = models;
|
||||
// Sync with server
|
||||
await Promise.all(modelsToShow.map((model) => updateModelById(localStorage.token, model.id, model)));
|
||||
toast.success($i18n.t('All models are now visible'));
|
||||
};
|
||||
|
||||
const hideAllHandler = async () => {
|
||||
const modelsToHide = (models ?? []).filter((m) => !(m?.meta?.hidden ?? false));
|
||||
// Optimistic UI update
|
||||
modelsToHide.forEach((m) => {
|
||||
m.meta = { ...m.meta, hidden: true };
|
||||
});
|
||||
models = models;
|
||||
// Sync with server
|
||||
await Promise.all(modelsToHide.map((model) => updateModelById(localStorage.token, model.id, model)));
|
||||
toast.success($i18n.t('All models are now hidden'));
|
||||
};
|
||||
|
||||
onMount(async () => {
|
||||
viewOption = localStorage.workspaceViewOption ?? '';
|
||||
page = 1;
|
||||
|
|
@ -420,6 +469,7 @@
|
|||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
|
@ -453,6 +503,71 @@
|
|||
/>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="flex-1"></div>
|
||||
|
||||
<Dropdown>
|
||||
<Tooltip content={$i18n.t('Actions')}>
|
||||
<button
|
||||
class="p-1 rounded-lg hover:bg-gray-100 dark:hover:bg-gray-800 transition"
|
||||
type="button"
|
||||
>
|
||||
<EllipsisHorizontal className="size-4" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
|
||||
<div slot="content">
|
||||
<DropdownMenu.Content
|
||||
class="w-full max-w-[170px] rounded-xl p-1 border border-gray-100 dark:border-gray-800 z-50 bg-white dark:bg-gray-850 dark:text-white shadow-sm"
|
||||
sideOffset={-2}
|
||||
side="bottom"
|
||||
align="end"
|
||||
transition={flyAndScale}
|
||||
>
|
||||
<DropdownMenu.Item
|
||||
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
enableAllHandler();
|
||||
}}
|
||||
>
|
||||
<CheckCircle className="size-4" />
|
||||
<div class="flex items-center">{$i18n.t('Enable All')}</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
disableAllHandler();
|
||||
}}
|
||||
>
|
||||
<Minus className="size-4" />
|
||||
<div class="flex items-center">{$i18n.t('Disable All')}</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<hr class="border-gray-100 dark:border-gray-800 my-1" />
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
showAllHandler();
|
||||
}}
|
||||
>
|
||||
<Eye className="size-4" />
|
||||
<div class="flex items-center">{$i18n.t('Show All')}</div>
|
||||
</DropdownMenu.Item>
|
||||
|
||||
<DropdownMenu.Item
|
||||
class="select-none flex gap-2 items-center px-3 py-1.5 text-sm font-medium cursor-pointer hover:bg-gray-50 dark:hover:bg-gray-800 rounded-md"
|
||||
on:click={() => {
|
||||
hideAllHandler();
|
||||
}}
|
||||
>
|
||||
<EyeSlash className="size-4" />
|
||||
<div class="flex items-center">{$i18n.t('Hide All')}</div>
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</div>
|
||||
</Dropdown>
|
||||
</div>
|
||||
|
||||
{#if models !== null}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue