import { useRef, useState } from "react"; import { useNavigate, Link } from "react-router-dom"; import { useMutation, useQueryClient, useQuery } from "@tanstack/react-query"; import { Camera, Upload, X, ImageOff, CircleAlert, ZoomIn, Sun, AlignCenter, Check, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Modal } from "@/components/ui/modal"; import type { DiagnosisRecord } from "@zeavis/shared"; import { apiClient } from "@/lib/api-client"; import { trackScan, trackDiagnosisResult } from "@/lib/telemetry"; import { DiagnosisResultView } from "../components/diagnose-result-view"; export function ScanPage() { const [fileName, setFileName] = useState(null); const [previewUrl, setPreviewUrl] = useState(null); const [imageDimensions, setImageDimensions] = useState<{ width: number; height: number; } | null>(null); const inputRef = useRef(null); const imageRef = useRef(null); const navigate = useNavigate(); const queryClient = useQueryClient(); const mutation = useMutation({ mutationFn: (file: File) => apiClient.createDiagnosis(file), onSuccess: (diagnosis) => { trackDiagnosisResult(diagnosis.status !== "failed"); queryClient.invalidateQueries({ queryKey: ["diagnoses"] }); setDiagnosisPreview(diagnosis); setPreviewOpen(true); setFileName(null); setPreviewUrl(null); if (inputRef.current) inputRef.current.value = ""; }, onError: () => { trackDiagnosisResult(false); }, }); const handleFile = (f?: File) => { if (!f) return; if (f.size > 5 * 1024 * 1024) { alert("Ukuran file melebihi batas 5MB"); return; } setFileName(f.name); const url = URL.createObjectURL(f); setPreviewUrl(url); const img = new Image(); img.onload = () => { setImageDimensions({ width: img.width, height: img.height }); }; img.src = url; }; const handleUpload = () => { const file = inputRef.current?.files?.[0]; if (!file) return; trackScan(); mutation.mutate(file); }; const [previewOpen, setPreviewOpen] = useState(false); const [diagnosisPreview, setDiagnosisPreview] = useState(null); const diagnosesQuery = useQuery({ queryKey: ["diagnoses"], queryFn: () => apiClient.getDiagnoses(), enabled: previewOpen, }); return (
{/* Main Header */}

Scan Tanaman

Unggah foto daun jagung untuk dianalisis oleh sistem AI kami secara real-time.

{/* Main Content */}
{/* Left Column: Upload Area */}
Area Unggah Gambar
{!previewUrl ? (
inputRef.current?.click()} >

Seret & Lepas Foto Daun

atau klik untuk memilih file berkas dari perangkat Anda

PNG, JPG, JPEG, WEBP
Maks. 5 MB
) : (
Preview { const img = e.currentTarget; setImageDimensions({ width: img.naturalWidth, height: img.naturalHeight, }); }} /> {imageDimensions && (
AREA SCAN {imageDimensions.width} × {imageDimensions.height}
)}

File: {fileName}

)} handleFile(e.target.files?.[0])} /> {mutation.isError && (

Upload gagal

{mutation.error instanceof Error ? mutation.error.message : "Terjadi kesalahan saat memproses gambar"}

)}
{/* Right Column: Guidelines */}
{/* Modal Preview Diagnosis */} { setPreviewOpen(false); setDiagnosisPreview(null); }} title="Hasil Analisis AI" size="lg" footer={ diagnosisPreview && (
) } > {diagnosisPreview ? (
{/* Warning if expert review is needed */} {diagnosisPreview.status === "needs_review" && (
Catatan: Hasil ini masih sementara karena tingkat keyakinan (confidence) di bawah standar minimum.
)} {/* Message if AI fails to process */} {diagnosisPreview.failureReason && (
Gagal Memproses:{" "} {diagnosisPreview.failureReason}
)} {/* All Model Predictions */} {diagnosisPreview.predictions && diagnosisPreview.predictions.length > 0 && (

Semua Prediksi Model

Tebakan alternatif lain dari sistem AI ZeaVis

{diagnosisPreview.predictions .sort((a, b) => a.rank - b.rank) .map((pred) => (
{pred.modelLabel} {(pred.confidence * 100).toFixed(1)}%
))}
)}
) : null}
); }