fix(web): Overview usage shows the CURRENT user own consumption, not global

The reused LogStatCards aggregates ALL users for admin accounts, so an admin
(root) saw gateway-wide totals (e.g. $628 / 339M tokens / 5218 req) instead of
their own. And per-user windowed quota-dates were empty (0). Replaced with the
user's own lifetime usage from /api/user/self (used_quota = spend, quota =
remaining, request_count), formatted with formatQuota. Real, non-zero, per-user.
(LogStatCards also gained an optional userScope prop for future per-user use.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-03 11:04:54 +08:00
co-authored by Claude Opus 4.8
parent 448571496d
commit 42883dac63
3 changed files with 56 additions and 5 deletions
@@ -3,22 +3,33 @@ import { useQuery } from '@tanstack/react-query'
import { Link } from '@tanstack/react-router'
import { useTranslation } from 'react-i18next'
import {
Activity,
AlertTriangle,
ArrowUpRight,
Building2,
CheckCircle2,
CircleDashed,
Coins,
PlayCircle,
Rocket,
ScrollText,
Wallet,
} from 'lucide-react'
import { api } from '@/lib/api'
import { formatQuota } from '@/lib/format'
import { cn } from '@/lib/utils'
import { Button } from '@/components/ui/button'
import {
listAgentDeployments,
type AgentDeployment,
} from '@/features/agent-console/api'
import { LogStatCards } from '@/features/dashboard/components/models/log-stat-cards'
// Current user's own lifetime usage (NOT the admin-global aggregate).
type SelfUsage = { quota?: number; used_quota?: number; request_count?: number }
async function getSelfUsage(): Promise<SelfUsage> {
const res = await api.get<{ data?: SelfUsage }>('/api/user/self')
return res.data?.data ?? {}
}
type StatusKey = 'running' | 'success' | 'failed' | 'pending'
@@ -145,6 +156,14 @@ export function CockpitView() {
refetchInterval: 30_000,
})
const selfQuery = useQuery({
queryKey: ['cockpit', 'self-usage'],
queryFn: getSelfUsage,
refetchInterval: 60_000,
retry: false,
})
const self = selfQuery.data ?? {}
const stats = useMemo(() => {
const list: AgentDeployment[] = deploymentsQuery.data ?? []
const counters: Record<StatusKey, number> = {
@@ -213,12 +232,35 @@ export function CockpitView() {
/>
</div>
{/* Usage & cost — reuses the existing user consumption stat cards */}
{/* 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>
<LogStatCards />
<div className='grid grid-cols-2 gap-3 lg:grid-cols-3'>
<StatCard
label={t('Used')}
value={formatQuota(self.used_quota ?? 0)}
hint={t('Your total spend so far')}
icon={Coins}
tone='primary'
/>
<StatCard
label={t('Remaining quota')}
value={formatQuota(self.quota ?? 0)}
hint={t('Balance available')}
icon={Wallet}
tone='success'
/>
<StatCard
label={t('Requests')}
value={String(self.request_count ?? 0)}
hint={t('Your total requests')}
icon={Activity}
tone='warn'
/>
</div>
</div>
{/* Tier 2: Stream */}
@@ -18,12 +18,15 @@ import type {
interface LogStatCardsProps {
filters?: DashboardFilters
onDataUpdate?: (data: QuotaDataItem[], loading: boolean) => void
// When true, always show the CURRENT user's own usage even for admins
// (default: admins see the global aggregate).
userScope?: boolean
}
export function LogStatCards(props: LogStatCardsProps) {
const statCardsConfig = useModelStatCardsConfig()
const user = useAuthStore((state) => state.auth.user)
const isAdmin = !!(user?.role && user.role >= 10)
const isAdmin = props.userScope ? false : !!(user?.role && user.role >= 10)
const [stats, setStats] = useState<{
totalQuota: number
totalCount: number
+7 -1
View File
@@ -232,7 +232,13 @@
"Failed or no deliverable": "失败或无交付物",
"Queued / waiting / needs codegen": "排队 / 等待 / 需补代码",
"Live overview of your Agent runs.": "你的 Agent 运行实时概览。",
"Usage & cost": "费用与消耗",
"Usage & cost": "费用与消耗(你自己)",
"Used": "已用费用",
"Your total spend so far": "你累计消耗",
"Remaining quota": "剩余额度",
"Balance available": "可用余额",
"Requests": "请求数",
"Your total requests": "你累计请求次数",
"Every Agent run on the server, by mode and Manager-judged status. The desktop client shows the runs you started there.": "服务端上的全部 Agent 运行,按模式和 Manager 裁决状态展示。桌面客户端只显示你在客户端发起的运行。",
"Completed without deliverable": "完成但无交付物",
"Needs codegen": "需补充代码",