import React, { useState, useRef } from 'react'; import { FilterIcon, SparklesIcon, UploadCloudIcon, DropletIcon, AlertTriangleIcon } from '../components/icons'; import Spinner from '../components/Spinner'; import { analyzeSoilComposition, isAIConfigured } from '../services/geminiService'; import type { SoilAnalysis, View } from '../types'; import { AppStatus } from '../types'; const SoilAnalyzerView: React.FC<{ setActiveView: (view: View) => void }> = ({ setActiveView }) => { const [status, setStatus] = useState(AppStatus.IDLE); const [image, setImage] = useState<{ preview: string; base64: string } | null>(null); const [species, setSpecies] = useState(''); const [location, setLocation] = useState(''); const [result, setResult] = useState(null); const [error, setError] = useState(''); const fileInputRef = useRef(null); const aiConfigured = isAIConfigured(); const handleFileChange = (event: React.ChangeEvent) => { const file = event.target.files?.[0]; if (file) { if (file.size > 4 * 1024 * 1024) { // 4MB limit setError("File size exceeds 4MB. Please upload a smaller image."); return; } const reader = new FileReader(); reader.onloadend = () => { const base64String = (reader.result as string).split(',')[1]; setImage({ preview: reader.result as string, base64: base64String }); setError(''); setStatus(AppStatus.IDLE); setResult(null); }; reader.onerror = () => setError("Failed to read the file."); reader.readAsDataURL(file); } }; const handleAnalyze = async () => { if (!image) { setError("Please upload an image of your soil mix."); return; } if (!species.trim()) { setError("Please enter the target species."); return; } if (!location.trim()) { setError("Please enter your location."); return; } setStatus(AppStatus.ANALYZING); setError(''); setResult(null); try { const analysisResult = await analyzeSoilComposition(image.base64, species, location); if (analysisResult) { setResult(analysisResult); setStatus(AppStatus.SUCCESS); } else { throw new Error("Could not analyze the soil. The AI may be busy or the image may not be clear enough. Please try again."); } } catch (e: any) { setError(e.message); setStatus(AppStatus.ERROR); } }; const renderResults = () => { if (!result) return null; const ratingColors = { Poor: 'bg-red-500', Average: 'bg-yellow-500', Good: 'bg-blue-500', Excellent: 'bg-green-500', Low: 'bg-orange-500', Medium: 'bg-yellow-500', High: 'bg-blue-500' }; return (

Soil Analysis Results

Estimated Composition

{result.components.map(comp => (
{comp.name} {comp.percentage}%
))}

Properties

Drainage: {result.drainageRating}
Water Retention: {result.waterRetention}
Suitability for {species}

{result.suitabilityAnalysis}

Improvement Suggestions

{result.improvementSuggestions}

); } return (

Soil Analyzer

Take a photo of your bonsai soil to get an AI-powered analysis of its composition and suitability.

{status !== AppStatus.SUCCESS && (
fileInputRef.current?.click()} className="flex justify-center rounded-lg border-2 border-dashed border-stone-300 px-6 py-10 hover:border-amber-600 transition-colors cursor-pointer">
{image ? Soil preview : ( <>

Upload a close-up photo of your soil

PNG, JPG up to 4MB

)}
setSpecies(e.target.value)} className="block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 placeholder:text-stone-400 focus:ring-2 focus:ring-inset focus:ring-amber-600" placeholder="Target Species (e.g., Juniper)" /> setLocation(e.target.value)} className="block w-full rounded-md border-0 py-2 px-3 text-stone-900 shadow-sm ring-1 ring-inset ring-stone-300 placeholder:text-stone-400 focus:ring-2 focus:ring-inset focus:ring-amber-600" placeholder="Your Location (e.g., Phoenix, AZ)" />
{error &&

{error}

} {!aiConfigured && (

Please set your Gemini API key in the{' '} {' '}to enable this feature.

)}
)} {status === AppStatus.ANALYZING && } {status === AppStatus.SUCCESS && renderResults()} {status === AppStatus.ERROR &&

{error}

}
); }; export default SoilAnalyzerView;