FetalCLIP / frontend /src /components /PreprocessingBadge.tsx
Numan Saeed
Upgrade to React + FastAPI (Docker-based)
a874986
import { CheckCircle, AlertCircle, Info } from 'lucide-react';
import { useState } from 'react';
import type { PreprocessingInfo } from '../lib/api';
interface PreprocessingBadgeProps {
info: PreprocessingInfo | null;
fileType?: 'dicom' | 'image' | null;
compact?: boolean;
}
const STEP_LABELS: Record<string, string> = {
dicom_parsing: 'DICOM Parsing',
us_region_extraction: 'US Region Extraction',
text_box_removal: 'Text Box Removal',
fan_extraction: 'Fan Extraction',
annotation_detection: 'Annotation Detection',
inpainting: 'Inpainting',
denoising: 'Denoising',
normalization: 'Normalization',
square_padding: 'Square Padding',
resize_512: 'Resize to 512×512',
rgb_conversion: 'RGB Conversion',
};
export function PreprocessingBadge({ info, fileType, compact = false }: PreprocessingBadgeProps) {
const [expanded, setExpanded] = useState(false);
// Show pending state when file is selected but not yet processed
if (!info && fileType) {
const isDicom = fileType === 'dicom';
return (
<div className={`rounded-xl border shadow-card ${isDicom ? 'border-nvidia-green/30 bg-nvidia-green/5' : 'border-accent-blue/30 bg-accent-blue/5'} ${compact ? 'px-3 py-2' : 'px-4 py-3'}`}>
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${isDicom ? 'bg-nvidia-green' : 'bg-accent-blue'}`} />
<span className={`font-semibold ${isDicom ? 'text-nvidia-green' : 'text-accent-blue'} ${compact ? 'text-xs' : 'text-sm'}`}>
{isDicom ? 'DICOM' : 'PNG/JPEG'}
</span>
<span className={`text-text-secondary ${compact ? 'text-xs' : 'text-sm'}`}>
• {isDicom ? 'Full Pipeline' : 'Basic Pipeline'}
</span>
</div>
{!compact && (
<p className="text-xs text-text-muted mt-1">
{isDicom
? 'Will apply: Fan extraction, text removal, denoising'
: 'Will apply: Square padding only. For best accuracy, use DICOM files.'
}
</p>
)}
</div>
);
}
if (!info) return null;
const isDicom = info.type === 'dicom';
const isFull = info.pipeline === 'full';
return (
<div className={`rounded-xl border shadow-card ${isFull ? 'border-nvidia-green/30 bg-nvidia-green/5' : 'border-amber-500/30 bg-amber-500/5'} ${compact ? 'px-3 py-2' : 'px-4 py-3'}`}>
{/* Header */}
<button
onClick={() => setExpanded(!expanded)}
className="w-full flex items-center justify-between"
>
<div className="flex items-center gap-2">
<div className={`w-2 h-2 rounded-full ${isFull ? 'bg-nvidia-green' : 'bg-amber-500'}`} />
<span className={`font-semibold ${isFull ? 'text-nvidia-green' : 'text-amber-600'} ${compact ? 'text-xs' : 'text-sm'}`}>
{isDicom ? 'DICOM' : 'PNG/JPEG'}
</span>
<span className={`text-text-secondary ${compact ? 'text-xs' : 'text-sm'}`}>
• {isFull ? 'Full Pipeline' : 'Basic Pipeline'}
</span>
</div>
<Info className={`text-text-muted ${compact ? 'w-3 h-3' : 'w-4 h-4'}`} />
</button>
{/* Expanded Details */}
{expanded && (
<div className="mt-3 pt-3 border-t border-dark-border">
<p className="text-xs text-text-muted mb-2 font-medium">Steps Applied:</p>
<div className="space-y-1.5">
{info.steps_applied.map((step) => (
<div key={step} className="flex items-center gap-2">
<CheckCircle className="w-3.5 h-3.5 text-nvidia-green" />
<span className="text-xs text-text-primary">
{STEP_LABELS[step] || step}
</span>
</div>
))}
</div>
{/* Missing steps for basic pipeline */}
{!isFull && (
<div className="mt-3 pt-3 border-t border-dark-border">
<p className="text-xs text-text-muted mb-2 font-medium">Not Applied:</p>
<div className="space-y-1.5">
{['fan_extraction', 'annotation_detection', 'inpainting', 'denoising'].map((step) => (
<div key={step} className="flex items-center gap-2">
<AlertCircle className="w-3.5 h-3.5 text-amber-500" />
<span className="text-xs text-text-muted">
{STEP_LABELS[step] || step}
</span>
</div>
))}
</div>
<p className="text-xs text-amber-600 mt-2 font-medium">
⚠️ For best accuracy, use DICOM files from the ultrasound machine.
</p>
</div>
)}
{/* Metadata */}
{info.metadata.pixel_spacing && (
<div className="mt-3 pt-3 border-t border-dark-border">
<p className="text-xs text-text-muted">
Pixel Spacing: <span className="text-text-primary font-medium">{info.metadata.pixel_spacing.toFixed(3)} mm/px</span>
</p>
</div>
)}
</div>
)}
</div>
);
}