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:
@@ -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 */}
|
||||
|
||||
+4
-1
@@ -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
@@ -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": "需补充代码",
|
||||
|
||||
Reference in New Issue
Block a user