import { Copy, Check, Route, Settings2, AlertTriangle, Headphones, Monitor, Cloud, Globe, ShieldCheck, UserCog, Info, } from 'lucide-react' import { useTranslation } from 'react-i18next' import { formatBillingCurrencyFromUSD } from '@/lib/currency' import { formatLogQuota, formatTokens, formatUseTime } from '@/lib/format' import { cn } from '@/lib/utils' import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard' import { Button } from '@/components/ui/button' import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Label } from '@/components/ui/label' import { ScrollArea } from '@/components/ui/scroll-area' import { StatusBadge, type StatusBadgeProps } from '@/components/status-badge' import { DynamicPricingBreakdown } from '@/features/pricing/components/dynamic-pricing-breakdown' import type { UsageLog } from '../../data/schema' import { parseLogOther, getParamOverrideActionLabel, parseAuditLine, decodeBillingExprB64, getTieredBillingSummary, hasAnyCacheTokens, isViolationFeeLog, getFirstResponseTimeColor, getResponseTimeColor, } from '../../lib/format' import { getLogTypeConfig, isPerCallBilling, isTimingLogType, } from '../../lib/utils' import type { LogOtherData } from '../../types' function timingTextColorClass( variant: 'success' | 'warning' | 'danger' ): string { if (variant === 'success') return 'text-emerald-600' if (variant === 'warning') return 'text-amber-600' return 'text-rose-600' } function DetailRow(props: { label: React.ReactNode value: React.ReactNode mono?: boolean muted?: boolean }) { return (
{props.label} {props.value}
) } function DetailSection(props: { icon?: React.ReactNode label: string variant?: 'default' | 'danger' children: React.ReactNode }) { const isDanger = props.variant === 'danger' return (
{props.children}
) } function formatRatio(ratio: number | undefined): string { if (ratio == null) return '-' return ratio.toFixed(4) } function BillingBreakdown(props: { log: UsageLog other: LogOtherData isAdmin: boolean }) { const { t } = useTranslation() const { log, other, isAdmin } = props const isPerCall = isPerCallBilling(other.model_price) const isClaude = other.claude === true const isTieredExpr = other.billing_mode === 'tiered_expr' const tieredSummary = getTieredBillingSummary(other) const rows: Array<{ label: string; value: string }> = [] const priceOpts = { digitsLarge: 4, digitsSmall: 6, abbreviate: false } const fmtPrice = (usd: number) => formatBillingCurrencyFromUSD(usd, priceOpts) const baseInputUSD = other.model_ratio != null ? other.model_ratio * 2.0 : 0 if (isTieredExpr) { rows.push({ label: t('Billing Mode'), value: t('Dynamic Pricing'), }) if (tieredSummary) { if (tieredSummary.tier.label) { rows.push({ label: t('Matched Tier'), value: tieredSummary.tier.label, }) } for (const entry of tieredSummary.priceEntries) { rows.push({ label: t(entry.shortLabel), value: `${fmtPrice(entry.price)}/M`, }) } } else { rows.push({ label: t('Matched Tier'), value: t('No matching results'), }) } } else if (isPerCall) { rows.push({ label: t('Billing Mode'), value: t('Per-call') }) if (other.model_price != null) { rows.push({ label: t('Model Price'), value: fmtPrice(other.model_price), }) } } else { rows.push({ label: t('Billing Mode'), value: t('Per-token') }) if (other.model_ratio != null) { rows.push({ label: t('Input'), value: `${fmtPrice(baseInputUSD)}/M`, }) } if (other.completion_ratio != null && other.model_ratio != null) { rows.push({ label: t('Output'), value: `${fmtPrice(baseInputUSD * other.completion_ratio)}/M`, }) } } const userGR = other.user_group_ratio const isUserGR = userGR != null && Number.isFinite(userGR) && userGR !== -1 const effectiveGR = isUserGR ? userGR : other.group_ratio if (effectiveGR != null && Number.isFinite(effectiveGR)) { rows.push({ label: isUserGR ? t('User Exclusive Ratio') : t('Group Ratio'), value: `${formatRatio(effectiveGR)}x`, }) } if (!isTieredExpr && isClaude && hasAnyCacheTokens(other)) { if (other.cache_ratio != null && other.cache_ratio !== 1) { rows.push({ label: t('Cache Read'), value: `${fmtPrice(baseInputUSD * other.cache_ratio)}/M`, }) } if ( other.cache_creation_ratio != null && other.cache_creation_ratio !== 1 ) { rows.push({ label: t('Cache Creation'), value: `${fmtPrice(baseInputUSD * other.cache_creation_ratio)}/M`, }) } if ( other.cache_creation_ratio_5m != null && other.cache_creation_ratio_5m !== 0 ) { rows.push({ label: t('Cache Creation (5m)'), value: `${fmtPrice(baseInputUSD * other.cache_creation_ratio_5m)}/M`, }) } if ( other.cache_creation_ratio_1h != null && other.cache_creation_ratio_1h !== 0 ) { rows.push({ label: t('Cache Creation (1h)'), value: `${fmtPrice(baseInputUSD * other.cache_creation_ratio_1h)}/M`, }) } } if (!isTieredExpr) { if (other.audio_ratio != null && other.audio_ratio !== 1) { rows.push({ label: t('Audio input'), value: `${fmtPrice(baseInputUSD * other.audio_ratio)}/M`, }) } if ( other.audio_completion_ratio != null && other.audio_completion_ratio !== 1 ) { rows.push({ label: t('Audio output'), value: `${fmtPrice(baseInputUSD * other.audio_completion_ratio)}/M`, }) } if (other.image_ratio != null && other.image_ratio !== 1) { rows.push({ label: t('Image input'), value: `${fmtPrice(baseInputUSD * other.image_ratio)}/M`, }) } } if (other.web_search && other.web_search_call_count) { rows.push({ label: t('Web Search'), value: `${other.web_search_call_count}x${other.web_search_price ? ` (${fmtPrice(other.web_search_price)})` : ''}`, }) } if (other.file_search && other.file_search_call_count) { rows.push({ label: t('File Search'), value: `${other.file_search_call_count}x${other.file_search_price ? ` (${fmtPrice(other.file_search_price)})` : ''}`, }) } if (other.image_generation_call && other.image_generation_call_price) { rows.push({ label: t('Image Generation'), value: fmtPrice(other.image_generation_call_price), }) } if (other.audio_input_seperate_price && other.audio_input_price) { rows.push({ label: t('Audio Input Price'), value: fmtPrice(other.audio_input_price), }) } if (isAdmin && other.admin_info) { rows.push({ label: t('Billing Source'), value: other.admin_info.local_count_tokens ? t('Local Billing') : t('Upstream Response'), }) } rows.push({ label: t('Total Cost'), value: formatLogQuota(log.quota), }) if (rows.length === 0) return null return ( {rows.map((row, idx) => ( ))} ) } function TokenBreakdown(props: { log: UsageLog; other: LogOtherData }) { const { t } = useTranslation() const { log, other } = props const promptTokens = log.prompt_tokens || 0 const completionTokens = log.completion_tokens || 0 const cacheRead = other.cache_tokens || 0 const cacheWrite = other.cache_creation_tokens || 0 const cacheWrite5m = other.cache_creation_tokens_5m || 0 const cacheWrite1h = other.cache_creation_tokens_1h || 0 const hasTokens = promptTokens > 0 || completionTokens > 0 if (!hasTokens) return null const rows: Array<{ label: string; value: string }> = [] rows.push({ label: t('Input Tokens'), value: promptTokens.toLocaleString() }) rows.push({ label: t('Output Tokens'), value: completionTokens.toLocaleString(), }) if (cacheRead > 0) { rows.push({ label: t('Cache Read'), value: cacheRead.toLocaleString(), }) } if (cacheWrite > 0 && cacheWrite5m === 0 && cacheWrite1h === 0) { rows.push({ label: t('Cache Write'), value: cacheWrite.toLocaleString(), }) } if (cacheWrite5m > 0) { rows.push({ label: t('Cache Write (5m)'), value: cacheWrite5m.toLocaleString(), }) } if (cacheWrite1h > 0) { rows.push({ label: t('Cache Write (1h)'), value: cacheWrite1h.toLocaleString(), }) } if (other.image && other.image_output) { rows.push({ label: t('Image Tokens'), value: other.image_output.toLocaleString(), }) } return ( {rows.map((row, idx) => ( ))} ) } interface DetailsDialogProps { log: UsageLog isAdmin: boolean open: boolean onOpenChange: (open: boolean) => void } export function DetailsDialog(props: DetailsDialogProps) { const { t } = useTranslation() const { copiedText, copyToClipboard } = useCopyToClipboard({ notify: false }) const details = props.log.content ?? '' const other = parseLogOther(props.log.other) const typeConfig = getLogTypeConfig(props.log.type) const isViolation = isViolationFeeLog(other) const isRefund = props.log.type === 6 const isConsume = props.log.type === 2 const isTopup = props.log.type === 1 const isManage = props.log.type === 3 const isSubscription = other?.billing_source === 'subscription' const isTieredBilling = isConsume && !isViolation && other?.billing_mode === 'tiered_expr' && !!other?.expr_b64 const hasAudioTokens = other?.ws || other?.audio const showTiming = isTimingLogType(props.log.type) const showAdminIp = !!props.log.ip && (showTiming || (props.isAdmin && isTopup)) const adminInfo = other?.admin_info const topupAuditFields = isTopup && props.isAdmin && adminInfo ? ([ adminInfo.payment_method && { label: t('Order Payment Method'), value: adminInfo.payment_method, }, adminInfo.callback_payment_method && { label: t('Callback Payment Method'), value: adminInfo.callback_payment_method, }, adminInfo.caller_ip && { label: t('Callback Caller IP'), value: adminInfo.caller_ip, }, adminInfo.server_ip && { label: t('Server IP'), value: adminInfo.server_ip, }, adminInfo.node_name && { label: t('Node Name'), value: adminInfo.node_name, }, adminInfo.version && { label: t('System Version'), value: adminInfo.version, }, ].filter(Boolean) as Array<{ label: string; value: string }>) : [] const showLegacyTopupWarning = isTopup && props.isAdmin && !adminInfo const showTopupAuditSection = isTopup && props.isAdmin && (topupAuditFields.length > 0 || showLegacyTopupWarning) const manageOperator = (() => { if (!isManage || !props.isAdmin || !adminInfo) return null const username = adminInfo.admin_username const id = adminInfo.admin_id const hasUsername = username != null && String(username).trim() !== '' const hasId = id != null && String(id).trim() !== '' if (!hasUsername && !hasId) return null if (hasUsername && hasId) return `${username} (ID: ${id})` if (hasUsername) return String(username) return `ID: ${id}` })() const conversionChain = other && Array.isArray(other.request_conversion) ? other.request_conversion.filter(Boolean) : [] const conversionLabel = conversionChain.length <= 1 ? t('Native format') : conversionChain.join(' -> ') const showConversion = props.isAdmin && props.log.type !== 6 && (other?.request_path || conversionChain.length > 0) const useChannel = other?.admin_info?.use_channel const channelChain = useChannel && useChannel.length > 0 ? useChannel.join(' → ') : undefined return ( {t('Log Details')} {t('View the complete details for this log entry')}
{/* Overview section - key identifiers */}
{props.log.request_id && ( )} {props.isAdmin && props.log.channel > 0 && ( {props.log.channel} {props.log.channel_name && ( {' '} ({props.log.channel_name}) )} } mono /> )} {channelChain && props.isAdmin && ( )} {props.log.token_name && ( )} {(props.log.group || other?.group) && ( )} {showAdminIp && (
{/* Request conversion (admin only, not for refund) */} {showConversion && (
{other?.request_path && ( )}
)} {/* Reject reason (admin only) */} {props.isAdmin && other?.reject_reason && ( )} {/* Violation fee info */} {isViolation && other && ( )} {/* Refund details (type=6) */} {isRefund && other && (other.task_id || other.reason) && ( {other.task_id && ( )} {other.reason && ( )} )} {/* Top-up audit info (type=1, admin only) */} {showTopupAuditSection && ( )} {/* Manage operator (type=3, admin only) */} {manageOperator && (
) } function isDisplayableType(type: number): boolean { return [0, 2, 5, 6].includes(type) }