mirror of
https://github.com/langgenius/dify.git
synced 2026-05-10 05:56:31 +08:00
tweaks
This commit is contained in:
parent
3f36471ec0
commit
cfb1e0217f
@ -7,7 +7,8 @@ import { useQuery } from '@tanstack/react-query'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { getAppModeLabel } from '@/app/components/app-sidebar/app-info/app-mode-labels'
|
import { getAppModeLabel } from '@/app/components/app-sidebar/app-info/app-mode-labels'
|
||||||
import useDocumentTitle from '@/hooks/use-document-title'
|
import useDocumentTitle from '@/hooks/use-document-title'
|
||||||
import { useRouter, useSelectedLayoutSegment } from '@/next/navigation'
|
import Link from '@/next/link'
|
||||||
|
import { useSelectedLayoutSegment } from '@/next/navigation'
|
||||||
import { consoleQuery } from '@/service/client'
|
import { consoleQuery } from '@/service/client'
|
||||||
import { DeployDrawer } from '../components/deploy-drawer'
|
import { DeployDrawer } from '../components/deploy-drawer'
|
||||||
import { RollbackModal } from '../components/rollback-modal'
|
import { RollbackModal } from '../components/rollback-modal'
|
||||||
@ -20,7 +21,6 @@ export function InstanceDetail({ instanceId, children }: {
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation('deployments')
|
const { t } = useTranslation('deployments')
|
||||||
const { t: tCommon } = useTranslation()
|
const { t: tCommon } = useTranslation()
|
||||||
const router = useRouter()
|
|
||||||
const selectedSegment = useSelectedLayoutSegment()
|
const selectedSegment = useSelectedLayoutSegment()
|
||||||
const selectedTab = selectedSegment ?? undefined
|
const selectedTab = selectedSegment ?? undefined
|
||||||
const activeTab: InstanceDetailTabKey = isInstanceDetailTabKey(selectedTab) ? selectedTab : 'overview'
|
const activeTab: InstanceDetailTabKey = isInstanceDetailTabKey(selectedTab) ? selectedTab : 'overview'
|
||||||
@ -47,7 +47,7 @@ export function InstanceDetail({ instanceId, children }: {
|
|||||||
return (
|
return (
|
||||||
<div className="flex h-full flex-col items-center justify-center gap-3 bg-background-body">
|
<div className="flex h-full flex-col items-center justify-center gap-3 bg-background-body">
|
||||||
<div className="title-xl-semi-bold text-text-primary">{t('detail.notFound')}</div>
|
<div className="title-xl-semi-bold text-text-primary">{t('detail.notFound')}</div>
|
||||||
<Button variant="secondary" onClick={() => router.push('/deployments')}>
|
<Button nativeButton={false} variant="secondary" render={<Link href="/deployments" />}>
|
||||||
<span aria-hidden className="i-ri-arrow-left-line h-4 w-4" />
|
<span aria-hidden className="i-ri-arrow-left-line h-4 w-4" />
|
||||||
{t('detail.backToInstances')}
|
{t('detail.backToInstances')}
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { cn } from '@langgenius/dify-ui/cn'
|
|||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { getAppModeLabel } from '@/app/components/app-sidebar/app-info/app-mode-labels'
|
import { getAppModeLabel } from '@/app/components/app-sidebar/app-info/app-mode-labels'
|
||||||
import { useRouter } from '@/next/navigation'
|
import Link from '@/next/link'
|
||||||
import { consoleQuery } from '@/service/client'
|
import { consoleQuery } from '@/service/client'
|
||||||
import { StatusBadge } from '../components/status-badge'
|
import { StatusBadge } from '../components/status-badge'
|
||||||
import { DEPLOYMENT_PAGE_SIZE } from '../data'
|
import { DEPLOYMENT_PAGE_SIZE } from '../data'
|
||||||
@ -17,6 +17,10 @@ import {
|
|||||||
|
|
||||||
type SwitchableTab = 'deploy' | 'versions' | 'access' | 'settings'
|
type SwitchableTab = 'deploy' | 'versions' | 'access' | 'settings'
|
||||||
|
|
||||||
|
function tabHref(appId: string, tab: SwitchableTab): string {
|
||||||
|
return `/deployments/${appId}/${tab}`
|
||||||
|
}
|
||||||
|
|
||||||
function Section({ title, action, children }: {
|
function Section({ title, action, children }: {
|
||||||
title: string
|
title: string
|
||||||
action?: ReactNode
|
action?: ReactNode
|
||||||
@ -79,7 +83,7 @@ function AccessOverviewRow({ label, enabled, hint, meta }: AccessOverviewRowProp
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function overviewDeploymentStatus(status?: string) {
|
function overviewDeploymentStatus(status?: string): 'deploying' | 'deploy_failed' | 'ready' {
|
||||||
const normalized = status?.toLowerCase() ?? ''
|
const normalized = status?.toLowerCase() ?? ''
|
||||||
if (normalized.includes('deploying') || normalized.includes('pending'))
|
if (normalized.includes('deploying') || normalized.includes('pending'))
|
||||||
return 'deploying'
|
return 'deploying'
|
||||||
@ -93,7 +97,6 @@ export function OverviewTab({ instanceId }: {
|
|||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation('deployments')
|
const { t } = useTranslation('deployments')
|
||||||
const { t: tCommon } = useTranslation()
|
const { t: tCommon } = useTranslation()
|
||||||
const router = useRouter()
|
|
||||||
const input = { params: { appInstanceId: instanceId } }
|
const input = { params: { appInstanceId: instanceId } }
|
||||||
const { data: overview } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceOverview.queryOptions({
|
const { data: overview } = useQuery(consoleQuery.enterprise.appDeploy.getAppInstanceOverview.queryOptions({
|
||||||
input,
|
input,
|
||||||
@ -121,10 +124,6 @@ export function OverviewTab({ instanceId }: {
|
|||||||
|
|
||||||
const appId = overviewApp.id
|
const appId = overviewApp.id
|
||||||
const appName = overviewApp.name ?? appId
|
const appName = overviewApp.name ?? appId
|
||||||
const switchTab = (tab: SwitchableTab) => {
|
|
||||||
router.push(`/deployments/${appId}/${tab}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
const appModeLabel = getAppModeLabel(overviewApp.mode ?? 'workflow', tCommon)
|
const appModeLabel = getAppModeLabel(overviewApp.mode ?? 'workflow', tCommon)
|
||||||
const webappAccessUrl = webappUrl(overview?.access?.webappUrl)
|
const webappAccessUrl = webappUrl(overview?.access?.webappUrl)
|
||||||
const cliUrl = overview?.access?.cliUrl
|
const cliUrl = overview?.access?.cliUrl
|
||||||
@ -145,7 +144,7 @@ export function OverviewTab({ instanceId }: {
|
|||||||
<Section
|
<Section
|
||||||
title={t('overview.deploymentStatus')}
|
title={t('overview.deploymentStatus')}
|
||||||
action={(
|
action={(
|
||||||
<Button size="small" variant="secondary" onClick={() => switchTab('deploy')}>
|
<Button nativeButton={false} size="small" variant="secondary" render={<Link href={tabHref(appId, 'deploy')} />}>
|
||||||
{t('overview.viewDeployments')}
|
{t('overview.viewDeployments')}
|
||||||
<span className="i-ri-arrow-right-up-line h-3.5 w-3.5" />
|
<span className="i-ri-arrow-right-up-line h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
@ -160,20 +159,23 @@ export function OverviewTab({ instanceId }: {
|
|||||||
? t(canCreateRelease ? 'overview.noReleaseYet' : 'overview.noReleaseSourceUnavailable')
|
? t(canCreateRelease ? 'overview.noReleaseYet' : 'overview.noReleaseSourceUnavailable')
|
||||||
: t('overview.notDeployedYet')}
|
: t('overview.notDeployedYet')}
|
||||||
</div>
|
</div>
|
||||||
<Button
|
{releaseRows.length === 0
|
||||||
size="small"
|
? canCreateRelease
|
||||||
variant="primary"
|
? (
|
||||||
disabled={releaseRows.length === 0 && !canCreateRelease}
|
<Button nativeButton={false} size="small" variant="primary" render={<Link href={tabHref(appId, 'versions')} />}>
|
||||||
onClick={() => {
|
{t('overview.createRelease')}
|
||||||
if (releaseRows.length === 0) {
|
</Button>
|
||||||
switchTab('versions')
|
)
|
||||||
return
|
: (
|
||||||
}
|
<Button size="small" variant="primary" disabled>
|
||||||
openDeployDrawer({ appInstanceId: appId })
|
{t('overview.createRelease')}
|
||||||
}}
|
</Button>
|
||||||
>
|
)
|
||||||
{releaseRows.length === 0 ? t('overview.createRelease') : t('overview.deploy')}
|
: (
|
||||||
</Button>
|
<Button size="small" variant="primary" onClick={() => openDeployDrawer({ appInstanceId: appId })}>
|
||||||
|
{t('overview.deploy')}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
@ -199,7 +201,7 @@ export function OverviewTab({ instanceId }: {
|
|||||||
<Section
|
<Section
|
||||||
title={t('overview.accessStatus')}
|
title={t('overview.accessStatus')}
|
||||||
action={(
|
action={(
|
||||||
<Button size="small" variant="secondary" onClick={() => switchTab('access')}>
|
<Button nativeButton={false} size="small" variant="secondary" render={<Link href={tabHref(appId, 'access')} />}>
|
||||||
{t('overview.configureAccess')}
|
{t('overview.configureAccess')}
|
||||||
<span className="i-ri-arrow-right-up-line h-3.5 w-3.5" />
|
<span className="i-ri-arrow-right-up-line h-3.5 w-3.5" />
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import type { AppInstanceCard } from '@dify/contracts/enterprise/types.gen'
|
import type { AppInstanceCard } from '@dify/contracts/enterprise/types.gen'
|
||||||
import type { MouseEvent } from 'react'
|
|
||||||
import type { AppModeEnum } from '@/types/app'
|
import type { AppModeEnum } from '@/types/app'
|
||||||
import { cn } from '@langgenius/dify-ui/cn'
|
import { cn } from '@langgenius/dify-ui/cn'
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
DropdownMenuContent,
|
DropdownMenuContent,
|
||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLinkItem,
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@langgenius/dify-ui/dropdown-menu'
|
} from '@langgenius/dify-ui/dropdown-menu'
|
||||||
@ -17,14 +17,13 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import { AppTypeIcon } from '@/app/components/app/type-selector'
|
import { AppTypeIcon } from '@/app/components/app/type-selector'
|
||||||
import AppIcon from '@/app/components/base/app-icon'
|
import AppIcon from '@/app/components/base/app-icon'
|
||||||
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
import { useFormatTimeFromNow } from '@/hooks/use-format-time-from-now'
|
||||||
import { useRouter } from '@/next/navigation'
|
import Link from '@/next/link'
|
||||||
import { useDeploymentsStore } from '../store'
|
import { useDeploymentsStore } from '../store'
|
||||||
|
|
||||||
export function InstanceCard({ app }: {
|
export function InstanceCard({ app }: {
|
||||||
app: AppInstanceCard
|
app: AppInstanceCard
|
||||||
}) {
|
}) {
|
||||||
const { t } = useTranslation('deployments')
|
const { t } = useTranslation('deployments')
|
||||||
const router = useRouter()
|
|
||||||
const { formatTimeFromNow } = useFormatTimeFromNow()
|
const { formatTimeFromNow } = useFormatTimeFromNow()
|
||||||
const [menuOpen, setMenuOpen] = useState(false)
|
const [menuOpen, setMenuOpen] = useState(false)
|
||||||
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
|
const openDeployDrawer = useDeploymentsStore(state => state.openDeployDrawer)
|
||||||
@ -35,14 +34,7 @@ export function InstanceCard({ app }: {
|
|||||||
const appId = app.id
|
const appId = app.id
|
||||||
const appName = app.name ?? appId
|
const appName = app.name ?? appId
|
||||||
const appMode = app.mode ?? 'workflow'
|
const appMode = app.mode ?? 'workflow'
|
||||||
const navigateToDetail = () => router.push(`/deployments/${appId}/overview`)
|
const detailHref = `/deployments/${appId}/overview`
|
||||||
|
|
||||||
const handleMenuAction = (e: MouseEvent<HTMLElement>, action: () => void) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
setMenuOpen(false)
|
|
||||||
action()
|
|
||||||
}
|
|
||||||
|
|
||||||
const statusCount = (status: string) =>
|
const statusCount = (status: string) =>
|
||||||
app.statuses?.find(item => item.status === status)?.count ?? 0
|
app.statuses?.find(item => item.status === status)?.count ?? 0
|
||||||
@ -122,75 +114,78 @@ export function InstanceCard({ app }: {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
navigateToDetail()
|
|
||||||
}}
|
|
||||||
className="group relative col-span-1 inline-flex h-[160px] cursor-pointer flex-col rounded-xl border border-solid border-components-card-border bg-components-card-bg shadow-sm transition-all duration-200 ease-in-out hover:shadow-lg"
|
className="group relative col-span-1 inline-flex h-[160px] cursor-pointer flex-col rounded-xl border border-solid border-components-card-border bg-components-card-bg shadow-sm transition-all duration-200 ease-in-out hover:shadow-lg"
|
||||||
>
|
>
|
||||||
<div className="flex h-[66px] shrink-0 grow-0 items-center gap-3 px-[14px] pt-[14px] pb-3">
|
<Link
|
||||||
<div className="relative shrink-0">
|
href={detailHref}
|
||||||
<AppIcon
|
className="flex h-full flex-col rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-state-accent-solid"
|
||||||
size="large"
|
>
|
||||||
iconType="emoji"
|
<div className="flex h-[66px] shrink-0 grow-0 items-center gap-3 px-[14px] pt-[14px] pb-3">
|
||||||
icon={app.icon}
|
<div className="relative shrink-0">
|
||||||
background={app.iconBackground}
|
<AppIcon
|
||||||
/>
|
size="large"
|
||||||
<AppTypeIcon
|
iconType="emoji"
|
||||||
type={appMode as AppModeEnum}
|
icon={app.icon}
|
||||||
wrapperClassName="absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm"
|
background={app.iconBackground}
|
||||||
className="h-3 w-3"
|
/>
|
||||||
/>
|
<AppTypeIcon
|
||||||
</div>
|
type={appMode as AppModeEnum}
|
||||||
<div className="w-0 grow py-px">
|
wrapperClassName="absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm"
|
||||||
<div className="flex items-center text-sm leading-5 font-semibold text-text-secondary">
|
className="h-3 w-3"
|
||||||
<div className="truncate" title={appName}>{appName}</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="truncate text-[10px] leading-[18px] font-medium text-text-tertiary" title={appModeLabel}>
|
<div className="w-0 grow py-px">
|
||||||
{appModeLabel}
|
<div className="flex items-center text-sm leading-5 font-semibold text-text-secondary">
|
||||||
|
<div className="truncate" title={appName}>{appName}</div>
|
||||||
|
</div>
|
||||||
|
<div className="truncate text-[10px] leading-[18px] font-medium text-text-tertiary" title={appModeLabel}>
|
||||||
|
{appModeLabel}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="flex grow flex-col gap-2 px-[14px]">
|
||||||
<div className="flex grow flex-col gap-2 px-[14px]">
|
<Tooltip>
|
||||||
<Tooltip>
|
<TooltipTrigger
|
||||||
<TooltipTrigger
|
render={(
|
||||||
render={(
|
<div className="flex min-w-0 items-center gap-1.5">
|
||||||
<div className="flex min-w-0 items-center gap-1.5">
|
<span
|
||||||
<span
|
className={cn(
|
||||||
className={cn(
|
'inline-flex h-5 shrink-0 items-center gap-1 rounded-md px-1.5 system-xs-medium',
|
||||||
'inline-flex h-5 shrink-0 items-center gap-1 rounded-md px-1.5 system-xs-medium',
|
healthPillClass,
|
||||||
healthPillClass,
|
)}
|
||||||
)}
|
>
|
||||||
>
|
<span className={cn('h-1.5 w-1.5 rounded-full', healthDotClass)} />
|
||||||
<span className={cn('h-1.5 w-1.5 rounded-full', healthDotClass)} />
|
{primaryText}
|
||||||
{primaryText}
|
|
||||||
</span>
|
|
||||||
{secondaryParts.length > 0 && (
|
|
||||||
<span className="truncate system-xs-regular text-text-tertiary">
|
|
||||||
{secondaryParts.join(' · ')}
|
|
||||||
</span>
|
</span>
|
||||||
)}
|
{secondaryParts.length > 0 && (
|
||||||
</div>
|
<span className="truncate system-xs-regular text-text-tertiary">
|
||||||
)}
|
{secondaryParts.join(' · ')}
|
||||||
/>
|
</span>
|
||||||
<TooltipContent>{statusTooltip}</TooltipContent>
|
)}
|
||||||
</Tooltip>
|
</div>
|
||||||
<div className="flex min-w-0 items-center gap-1.5 system-xs-regular text-text-tertiary">
|
)}
|
||||||
<span aria-hidden className="i-ri-apps-2-line h-3.5 w-3.5 shrink-0 text-text-quaternary" />
|
/>
|
||||||
<span className="truncate" title={app.sourceAppName ?? appName}>
|
<TooltipContent>{statusTooltip}</TooltipContent>
|
||||||
{t('card.fromApp', { name: app.sourceAppName ?? appName })}
|
</Tooltip>
|
||||||
</span>
|
<div className="flex min-w-0 items-center gap-1.5 system-xs-regular text-text-tertiary">
|
||||||
|
<span aria-hidden className="i-ri-apps-2-line h-3.5 w-3.5 shrink-0 text-text-quaternary" />
|
||||||
|
<span className="truncate" title={app.sourceAppName ?? appName}>
|
||||||
|
{t('card.fromApp', { name: app.sourceAppName ?? appName })}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
<div className="absolute right-0 bottom-1 left-0 flex h-[42px] shrink-0 items-center pt-1 pr-[6px] pb-[6px] pl-[14px]">
|
||||||
<div className="absolute right-0 bottom-1 left-0 flex h-[42px] shrink-0 items-center pt-1 pr-[6px] pb-[6px] pl-[14px]">
|
<div className="mr-[41px] flex min-w-0 grow items-center gap-1.5 system-xs-regular text-text-tertiary">
|
||||||
<div className="mr-[41px] flex min-w-0 grow items-center gap-1.5 system-xs-regular text-text-tertiary">
|
<span aria-hidden className="i-ri-time-line h-3.5 w-3.5 shrink-0 text-text-quaternary" />
|
||||||
<span aria-hidden className="i-ri-time-line h-3.5 w-3.5 shrink-0 text-text-quaternary" />
|
<span className="truncate">
|
||||||
<span className="truncate">
|
{lastDeployedAt
|
||||||
{lastDeployedAt
|
? t('card.lastDeployed', { time: formatTimeFromNow(lastDeployedAt) })
|
||||||
? t('card.lastDeployed', { time: formatTimeFromNow(lastDeployedAt) })
|
: t('card.neverDeployed')}
|
||||||
: t('card.neverDeployed')}
|
</span>
|
||||||
</span>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</Link>
|
||||||
|
<div className="pointer-events-none absolute right-0 bottom-1 left-0 flex h-[42px] shrink-0 items-center pt-1 pr-[6px] pb-[6px] pl-[14px]">
|
||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
'absolute top-1/2 right-[6px] flex -translate-y-1/2 items-center transition-opacity',
|
'absolute top-1/2 right-[6px] flex -translate-y-1/2 items-center transition-opacity',
|
||||||
@ -206,10 +201,6 @@ export function InstanceCard({ app }: {
|
|||||||
menuOpen ? 'bg-state-base-hover shadow-none' : 'bg-transparent',
|
menuOpen ? 'bg-state-base-hover shadow-none' : 'bg-transparent',
|
||||||
'flex h-8 w-8 items-center justify-center rounded-md border-none p-2 hover:bg-state-base-hover',
|
'flex h-8 w-8 items-center justify-center rounded-md border-none p-2 hover:bg-state-base-hover',
|
||||||
)}
|
)}
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation()
|
|
||||||
e.preventDefault()
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<span aria-hidden className="i-ri-more-fill h-4 w-4 text-text-tertiary" />
|
<span aria-hidden className="i-ri-more-fill h-4 w-4 text-text-tertiary" />
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
@ -217,16 +208,19 @@ export function InstanceCard({ app }: {
|
|||||||
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[216px]">
|
<DropdownMenuContent placement="bottom-end" sideOffset={4} popupClassName="w-[216px]">
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
className="gap-2 px-3"
|
className="gap-2 px-3"
|
||||||
onClick={e => handleMenuAction(e, () => openDeployDrawer({ appInstanceId: appId }))}
|
onClick={() => {
|
||||||
|
setMenuOpen(false)
|
||||||
|
openDeployDrawer({ appInstanceId: appId })
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
<span className="system-sm-regular text-text-secondary">{t('card.menu.deploy')}</span>
|
<span className="system-sm-regular text-text-secondary">{t('card.menu.deploy')}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuLinkItem
|
||||||
className="gap-2 px-3"
|
className="gap-2 px-3"
|
||||||
onClick={e => handleMenuAction(e, navigateToDetail)}
|
render={<Link href={detailHref} />}
|
||||||
>
|
>
|
||||||
<span className="system-sm-regular text-text-secondary">{t('card.menu.viewDetail')}</span>
|
<span className="system-sm-regular text-text-secondary">{t('card.menu.viewDetail')}</span>
|
||||||
</DropdownMenuItem>
|
</DropdownMenuLinkItem>
|
||||||
<DropdownMenuSeparator />
|
<DropdownMenuSeparator />
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
aria-disabled
|
aria-disabled
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user