fix(usage-logs): handle mobile card row fields safely
- read generic table row fields through an unknown-safe helper to avoid invalid property access on unconstrained data. - keep mobile time status inputs typed as unknown while preserving existing rendering behavior.
This commit is contained in:
+46
-55
@@ -16,11 +16,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
For commercial licensing, please contact support@quantumnous.com
|
||||
*/
|
||||
import {
|
||||
flexRender,
|
||||
type Cell,
|
||||
type Table,
|
||||
} from '@tanstack/react-table'
|
||||
import { flexRender, type Cell, type Table } from '@tanstack/react-table'
|
||||
import { Database } from 'lucide-react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { formatTimestampToDate } from '@/lib/format'
|
||||
@@ -33,14 +29,20 @@ import {
|
||||
EmptyTitle,
|
||||
} from '@/components/ui/empty'
|
||||
import { Skeleton } from '@/components/ui/skeleton'
|
||||
import { dotColorMap, textColorMap, type StatusVariant } from '@/components/status-badge'
|
||||
import type { LogCategory } from '../types'
|
||||
import {
|
||||
dotColorMap,
|
||||
textColorMap,
|
||||
type StatusVariant,
|
||||
} from '@/components/status-badge'
|
||||
import { LOG_TYPE_ENUM } from '../constants'
|
||||
import { getLogTypeConfig } from '../lib/utils'
|
||||
import type { LogCategory } from '../types'
|
||||
|
||||
const logTypeRowTint: Record<number, string> = {
|
||||
[LOG_TYPE_ENUM.ERROR]: 'bg-rose-50/40 dark:bg-rose-950/20 border-rose-200/50 dark:border-rose-900/30',
|
||||
[LOG_TYPE_ENUM.REFUND]: 'bg-blue-50/30 dark:bg-blue-950/15 border-blue-200/50 dark:border-blue-900/30',
|
||||
[LOG_TYPE_ENUM.ERROR]:
|
||||
'bg-rose-50/40 dark:bg-rose-950/20 border-rose-200/50 dark:border-rose-900/30',
|
||||
[LOG_TYPE_ENUM.REFUND]:
|
||||
'bg-blue-50/30 dark:bg-blue-950/15 border-blue-200/50 dark:border-blue-900/30',
|
||||
}
|
||||
|
||||
interface UsageLogsMobileListProps<TData> {
|
||||
@@ -53,11 +55,11 @@ interface UsageLogsMobileListProps<TData> {
|
||||
|
||||
function UsageLogsMobileSkeleton() {
|
||||
return (
|
||||
<div className='overflow-hidden rounded-lg border border-border/50 bg-card'>
|
||||
<div className='border-border/50 bg-card overflow-hidden rounded-lg border'>
|
||||
{[1, 2, 3].map((i) => (
|
||||
<div
|
||||
key={i}
|
||||
className='space-y-2.5 border-b border-border/40 p-3 last:border-b-0'
|
||||
className='border-border/40 space-y-2.5 border-b p-3 last:border-b-0'
|
||||
>
|
||||
<div className='flex items-center justify-between gap-3'>
|
||||
<Skeleton className='h-5 w-40 rounded-md' />
|
||||
@@ -93,7 +95,7 @@ function CompactCell<TData>({
|
||||
className={cn(
|
||||
'min-w-0 overflow-hidden leading-tight [&_button]:max-w-full [&_span]:max-w-full',
|
||||
primaryOnly &&
|
||||
'[&_.flex-col>*:not(:first-child)]:hidden [&_.flex-col]:min-w-0',
|
||||
'[&_.flex-col]:min-w-0 [&_.flex-col>*:not(:first-child)]:hidden',
|
||||
className
|
||||
)}
|
||||
>
|
||||
@@ -123,10 +125,7 @@ function SummaryField<TData>({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
'min-w-0 rounded-md bg-muted/20 px-2 py-1.5',
|
||||
className
|
||||
)}
|
||||
className={cn('bg-muted/20 min-w-0 rounded-md px-2 py-1.5', className)}
|
||||
>
|
||||
<div className='text-muted-foreground mb-1 text-[11px] leading-none font-medium select-none'>
|
||||
{label}
|
||||
@@ -174,6 +173,19 @@ function MobileLogTimeStatus({
|
||||
)
|
||||
}
|
||||
|
||||
function getCellOriginalField<TData>(
|
||||
cell: Cell<TData, unknown> | undefined,
|
||||
field: string
|
||||
): unknown {
|
||||
const original = cell?.row.original
|
||||
|
||||
if (!original || typeof original !== 'object') {
|
||||
return undefined
|
||||
}
|
||||
|
||||
return (original as Record<string, unknown>)[field]
|
||||
}
|
||||
|
||||
function CommonLogsCard<TData>({
|
||||
cells,
|
||||
}: {
|
||||
@@ -183,6 +195,7 @@ function CommonLogsCard<TData>({
|
||||
|
||||
const modelCell = cells.get('model_name')
|
||||
const quotaCell = cells.get('quota')
|
||||
const createdAtCell = cells.get('created_at')
|
||||
|
||||
return (
|
||||
<div className='space-y-2.5'>
|
||||
@@ -195,13 +208,13 @@ function CommonLogsCard<TData>({
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-[minmax(0,1.15fr)_minmax(0,0.85fr)] gap-1.5'>
|
||||
<div className='min-w-0 rounded-md bg-muted/20 px-2 py-1.5'>
|
||||
<div className='bg-muted/20 min-w-0 rounded-md px-2 py-1.5'>
|
||||
<div className='text-muted-foreground mb-1 text-[11px] leading-none font-medium select-none'>
|
||||
{t('Time')}
|
||||
</div>
|
||||
<MobileLogTimeStatus
|
||||
createdAt={cells.get('created_at')?.row.original?.created_at}
|
||||
type={cells.get('created_at')?.row.original?.type}
|
||||
createdAt={getCellOriginalField(createdAtCell, 'created_at')}
|
||||
type={getCellOriginalField(createdAtCell, 'type')}
|
||||
/>
|
||||
</div>
|
||||
<SummaryField
|
||||
@@ -254,15 +267,8 @@ function TaskLogsCard<TData>({
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-2 gap-1.5'>
|
||||
<SummaryField
|
||||
label={t('Submit Time')}
|
||||
cell={submitTimeCell}
|
||||
/>
|
||||
<SummaryField
|
||||
label={t('User')}
|
||||
cell={cells.get('user')}
|
||||
primaryOnly
|
||||
/>
|
||||
<SummaryField label={t('Submit Time')} cell={submitTimeCell} />
|
||||
<SummaryField label={t('User')} cell={cells.get('user')} primaryOnly />
|
||||
<SummaryField
|
||||
label={t('Result')}
|
||||
cell={cells.get('fail_reason')}
|
||||
@@ -292,28 +298,19 @@ function DrawingLogsCard<TData>({
|
||||
</div>
|
||||
|
||||
<div className='grid grid-cols-2 gap-1.5'>
|
||||
<SummaryField
|
||||
label={t('Submit Time')}
|
||||
cell={submitTimeCell}
|
||||
/>
|
||||
<SummaryField label={t('Submit Time')} cell={submitTimeCell} />
|
||||
<SummaryField
|
||||
label={t('Channel')}
|
||||
cell={cells.get('channel')}
|
||||
primaryOnly
|
||||
/>
|
||||
<SummaryField
|
||||
label={t('Task ID')}
|
||||
cell={cells.get('mj_id')}
|
||||
/>
|
||||
<SummaryField label={t('Task ID')} cell={cells.get('mj_id')} />
|
||||
<SummaryField
|
||||
label={t('Duration')}
|
||||
cell={cells.get('duration')}
|
||||
primaryOnly
|
||||
/>
|
||||
<SummaryField
|
||||
label={t('Image')}
|
||||
cell={cells.get('image_url')}
|
||||
/>
|
||||
<SummaryField label={t('Image')} cell={cells.get('image_url')} />
|
||||
<SummaryField
|
||||
label={t('Prompt')}
|
||||
cell={cells.get('prompt')}
|
||||
@@ -351,11 +348,11 @@ export function UsageLogsMobileList<TData>({
|
||||
|
||||
if (!rows || rows.length === 0) {
|
||||
return (
|
||||
<div className="rounded-lg border p-6">
|
||||
<Empty className="border-none p-0">
|
||||
<div className='rounded-lg border p-6'>
|
||||
<Empty className='border-none p-0'>
|
||||
<EmptyHeader>
|
||||
<EmptyMedia variant="icon">
|
||||
<Database className="size-6" />
|
||||
<EmptyMedia variant='icon'>
|
||||
<Database className='size-6' />
|
||||
</EmptyMedia>
|
||||
<EmptyTitle>{resolvedEmptyTitle}</EmptyTitle>
|
||||
<EmptyDescription>{resolvedEmptyDescription}</EmptyDescription>
|
||||
@@ -366,7 +363,7 @@ export function UsageLogsMobileList<TData>({
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='overflow-hidden rounded-lg border border-border/50 bg-card'>
|
||||
<div className='border-border/50 bg-card overflow-hidden rounded-lg border'>
|
||||
{rows.map((row) => {
|
||||
const cells = new Map(
|
||||
row.getVisibleCells().map((cell) => [cell.column.id, cell])
|
||||
@@ -381,19 +378,13 @@ export function UsageLogsMobileList<TData>({
|
||||
<div
|
||||
key={row.id}
|
||||
className={cn(
|
||||
'border-l-2 border-l-transparent border-b border-border/40 p-3 transition-colors last:border-b-0',
|
||||
'border-border/40 border-b border-l-2 border-l-transparent p-3 transition-colors last:border-b-0',
|
||||
tintClass
|
||||
)}
|
||||
>
|
||||
{logCategory === 'common' && (
|
||||
<CommonLogsCard cells={cells} />
|
||||
)}
|
||||
{logCategory === 'task' && (
|
||||
<TaskLogsCard cells={cells} />
|
||||
)}
|
||||
{logCategory === 'drawing' && (
|
||||
<DrawingLogsCard cells={cells} />
|
||||
)}
|
||||
{logCategory === 'common' && <CommonLogsCard cells={cells} />}
|
||||
{logCategory === 'task' && <TaskLogsCard cells={cells} />}
|
||||
{logCategory === 'drawing' && <DrawingLogsCard cells={cells} />}
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
|
||||
Reference in New Issue
Block a user