Commit 9b9b19e9 by yyhhyyyyyy Committed by GitHub

fix(web): prevent usage log details dialog from clipping bottom content (#5533)

parent 43c7e30a
......@@ -38,7 +38,6 @@ import { cn } from '@/lib/utils'
import { useCopyToClipboard } from '@/hooks/use-copy-to-clipboard'
import { Button } from '@/components/ui/button'
import { Label } from '@/components/ui/label'
import { ScrollArea } from '@/components/ui/scroll-area'
import { Dialog } from '@/components/dialog'
import { StatusBadge, type StatusBadgeProps } from '@/components/status-badge'
import { DynamicPricingBreakdown } from '@/features/pricing/components/dynamic-pricing-breakdown'
......@@ -94,7 +93,7 @@ function DetailRow(props: {
</span>
<span
className={cn(
'max-w-full min-w-0 text-xs break-all sm:break-words',
'max-w-full min-w-0 text-xs break-all sm:wrap-break-word',
props.mono && 'font-mono',
props.muted && 'text-muted-foreground'
)}
......@@ -558,600 +557,592 @@ export function DetailsDialog(props: DetailsDialogProps) {
headerClassName='max-sm:gap-1'
titleClassName='flex items-center gap-2 text-base'
descriptionClassName='sr-only'
contentHeight='min(72vh, 720px)'
bodyClassName='space-y-4'
contentHeight='min(72dvh, 720px)'
bodyClassName='pr-2 sm:pr-4'
>
<ScrollArea className='max-h-[70vh] min-w-0 overflow-hidden pr-2 max-sm:max-h-[calc(100dvh-7rem)] sm:pr-4'>
<div className='w-full max-w-full min-w-0 space-y-2.5 overflow-hidden py-1 sm:space-y-3'>
{/* Overview section - key identifiers */}
<div className='min-w-0 space-y-1'>
{props.log.request_id && (
<div className='w-full max-w-full min-w-0 space-y-2.5 overflow-x-hidden py-1 sm:space-y-3'>
{/* Overview section - key identifiers */}
<div className='min-w-0 space-y-1'>
{props.log.request_id && (
<DetailRow
label={t('Request ID')}
value={props.log.request_id}
mono
/>
)}
{props.log.upstream_request_id && (
<DetailRow
label={t('Upstream Request ID')}
value={props.log.upstream_request_id}
mono
/>
)}
{props.isAdmin && props.log.channel > 0 && (
<DetailRow
label={t('Channel')}
value={
<span>
{props.log.channel}
{props.log.channel_name && (
<span className='text-muted-foreground'>
{' '}
({props.log.channel_name})
</span>
)}
</span>
}
mono
/>
)}
{channelChain && props.isAdmin && (
<DetailRow label={t('Retry Chain')} value={channelChain} mono />
)}
{props.log.token_name && (
<DetailRow label={t('Token')} value={props.log.token_name} mono />
)}
{(props.log.group || other?.group) && (
<DetailRow
label={t('Group')}
value={props.log.group || other?.group || ''}
mono
/>
)}
{showAdminIp && (
<DetailRow
label={t('IP Address')}
value={
<span className='flex items-center gap-1'>
<Globe className='size-3 text-amber-500' aria-hidden='true' />
{props.log.ip}
</span>
}
mono
/>
)}
{showTiming && props.log.use_time > 0 && (
<DetailRow
label={t('Response Time')}
value={
<span
className={cn(
'font-medium',
timingTextColorClass(
getResponseTimeColor(
props.log.use_time,
props.log.completion_tokens
)
)
)}
>
{formatUseTime(props.log.use_time)}
{props.log.is_stream &&
other?.frt != null &&
other.frt > 0 && (
<span
className={cn(
'font-normal',
timingTextColorClass(
getFirstResponseTimeColor(other.frt / 1000)
)
)}
>
{' '}
(FRT: {formatUseTime(other.frt / 1000)})
</span>
)}
</span>
}
/>
)}
</div>
{/* Request conversion (admin only, not for refund) */}
{showConversion && (
<DetailSection label={t('Request Conversion')}>
<div className='relative min-w-0'>
<Button
variant='ghost'
size='sm'
className='absolute top-0 right-0 h-5 w-5 p-0'
onClick={() => copyToClipboard(conversionLabel)}
title={t('Copy to clipboard')}
aria-label={t('Copy to clipboard')}
>
{copiedText === conversionLabel ? (
<Check className='size-3 text-green-600' />
) : (
<Copy className='size-3' />
)}
</Button>
<div className='min-w-0 space-y-1 pr-6'>
{other?.request_path && (
<DetailRow
label={t('Path')}
value={other.request_path}
mono
/>
)}
<div className='flex min-w-0 items-center gap-1.5 text-xs'>
<Route
className='text-muted-foreground size-3'
aria-hidden='true'
/>
<span className='min-w-0 break-all sm:wrap-break-word'>
{conversionLabel}
</span>
</div>
</div>
</div>
</DetailSection>
)}
{/* Reject reason (admin only) */}
{props.isAdmin && other?.reject_reason && (
<DetailSection
icon={<AlertTriangle className='size-3.5' aria-hidden='true' />}
label={t('Reject Reason')}
variant='danger'
>
<p className='text-xs wrap-break-word'>{other.reject_reason}</p>
</DetailSection>
)}
{/* Violation fee info */}
{isViolation && other && (
<DetailSection
icon={<AlertTriangle className='size-3.5' aria-hidden='true' />}
label={t('Violation Fee')}
variant='danger'
>
{other.violation_fee_code && (
<DetailRow
label={t('Request ID')}
value={props.log.request_id}
label={t('Violation Code')}
value={other.violation_fee_code}
mono
/>
)}
{props.log.upstream_request_id && (
{other.violation_fee_marker && (
<DetailRow
label={t('Upstream Request ID')}
value={props.log.upstream_request_id}
mono
label={t('Violation Marker')}
value={other.violation_fee_marker}
/>
)}
<DetailRow
label={t('Fee Amount')}
value={formatLogQuota(other.fee_quota ?? props.log.quota)}
mono
/>
</DetailSection>
)}
{/* Refund details (type=6) */}
{isRefund && other && (other.task_id || other.reason) && (
<DetailSection label={t('Refund Details')}>
{other.task_id && (
<DetailRow label={t('Task ID')} value={other.task_id} mono />
)}
{other.reason && (
<DetailRow label={t('Reason')} value={other.reason} />
)}
</DetailSection>
)}
{props.isAdmin && props.log.channel > 0 && (
{/* Top-up audit info (type=1, admin only) */}
{showTopupAuditSection && (
<DetailSection
icon={<ShieldCheck className='size-3.5' aria-hidden='true' />}
label={t('Top-up Audit Info')}
>
{topupAuditFields.map((field, idx) => (
<DetailRow
label={t('Channel')}
value={
<span>
{props.log.channel}
{props.log.channel_name && (
<span className='text-muted-foreground'>
{' '}
({props.log.channel_name})
</span>
)}
</span>
}
key={idx}
label={field.label}
value={field.value}
mono
/>
))}
{showLegacyTopupWarning && (
<div className='flex items-start gap-1.5 text-xs text-amber-600 dark:text-amber-400'>
<Info className='mt-0.5 size-3.5 shrink-0' aria-hidden='true' />
<span>
{t(
'This record was written by a pre-upgrade instance and lacks audit info. Upgrade the instance to record server IP, callback IP, payment method and system version.'
)}
</span>
</div>
)}
</DetailSection>
)}
{channelChain && props.isAdmin && (
<DetailRow label={t('Retry Chain')} value={channelChain} mono />
)}
{/* Manage operator (type=3, admin only) */}
{manageOperator && (
<DetailRow
label={
<span className='flex items-center gap-1.5'>
<UserCog
className='text-muted-foreground size-3.5'
aria-hidden='true'
/>
{t('Operator Admin')}
</span>
}
value={manageOperator}
mono
/>
)}
{props.log.token_name && (
<DetailRow label={t('Token')} value={props.log.token_name} mono />
{/* Operation audit info (type=3, admin only) */}
{showManageAuditSection && (
<DetailSection
icon={<ShieldCheck className='size-3.5' aria-hidden='true' />}
label={t('Operation Audit Info')}
>
{operationText != null && (
<DetailRow label={t('Operation')} value={operationText} />
)}
{(props.log.group || other?.group) && (
{authMethodLabel !== '' && (
<DetailRow
label={t('Group')}
value={props.log.group || other?.group || ''}
mono
label={t('Authentication Method')}
value={authMethodLabel}
/>
)}
{showAdminIp && (
{changedFieldsText !== '' && (
<DetailRow
label={t('IP Address')}
value={
<span className='flex items-center gap-1'>
<Globe
className='size-3 text-amber-500'
aria-hidden='true'
/>
{props.log.ip}
</span>
}
label={t('Changed Fields')}
value={changedFieldsText}
/>
)}
{auditRoute?.method && auditRoute?.route && (
<DetailRow
label={t('Request')}
value={`${auditRoute.method} ${auditRoute.route}`}
mono
/>
)}
{showTiming && props.log.use_time > 0 && (
{auditRoute?.status != null && (
<DetailRow
label={t('Response Time')}
label={t('Result')}
value={
<span
className={cn(
'font-medium',
timingTextColorClass(
getResponseTimeColor(
props.log.use_time,
props.log.completion_tokens
)
)
)}
>
{formatUseTime(props.log.use_time)}
{props.log.is_stream &&
other?.frt != null &&
other.frt > 0 && (
<span
className={cn(
'font-normal',
timingTextColorClass(
getFirstResponseTimeColor(other.frt / 1000)
)
)}
>
{' '}
(FRT: {formatUseTime(other.frt / 1000)})
</span>
)}
</span>
auditRoute.success
? `${t('Success')} (${auditRoute.status})`
: `${t('Failed')} (${auditRoute.status})`
}
mono
/>
)}
</div>
{/* Request conversion (admin only, not for refund) */}
{showConversion && (
<DetailSection label={t('Request Conversion')}>
<div className='relative min-w-0'>
<Button
variant='ghost'
size='sm'
className='absolute top-0 right-0 h-5 w-5 p-0'
onClick={() => copyToClipboard(conversionLabel)}
title={t('Copy to clipboard')}
aria-label={t('Copy to clipboard')}
>
{copiedText === conversionLabel ? (
<Check className='size-3 text-green-600' />
) : (
<Copy className='size-3' />
)}
</Button>
<div className='min-w-0 space-y-1 pr-6'>
{other?.request_path && (
<DetailRow
label={t('Path')}
value={other.request_path}
mono
/>
)}
<div className='flex min-w-0 items-center gap-1.5 text-xs'>
<Route
className='text-muted-foreground size-3'
aria-hidden='true'
/>
<span className='min-w-0 break-all sm:break-words'>
{conversionLabel}
</span>
</div>
</div>
</div>
</DetailSection>
)}
</DetailSection>
)}
{/* Reject reason (admin only) */}
{props.isAdmin && other?.reject_reason && (
<DetailSection
icon={<AlertTriangle className='size-3.5' aria-hidden='true' />}
label={t('Reject Reason')}
variant='danger'
>
<p className='text-xs break-words'>{other.reject_reason}</p>
</DetailSection>
)}
{/* Login audit info (type=7) */}
{isLogin && loginAuditFields.length > 0 && (
<DetailSection
icon={<LogIn className='size-3.5' aria-hidden='true' />}
label={t('Login Info')}
>
{operationText != null && (
<DetailRow label={t('Operation')} value={operationText} />
)}
{loginAuditFields.map((field, idx) => (
<DetailRow
key={idx}
label={field.label}
value={field.value}
mono
/>
))}
</DetailSection>
)}
{/* Violation fee info */}
{isViolation && other && (
<DetailSection
icon={<AlertTriangle className='size-3.5' aria-hidden='true' />}
label={t('Violation Fee')}
variant='danger'
>
{other.violation_fee_code && (
<DetailRow
label={t('Violation Code')}
value={other.violation_fee_code}
mono
/>
)}
{other.violation_fee_marker && (
<DetailRow
label={t('Violation Marker')}
value={other.violation_fee_marker}
/>
)}
{/* Audio/WebSocket token breakdown */}
{hasAudioTokens && other && (
<DetailSection
icon={<Headphones className='size-3.5' aria-hidden='true' />}
label={t('Audio Tokens')}
>
{other.audio_input != null && other.audio_input > 0 && (
<DetailRow
label={t('Fee Amount')}
value={formatLogQuota(other.fee_quota ?? props.log.quota)}
label={t('Audio Input')}
value={formatTokens(other.audio_input)}
mono
/>
</DetailSection>
)}
)}
{other.audio_output != null && other.audio_output > 0 && (
<DetailRow
label={t('Audio Output')}
value={formatTokens(other.audio_output)}
mono
/>
)}
{other.text_input != null && other.text_input > 0 && (
<DetailRow
label={t('Text Input')}
value={formatTokens(other.text_input)}
mono
/>
)}
{other.text_output != null && other.text_output > 0 && (
<DetailRow
label={t('Text Output')}
value={formatTokens(other.text_output)}
mono
/>
)}
</DetailSection>
)}
{/* Refund details (type=6) */}
{isRefund && other && (other.task_id || other.reason) && (
<DetailSection label={t('Refund Details')}>
{other.task_id && (
<DetailRow label={t('Task ID')} value={other.task_id} mono />
)}
{other.reason && (
<DetailRow label={t('Reason')} value={other.reason} />
)}
</DetailSection>
)}
{/* Reasoning effort */}
{other?.reasoning_effort && (
<DetailRow
label={t('Reasoning Effort')}
value={
<StatusBadge
label={other.reasoning_effort}
variant={
other.reasoning_effort === 'high'
? 'orange'
: other.reasoning_effort === 'medium'
? 'yellow'
: 'green'
}
size='sm'
copyable={false}
/>
}
/>
)}
{/* Top-up audit info (type=1, admin only) */}
{showTopupAuditSection && (
<DetailSection
icon={<ShieldCheck className='size-3.5' aria-hidden='true' />}
label={t('Top-up Audit Info')}
>
{topupAuditFields.map((field, idx) => (
<DetailRow
key={idx}
label={field.label}
value={field.value}
mono
/>
))}
{showLegacyTopupWarning && (
<div className='flex items-start gap-1.5 text-xs text-amber-600 dark:text-amber-400'>
<Info
className='mt-0.5 size-3.5 shrink-0'
aria-hidden='true'
/>
<span>
{t(
'This record was written by a pre-upgrade instance and lacks audit info. Upgrade the instance to record server IP, callback IP, payment method and system version.'
)}
</span>
</div>
)}
</DetailSection>
)}
{/* System prompt override */}
{other?.is_system_prompt_overwritten && (
<DetailRow
label={t('System Prompt')}
value={
<StatusBadge
label={t('Overwritten')}
variant='orange'
size='sm'
copyable={false}
/>
}
/>
)}
{/* Manage operator (type=3, admin only) */}
{manageOperator && (
{/* Model mapping */}
{other?.is_model_mapped && other?.upstream_model_name && (
<DetailSection label={t('Model Mapping')}>
<DetailRow
label={
<span className='flex items-center gap-1.5'>
<UserCog
className='text-muted-foreground size-3.5'
aria-hidden='true'
/>
{t('Operator Admin')}
</span>
}
value={manageOperator}
label={t('Request Model')}
value={props.log.model_name}
mono
/>
)}
{/* Operation audit info (type=3, admin only) */}
{showManageAuditSection && (
<DetailSection
icon={<ShieldCheck className='size-3.5' aria-hidden='true' />}
label={t('Operation Audit Info')}
>
{operationText != null && (
<DetailRow label={t('Operation')} value={operationText} />
)}
{authMethodLabel !== '' && (
<DetailRow
label={t('Authentication Method')}
value={authMethodLabel}
/>
)}
{changedFieldsText !== '' && (
<DetailRow
label={t('Changed Fields')}
value={changedFieldsText}
/>
)}
{auditRoute?.method && auditRoute?.route && (
<DetailRow
label={t('Request')}
value={`${auditRoute.method} ${auditRoute.route}`}
mono
/>
)}
{auditRoute?.status != null && (
<DetailRow
label={t('Result')}
value={
auditRoute.success
? `${t('Success')} (${auditRoute.status})`
: `${t('Failed')} (${auditRoute.status})`
}
mono
/>
)}
</DetailSection>
)}
<DetailRow
label={t('Actual Model')}
value={other.upstream_model_name}
mono
/>
</DetailSection>
)}
{/* Login audit info (type=7) */}
{isLogin && loginAuditFields.length > 0 && (
<DetailSection
icon={<LogIn className='size-3.5' aria-hidden='true' />}
label={t('Login Info')}
>
{operationText != null && (
<DetailRow label={t('Operation')} value={operationText} />
)}
{loginAuditFields.map((field, idx) => (
<DetailRow
key={idx}
label={field.label}
value={field.value}
mono
/>
))}
</DetailSection>
)}
{/* Token breakdown (for consume/error types with token data) */}
{isDisplayableType(props.log.type) && other && (
<TokenBreakdown log={props.log} other={other} />
)}
{/* Audio/WebSocket token breakdown */}
{hasAudioTokens && other && (
<DetailSection
icon={<Headphones className='size-3.5' aria-hidden='true' />}
label={t('Audio Tokens')}
>
{other.audio_input != null && other.audio_input > 0 && (
<DetailRow
label={t('Audio Input')}
value={formatTokens(other.audio_input)}
mono
/>
)}
{other.audio_output != null && other.audio_output > 0 && (
<DetailRow
label={t('Audio Output')}
value={formatTokens(other.audio_output)}
mono
/>
)}
{other.text_input != null && other.text_input > 0 && (
<DetailRow
label={t('Text Input')}
value={formatTokens(other.text_input)}
mono
/>
)}
{other.text_output != null && other.text_output > 0 && (
<DetailRow
label={t('Text Output')}
value={formatTokens(other.text_output)}
mono
/>
)}
</DetailSection>
)}
{/* Billing breakdown (consume type) */}
{isConsume && other && !isViolation && (
<BillingBreakdown
log={props.log}
other={other}
isAdmin={props.isAdmin}
/>
)}
{/* Reasoning effort */}
{other?.reasoning_effort && (
<DetailRow
label={t('Reasoning Effort')}
value={
<StatusBadge
label={other.reasoning_effort}
variant={
other.reasoning_effort === 'high'
? 'orange'
: other.reasoning_effort === 'medium'
? 'yellow'
: 'green'
}
size='sm'
copyable={false}
/>
}
{/* Tiered pricing breakdown (when billing_mode is tiered_expr) */}
{isTieredBilling && other?.expr_b64 && (
<div className='bg-muted/30 min-w-0 overflow-hidden rounded-md border px-3 max-sm:px-2'>
<DynamicPricingBreakdown
billingExpr={decodeBillingExprB64(other.expr_b64)}
matchedTierLabel={other.matched_tier}
hideCacheColumns={!hasAnyCacheTokens(other)}
/>
)}
</div>
)}
{/* System prompt override */}
{other?.is_system_prompt_overwritten && (
{/* Admin billing mode indicator for non-consume */}
{props.isAdmin &&
!isConsume &&
props.log.type !== 6 &&
other?.admin_info && (
<DetailRow
label={t('System Prompt')}
label={t('Billing Source')}
value={
<StatusBadge
label={t('Overwritten')}
variant='orange'
size='sm'
copyable={false}
/>
<span className='flex items-center gap-1'>
{other.admin_info.local_count_tokens ? (
<Monitor className='size-3 text-blue-500' />
) : (
<Cloud className='size-3 text-emerald-500' />
)}
<span className='text-xs'>
{other.admin_info.local_count_tokens
? t('Local Billing')
: t('Upstream Response')}
</span>
</span>
}
/>
)}
{/* Model mapping */}
{other?.is_model_mapped && other?.upstream_model_name && (
<DetailSection label={t('Model Mapping')}>
<DetailRow
label={t('Request Model')}
value={props.log.model_name}
mono
/>
<DetailRow
label={t('Actual Model')}
value={other.upstream_model_name}
mono
/>
</DetailSection>
)}
{/* Token breakdown (for consume/error types with token data) */}
{isDisplayableType(props.log.type) && other && (
<TokenBreakdown log={props.log} other={other} />
)}
{/* Billing breakdown (consume type) */}
{isConsume && other && !isViolation && (
<BillingBreakdown
log={props.log}
other={other}
isAdmin={props.isAdmin}
/>
)}
{/* Tiered pricing breakdown (when billing_mode is tiered_expr) */}
{isTieredBilling && other?.expr_b64 && (
<div className='bg-muted/30 min-w-0 overflow-hidden rounded-md border px-3 max-sm:px-2'>
<DynamicPricingBreakdown
billingExpr={decodeBillingExprB64(other.expr_b64)}
matchedTierLabel={other.matched_tier}
hideCacheColumns={!hasAnyCacheTokens(other)}
/>
</div>
)}
{/* Admin billing mode indicator for non-consume */}
{props.isAdmin &&
!isConsume &&
props.log.type !== 6 &&
other?.admin_info && (
{/* Stream status details (admin only) */}
{props.isAdmin &&
other?.stream_status &&
other.stream_status.status !== 'ok' && (
<DetailSection label={t('Stream Status')}>
<DetailRow
label={t('Billing Source')}
label={t('Status')}
value={
<span className='flex items-center gap-1'>
{other.admin_info.local_count_tokens ? (
<Monitor className='size-3 text-blue-500' />
) : (
<Cloud className='size-3 text-emerald-500' />
)}
<span className='text-xs'>
{other.admin_info.local_count_tokens
? t('Local Billing')
: t('Upstream Response')}
</span>
</span>
<StatusBadge
label={other.stream_status.status || t('Error')}
variant='red'
size='sm'
copyable={false}
/>
}
/>
)}
{/* Stream status details (admin only) */}
{props.isAdmin &&
other?.stream_status &&
other.stream_status.status !== 'ok' && (
<DetailSection label={t('Stream Status')}>
{other.stream_status.end_reason && (
<DetailRow
label={t('Status')}
value={
<StatusBadge
label={other.stream_status.status || t('Error')}
variant='red'
size='sm'
copyable={false}
/>
}
/>
{other.stream_status.end_reason && (
<DetailRow
label={t('End Reason')}
value={other.stream_status.end_reason}
/>
)}
{(other.stream_status.error_count ?? 0) > 0 && (
<DetailRow
label={t('Soft Errors')}
value={String(other.stream_status.error_count)}
/>
)}
{other.stream_status.end_error && (
<DetailRow
label={t('End Error')}
value={other.stream_status.end_error}
/>
)}
{Array.isArray(other.stream_status.errors) &&
other.stream_status.errors.length > 0 && (
<pre className='bg-background/60 mt-1 max-h-32 overflow-y-auto rounded border p-2 font-mono text-[11px] leading-relaxed break-words whitespace-pre-wrap'>
{other.stream_status.errors.join('\n')}
</pre>
)}
</DetailSection>
)}
{/* Subscription billing details */}
{isSubscription && other && (
<DetailSection label={t('Subscription Billing')}>
{other.subscription_plan_id && (
<DetailRow
label={t('Plan')}
value={`#${other.subscription_plan_id} ${other.subscription_plan_title || ''}`.trim()}
label={t('End Reason')}
value={other.stream_status.end_reason}
/>
)}
{other.subscription_id && (
{(other.stream_status.error_count ?? 0) > 0 && (
<DetailRow
label={t('Instance')}
value={`#${other.subscription_id}`}
mono
label={t('Soft Errors')}
value={String(other.stream_status.error_count)}
/>
)}
{other.subscription_pre_consumed != null && (
{other.stream_status.end_error && (
<DetailRow
label={t('Pre-consumed')}
value={formatLogQuota(other.subscription_pre_consumed)}
mono
label={t('End Error')}
value={other.stream_status.end_error}
/>
)}
{other.subscription_post_delta != null &&
other.subscription_post_delta !== 0 && (
<DetailRow
label={t('Post Delta')}
value={formatLogQuota(other.subscription_post_delta)}
mono
/>
{Array.isArray(other.stream_status.errors) &&
other.stream_status.errors.length > 0 && (
<pre className='bg-background/60 mt-1 max-h-32 overflow-y-auto rounded border p-2 font-mono text-[11px] leading-relaxed wrap-break-word whitespace-pre-wrap'>
{other.stream_status.errors.join('\n')}
</pre>
)}
{other.subscription_consumed != null && (
<DetailRow
label={t('Final Consumed')}
value={formatLogQuota(other.subscription_consumed)}
mono
/>
)}
{other.subscription_remain != null && (
</DetailSection>
)}
{/* Subscription billing details */}
{isSubscription && other && (
<DetailSection label={t('Subscription Billing')}>
{other.subscription_plan_id && (
<DetailRow
label={t('Plan')}
value={`#${other.subscription_plan_id} ${other.subscription_plan_title || ''}`.trim()}
/>
)}
{other.subscription_id && (
<DetailRow
label={t('Instance')}
value={`#${other.subscription_id}`}
mono
/>
)}
{other.subscription_pre_consumed != null && (
<DetailRow
label={t('Pre-consumed')}
value={formatLogQuota(other.subscription_pre_consumed)}
mono
/>
)}
{other.subscription_post_delta != null &&
other.subscription_post_delta !== 0 && (
<DetailRow
label={t('Remaining')}
value={`${formatLogQuota(other.subscription_remain)}${other.subscription_total != null ? ` / ${formatLogQuota(other.subscription_total)}` : ''}`}
label={t('Post Delta')}
value={formatLogQuota(other.subscription_post_delta)}
mono
/>
)}
</DetailSection>
)}
{/* Param override */}
{other?.po && Array.isArray(other.po) && other.po.length > 0 && (
<DetailSection
icon={<Settings2 className='size-3.5' aria-hidden='true' />}
label={`${t('Param Override')} (${other.po.length})`}
>
{other.po.filter(Boolean).map((line, idx) => {
const parsed = parseAuditLine(line)
if (!parsed) return null
return (
<div
key={idx}
className='bg-background/60 flex min-w-0 flex-col gap-1.5 rounded border p-2 sm:flex-row sm:items-start sm:gap-2'
>
<StatusBadge
variant='neutral'
label={getParamOverrideActionLabel(parsed.action, t)}
className='shrink-0 font-medium'
copyable={false}
/>
<span className='min-w-0 font-mono text-[11px] leading-relaxed break-all sm:break-words'>
{parsed.content}
</span>
</div>
)
})}
</DetailSection>
)}
{other.subscription_consumed != null && (
<DetailRow
label={t('Final Consumed')}
value={formatLogQuota(other.subscription_consumed)}
mono
/>
)}
{other.subscription_remain != null && (
<DetailRow
label={t('Remaining')}
value={`${formatLogQuota(other.subscription_remain)}${other.subscription_total != null ? ` / ${formatLogQuota(other.subscription_total)}` : ''}`}
mono
/>
)}
</DetailSection>
)}
{/* Content */}
{details && (
<div className='space-y-1.5'>
<Label className='text-xs font-semibold'>{t('Content')}</Label>
<div className='bg-muted/30 relative min-w-0 overflow-hidden rounded-md border p-2.5'>
<Button
variant='ghost'
size='sm'
className='absolute top-1.5 right-1.5 h-5 w-5 p-0'
onClick={() => copyToClipboard(details)}
title={t('Copy to clipboard')}
aria-label={t('Copy to clipboard')}
{/* Param override */}
{other?.po && Array.isArray(other.po) && other.po.length > 0 && (
<DetailSection
icon={<Settings2 className='size-3.5' aria-hidden='true' />}
label={`${t('Param Override')} (${other.po.length})`}
>
{other.po.filter(Boolean).map((line, idx) => {
const parsed = parseAuditLine(line)
if (!parsed) return null
return (
<div
key={idx}
className='bg-background/60 flex min-w-0 flex-col gap-1.5 rounded border p-2 sm:flex-row sm:items-start sm:gap-2'
>
{copiedText === details ? (
<Check className='size-3 text-green-600' />
) : (
<Copy className='size-3' />
)}
</Button>
<p className='min-w-0 pr-6 text-xs leading-relaxed break-all whitespace-pre-wrap sm:break-words'>
{details}
</p>
</div>
<StatusBadge
variant='neutral'
label={getParamOverrideActionLabel(parsed.action, t)}
className='shrink-0 font-medium'
copyable={false}
/>
<span className='min-w-0 font-mono text-[11px] leading-relaxed break-all sm:wrap-break-word'>
{parsed.content}
</span>
</div>
)
})}
</DetailSection>
)}
{/* Content */}
{details && (
<div className='space-y-1.5'>
<Label className='text-xs font-semibold'>{t('Content')}</Label>
<div className='bg-muted/30 relative min-w-0 overflow-hidden rounded-md border p-2.5'>
<Button
variant='ghost'
size='sm'
className='absolute top-1.5 right-1.5 h-5 w-5 p-0'
onClick={() => copyToClipboard(details)}
title={t('Copy to clipboard')}
aria-label={t('Copy to clipboard')}
>
{copiedText === details ? (
<Check className='size-3 text-green-600' />
) : (
<Copy className='size-3' />
)}
</Button>
<p className='min-w-0 pr-6 text-xs leading-relaxed break-all whitespace-pre-wrap sm:wrap-break-word'>
{details}
</p>
</div>
)}
</div>
</ScrollArea>
</div>
)}
</div>
</Dialog>
)
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment