dify/web/features/agent-v2/agent-detail/access/components/workflow-references-table.tsx
yyh d0ea5a5e0d
chore(agent-v2): sync changes (#38442)
Co-authored-by: Joel <iamjoel007@gmail.com>
Co-authored-by: zyssyz123 <916125788@qq.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: 林玮 (Jade Lin) <linw1995@icloud.com>
Co-authored-by: 盐粒 Yanli <mail@yanli.one>
2026-07-06 13:51:33 +00:00

177 lines
6.3 KiB
TypeScript

'use client'
import type { AgentIconType, AgentReferencingWorkflowResponse } from '@dify/contracts/api/console/agent/types.gen'
import type { ReactNode } from 'react'
import { Button } from '@langgenius/dify-ui/button'
import { useQuery } from '@tanstack/react-query'
import { useTranslation } from 'react-i18next'
import AppIcon from '@/app/components/base/app-icon'
import useTimestamp from '@/hooks/use-timestamp'
import Link from '@/next/link'
import { consoleQuery } from '@/service/client'
type WorkflowReferencesTableProps = {
agentId: string
enabled?: boolean
}
const workflowTableColSpan = 5
const getWorkflowReferenceHref = (reference: AgentReferencingWorkflowResponse) => `/app/${reference.app_id}/workflow`
export function WorkflowReferencesTable({
agentId,
enabled = true,
}: WorkflowReferencesTableProps) {
const { t } = useTranslation('agentV2')
const { t: tCommon } = useTranslation('common')
const workflowReferencesQuery = useQuery(consoleQuery.agent.byAgentId.referencingWorkflows.get.queryOptions({
input: {
params: {
agent_id: agentId,
},
},
enabled,
}))
const workflowReferences = workflowReferencesQuery.data?.data ?? []
return (
<div className="min-w-0 overflow-x-auto">
<table className="w-full min-w-[1212px] table-fixed border-collapse">
<colgroup>
<col className="w-[572px]" />
<col className="w-40" />
<col className="w-32" />
<col className="w-[204px]" />
<col className="w-36" />
</colgroup>
<thead>
<tr className="h-7 rounded-lg bg-background-section-burn text-left system-xs-semibold-uppercase text-text-tertiary">
<th scope="col" className="rounded-l-lg px-3 font-semibold">
{t('agentDetail.access.workflow.table.name')}
</th>
<th scope="col" className="px-3 font-semibold">
{t('agentDetail.access.workflow.table.version')}
</th>
<th scope="col" className="px-3 font-semibold">
{t('agentDetail.access.workflow.table.nodes')}
</th>
<th scope="col" className="px-3 font-semibold">
{t('agentDetail.access.workflow.table.lastUpdated')}
</th>
<th scope="col" className="rounded-r-lg px-3 font-semibold">
{t('agentDetail.access.workflow.table.actions')}
</th>
</tr>
</thead>
<tbody className="system-sm-regular text-text-secondary">
{enabled && workflowReferencesQuery.isPending && (
<WorkflowAccessStateRow>
{t('agentDetail.access.workflow.loading')}
</WorkflowAccessStateRow>
)}
{enabled && workflowReferencesQuery.isError && (
<WorkflowAccessStateRow>
<div className="flex items-center justify-center gap-2">
<span>{t('agentDetail.access.workflow.loadFailed')}</span>
<Button
variant="secondary"
size="small"
onClick={() => {
void workflowReferencesQuery.refetch()
}}
>
{tCommon('operation.retry')}
</Button>
</div>
</WorkflowAccessStateRow>
)}
{enabled && workflowReferencesQuery.isSuccess && workflowReferences.length === 0 && (
<WorkflowAccessStateRow>
{t('agentDetail.access.workflow.empty')}
</WorkflowAccessStateRow>
)}
{enabled && workflowReferencesQuery.isSuccess && workflowReferences.map(reference => (
<WorkflowAccessRow
key={`${reference.app_id}:${reference.workflow_id}`}
reference={reference}
/>
))}
</tbody>
</table>
</div>
)
}
function WorkflowAccessRow({
reference,
}: {
reference: AgentReferencingWorkflowResponse
}) {
const { t } = useTranslation('agentV2')
const { formatTime } = useTimestamp()
const imageUrl = (reference.app_icon_type === 'image' || reference.app_icon_type === 'link') ? reference.app_icon : undefined
const iconType = (imageUrl ? 'image' : reference.app_icon_type) as AgentIconType | null | undefined
const updatedAt = reference.app_updated_at != null
? formatTime(reference.app_updated_at, t('roster.dateTimeFormat'))
: t('agentDetail.access.workflow.notAvailable')
const nodeCount = reference.node_ids?.length ?? 0
return (
<tr className="h-10 border-b border-divider-subtle hover:bg-background-default-hover">
<td className="px-3">
<div className="flex min-w-0 items-center gap-2.5">
<span aria-hidden className="shrink-0">
<AppIcon
size="small"
rounded
iconType={iconType}
icon={reference.app_icon ?? undefined}
background={reference.app_icon_background}
imageUrl={imageUrl}
/>
</span>
<span className="truncate">
{reference.app_name}
</span>
</div>
</td>
<td className="truncate px-3" translate="no">
{reference.workflow_version}
</td>
<td className="px-3 tabular-nums">
{t('agentDetail.access.workflow.nodeCount', { count: nodeCount })}
</td>
<td className="px-3 tabular-nums" translate="no">
{updatedAt}
</td>
<td className="px-3">
<Link
href={getWorkflowReferenceHref(reference)}
target="_blank"
rel="noopener noreferrer"
aria-label={t('agentDetail.access.workflow.openInStudioFor', { name: reference.app_name })}
className="inline-flex items-center gap-0.5 rounded-sm text-text-secondary hover:text-text-accent hover:underline focus-visible:ring-2 focus-visible:ring-state-accent-solid focus-visible:outline-hidden"
>
{t('agentDetail.access.workflow.openInStudio')}
<span aria-hidden className="i-ri-external-link-line size-4" />
</Link>
</td>
</tr>
)
}
function WorkflowAccessStateRow({
children,
}: {
children: ReactNode
}) {
return (
<tr className="h-20 border-b border-divider-subtle">
<td colSpan={workflowTableColSpan} aria-live="polite" className="px-3 text-center text-text-tertiary">
{children}
</td>
</tr>
)
}