|
|
|
import React, { useState, useMemo } from 'react'; |
|
import { BeakerIcon } from '../components/icons'; |
|
|
|
type WaterUnit = 'L' | 'gal'; |
|
type ConcentrateUnit = 'ml' | 'tsp' | 'tbsp'; |
|
type Strength = 'full' | 'half' | 'quarter'; |
|
|
|
const STRENGTH_MULTIPLIERS: Record<Strength, number> = { |
|
full: 1, |
|
half: 0.5, |
|
quarter: 0.25, |
|
}; |
|
|
|
const CONVERSION_TO_ML = { |
|
ml: 1, |
|
tsp: 4.92892, |
|
tbsp: 14.7868, |
|
}; |
|
|
|
const CONVERSION_TO_LITER = { |
|
L: 1, |
|
gal: 3.78541, |
|
}; |
|
|
|
const FertilizerMixerView: React.FC = () => { |
|
const [waterAmount, setWaterAmount] = useState(1); |
|
const [waterUnit, setWaterUnit] = useState<WaterUnit>('gal'); |
|
const [concentrateAmount, setConcentrateAmount] = useState(1); |
|
const [concentrateUnit, setConcentrateUnit] = useState<ConcentrateUnit>('tsp'); |
|
const [strength, setStrength] = useState<Strength>('half'); |
|
const [baseWaterAmount, setBaseWaterAmount] = useState(1); |
|
const [baseWaterUnit, setBaseWaterUnit] = useState<WaterUnit>('gal'); |
|
|
|
const result = useMemo(() => { |
|
try { |
|
const desiredWaterL = waterAmount * CONVERSION_TO_LITER[waterUnit]; |
|
const baseWaterL = baseWaterAmount * CONVERSION_TO_LITER[baseWaterUnit]; |
|
const baseConcentrateMl = concentrateAmount * CONVERSION_TO_ML[concentrateUnit]; |
|
|
|
if (baseWaterL === 0) return { amount: 0, unit: 'ml' }; |
|
|
|
const ratio = baseConcentrateMl / baseWaterL; |
|
const strengthMultiplier = STRENGTH_MULTIPLIERS[strength]; |
|
|
|
const neededMl = desiredWaterL * ratio * strengthMultiplier; |
|
|
|
|
|
if (neededMl < 15 && concentrateUnit !== 'ml') return { amount: neededMl / CONVERSION_TO_ML.tsp, unit: 'tsp' }; |
|
if (neededMl < 45 && concentrateUnit !== 'ml') return { amount: neededMl / CONVERSION_TO_ML.tbsp, unit: 'tbsp' }; |
|
return { amount: neededMl, unit: 'ml' }; |
|
|
|
} catch (e) { |
|
return { amount: 0, unit: 'ml' }; |
|
} |
|
}, [waterAmount, waterUnit, concentrateAmount, concentrateUnit, strength, baseWaterAmount, baseWaterUnit]); |
|
|
|
const renderUnitSelector = (value: string, setter: (val: any) => void, units: string[]) => ( |
|
<div className="flex gap-1 bg-stone-100 p-1 rounded-lg"> |
|
{units.map(unit => ( |
|
<button key={unit} onClick={() => setter(unit)} className={`px-3 py-1 text-sm font-medium rounded-md ${value === unit ? 'bg-white shadow' : ''}`}> |
|
{unit} |
|
</button> |
|
))} |
|
</div> |
|
); |
|
|
|
return ( |
|
<div className="space-y-8 max-w-2xl mx-auto"> |
|
<header className="text-center"> |
|
<h2 className="text-3xl font-bold tracking-tight text-stone-900 sm:text-4xl flex items-center justify-center gap-3"> |
|
<BeakerIcon className="w-8 h-8 text-cyan-600" /> |
|
Fertilizer Mixer |
|
</h2> |
|
<p className="mt-4 text-lg leading-8 text-stone-600"> |
|
Calculate the perfect fertilizer dilution every time. No more over or under-feeding. |
|
</p> |
|
</header> |
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 items-start"> |
|
<div className="bg-white p-6 rounded-xl shadow-lg border border-stone-200 space-y-6"> |
|
<div> |
|
<h3 className="font-semibold text-stone-800 mb-2">1. Dosage from Bottle</h3> |
|
<p className="text-xs text-stone-500 mb-2">Enter the recommended full-strength dose from the fertilizer label.</p> |
|
<div className="flex items-center gap-2"> |
|
<input type="number" value={concentrateAmount} onChange={e => setConcentrateAmount(Number(e.target.value))} className="w-full p-2 border rounded-md" /> |
|
{renderUnitSelector(concentrateUnit, setConcentrateUnit, ['ml', 'tsp', 'tbsp'])} |
|
</div> |
|
<p className="text-center my-1 text-stone-600 font-medium">per</p> |
|
<div className="flex items-center gap-2"> |
|
<input type="number" value={baseWaterAmount} onChange={e => setBaseWaterAmount(Number(e.target.value))} className="w-full p-2 border rounded-md" /> |
|
{renderUnitSelector(baseWaterUnit, setBaseWaterUnit, ['L', 'gal'])} |
|
</div> |
|
</div> |
|
<div> |
|
<h3 className="font-semibold text-stone-800 mb-2">2. Your Watering Can</h3> |
|
<p className="text-xs text-stone-500 mb-2">How much water are you preparing?</p> |
|
<div className="flex items-center gap-2"> |
|
<input type="number" value={waterAmount} onChange={e => setWaterAmount(Number(e.target.value))} className="w-full p-2 border rounded-md" /> |
|
{renderUnitSelector(waterUnit, setWaterUnit, ['L', 'gal'])} |
|
</div> |
|
</div> |
|
<div> |
|
<h3 className="font-semibold text-stone-800 mb-2">3. Desired Strength</h3> |
|
<div className="flex gap-1 bg-stone-100 p-1 rounded-lg"> |
|
<button onClick={() => setStrength('full')} className={`flex-1 py-2 text-sm font-medium rounded-md ${strength === 'full' ? 'bg-white shadow' : ''}`}>Full (100%)</button> |
|
<button onClick={() => setStrength('half')} className={`flex-1 py-2 text-sm font-medium rounded-md ${strength === 'half' ? 'bg-white shadow' : ''}`}>Half (50%)</button> |
|
<button onClick={() => setStrength('quarter')} className={`flex-1 py-2 text-sm font-medium rounded-md ${strength === 'quarter' ? 'bg-white shadow' : ''}`}>Quarter (25%)</button> |
|
</div> |
|
</div> |
|
</div> |
|
|
|
<div className="bg-white p-6 rounded-xl shadow-lg border-2 border-cyan-600 space-y-4 text-center"> |
|
<h3 className="text-xl font-bold text-stone-900">Your Custom Mix</h3> |
|
<p className="text-stone-600">Add this much concentrate to your watering can:</p> |
|
<div> |
|
<p className="text-5xl font-bold text-cyan-700">{result.amount.toFixed(2)}</p> |
|
<p className="text-lg text-stone-600">{result.unit}</p> |
|
</div> |
|
<div className="mt-4 pt-4 border-t-2 border-dashed"> |
|
<p className="text-sm text-stone-700"> |
|
This creates a {strength}-strength solution of {waterAmount} {waterUnit} of fertilizer. |
|
</p> |
|
</div> |
|
</div> |
|
</div> |
|
</div> |
|
); |
|
}; |
|
|
|
export default FertilizerMixerView; |
|
|