Files
dify/web/app/components/app-sidebar/app-info/app-info-detail-panel.tsx
Stephen Zhou 1873b22e96 refactor: update to tailwind v4 (#34415)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-04-02 07:06:11 +00:00

152 lines
4.7 KiB
TypeScript

import type { Operation } from './app-operations'
import type { AppInfoModalType } from './use-app-info-actions'
import type { App, AppSSO } from '@/types/app'
import {
RiDeleteBinLine,
RiEditLine,
RiExchange2Line,
RiFileCopy2Line,
RiFileDownloadLine,
RiFileUploadLine,
} from '@remixicon/react'
import * as React from 'react'
import { useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import CardView from '@/app/(commonLayout)/app/(appDetailLayout)/[appId]/overview/card-view'
import Button from '@/app/components/base/button'
import ContentDialog from '@/app/components/base/content-dialog'
import { AppModeEnum } from '@/types/app'
import AppIcon from '../../base/app-icon'
import { getAppModeLabel } from './app-mode-labels'
import AppOperations from './app-operations'
type AppInfoDetailPanelProps = {
appDetail: App & Partial<AppSSO>
show: boolean
onClose: () => void
openModal: (modal: Exclude<AppInfoModalType, null>) => void
exportCheck: () => void
}
const AppInfoDetailPanel = ({
appDetail,
show,
onClose,
openModal,
exportCheck,
}: AppInfoDetailPanelProps) => {
const { t } = useTranslation()
const primaryOperations = useMemo<Operation[]>(() => [
{
id: 'edit',
title: t('editApp', { ns: 'app' }),
icon: <RiEditLine />,
onClick: () => openModal('edit'),
},
{
id: 'duplicate',
title: t('duplicate', { ns: 'app' }),
icon: <RiFileCopy2Line />,
onClick: () => openModal('duplicate'),
},
{
id: 'export',
title: t('export', { ns: 'app' }),
icon: <RiFileDownloadLine />,
onClick: exportCheck,
},
], [t, openModal, exportCheck])
const secondaryOperations = useMemo<Operation[]>(() => [
...(appDetail.mode === AppModeEnum.ADVANCED_CHAT || appDetail.mode === AppModeEnum.WORKFLOW)
? [{
id: 'import',
title: t('common.importDSL', { ns: 'workflow' }),
icon: <RiFileUploadLine />,
onClick: () => openModal('importDSL'),
}]
: [],
{
id: 'divider-1',
title: '',
icon: <></>,
onClick: () => {},
type: 'divider' as const,
},
{
id: 'delete',
title: t('operation.delete', { ns: 'common' }),
icon: <RiDeleteBinLine />,
onClick: () => openModal('delete'),
},
], [appDetail.mode, t, openModal])
const switchOperation = useMemo(() => {
if (appDetail.mode !== AppModeEnum.COMPLETION && appDetail.mode !== AppModeEnum.CHAT)
return null
return {
id: 'switch',
title: t('switch', { ns: 'app' }),
icon: <RiExchange2Line />,
onClick: () => openModal('switch'),
}
}, [appDetail.mode, t, openModal])
return (
<ContentDialog
show={show}
onClose={onClose}
className="absolute bottom-2 left-2 top-2 flex w-[420px] flex-col rounded-2xl p-0!"
>
<div className="flex shrink-0 flex-col items-start justify-center gap-3 self-stretch p-4">
<div className="flex items-center gap-3 self-stretch">
<AppIcon
size="large"
iconType={appDetail.icon_type}
icon={appDetail.icon}
background={appDetail.icon_background}
imageUrl={appDetail.icon_url}
/>
<div className="flex flex-1 flex-col items-start justify-center overflow-hidden">
<div className="w-full truncate text-text-secondary system-md-semibold">{appDetail.name}</div>
<div className="text-text-tertiary system-2xs-medium-uppercase">
{getAppModeLabel(appDetail.mode, t)}
</div>
</div>
</div>
{appDetail.description && (
<div className="overflow-wrap-anywhere max-h-[105px] w-full max-w-full overflow-y-auto whitespace-normal wrap-break-word text-text-tertiary system-xs-regular">
{appDetail.description}
</div>
)}
<AppOperations
gap={4}
primaryOperations={primaryOperations}
secondaryOperations={secondaryOperations}
/>
</div>
<CardView
appId={appDetail.id}
isInPanel={true}
className="flex flex-1 flex-col gap-2 overflow-auto px-2 py-1"
/>
{switchOperation && (
<div className="flex min-h-fit shrink-0 flex-col items-start justify-center gap-3 self-stretch pb-2">
<Button
size="medium"
variant="ghost"
className="gap-0.5"
onClick={switchOperation.onClick}
>
{switchOperation.icon}
<span className="text-text-tertiary system-sm-medium">{switchOperation.title}</span>
</Button>
</div>
)}
</ContentDialog>
)
}
export default React.memo(AppInfoDetailPanel)