'use client' import type { AppInstanceCard } from '@dify/contracts/enterprise/types.gen' import type { InstanceDetailTabKey } from '../detail/tabs' import { cn } from '@langgenius/dify-ui/cn' import { DropdownMenu, DropdownMenuContent, DropdownMenuLinkItem, DropdownMenuTrigger, } from '@langgenius/dify-ui/dropdown-menu' import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { useTranslation } from 'react-i18next' import { AppTypeIcon } from '@/app/components/app/type-selector' import AppIcon from '@/app/components/base/app-icon' import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now' import Link from '@/next/link' import { toAppMode } from '../app-mode' const INSTANCE_CARD_MENU_TAB_KEYS = ['deploy', 'versions', 'settings'] satisfies InstanceDetailTabKey[] function getInstanceTabHref(appInstanceId: string, tabKey: InstanceDetailTabKey) { return `/deployments/${appInstanceId}/${tabKey}` } export function InstanceCard({ app }: { app: AppInstanceCard }) { const { t } = useTranslation('deployments') const { formatTimeFromNow } = useFormatTimeFromNow() if (!app.id) return null const appInstanceId = app.id const appName = app.name ?? appInstanceId const appMode = toAppMode(app.mode) const detailHref = `/deployments/${appInstanceId}/overview` const statusCount = (status: string) => app.statuses?.find(item => item.status === status)?.count ?? 0 const failedCount = statusCount('failed') + statusCount('deploy_failed') const deployingCount = statusCount('deploying') const readyCount = statusCount('ready') const envCount = failedCount + deployingCount + readyCount const lastDeployedAt = app.lastDeployedAt ? Date.parse(app.lastDeployedAt) : null const primaryStatus: 'none' | 'failed' | 'deploying' | 'ready' = envCount === 0 ? 'none' : failedCount > 0 ? 'failed' : deployingCount > 0 ? 'deploying' : 'ready' const primaryText = primaryStatus === 'none' ? t('card.notDeployed') : primaryStatus === 'failed' ? t('card.failed', { count: failedCount }) : primaryStatus === 'deploying' ? t('card.deploying', { count: deployingCount }) : t('card.ready', { count: readyCount }) const secondaryParts: string[] = [] if (primaryStatus === 'failed' && deployingCount > 0) secondaryParts.push(t('card.deploying', { count: deployingCount })) if ((primaryStatus === 'failed' || primaryStatus === 'deploying') && readyCount > 0) secondaryParts.push(t('card.ready', { count: readyCount })) const statusSummaryLabel = (status?: string) => { if (status === 'failed' || status === 'deploy_failed') return t('status.deployFailed') if (status === 'deploying') return t('status.deploying') if (status === 'ready') return t('status.ready') return status || 'unknown' } const statusSummaryTooltip = app.statuses?.filter(item => item.count && item.status !== 'undeployed') ?? [] const statusTooltip = primaryStatus === 'none' ? t('card.tooltip.notDeployed') : (
{t('overview.deploymentStatus')}
{statusSummaryTooltip.map(item => (
{statusSummaryLabel(item.status)} {item.count}
))}
) const healthPillClass = primaryStatus === 'none' ? 'text-text-tertiary bg-background-section-burn' : primaryStatus === 'failed' ? 'text-util-colors-red-red-700 bg-util-colors-red-red-50' : primaryStatus === 'deploying' ? 'text-util-colors-warning-warning-700 bg-util-colors-warning-warning-50' : 'text-util-colors-green-green-700 bg-util-colors-green-green-50' const healthDotClass = primaryStatus === 'none' ? 'bg-text-quaternary' : primaryStatus === 'failed' ? 'bg-util-colors-red-red-500' : primaryStatus === 'deploying' ? 'bg-util-colors-warning-warning-500 animate-pulse' : 'bg-util-colors-green-green-500' const appModeLabel = t(`appMode.${appMode}`, { defaultValue: appMode }) return (
{appName}
{appModeLabel}
{primaryText} {secondaryParts.length > 0 && ( {secondaryParts.join(' ยท ')} )}
)} /> {statusTooltip}
{t('card.fromApp', { name: app.sourceAppName ?? appName })}
{lastDeployedAt ? t('card.lastDeployed', { time: formatTimeFromNow(lastDeployedAt) }) : t('card.neverDeployed')}
) } function InstanceCardActions({ appInstanceId }: { appInstanceId: string }) { const { t } = useTranslation('deployments') return ( {INSTANCE_CARD_MENU_TAB_KEYS.map((tabKey) => { const href = getInstanceTabHref(appInstanceId, tabKey) return ( } > {t(`tabs.${tabKey}.name`)} ) })} ) }