Merge branch 'fix/upload-restrictions' into deploy/dev

This commit is contained in:
twwu 2025-12-10 15:33:18 +08:00
commit c05d9bd813
27 changed files with 311 additions and 115 deletions

View File

@ -422,7 +422,6 @@ class DatasetApi(Resource):
raise NotFound("Dataset not found.")
payload = DatasetUpdatePayload.model_validate(console_ns.payload or {})
payload_data = payload.model_dump(exclude_unset=True)
current_user, current_tenant_id = current_account_with_tenant()
# check embedding model setting
if (
@ -434,6 +433,7 @@ class DatasetApi(Resource):
dataset.tenant_id, payload.embedding_model_provider, payload.embedding_model
)
payload.is_multimodal = is_multimodal
payload_data = payload.model_dump(exclude_unset=True)
# The role of the current user in the ta table must be admin, owner, editor, or dataset_operator
DatasetPermissionService.check_permission(
current_user, dataset, payload.permission, payload.partial_member_list

View File

@ -417,13 +417,6 @@ class RetrievalService:
DocumentSegment.status == "completed",
DocumentSegment.id == segment_id,
)
.options(
load_only(
DocumentSegment.id,
DocumentSegment.content,
DocumentSegment.answer,
)
)
.first()
)

View File

@ -1,13 +1,9 @@
import {
useCallback,
useEffect,
useRef,
useState,
} from 'react'
import { TaskStatus } from '@/app/components/plugins/types'
import type { PluginStatus } from '@/app/components/plugins/types'
import {
useMutationClearAllTaskPlugin,
useMutationClearTaskPlugin,
usePluginTaskList,
} from '@/service/use-plugins'
@ -18,7 +14,6 @@ export const usePluginTaskStatus = () => {
handleRefetch,
} = usePluginTaskList()
const { mutateAsync } = useMutationClearTaskPlugin()
const { mutateAsync: mutateAsyncClearAll } = useMutationClearAllTaskPlugin()
const allPlugins = pluginTasks.map(task => task.plugins.map((plugin) => {
return {
...plugin,
@ -45,10 +40,6 @@ export const usePluginTaskStatus = () => {
})
handleRefetch()
}, [mutateAsync, handleRefetch])
const handleClearAllErrorPlugin = useCallback(async () => {
await mutateAsyncClearAll()
handleRefetch()
}, [mutateAsyncClearAll, handleRefetch])
const totalPluginsLength = allPlugins.length
const runningPluginsLength = runningPlugins.length
const errorPluginsLength = errorPlugins.length
@ -60,26 +51,6 @@ export const usePluginTaskStatus = () => {
const isSuccess = successPluginsLength === totalPluginsLength && totalPluginsLength > 0
const isFailed = runningPluginsLength === 0 && (errorPluginsLength + successPluginsLength) === totalPluginsLength && totalPluginsLength > 0 && errorPluginsLength > 0
const [opacity, setOpacity] = useState(1)
const timerRef = useRef<NodeJS.Timeout | null>(null)
useEffect(() => {
if (isSuccess) {
if (timerRef.current) {
clearTimeout(timerRef.current)
timerRef.current = null
}
if (opacity > 0) {
timerRef.current = setTimeout(() => {
setOpacity(v => v - 0.1)
}, 200)
}
}
if (!isSuccess)
setOpacity(1)
}, [isSuccess, opacity])
return {
errorPlugins,
successPlugins,
@ -94,7 +65,5 @@ export const usePluginTaskStatus = () => {
isSuccess,
isFailed,
handleClearErrorPlugin,
handleClearAllErrorPlugin,
opacity,
}
}

View File

@ -1,4 +1,5 @@
import {
useCallback,
useMemo,
useState,
} from 'react'
@ -6,6 +7,7 @@ import {
RiCheckboxCircleFill,
RiErrorWarningFill,
RiInstallLine,
RiLoaderLine,
} from '@remixicon/react'
import { useTranslation } from 'react-i18next'
import { usePluginTaskStatus } from './hooks'
@ -14,7 +16,6 @@ import {
PortalToFollowElemContent,
PortalToFollowElemTrigger,
} from '@/app/components/base/portal-to-follow-elem'
import Tooltip from '@/app/components/base/tooltip'
import Button from '@/app/components/base/button'
import ProgressCircle from '@/app/components/base/progress-bar/progress-circle'
import CardIcon from '@/app/components/plugins/card/base/card-icon'
@ -22,6 +23,7 @@ import cn from '@/utils/classnames'
import { useGetLanguage } from '@/context/i18n'
import useGetIcon from '@/app/components/plugins/install-plugin/base/use-get-icon'
import DownloadingIcon from '@/app/components/header/plugins-nav/downloading-icon'
import Tooltip from '@/app/components/base/tooltip'
const PluginTasks = () => {
const { t } = useTranslation()
@ -29,6 +31,8 @@ const PluginTasks = () => {
const [open, setOpen] = useState(false)
const {
errorPlugins,
successPlugins,
runningPlugins,
runningPluginsLength,
successPluginsLength,
errorPluginsLength,
@ -39,33 +43,69 @@ const PluginTasks = () => {
isSuccess,
isFailed,
handleClearErrorPlugin,
handleClearAllErrorPlugin,
opacity,
} = usePluginTaskStatus()
const { getIconUrl } = useGetIcon()
const handleClearAllWithModal = useCallback(async () => {
// Clear all completed plugins (success and error) but keep running ones
const completedPlugins = [...successPlugins, ...errorPlugins]
// Clear all completed plugins individually
for (const plugin of completedPlugins)
await handleClearErrorPlugin(plugin.taskId, plugin.plugin_unique_identifier)
// Only close modal if no plugins are still installing
if (runningPluginsLength === 0)
setOpen(false)
}, [successPlugins, errorPlugins, handleClearErrorPlugin, runningPluginsLength])
const handleClearErrorsWithModal = useCallback(async () => {
// Clear only error plugins, not all plugins
for (const plugin of errorPlugins)
await handleClearErrorPlugin(plugin.taskId, plugin.plugin_unique_identifier)
// Only close modal if no plugins are still installing
if (runningPluginsLength === 0)
setOpen(false)
}, [errorPlugins, handleClearErrorPlugin, runningPluginsLength])
const handleClearSingleWithModal = useCallback(async (taskId: string, pluginId: string) => {
await handleClearErrorPlugin(taskId, pluginId)
// Only close modal if no plugins are still installing
if (runningPluginsLength === 0)
setOpen(false)
}, [handleClearErrorPlugin, runningPluginsLength])
const tip = useMemo(() => {
if (isInstalling)
return t('plugin.task.installing', { installingLength: runningPluginsLength })
if (isInstallingWithSuccess)
return t('plugin.task.installingWithSuccess', { installingLength: runningPluginsLength, successLength: successPluginsLength })
if (isInstallingWithError)
return t('plugin.task.installingWithError', { installingLength: runningPluginsLength, successLength: successPluginsLength, errorLength: errorPluginsLength })
if (isInstallingWithSuccess)
return t('plugin.task.installingWithSuccess', { installingLength: runningPluginsLength, successLength: successPluginsLength })
if (isInstalling)
return t('plugin.task.installing')
if (isFailed)
return t('plugin.task.installError', { errorLength: errorPluginsLength })
}, [isInstalling, isInstallingWithSuccess, isInstallingWithError, isFailed, errorPluginsLength, runningPluginsLength, successPluginsLength, t])
return t('plugin.task.installedError', { errorLength: errorPluginsLength })
if (isSuccess)
return t('plugin.task.installSuccess', { successLength: successPluginsLength })
return t('plugin.task.installed')
}, [
errorPluginsLength,
isFailed,
isInstalling,
isInstallingWithError,
isInstallingWithSuccess,
isSuccess,
runningPluginsLength,
successPluginsLength,
t,
])
if (!totalPluginsLength)
// Show icon if there are any plugin tasks (completed, running, or failed)
// Only hide when there are absolutely no plugin tasks
if (totalPluginsLength === 0)
return null
return (
<div
className={cn('flex items-center', opacity < 0 && 'hidden')}
style={{ opacity }}
>
<div className='flex items-center'>
<PortalToFollowElem
open={open}
onOpenChange={setOpen}
@ -77,15 +117,20 @@ const PluginTasks = () => {
>
<PortalToFollowElemTrigger
onClick={() => {
if (isFailed)
if (isFailed || isInstalling || isInstallingWithSuccess || isInstallingWithError || isSuccess)
setOpen(v => !v)
}}
>
<Tooltip popupContent={tip}>
<Tooltip
popupContent={tip}
asChild
offset={8}
>
<div
className={cn(
'relative flex h-8 w-8 items-center justify-center rounded-lg border-[0.5px] border-components-button-secondary-border bg-components-button-secondary-bg shadow-xs hover:bg-components-button-secondary-bg-hover',
(isInstallingWithError || isFailed) && 'cursor-pointer border-components-button-destructive-secondary-border-hover bg-state-destructive-hover hover:bg-state-destructive-hover-alt',
(isInstalling || isInstallingWithSuccess || isSuccess) && 'cursor-pointer hover:bg-components-button-secondary-bg-hover',
)}
id="plugin-task-trigger"
>
@ -124,7 +169,7 @@ const PluginTasks = () => {
)
}
{
isSuccess && (
(isSuccess || (successPluginsLength > 0 && runningPluginsLength === 0 && errorPluginsLength === 0)) && (
<RiCheckboxCircleFill className='h-3.5 w-3.5 text-text-success' />
)
}
@ -138,52 +183,129 @@ const PluginTasks = () => {
</Tooltip>
</PortalToFollowElemTrigger>
<PortalToFollowElemContent className='z-[11]'>
<div className='w-[320px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 pb-2 shadow-lg'>
<div className='system-sm-semibold-uppercase sticky top-0 flex h-7 items-center justify-between px-2 pt-1'>
{t('plugin.task.installedError', { errorLength: errorPluginsLength })}
<Button
className='shrink-0'
size='small'
variant='ghost'
onClick={() => handleClearAllErrorPlugin()}
>
{t('plugin.task.clearAll')}
</Button>
</div>
<div className='max-h-[400px] overflow-y-auto'>
{
errorPlugins.map(errorPlugin => (
<div
key={errorPlugin.plugin_unique_identifier}
className='flex rounded-lg p-2 hover:bg-state-base-hover'
>
<div className='relative mr-2 flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'>
<RiErrorWarningFill className='absolute -bottom-0.5 -right-0.5 z-10 h-3 w-3 text-text-destructive' />
<CardIcon
size='tiny'
src={getIconUrl(errorPlugin.icon)}
/>
</div>
<div className='grow'>
<div className='system-md-regular truncate text-text-secondary'>
{errorPlugin.labels[language]}
</div>
<div className='system-xs-regular break-all text-text-destructive'>
{errorPlugin.message}
</div>
</div>
<Button
className='shrink-0'
size='small'
variant='ghost'
onClick={() => handleClearErrorPlugin(errorPlugin.taskId, errorPlugin.plugin_unique_identifier)}
<div className='w-[360px] rounded-xl border-[0.5px] border-components-panel-border bg-components-panel-bg-blur p-1 shadow-lg'>
{/* Running Plugins */}
{runningPlugins.length > 0 && (
<>
<div className='system-sm-semibold-uppercase sticky top-0 flex h-7 items-center justify-between px-2 pt-1'>
{t('plugin.task.installing')} ({runningPlugins.length})
</div>
<div className='max-h-[200px] overflow-y-auto'>
{runningPlugins.map(runningPlugin => (
<div
key={runningPlugin.plugin_unique_identifier}
className='flex items-center rounded-lg p-2 hover:bg-state-base-hover'
>
{t('common.operation.clear')}
</Button>
</div>
))
}
</div>
<div className='relative mr-2 flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'>
<RiLoaderLine className='absolute -bottom-0.5 -right-0.5 z-10 h-3 w-3 animate-spin text-text-accent' />
<CardIcon
size='tiny'
src={getIconUrl(runningPlugin.icon)}
/>
</div>
<div className='grow'>
<div className='system-md-regular truncate text-text-secondary'>
{runningPlugin.labels[language]}
</div>
<div className='system-xs-regular text-text-tertiary'>
{t('plugin.task.installing')}
</div>
</div>
</div>
))}
</div>
</>
)}
{/* Success Plugins */}
{successPlugins.length > 0 && (
<>
<div className='system-sm-semibold-uppercase sticky top-0 flex h-7 items-center justify-between px-2 pt-1'>
{t('plugin.task.installed')} ({successPlugins.length})
<Button
className='shrink-0'
size='small'
variant='ghost'
onClick={() => handleClearAllWithModal()}
>
{t('plugin.task.clearAll')}
</Button>
</div>
<div className='max-h-[200px] overflow-y-auto'>
{successPlugins.map(successPlugin => (
<div
key={successPlugin.plugin_unique_identifier}
className='flex items-center rounded-lg p-2 hover:bg-state-base-hover'
>
<div className='relative mr-2 flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'>
<RiCheckboxCircleFill className='absolute -bottom-0.5 -right-0.5 z-10 h-3 w-3 text-text-success' />
<CardIcon
size='tiny'
src={getIconUrl(successPlugin.icon)}
/>
</div>
<div className='grow'>
<div className='system-md-regular truncate text-text-secondary'>
{successPlugin.labels[language]}
</div>
<div className='system-xs-regular text-text-success'>
{successPlugin.message || t('plugin.task.installed')}
</div>
</div>
</div>
))}
</div>
</>
)}
{/* Error Plugins */}
{errorPlugins.length > 0 && (
<>
<div className='system-sm-semibold-uppercase sticky top-0 flex h-7 items-center justify-between px-2 pt-1'>
{t('plugin.task.installError', { errorLength: errorPlugins.length })}
<Button
className='shrink-0'
size='small'
variant='ghost'
onClick={() => handleClearErrorsWithModal()}
>
{t('plugin.task.clearAll')}
</Button>
</div>
<div className='max-h-[200px] overflow-y-auto'>
{errorPlugins.map(errorPlugin => (
<div
key={errorPlugin.plugin_unique_identifier}
className='flex items-center rounded-lg p-2 hover:bg-state-base-hover'
>
<div className='relative mr-2 flex h-6 w-6 items-center justify-center rounded-md border-[0.5px] border-components-panel-border-subtle bg-background-default-dodge'>
<RiErrorWarningFill className='absolute -bottom-0.5 -right-0.5 z-10 h-3 w-3 text-text-destructive' />
<CardIcon
size='tiny'
src={getIconUrl(errorPlugin.icon)}
/>
</div>
<div className='grow'>
<div className='system-md-regular truncate text-text-secondary'>
{errorPlugin.labels[language]}
</div>
<div className='system-xs-regular break-all text-text-destructive'>
{errorPlugin.message}
</div>
</div>
<Button
className='shrink-0'
size='small'
variant='ghost'
onClick={() => handleClearSingleWithModal(errorPlugin.taskId, errorPlugin.plugin_unique_identifier)}
>
{t('common.operation.clear')}
</Button>
</div>
))}
</div>
</>
)}
</div>
</PortalToFollowElemContent>
</PortalToFollowElem>

View File

@ -230,6 +230,11 @@ const translation = {
installing: 'Installation von {{installingLength}} Plugins, 0 erledigt.',
installError:
'{{errorLength}} Plugins konnten nicht installiert werden, klicken Sie hier, um sie anzusehen',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
allCategories: 'Alle Kategorien',
install: '{{num}} Installationen',

View File

@ -270,12 +270,17 @@ const translation = {
partnerTip: 'Verified by a Dify partner',
},
task: {
installing: 'Installing {{installingLength}} plugins, 0 done.',
installing: 'Installing plugins',
installingWithSuccess: 'Installing {{installingLength}} plugins, {{successLength}} success.',
installingWithError: 'Installing {{installingLength}} plugins, {{successLength}} success, {{errorLength}} failed',
installError: '{{errorLength}} plugins failed to install, click to view',
installedError: '{{errorLength}} plugins failed to install',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
clearAll: 'Clear all',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
requestAPlugin: 'Request a plugin',
publishPlugins: 'Publish plugins',

View File

@ -230,6 +230,11 @@ const translation = {
'Los complementos {{errorLength}} no se pudieron instalar, haga clic para ver',
installingWithError:
'Instalando plugins {{installingLength}}, {{successLength}} éxito, {{errorLength}} fallido',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
fromMarketplace: 'De Marketplace',
endpointsEnabled: '{{num}} conjuntos de puntos finales habilitados',

View File

@ -223,6 +223,11 @@ const translation = {
'نصب پلاگین های {{installingLength}}، {{successLength}} موفقیت آمیز است.',
installingWithError:
'نصب پلاگین های {{installingLength}}، {{successLength}} با موفقیت مواجه شد، {{errorLength}} ناموفق بود',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
searchTools: 'ابزارهای جستجو...',
findMoreInMarketplace: 'اطلاعات بیشتر در Marketplace',

View File

@ -228,6 +228,11 @@ const translation = {
installedError: '{{errorLength}} les plugins nont pas pu être installés',
clearAll: 'Effacer tout',
installing: 'Installation des plugins {{installingLength}}, 0 fait.',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
search: 'Rechercher',
installAction: 'Installer',

View File

@ -227,6 +227,11 @@ const translation = {
'{{installingLength}} प्लगइन्स स्थापित कर रहे हैं, {{successLength}} सफल, {{errorLength}} विफल',
installingWithSuccess:
'{{installingLength}} प्लगइन्स स्थापित कर रहे हैं, {{successLength}} सफल।',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
installFrom: 'से इंस्टॉल करें',
fromMarketplace: 'मार्केटप्लेस से',

View File

@ -261,6 +261,11 @@ const translation = {
installingWithError: 'Memasang {{installingLength}} plugin, {{successLength}} berhasil, {{errorLength}} gagal',
installError: 'Gagal menginstal plugin {{errorLength}}, klik untuk melihat',
installedError: 'Gagal menginstal {{errorLength}} plugin',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
auth: {
customCredentialUnavailable: 'Kredensial kustom saat ini tidak tersedia',

View File

@ -208,6 +208,11 @@ const translation = {
installedError: 'Impossibile installare i plugin di {{errorLength}}',
installingWithError: 'Installazione dei plugin {{installingLength}}, {{successLength}} successo, {{errorLength}} fallito',
installingWithSuccess: 'Installazione dei plugin {{installingLength}}, {{successLength}} successo.',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
searchInMarketplace: 'Cerca nel Marketplace',
endpointsEnabled: '{{num}} set di endpoint abilitati',

View File

@ -208,6 +208,11 @@ const translation = {
installedError: '{{errorLength}} プラグインのインストールに失敗しました',
installingWithError: '{{installingLength}}個のプラグインをインストール中、{{successLength}}件成功、{{errorLength}}件失敗',
installing: '{{installingLength}}個のプラグインをインストール中、0 個完了。',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
from: 'インストール元',
install: '{{num}} インストール',

View File

@ -208,6 +208,11 @@ const translation = {
installingWithError: '{{installingLength}} 플러그인 설치, {{successLength}} 성공, {{errorLength}} 실패',
installError: '{{errorLength}} 플러그인 설치 실패, 보려면 클릭하십시오.',
clearAll: '모두 지우기',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
installAction: '설치하다',
searchTools: '검색 도구...',

View File

@ -208,6 +208,11 @@ const translation = {
installingWithSuccess: 'Instalacja wtyczek {{installingLength}}, {{successLength}} powodzenie.',
clearAll: 'Wyczyść wszystko',
installingWithError: 'Instalacja wtyczek {{installingLength}}, {{successLength}} powodzenie, {{errorLength}} niepowodzenie',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
search: 'Szukać',
installFrom: 'ZAINSTALUJ Z',

View File

@ -208,6 +208,11 @@ const translation = {
installingWithError: 'Instalando plug-ins {{installingLength}}, {{successLength}} sucesso, {{errorLength}} falhou',
installing: 'Instalando plugins {{installingLength}}, 0 feito.',
clearAll: 'Apagar tudo',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
installAction: 'Instalar',
endpointsEnabled: '{{num}} conjuntos de endpoints habilitados',

View File

@ -208,6 +208,11 @@ const translation = {
installingWithError: 'Instalarea pluginurilor {{installingLength}}, {{successLength}} succes, {{errorLength}} eșuat',
installingWithSuccess: 'Instalarea pluginurilor {{installingLength}}, {{successLength}} succes.',
installing: 'Instalarea pluginurilor {{installingLength}}, 0 terminat.',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
fromMarketplace: 'Din Marketplace',
from: 'Din',

View File

@ -208,6 +208,11 @@ const translation = {
installingWithSuccess: 'Установка плагинов {{installingLength}}, {{successLength}} успех.',
installedError: 'плагины {{errorLength}} не удалось установить',
installError: 'Плагины {{errorLength}} не удалось установить, нажмите для просмотра',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
install: '{{num}} установок',
searchCategories: 'Поиск категорий',

View File

@ -211,6 +211,11 @@ const translation = {
installingWithSuccess: 'Namestitev {{installingLength}} dodatkov, {{successLength}} uspešnih.',
installedError: '{{errorLength}} vtičnikov ni uspelo namestiti',
installingWithError: 'Namestitev {{installingLength}} vtičnikov, {{successLength}} uspešnih, {{errorLength}} neuspešnih',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
endpointsEnabled: '{{num}} nizov končnih točk omogočenih',
search: 'Iskanje',

View File

@ -208,6 +208,11 @@ const translation = {
installedError: '{{errorLength}} ปลั๊กอินติดตั้งไม่สําเร็จ',
clearAll: 'ล้างทั้งหมด',
installError: '{{errorLength}} ปลั๊กอินติดตั้งไม่สําเร็จ คลิกเพื่อดู',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
searchCategories: 'หมวดหมู่การค้นหา',
searchInMarketplace: 'ค้นหาใน Marketplace',

View File

@ -208,6 +208,11 @@ const translation = {
installingWithSuccess: '{{installingLength}} eklentileri yükleniyor, {{successLength}} başarılı.',
installError: '{{errorLength}} eklentileri yüklenemedi, görüntülemek için tıklayın',
installingWithError: '{{installingLength}} eklentileri yükleniyor, {{successLength}} başarılı, {{errorLength}} başarısız oldu',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
allCategories: 'Tüm Kategoriler',
installAction: 'Yüklemek',

View File

@ -208,6 +208,11 @@ const translation = {
installError: 'Плагіни {{errorLength}} не вдалося встановити, натисніть, щоб переглянути',
installing: 'Встановлення плагінів {{installingLength}}, 0 виконано.',
installingWithSuccess: 'Встановлення плагінів {{installingLength}}, успіх {{successLength}}.',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
from: 'Від',
searchInMarketplace: 'Пошук у Marketplace',

View File

@ -208,6 +208,11 @@ const translation = {
installError: '{{errorLength}} plugin không cài đặt được, nhấp để xem',
installedError: '{{errorLength}} plugin không cài đặt được',
clearAll: 'Xóa tất cả',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
from: 'Từ',
installAction: 'Cài đặt',

View File

@ -270,12 +270,17 @@ const translation = {
partnerTip: '此插件由 Dify 合作伙伴认证',
},
task: {
installing: '{{installingLength}} 个插件安装中0 已完成',
installing: '正在安装插件',
installingWithSuccess: '{{installingLength}} 个插件安装中,{{successLength}} 安装成功',
installingWithError: '{{installingLength}} 个插件安装中,{{successLength}} 安装成功,{{errorLength}} 安装失败',
installError: '{{errorLength}} 个插件安装失败,点击查看',
installedError: '{{errorLength}} 个插件安装失败',
installSuccess: '{{successLength}} 个插件安装成功',
installed: '已安装',
clearAll: '清除所有',
runningPlugins: '正在安装的插件',
successPlugins: '安装成功的插件',
errorPlugins: '安装失败的插件',
},
requestAPlugin: '申请插件',
publishPlugins: '发布插件',

View File

@ -208,6 +208,11 @@ const translation = {
installingWithSuccess: '安裝 {{installingLength}} 個插件,{{successLength}} 成功。',
clearAll: '全部清除',
installing: '安裝 {{installingLength}} 個插件0 個完成。',
installSuccess: '{{successLength}} plugins installed successfully',
installed: 'Installed',
runningPlugins: 'Installing Plugins',
successPlugins: 'Successfully Installed Plugins',
errorPlugins: 'Failed to Install Plugins',
},
requestAPlugin: '申请插件',
publishPlugins: '發佈插件',

View File

@ -2,16 +2,22 @@ import '@testing-library/jest-dom'
import { cleanup } from '@testing-library/react'
// Fix for @headlessui/react compatibility with happy-dom
// headlessui tries to set focus property which is read-only in happy-dom
// headlessui tries to override focus properties which may be read-only in happy-dom
if (typeof window !== 'undefined') {
// Ensure window.focus is writable for headlessui
if (!Object.getOwnPropertyDescriptor(window, 'focus')?.writable) {
Object.defineProperty(window, 'focus', {
value: jest.fn(),
writable: true,
configurable: true,
})
const ensureWritable = (target: object, prop: string) => {
const descriptor = Object.getOwnPropertyDescriptor(target, prop)
if (descriptor && !descriptor.writable) {
const original = descriptor.value ?? descriptor.get?.call(target)
Object.defineProperty(target, prop, {
value: typeof original === 'function' ? original : jest.fn(),
writable: true,
configurable: true,
})
}
}
ensureWritable(window, 'focus')
ensureWritable(HTMLElement.prototype, 'focus')
}
afterEach(() => {

View File

@ -634,7 +634,8 @@ export const usePluginTaskList = (category?: PluginCategoryEnum | string) => {
export const useMutationClearTaskPlugin = () => {
return useMutation({
mutationFn: ({ taskId, pluginId }: { taskId: string; pluginId: string }) => {
return post<{ success: boolean }>(`/workspaces/current/plugin/tasks/${taskId}/delete/${pluginId}`)
const encodedPluginId = encodeURIComponent(pluginId)
return post<{ success: boolean }>(`/workspaces/current/plugin/tasks/${taskId}/delete/${encodedPluginId}`)
},
})
}