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 { DiagnosisStatusBadge } from "@/components/diagnosis-status-badge"; import type { DiagnosisRecord } from "@zeavis/shared"; import { apiClient } from "@/lib/api-client"; 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) => { queryClient.invalidateQueries({ queryKey: ["diagnoses"] }); setDiagnosisPreview(diagnosis); setPreviewOpen(true); setFileName(null); setPreviewUrl(null); if (inputRef.current) inputRef.current.value = ""; }, }); 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); // Detect image dimensions 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; mutation.mutate(file); }; const [previewOpen, setPreviewOpen] = useState(false); const [diagnosisPreview, setDiagnosisPreview] = useState(null); const diagnosesQuery = useQuery({ queryKey: ["diagnoses"], queryFn: () => apiClient.getDiagnoses(), enabled: previewOpen, }); return (

Scan Tanaman

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

{/* Left Sidebar */}
Area Unggah Gambar
{/* Upload area */} {!previewUrl ? (
inputRef.current?.click()} >

Seret & Lepas Foto Daun

atau klik untuk memilih file berkas dari perangkat Anda

PNG, JPG, JPEG
Maks. 5 MB
) : (
Preview { const img = e.currentTarget; setImageDimensions({ width: img.naturalWidth, height: img.naturalHeight, }); }} /> {/* Scanner area detection overlay */} {imageDimensions && (
{/* Outer dark overlay */}
{/* Scan area frame - responsive to image */}
{/* Corner markers */}
{/* Center text */}
AREA SCAN {imageDimensions.width} × {imageDimensions.height}
)}

File: {fileName}

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

Upload gagal

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

)}
{/* Right Sidebar */}
{/* Result Modal */} { setPreviewOpen(false); setDiagnosisPreview(null); }} title={diagnosisPreview?.disease?.commonName ?? "Hasil Diagnosis"} size="md" footer={ diagnosisPreview && (
) } > {diagnosisPreview ? (
{diagnosisPreview.confidence !== null && ( Confidence: {(diagnosisPreview.confidence * 100).toFixed(1)}% )}
{diagnosisPreview.disease && (

{diagnosisPreview.disease.commonName}

{diagnosisPreview.disease.summary}

)} {diagnosisPreview.failureReason && (
{diagnosisPreview.failureReason}
)} {diagnosisPreview.predictions.length > 0 && (

Semua Prediksi

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

Riwayat Diagnosis Terbaru

{diagnosesQuery.isLoading && (
Memuat riwayat...
)} {!diagnosesQuery.isLoading && !diagnosesQuery.isError && (
{(diagnosesQuery.data ?? []).slice(0, 5).map((d) => (
thumb
{d.disease?.commonName ?? "Unknown"}
{new Date(d.createdAt).toLocaleString("id-ID")}
Lihat
))}
)}
) : null}
); }