'use client' import type { ComponentProps, PropsWithoutRef } from 'react' import type { InstanceDetailTabKey } from './tabs' import type { NavIcon } from '@/app/components/app-sidebar/nav-link' import { cn } from '@langgenius/dify-ui/cn' import { DialogTrigger } from '@langgenius/dify-ui/dialog' import { Kbd, KbdGroup } from '@langgenius/dify-ui/kbd' import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' import { formatForDisplay } from '@tanstack/react-hotkeys' import { useAtomValue } from 'jotai' import { useTranslation } from 'react-i18next' import NavLink from '@/app/components/app-sidebar/nav-link' import Divider from '@/app/components/base/divider' import SidebarLeftArrowIcon from '@/app/components/base/icons/src/vender/SidebarLeftArrowIcon' import { SkeletonContainer, SkeletonRectangle } from '@/app/components/base/skeleton' import { DetailSidebarToggleButton } from '@/app/components/detail-sidebar/toggle-button' import { gotoAnythingDialogHandle } from '@/app/components/goto-anything/dialog-handle' import { GOTO_ANYTHING_HOTKEY } from '@/app/components/goto-anything/hotkeys' import Link from '@/next/link' import { usePathname } from '@/next/navigation' import { DeploymentActionsMenu } from '../deployment-actions' import { deploymentRouteAppInstanceIdAtom } from '../route-state' import { TitleTooltip } from '../shared/components/title-tooltip' import { deploymentDetailAppInstanceAtom, deploymentDetailAppInstanceIsErrorAtom, deploymentDetailAppInstanceIsLoadingAtom, } from './state' type TabDef = { key: InstanceDetailTabKey icon: NavIcon selectedIcon: NavIcon hidden?: boolean } type TailwindNavIconProps = PropsWithoutRef> & { title?: string titleId?: string } function OverviewIcon({ className }: TailwindNavIconProps) { return } function OverviewSelectedIcon({ className }: TailwindNavIconProps) { return } function DeployIcon({ className }: TailwindNavIconProps) { return } function DeploySelectedIcon({ className }: TailwindNavIconProps) { return } function VersionsIcon({ className }: TailwindNavIconProps) { return } function VersionsSelectedIcon({ className }: TailwindNavIconProps) { return } function AccessIcon({ className }: TailwindNavIconProps) { return } function AccessSelectedIcon({ className }: TailwindNavIconProps) { return } function ApiIcon({ className }: TailwindNavIconProps) { return } function ApiSelectedIcon({ className }: TailwindNavIconProps) { return } const DEPLOYMENT_TABS: TabDef[] = [ { key: 'overview', icon: OverviewIcon, selectedIcon: OverviewSelectedIcon }, { key: 'instances', icon: DeployIcon, selectedIcon: DeploySelectedIcon }, { key: 'releases', icon: VersionsIcon, selectedIcon: VersionsSelectedIcon }, { key: 'access', icon: AccessIcon, selectedIcon: AccessSelectedIcon, hidden: true }, { key: 'api-tokens', icon: ApiIcon, selectedIcon: ApiSelectedIcon }, ] function DeploymentIcon({ expand }: { expand: boolean }) { return (
) } function DeploymentDetailInstanceInfo({ appInstanceId, expand, }: { appInstanceId: string expand: boolean }) { const { t } = useTranslation('deployments') const overview = useAtomValue(deploymentDetailAppInstanceAtom) const isOverviewLoading = useAtomValue(deploymentDetailAppInstanceIsLoadingAtom) const isOverviewError = useAtomValue(deploymentDetailAppInstanceIsErrorAtom) const app = overview?.appInstance const isLoading = !app && isOverviewLoading const isUnavailable = !app || isOverviewError const instanceName = app ? app.displayName : appInstanceId return (
{isLoading ? ( <> {expand && ( )} ) : isUnavailable ? ( <>
{expand && (
{t(($) => $['detail.notFound'])}
{appInstanceId}
)} ) : ( <> {expand && ( <>
{instanceName}
{app.description && (
{app.description}
)}
)} )}
) } export function DeploymentDetailTop({ expand = true, onToggle, }: { expand?: boolean onToggle?: () => void }) { const { t } = useTranslation() if (!expand) { return (
{onToggle && ( } className="size-8 rounded-[10px] border-0 bg-transparent px-0 text-text-tertiary shadow-none hover:border-0 hover:bg-state-base-hover hover:text-text-secondary" /> )}
) } return (
$['mainNav.home'], { ns: 'common' })} className="flex shrink-0 items-center rounded-lg py-2 pr-1.5 pl-0.5 text-text-tertiary transition-colors hover:bg-background-default-hover hover:text-text-secondary" > / {t(($) => $['menus.deployments'], { ns: 'common' })}
$['gotoAnything.searchTitle'], { ns: 'app' })} className="flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-[10px] text-text-tertiary transition-colors hover:bg-state-base-hover hover:text-text-secondary focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden" > } /> } /> {t(($) => $['gotoAnything.quickAction'], { ns: 'app' })} {GOTO_ANYTHING_HOTKEY.split('+').map((key) => ( {formatForDisplay(key)} ))} {onToggle && ( } className="size-8 rounded-[10px] border-0 bg-transparent px-0 text-text-tertiary shadow-none hover:border-0 hover:bg-state-base-hover hover:text-text-secondary" /> )}
) } export function DeploymentDetailSection({ expand = true }: { expand?: boolean }) { const { t } = useTranslation('deployments') const pathname = usePathname() const appInstanceId = useAtomValue(deploymentRouteAppInstanceIdAtom) if (!appInstanceId) return null return (
{!expand && (
)}
) }