From b07b68b5314e5847de2c05fa70d9baba29abfd83 Mon Sep 17 00:00:00 2001 From: Stephen Zhou <38493346+hyoban@users.noreply.github.com> Date: Sat, 9 May 2026 16:58:29 +0800 Subject: [PATCH] tweak dropdown menuy --- .../skills/how-to-write-component/SKILL.md | 2 + .../deployments/list/instance-card.tsx | 113 +++++++----------- 2 files changed, 45 insertions(+), 70 deletions(-) diff --git a/.agents/skills/how-to-write-component/SKILL.md b/.agents/skills/how-to-write-component/SKILL.md index 5d916645f7..e7de1fe09f 100644 --- a/.agents/skills/how-to-write-component/SKILL.md +++ b/.agents/skills/how-to-write-component/SKILL.md @@ -59,6 +59,8 @@ Do not copy existing code patterns blindly. Existing implementations are referen - Split deeper components by the data and state each layer actually needs. Each component should access only necessary data, and state should be pushed down to the lowest owner. - Keep cohesive forms, menu bodies, and one-off helpers local unless they need their own state, reuse, or semantic boundary. - Separate hidden secondary surfaces from the trigger's main flow. For dialogs, dropdowns, popovers, and similar branches, extract a small local component that owns the trigger, open state, and hidden content when that branch obscures the parent flow. +- Preserve composability by separating behavior ownership from layout ownership. A dropdown action may own its trigger, open state, and menu content; the caller should own placement such as slots, offsets, and alignment. +- Avoid unnecessary DOM hierarchy. Do not add wrapper elements unless they provide layout, semantics, accessibility, state ownership, or integration with a library API; prefer fragments or styling an existing element when possible. - Avoid shallow wrappers and prop renaming. Call the original function directly unless the wrapper adds validation, orchestration, error handling, state ownership, or a real semantic boundary. ## Navigation diff --git a/web/features/deployments/list/instance-card.tsx b/web/features/deployments/list/instance-card.tsx index dd58089400..dcff431ad9 100644 --- a/web/features/deployments/list/instance-card.tsx +++ b/web/features/deployments/list/instance-card.tsx @@ -1,26 +1,28 @@ '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, - DropdownMenuItem, DropdownMenuLinkItem, - DropdownMenuSeparator, DropdownMenuTrigger, } from '@langgenius/dify-ui/dropdown-menu' import { Tooltip, TooltipContent, TooltipTrigger } from '@langgenius/dify-ui/tooltip' -import { useSetAtom } from 'jotai' -import { useState } from 'react' 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 { openDeployDrawerAtom } from '../store' import { toAppMode } from '../utils' +const INSTANCE_CARD_MENU_TAB_KEYS = ['deploy', 'versions', 'access', 'settings'] satisfies InstanceDetailTabKey[] + +function getInstanceTabHref(appInstanceId: string, tabKey: InstanceDetailTabKey) { + return `/deployments/${appInstanceId}/${tabKey}` +} + export function InstanceCard({ app }: { app: AppInstanceCard }) { @@ -184,72 +186,43 @@ export function InstanceCard({ app }: { - - - ) -} - -function InstanceCardActions({ appInstanceId, detailHref }: { - appInstanceId: string - detailHref: string -}) { - const { t } = useTranslation('deployments') - const [menuOpen, setMenuOpen] = useState(false) - const openDeployDrawer = useSetAtom(openDeployDrawerAtom) - - return ( -
-
- - - - - {menuOpen && ( - - { - setMenuOpen(false) - openDeployDrawer({ appInstanceId }) - }} - > - {t('card.menu.deploy')} - - } - > - {t('card.menu.viewDetail')} - - - { - e.stopPropagation() - e.preventDefault() - }} - > - {t('card.menu.delete')} - - - )} - +
+
) } + +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`)} + + ) + })} + + + ) +}