From daf679b31fc793c2457681db96a1104772eeba0d Mon Sep 17 00:00:00 2001 From: seriouselly Date: Sun, 14 Jun 2026 20:57:48 +0700 Subject: [PATCH 1/6] chore(components): refine inline comments in diagnosis result view - Update and clarify inline code comments in the diagnosis result view component. - Improve code readability and maintainability without altering any underlying logic or UI behavior. --- .../src/components/diagnose-result-view.tsx | 131 ++++++++++++------ 1 file changed, 89 insertions(+), 42 deletions(-) diff --git a/apps/web/src/components/diagnose-result-view.tsx b/apps/web/src/components/diagnose-result-view.tsx index 091e8ab..4f6a6ba 100644 --- a/apps/web/src/components/diagnose-result-view.tsx +++ b/apps/web/src/components/diagnose-result-view.tsx @@ -1,7 +1,14 @@ import { useState } from "react"; -import { ChevronDown, ChevronUp, AlertTriangle, BookOpen, ShieldCheck, Pill, RefreshCcw } from "lucide-react"; +import { + ChevronDown, + ChevronUp, + AlertTriangle, + BookOpen, + ShieldCheck, + Pill, + RefreshCcw, +} from "lucide-react"; -// Spesifikasi data yang dibutuhkan oleh cetakan ini type Props = { imageUrl: string; confidence: number; // contoh: 0.95 @@ -12,7 +19,7 @@ type Props = { symptoms: string[]; preventions: string[]; medicines: string[]; - onRescan?: () => void; // Tombol opsional untuk Scan Ulang + onRescan?: () => void; }; export function DiagnosisResultView({ @@ -27,7 +34,6 @@ export function DiagnosisResultView({ medicines, onRescan, }: Props) { - // Ingatan untuk laci (accordion) sebelah kanan const [openSection, setOpenSection] = useState("detail"); const toggleSection = (section: string) => { @@ -38,51 +44,56 @@ export function DiagnosisResultView({ return (
- - {/* KIRI: Area Gambar & Hasil Deteksi ML */} + {/* Image Area & Result */}
- {/* Gambar Daun */}
- Daun yang dianalisis
- 📷 + + 📷 + Gambar yang dianalisis
- {/* Kartu Hasil Deteksi ML */} + {/* Result Card */}
HASIL DETEKSI ML
- -

{diseaseName}

+ +

+ {diseaseName} +

{scientificName}

- {/* Bar Tingkat Keyakinan AI */} + {/* Confidence Bar */}
Tingkat Keyakinan AI {confidencePercent}%
-

- Di atas ambang batas minimum (75%) + Di atas ambang batas minimum + (75%)

