/** * Field Console * * MVP-0: First Field Upload * * Four views: * 1. Upload — phone, video, image, GPS * 2. Queue — mission status * 3. Artifact Viewer — original, metadata, knowledge, history * 4. Processing Timeline — step-by-step progress */ import { useState, useEffect } from 'react'; interface Mission { id: string; status: string; progress: number; createdAt: string; steps: { uploaded: boolean; archived: boolean; metadataExtracted: boolean; knowledgeExtracted: boolean; decisionReady: boolean; }; } function FieldConsole() { const [activeTab, setActiveTab] = useState<'upload' | 'queue' | 'viewer' | 'timeline'>('upload'); const [missions, setMissions] = useState([]); useEffect(() => { fetch('/api/v1/missions') .then(r => r.json()) .then(data => setMissions(data)); }, []); return (

Field Console

{/* Tabs */}
{(['upload', 'queue', 'viewer', 'timeline'] as const).map(tab => ( ))}
{/* Content */} {activeTab === 'upload' && } {activeTab === 'queue' && } {activeTab === 'viewer' && } {activeTab === 'timeline' && }
); } // 1. Upload View function UploadView() { return (

Upload

); } function UploadCard({ icon, label }: { icon: string; label: string }) { return (
{icon}

{label}

); } // 2. Queue View function QueueView({ missions }: { missions: Mission[] }) { return (

Queue

{missions.map(mission => (
{mission.id} {mission.status}
))}
); } function ProgressBar({ progress }: { progress: number }) { return (
); } function StatusBadge({ label, done }: { label: string; done: boolean }) { return ( {done ? '✓' : '⏳'} {label} ); } // 3. Artifact Viewer View function ArtifactViewerView({ missions }: { missions: Mission[] }) { const [selectedMission, setSelectedMission] = useState(missions[0] || null); if (!selectedMission) return
No missions yet
; return (

Artifact Viewer

{/* Mission selector */}
{/* Original */}

Original

🎥
{/* Metadata */}

Metadata

GPS: 59.3293, 18.0686

Hash: sha256:abc123...

Storage: file:///uploads/...

{/* Knowledge */}

Knowledge

Objects: Pending

Ontology: Pending

{/* History */}

History

Created: {new Date(selectedMission.createdAt).toLocaleString()}

); } // 4. Processing Timeline View function TimelineView({ missions }: { missions: Mission[] }) { return (

Processing Timeline

{missions.map(mission => (

{mission.id}

))}
); } function TimelineStep({ label, time, done }: { label: string; time: string; done: boolean }) { return (
{time} {label}
); } export default FieldConsole;