fix: align Job type to camelCase matching backend to_dict() response

Frontend Job interface used snake_case (file_name, file_size, progress_pct,
step_label, provider_updates, result_csv_url, created_at) but backend
returns camelCase (fileName, fileSize, progressPct, stepLabel,
providerUpdates, resultCsvUrl, createdAt) — causing all fields to be
undefined and showing 'NaN MB', broken progress bar, empty labels.

Updated types/index.ts Job, ProviderUpdate, JobSummary interfaces and
JobProgressCard.tsx to use the correct camelCase field names.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Vadym Samoilenko 2026-03-23 15:14:56 +00:00
parent ba9af5f93c
commit c6025b02e3
2 changed files with 25 additions and 25 deletions

View file

@ -23,10 +23,10 @@ export default function JobProgressCard({ job, onDelete }: Props) {
<div className="flex items-start justify-between mb-2">
<div>
<div className="font-medium text-sm truncate max-w-xs" style={{ color: 'var(--text-primary)' }}>
{job.file_name}
{job.fileName}
</div>
<div className="text-xs mt-0.5" style={{ color: 'var(--text-muted)' }}>
{(job.file_size / 1024 / 1024).toFixed(1)} MB
{job.fileSize ? (job.fileSize / 1024 / 1024).toFixed(1) + ' MB' : '—'}
</div>
</div>
<div className="flex items-center gap-2">
@ -50,19 +50,19 @@ export default function JobProgressCard({ job, onDelete }: Props) {
<div className="h-1 rounded-full mb-2" style={{ background: 'var(--border)' }}>
<div
className="h-full rounded-full transition-all duration-500"
style={{ width: `${job.progress_pct}%`, background: isDone ? 'var(--success)' : 'var(--accent)' }}
style={{ width: `${job.progressPct ?? 0}%`, background: isDone ? 'var(--success)' : 'var(--accent)' }}
/>
</div>
)}
{job.step_label && (
<div className="text-xs mb-2" style={{ color: 'var(--text-muted)' }}>{job.step_label}</div>
{job.stepLabel && (
<div className="text-xs mb-2" style={{ color: 'var(--text-muted)' }}>{job.stepLabel}</div>
)}
{/* Provider updates */}
{Object.entries(job.provider_updates || {}).length > 0 && (
{Object.entries(job.providerUpdates || {}).length > 0 && (
<div className="flex gap-2 flex-wrap mb-2">
{Object.entries(job.provider_updates).map(([key, pu]) => (
{Object.entries(job.providerUpdates).map(([key, pu]) => (
<span key={key} className="text-xs px-2 py-0.5 rounded" style={{
background: pu.status === 'success' ? 'rgba(34,197,94,0.1)' : pu.status === 'error' ? 'rgba(239,68,68,0.1)' : 'rgba(255,255,255,0.05)',
color: pu.status === 'success' ? 'var(--success)' : pu.status === 'error' ? 'var(--danger)' : 'var(--text-muted)',
@ -95,7 +95,7 @@ export default function JobProgressCard({ job, onDelete }: Props) {
{isDone && job.summary && (
<div className="text-xs mt-2" style={{ color: 'var(--text-muted)' }}>
{job.summary.assets_extracted} assets · ${job.summary.cost_usd_total?.toFixed(4)} · {job.summary.processing_time_seconds?.toFixed(1)}s
{job.summary.assetsExtracted} assets · ${job.summary.costUsdTotal?.toFixed(4)} · {job.summary.processingTimeSeconds?.toFixed(1)}s
</div>
)}
</div>

View file

@ -55,34 +55,34 @@ export interface ProviderUpdate {
provider: string
model: string
status: string
latency_ms?: number
tokens_in?: number
tokens_out?: number
cost_usd?: number
latencyMs?: number
tokensIn?: number
tokensOut?: number
costUsd?: number
error?: string
}
export interface JobSummary {
assets_extracted: number
confidence_score: number
cost_usd_total: number
tokens_total: number
processing_time_seconds: number
assetsExtracted: number
confidenceScore: number
costUsdTotal: number
tokensTotal: number
processingTimeSeconds: number
}
export interface Job {
id: string
file_name: string
file_size: number
user_id: string
fileName: string
fileSize: number
userId: string
phase: JobPhase
progress_pct: number
step_label: string
provider_updates: Record<string, ProviderUpdate>
progressPct: number
stepLabel: string
providerUpdates: Record<string, ProviderUpdate>
error?: string
result_csv_url?: string
resultCsvUrl?: string
summary?: JobSummary
created_at: string
createdAt: string
}
export interface ModelConfiguration {