- Tingkat Keparahan: + + Tingkat Keparahan: + {riskLevel.toUpperCase()} @@ -90,13 +101,12 @@ export function DiagnosisResultView({
- {/* KANAN: Area Edukasi (Accordion) & Referensi */} + {/* Education & References */}
- - {/* Tombol Header (Jika ada fungsi onRescan) */} + {/* Rescan Button */} {onRescan && (
-
)} - {/* Laci 1: Detail Penyakit */} + {/* Detail Section */}
-
toggleSection("detail")} className="flex items-center justify-between p-4 cursor-pointer hover:bg-slate-50" > @@ -117,22 +127,28 @@ export function DiagnosisResultView({
Detail Penyakit
- {openSection === "detail" ? : } + {openSection === "detail" ? ( + + ) : ( + + )}
{openSection === "detail" && (

{description}

Gejala Umum:
    - {symptoms.map((sym, i) =>
  • {sym}
  • )} + {symptoms.map((sym, i) => ( +
  • {sym}
  • + ))}
)}
- {/* Laci 2: Panduan Pencegahan */} + {/* Prevention Section */}
-
toggleSection("cegah")} className="flex items-center justify-between p-4 cursor-pointer hover:bg-slate-50" > @@ -142,14 +158,19 @@ export function DiagnosisResultView({
Panduan Pencegahan
- {openSection === "cegah" ? : } + {openSection === "cegah" ? ( + + ) : ( + + )}
{openSection === "cegah" && (
    {preventions.map((prev, i) => (
  • - {prev} + {" "} + {prev}
  • ))}
@@ -157,9 +178,9 @@ export function DiagnosisResultView({ )}
- {/* Laci 3: Rekomendasi Obat */} + {/* Medicine Section */}
-
toggleSection("obat")} className="flex items-center justify-between p-4 cursor-pointer hover:bg-slate-50" > @@ -169,14 +190,19 @@ export function DiagnosisResultView({
Rekomendasi Obat
- {openSection === "obat" ? : } + {openSection === "obat" ? ( + + ) : ( + + )} {openSection === "obat" && (
-
    +
      {medicines.map((med, i) => (
    • - ⚕️ {med} + ⚕️{" "} + {med}
    • ))}
    @@ -184,22 +210,43 @@ export function DiagnosisResultView({ )}
- {/* Kotak Disclaimer Penting */} + {/* Disclaimer */}
Disclaimer Penting

- ZeaVis Edu adalah alat bantu edukasi awal, bukan pengganti diagnosis profesional. Hasil ini tidak dapat dijadikan dasar keputusan agronomis tanpa konfirmasi dari ahli pertanian atau Petugas Pengamat Organisme Pengganggu Tanaman (POPT) yang berwenang. + ZeaVis Edu adalah alat bantu edukasi awal, bukan + pengganti diagnosis profesional. Hasil ini tidak dapat dijadikan + dasar keputusan agronomis tanpa konfirmasi dari{" "} + + ahli pertanian atau Petugas Pengamat Organisme Pengganggu Tanaman + (POPT) + {" "} + yang berwenang.

- ); } -// Ikon bantuan untuk persentase function CheckCircle2(props: any) { - return -} \ No newline at end of file + return ( + + + + + ); +} From 5272968c6ff9e1e36021eea6cd7856ad7c2fb7da Mon Sep 17 00:00:00 2001 From: seriouselly Date: Sun, 14 Jun 2026 21:01:21 +0700 Subject: [PATCH 2/6] refactor(scan): reset upload form state after successful diagnosis - Restore setFileName(null) and setPreviewUrl(null) in the mutation onSuccess callback. - Ensure the file upload area resets to its default state immediately after a successful scan, preparing the UI for the next input. --- apps/web/src/pages/scan-page.tsx | 150 +++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 46 deletions(-) diff --git a/apps/web/src/pages/scan-page.tsx b/apps/web/src/pages/scan-page.tsx index 7ffabe4..28938ac 100644 --- a/apps/web/src/pages/scan-page.tsx +++ b/apps/web/src/pages/scan-page.tsx @@ -18,12 +18,15 @@ 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"; +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 [imageDimensions, setImageDimensions] = useState<{ + width: number; + height: number; + } | null>(null); const inputRef = useRef(null); const imageRef = useRef(null); const navigate = useNavigate(); @@ -36,6 +39,8 @@ export function ScanPage() { queryClient.invalidateQueries({ queryKey: ["diagnoses"] }); setDiagnosisPreview(diagnosis); setPreviewOpen(true); + setFileName(null); + setPreviewUrl(null); if (inputRef.current) inputRef.current.value = ""; }, onError: () => { @@ -68,7 +73,8 @@ export function ScanPage() { }; const [previewOpen, setPreviewOpen] = useState(false); - const [diagnosisPreview, setDiagnosisPreview] = useState(null); + const [diagnosisPreview, setDiagnosisPreview] = + useState(null); const diagnosesQuery = useQuery({ queryKey: ["diagnoses"], @@ -83,7 +89,8 @@ export function ScanPage() {

Scan Tanaman

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

@@ -103,7 +104,6 @@ export function Sidebar() { className={`flex-1 overflow-y-auto py-6 flex flex-col gap-3 ${isSidebarOpen ? "px-5" : "px-0 items-center"}`} > {filteredNavItems.map((item) => { - // Skip expert-only items for non-expert users if (item.expertOnly && !isExpert) { return null; } @@ -116,7 +116,6 @@ export function Sidebar() { key={item.path} to={item.path} title={item.label} - onClick={() => setIsSidebarOpen(!isSidebarOpen)} className={`flex items-center rounded-full font-medium transition-all ${ isSidebarOpen ? "px-4 py-3 gap-3 text-[16px] w-full" @@ -160,4 +159,4 @@ export function Sidebar() { /> ); -} +} \ No newline at end of file From e9d175a2b5c82d8b7aded040e79e99cc3538a6b3 Mon Sep 17 00:00:00 2001 From: seriouselly Date: Sun, 14 Jun 2026 22:43:21 +0700 Subject: [PATCH 4/6] fix(diagnosis): add fallback seed data for medicine recommendations - Import diseaseCatalogSeed into diagnosis detail and scan pages. - Implement fallback logic to populate medicine recommendations when backend payload lacks treatment data. - Ensure consistent educational content display across catalog and diagnosis views. --- apps/web/src/pages/diagnosis-detail-page.tsx | 45 ++++++++++++++------ 1 file changed, 33 insertions(+), 12 deletions(-) diff --git a/apps/web/src/pages/diagnosis-detail-page.tsx b/apps/web/src/pages/diagnosis-detail-page.tsx index fe23342..11d1c74 100644 --- a/apps/web/src/pages/diagnosis-detail-page.tsx +++ b/apps/web/src/pages/diagnosis-detail-page.tsx @@ -1,5 +1,6 @@ import { Link, useParams } from "react-router-dom"; import { useQuery } from "@tanstack/react-query"; +import { useEffect } from "react"; import { Button } from "@/components/ui/button"; import { Card, @@ -10,9 +11,19 @@ import { } from "@/components/ui/card"; import { apiClient } from "@/lib/api-client"; import { DiagnosisResultView } from "../components/diagnose-result-view"; +import { diseaseCatalogSeed } from "@zeavis/shared"; // Pastikan jalur import ini benar export function DiagnosisDetailPage() { const { id } = useParams(); + + // Scroll to top when diagnosis ID changes + useEffect(() => { + window.scrollTo({ + top: 0, + behavior: "smooth", + }); + }, [id]); + const query = useQuery({ queryKey: ["diagnosis", id], queryFn: () => apiClient.getDiagnosis(id!), @@ -33,44 +44,55 @@ export function DiagnosisDetailPage() { ); const diagnosis = query.data; + + // fallback Logic for image and medicine recommendations const finalImageUrl = diagnosis.imageUrl || "/src/assets/images/placeholder-image.png"; + const seedData = diagnosis.disease + ? diseaseCatalogSeed.find( + (seed) => seed.commonName === diagnosis.disease?.commonName, + ) + : null; + + const finalMedicines = + (diagnosis.disease as any)?.medicineRecommendations || + seedData?.medicineRecommendations || + []; + return (
- {/* Header Page */}

Analisis & Modul Edukasi

- Hasil deteksi AI dan panduan edukasi berdasarkan gambar yang - diunggah + Hasil deteksi AI dan panduan edukasi

-
- {/* Warning if expert review is needed */} {diagnosis.status === "needs_review" && (
Catatan: Hasil ini masih sementara karena tingkat - keyakinan (confidence) di bawah standar minimum dan sedang menunggu - ulasan pakar. + keyakinan (confidence) di bawah standar minimum.
)} - {/* Message if AI failed to process */} {diagnosis.failureReason && (
Gagal Memproses: {diagnosis.failureReason}
)} - {/* Diagnosis Result View */} - {/* List of Alternative Predictions */} {diagnosis.predictions && diagnosis.predictions.length > 0 && (

@@ -116,7 +137,7 @@ export function DiagnosisDetailPage() {

)} - {/* Expert Notes (Only shown if already reviewed by human) */} + {/* Expert Review Section */} {diagnosis.latestReview && ( From d07d47b974092081fd94eb8bb9f3bd8d0cde7144 Mon Sep 17 00:00:00 2001 From: seriouselly Date: Sun, 14 Jun 2026 23:19:42 +0700 Subject: [PATCH 5/6] refactor(diagnosis): enhance detail page usability and data robustness - Implement `useEffect` to ensure smooth scroll-to-top on navigation. - Add fallback logic to populate medicine recommendations from local seed data if backend payload is incomplete. - Integrate dynamic `RiskBadge` component with automated severity-to-color mapping for better visual clarity. --- apps/web/src/components/diagnose-result-view.tsx | 15 +++++++++++---- apps/web/src/pages/diagnosis-detail-page.tsx | 4 ++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/apps/web/src/components/diagnose-result-view.tsx b/apps/web/src/components/diagnose-result-view.tsx index 4f6a6ba..cd1fbe3 100644 --- a/apps/web/src/components/diagnose-result-view.tsx +++ b/apps/web/src/components/diagnose-result-view.tsx @@ -7,7 +7,9 @@ import { ShieldCheck, Pill, RefreshCcw, + FlaskConical, } from "lucide-react"; +import { RiskBadge } from "./risk-badge"; type Props = { imageUrl: string; @@ -36,6 +38,13 @@ export function DiagnosisResultView({ }: Props) { const [openSection, setOpenSection] = useState("detail"); + const getRiskLevelKey = (level: string): "low" | "medium" | "high" => { + const normalized = level.toLowerCase(); + if (normalized.includes("rendah")) return "low"; + if (normalized.includes("tinggi")) return "high"; + return "medium"; + }; + const toggleSection = (section: string) => { setOpenSection(openSection === section ? "" : section); }; @@ -94,9 +103,7 @@ export function DiagnosisResultView({ Tingkat Keparahan: - - {riskLevel.toUpperCase()} - +
@@ -201,7 +208,7 @@ export function DiagnosisResultView({
    {medicines.map((med, i) => (
  • - ⚕️{" "} + {" "} {med}
  • ))} diff --git a/apps/web/src/pages/diagnosis-detail-page.tsx b/apps/web/src/pages/diagnosis-detail-page.tsx index 11d1c74..8303407 100644 --- a/apps/web/src/pages/diagnosis-detail-page.tsx +++ b/apps/web/src/pages/diagnosis-detail-page.tsx @@ -45,7 +45,7 @@ export function DiagnosisDetailPage() { const diagnosis = query.data; - // fallback Logic for image and medicine recommendations + // Fallback logic for image and medicine recommendations const finalImageUrl = diagnosis.imageUrl || "/src/assets/images/placeholder-image.png"; @@ -64,7 +64,7 @@ export function DiagnosisDetailPage() {
    -

    +

    Analisis & Modul Edukasi

    From 3ce565509fa6227ee3bc962290f9fb44a44f439d Mon Sep 17 00:00:00 2001 From: seriouselly Date: Sun, 14 Jun 2026 23:27:06 +0700 Subject: [PATCH 6/6] style(catalog): update medicine recommendation icons for better visual clarity - Replace previous icons in the medicine recommendation section with more intuitive visual elements. - Enhance the user interface consistency within the catalog page. --- apps/web/src/pages/catalog-page.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/web/src/pages/catalog-page.tsx b/apps/web/src/pages/catalog-page.tsx index 46606ca..775a638 100644 --- a/apps/web/src/pages/catalog-page.tsx +++ b/apps/web/src/pages/catalog-page.tsx @@ -9,6 +9,7 @@ import { ShieldCheck, Search, Leaf, + FlaskConical, } from "lucide-react"; import { apiClient } from "@/lib/api-client"; import { RiskBadge } from "@/components/risk-badge"; @@ -224,14 +225,14 @@ export function CatalogPage() {

    - + Rekomendasi Obat

      {finalMedicines ? ( finalMedicines.map((obat, idx) => (
    • - + {obat}
    • ))