"use client"; import { useState, useCallback, useMemo } from "react"; import type { Disease, CausalAgentType, Prevalence, Severity } from "@/lib/types"; import ImageLightbox from "@/components/ImageLightbox"; import FlagButton from "@/components/FlagButton"; // ─── Severity badge ─── function SeverityBadge({ severity }: { severity: Severity }) { const colors: Record = { low: "bg-leaf-green-100 text-leaf-green-800 dark:bg-leaf-green-900/40 dark:text-leaf-green-300", moderate: "bg-warning-amber-100 text-warning-amber-800 dark:bg-warning-amber-900/40 dark:text-warning-amber-300", high: "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300", critical: "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300", }; const labels: Record = { low: "Low", moderate: "Moderate", high: "High", critical: "Critical", }; return ( {severity === "critical" ? "🚨 " : ""} {labels[severity]} Severity ); } // ─── Disease type badge ─── function TypeBadge({ type }: { type: CausalAgentType }) { const colors: Record = { fungal: "bg-purple-100 text-purple-800 dark:bg-purple-900/40 dark:text-purple-300", bacterial: "bg-blue-100 text-blue-800 dark:bg-blue-900/40 dark:text-blue-300", viral: "bg-pink-100 text-pink-800 dark:bg-pink-900/40 dark:text-pink-300", environmental: "bg-orange-100 text-orange-800 dark:bg-orange-900/40 dark:text-orange-300", }; return ( {type === "environmental" ? "Environmental" : type.charAt(0).toUpperCase() + type.slice(1)} ); } // ─── Disease card ─── function DiseaseCard({ disease, onImageClick, }: { disease: Disease; onImageClick: (disease: Disease) => void; }) { return (
{/* Card header */}

{disease.name}

{disease.scientificName && (

{disease.scientificName}

)}
{/* Disease image or placeholder */}
{disease.imageUrl ? ( ) : (

{disease.causalAgentType === "fungal" ? "Fungal pathogen" : disease.causalAgentType === "bacterial" ? "Bacterial infection" : disease.causalAgentType === "viral" ? "Viral infection" : "Environmental disorder"}

)}
{/* Flag button for disease image */}

{disease.description}

{/* Details grid */}
{/* Symptoms */}

Symptoms

    {disease.symptoms.map((symptom, i) => (
  • {symptom}
  • ))}
{/* Causes */}

Causes

    {disease.causes.map((cause, i) => (
  • {cause}
  • ))}
{/* Treatment Steps */}

Treatment Steps

    {disease.treatment.map((step, i) => (
  1. {step}
  2. ))}
{/* Prevention Tips */}

Prevention Tips

    {disease.prevention.map((tip, i) => (
  • {tip}
  • ))}
); } // ─── Prevalence badge ─── function PrevalenceBadge({ prevalence }: { prevalence: Prevalence }) { const icons: Record = { common: "📊", uncommon: "📋", rare: "📌", very_rare: "🔍", }; const colors: Record = { common: "bg-emerald-100 text-emerald-800 dark:bg-emerald-900/40 dark:text-emerald-300", uncommon: "bg-zinc-100 text-zinc-700 dark:bg-zinc-800/60 dark:text-zinc-300", rare: "bg-amber-100 text-amber-800 dark:bg-amber-900/40 dark:text-amber-300", very_rare: "bg-red-100 text-red-800 dark:bg-red-900/40 dark:text-red-300", }; const label = prevalence.replace(/_/g, " ").replace(/\b\w/g, (c) => c.toUpperCase()); return ( {icons[prevalence]} {label} ); } // ─── Sort / Search controls ─── const SEVERITY_RANK: Record = { critical: 4, high: 3, moderate: 2, low: 1, }; const PREVALENCE_RANK: Record = { common: 4, uncommon: 3, rare: 2, very_rare: 1, }; type SortField = "prevalence" | "danger"; function SearchSortBar({ searchQuery, onSearchChange, sortField, onSortFieldChange, sortOrder, onSortOrderToggle, resultCount, }: { searchQuery: string; onSearchChange: (q: string) => void; sortField: SortField; onSortFieldChange: (f: SortField) => void; sortOrder: "asc" | "desc"; onSortOrderToggle: () => void; resultCount: number; }) { return (
{/* Search */}
onSearchChange(e.target.value)} placeholder="Search diseases by name…" className="w-full rounded-lg border border-zinc-300 dark:border-zinc-600 bg-white dark:bg-zinc-800 py-2 pl-10 pr-3 text-sm text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 dark:placeholder-zinc-500 focus:outline-none focus:ring-2 focus:ring-leaf-green-500 focus:border-leaf-green-500 transition-colors" aria-label="Search diseases" />
{/* Sort controls */}
Sort by:
{/* Direction toggle */} {resultCount} {resultCount === 1 ? "result" : "results"}
); } // ─── Client component wrapper ─── export default function DiseaseCards({ diseases }: { diseases: Disease[] }) { const [lightboxOpen, setLightboxOpen] = useState(false); const [lightboxIndex, setLightboxIndex] = useState(0); const [searchQuery, setSearchQuery] = useState(""); const [sortField, setSortField] = useState("danger"); const [sortOrder, setSortOrder] = useState<"asc" | "desc">("desc"); // ── Filtered + sorted diseases ── const processed = useMemo(() => { // Filter let result = diseases; const trimmed = searchQuery.trim().toLowerCase(); if (trimmed) { result = result.filter( (d) => d.name.toLowerCase().includes(trimmed) || d.scientificName.toLowerCase().includes(trimmed), ); } // Sort const sorted = [...result].sort((a, b) => { let cmp: number; if (sortField === "danger") { cmp = SEVERITY_RANK[a.severity] - SEVERITY_RANK[b.severity]; } else { cmp = PREVALENCE_RANK[a.prevalence] - PREVALENCE_RANK[b.prevalence]; } return sortOrder === "desc" ? -cmp : cmp; }); return sorted; }, [diseases, searchQuery, sortField, sortOrder]); // Build list of images from processed diseases that have imageUrls const images = useMemo( () => processed .filter((d) => d.imageUrl) .map((d) => ({ src: d.imageUrl!, alt: `${d.name} symptoms` })), [processed], ); const handleImageClick = useCallback( (disease: Disease) => { const index = images.findIndex((img) => img.src === disease.imageUrl); setLightboxIndex(index >= 0 ? index : 0); setLightboxOpen(true); }, [images], ); const handleClose = useCallback(() => setLightboxOpen(false), []); const handleSortOrderToggle = useCallback(() => { setSortOrder((prev) => (prev === "desc" ? "asc" : "desc")); }, []); if (diseases.length === 0) return null; return ( <> {processed.length > 0 ? (
{processed.map((disease) => ( ))}
) : (

No diseases match “{searchQuery}”.

)} {lightboxOpen && images.length > 0 && ( )} ); }