fix(web): show unsupported resources (vm) disabled; Overview uses new-model framing

1) Deploy Sub Agent: stop hiding resource types AM doesn't support yet (vm) —
   show them but disabled with a "暂不支持" badge + tooltip, so users see their
   bindings instead of wondering where the vm went.
2) Overview (概述/cockpit): replaced the old task-model status cards
   (Running/Completed/Failed/裁决 + "code delivery runs") with template-agent
   semantics — Agent 总数 / 运行中 / 启动中 / 异常, "最近部署 · 我的 Agent",
   all Chinese. Dropped unused t()/useTranslation/PlayCircle.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-04 17:59:12 +08:00
co-authored by Claude Opus 4.8
parent c4316f7fcd
commit 3a2ad36e5d
3 changed files with 57 additions and 60 deletions
+1 -1
View File
@@ -20,7 +20,7 @@
<meta property="og:type" content="website" />
<meta name="theme-color" content="#7B6BE3" />
<link rel="icon" href="/favicon.ico"><script defer src="/static/js/vendor-radix.829c7e3fad.js"></script><script defer src="/static/js/vendor-tanstack.632dbe8908.js"></script><script defer src="/static/js/lib-react.5c8909c28c.js"></script><script defer src="/static/js/6374.bc21d8b214.js"></script><script defer src="/static/js/index.0ac7693b4a.js"></script><link href="/static/css/index.08eae7aeba.css" rel="stylesheet"></head>
<link rel="icon" href="/favicon.ico"><script defer src="/static/js/vendor-radix.829c7e3fad.js"></script><script defer src="/static/js/vendor-tanstack.632dbe8908.js"></script><script defer src="/static/js/lib-react.5c8909c28c.js"></script><script defer src="/static/js/6374.bc21d8b214.js"></script><script defer src="/static/js/index.8097a36f3b.js"></script><link href="/static/css/index.e9e9dfbb24.css" rel="stylesheet"></head>
<body>
<div id="root"></div>
@@ -1,14 +1,12 @@
import { useMemo } from 'react'
import { useQuery } from '@tanstack/react-query'
import { Link } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
import {
AlertTriangle,
ArrowUpRight,
Building2,
CheckCircle2,
CircleDashed,
PlayCircle,
Rocket,
ScrollText,
} from 'lucide-react'
@@ -159,8 +157,6 @@ function formatRelative(value: string | number | undefined): string {
}
export function CockpitView() {
const { t } = useTranslation()
const deploymentsQuery = useQuery({
queryKey: ['cockpit', 'agents'],
queryFn: listMyAgents,
@@ -177,24 +173,17 @@ export function CockpitView() {
const stats = useMemo(() => {
const list: AgentItem[] = deploymentsQuery.data ?? []
const counters: Record<StatusKey, number> = {
running: 0,
success: 0,
failed: 0,
pending: 0,
}
list.forEach((d) => {
counters[classifyStatus(d.status || '')]++
})
const total = list.length || 1
const successRate = Math.round(
((counters.success + counters.running) / total) * 100
)
return {
list,
counters,
successRate: Number.isFinite(successRate) ? successRate : 0,
let running = 0
let starting = 0
let abnormal = 0
for (const a of list) {
const v = (a.status || '').toLowerCase()
if (['running', 'active', 'ready'].includes(v)) running++
else if (['pending', 'starting', 'provisioning'].includes(v)) starting++
else if (['failed', 'error', 'unhealthy', 'crashed', 'stopped'].includes(v))
abnormal++
}
return { list, running, starting, abnormal, total: list.length }
}, [deploymentsQuery.data])
const recentDeployments = useMemo(
@@ -211,43 +200,43 @@ export function CockpitView() {
return (
<div className='space-y-6'>
{/* Tier 1: Status */}
{/* Tier 1: Status (template-agent model) */}
<div className='grid grid-cols-2 gap-3 lg:grid-cols-4'>
<StatCard
label={t('Running agents')}
value={String(stats.counters.running)}
hint={t('Agent runs in progress')}
icon={PlayCircle}
tone='primary'
label='Agent 总数'
value={String(stats.total)}
hint='我部署的 Agent'
icon={Rocket}
tone='muted'
/>
<StatCard
label={t('Completed')}
value={String(stats.counters.success)}
hint={t('Finished with a deliverable')}
label='运行中'
value={String(stats.running)}
hint='正常对外服务'
icon={CheckCircle2}
tone='success'
/>
<StatCard
label={t('Failed')}
value={String(stats.counters.failed)}
hint={t('Failed or no deliverable')}
icon={AlertTriangle}
tone='danger'
/>
<StatCard
label={t('Pending')}
value={String(stats.counters.pending)}
hint={t('Queued / waiting / needs codegen')}
label='启动中'
value={String(stats.starting)}
hint='正在拉起'
icon={CircleDashed}
tone='warn'
/>
<StatCard
label='异常'
value={String(stats.abnormal)}
hint='已停止 / 失败 / 不健康'
icon={AlertTriangle}
tone='danger'
/>
</div>
{/* Usage & cost — the CURRENT user's OWN lifetime usage (from /api/user/self),
not the admin-global aggregate. */}
<div>
<p className='text-muted-foreground mb-2 text-[11px] font-semibold tracking-[0.16em] uppercase'>
{t('Usage & cost')}
费用与消耗
</p>
<WalletStatsCard
user={selfQuery.data ?? null}
@@ -261,15 +250,13 @@ export function CockpitView() {
<header className='mb-4 flex items-center justify-between'>
<div>
<p className='text-[11px] font-semibold tracking-[0.16em] text-muted-foreground uppercase'>
{t('Recent deployments')}
最近部署
</p>
<h3 className='mt-1 text-lg font-semibold'>
{t('Live code delivery runs')}
</h3>
<h3 className='mt-1 text-lg font-semibold'>我的 Agent</h3>
</div>
<Button asChild size='sm' variant='ghost' className='gap-1'>
<Link to='/deploy-agent'>
{t('Open Deployments')}
去部署
<ArrowUpRight className='h-3.5 w-3.5' />
</Link>
</Button>
@@ -286,7 +273,7 @@ export function CockpitView() {
</div>
) : recentDeployments.length === 0 ? (
<div className='rounded-xl border border-dashed border-[color-mix(in_oklch,var(--primary)_22%,var(--border))] p-8 text-center text-sm text-muted-foreground'>
{t('No deployments yet. Trigger a run to populate the cockpit.')}
还没有部署 Agent。去「部署 Sub Agent」部署一个。
</div>
) : (
<ul className='space-y-2'>
@@ -317,7 +304,7 @@ export function CockpitView() {
>
<Link to='/deploy-agent'>
<ScrollText className='mr-1 h-3.5 w-3.5' />
{t('Inspect')}
详情
</Link>
</Button>
</li>
@@ -138,12 +138,11 @@ export function DeploySubAgentPage() {
s.includes(id) ? s.filter((x) => x !== id) : [...s, id]
)
// Only resource types the agent runtime currently supports (git / mysql /
// postgres / azure-blob). vm & others are hidden until AM adds them.
// Resource types the agent runtime currently supports (git / mysql / postgres
// / azure-blob). Others (e.g. vm) are SHOWN but disabled, with an "暂不支持"
// hint, until AM adds support — don't hide the user's bindings.
const SUPPORTED_TYPES = new Set(['git', 'database', 'blob'])
const resources = (resourcesQ.data ?? []).filter((r) =>
SUPPORTED_TYPES.has(r.resource_type)
)
const resources = resourcesQ.data ?? []
const templates = templatesQ.data ?? []
const canDeploy = Boolean(selectedTemplate) && !deploy.isPending
@@ -182,17 +181,22 @@ export function DeploySubAgentPage() {
<div className='mt-4 grid gap-3 sm:grid-cols-2'>
{resources.map((r) => {
const Icon = RESOURCE_ICON[r.resource_type] ?? Database
const on = selectedBindings.includes(r.id)
const supported = SUPPORTED_TYPES.has(r.resource_type)
const on = supported && selectedBindings.includes(r.id)
return (
<button
type='button'
key={r.id}
onClick={() => toggleBinding(r.id)}
disabled={!supported}
title={supported ? undefined : '当前 Agent 暂不支持此类资源'}
onClick={() => supported && toggleBinding(r.id)}
className={cn(
'flex items-center gap-3 rounded-xl border p-3 text-left transition',
on
? 'border-primary/60 bg-[color-mix(in_oklch,var(--primary)_12%,transparent)]'
: 'border-[color-mix(in_oklch,var(--primary)_18%,var(--border))] hover:border-primary/40'
!supported
? 'cursor-not-allowed border-[color-mix(in_oklch,var(--border)_60%,transparent)] opacity-50'
: on
? 'border-primary/60 bg-[color-mix(in_oklch,var(--primary)_12%,transparent)]'
: 'border-[color-mix(in_oklch,var(--primary)_18%,var(--border))] hover:border-primary/40'
)}
>
<span
@@ -209,7 +213,13 @@ export function DeploySubAgentPage() {
{resourceTypeLabel(r.resource_type)} · {r.provider}
</span>
</span>
{on && <CheckCircle2 className='text-primary h-4 w-4 shrink-0' />}
{!supported ? (
<span className='shrink-0 rounded-full bg-muted/50 px-2 py-0.5 text-[10px] text-muted-foreground'>
暂不支持
</span>
) : on ? (
<CheckCircle2 className='text-primary h-4 w-4 shrink-0' />
) : null}
</button>
)
})}