709 lines
25 KiB
TypeScript
709 lines
25 KiB
TypeScript
import { useState } from 'react';
|
||
import {
|
||
GrowingEnvironment,
|
||
SoilComposition,
|
||
ClimateConditions,
|
||
LightingConditions,
|
||
NutrientProfile,
|
||
WateringSchedule,
|
||
ContainerInfo,
|
||
EnvironmentLocation,
|
||
SurroundingEnvironment,
|
||
} from '../lib/environment/types';
|
||
|
||
interface EnvironmentalFormProps {
|
||
value: Partial<GrowingEnvironment>;
|
||
onChange: (env: Partial<GrowingEnvironment>) => void;
|
||
compact?: boolean;
|
||
}
|
||
|
||
export default function EnvironmentalForm({
|
||
value,
|
||
onChange,
|
||
compact = false,
|
||
}: EnvironmentalFormProps) {
|
||
const [activeSection, setActiveSection] = useState<string>('soil');
|
||
|
||
const updateSection = <K extends keyof GrowingEnvironment>(
|
||
section: K,
|
||
updates: Partial<GrowingEnvironment[K]>
|
||
) => {
|
||
const currentSection = value[section] || {};
|
||
onChange({
|
||
...value,
|
||
[section]: {
|
||
...(currentSection as object || {}),
|
||
...updates,
|
||
},
|
||
});
|
||
};
|
||
|
||
const sections = [
|
||
{ id: 'soil', name: '🌱 Soil', icon: '🌱' },
|
||
{ id: 'nutrients', name: '🧪 Nutrients', icon: '🧪' },
|
||
{ id: 'lighting', name: '☀️ Light', icon: '☀️' },
|
||
{ id: 'climate', name: '🌡️ Climate', icon: '🌡️' },
|
||
{ id: 'location', name: '📍 Location', icon: '📍' },
|
||
{ id: 'container', name: '🪴 Container', icon: '🪴' },
|
||
{ id: 'watering', name: '💧 Water', icon: '💧' },
|
||
{ id: 'surroundings', name: '🌿 Surroundings', icon: '🌿' },
|
||
];
|
||
|
||
return (
|
||
<div className="bg-white rounded-lg shadow-lg">
|
||
{/* Section Tabs */}
|
||
<div className="flex overflow-x-auto border-b border-gray-200 bg-gray-50">
|
||
{sections.map((section) => (
|
||
<button
|
||
key={section.id}
|
||
onClick={() => setActiveSection(section.id)}
|
||
className={`px-4 py-3 text-sm font-medium whitespace-nowrap transition ${
|
||
activeSection === section.id
|
||
? 'border-b-2 border-green-600 text-green-600 bg-white'
|
||
: 'text-gray-600 hover:text-gray-900 hover:bg-gray-100'
|
||
}`}
|
||
>
|
||
<span className="mr-2">{section.icon}</span>
|
||
{section.name}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* Section Content */}
|
||
<div className="p-6">
|
||
{activeSection === 'soil' && (
|
||
<SoilSection
|
||
value={value.soil}
|
||
onChange={(soil) => updateSection('soil', soil)}
|
||
/>
|
||
)}
|
||
|
||
{activeSection === 'nutrients' && (
|
||
<NutrientsSection
|
||
value={value.nutrients}
|
||
onChange={(nutrients) => updateSection('nutrients', nutrients)}
|
||
/>
|
||
)}
|
||
|
||
{activeSection === 'lighting' && (
|
||
<LightingSection
|
||
value={value.lighting}
|
||
onChange={(lighting) => updateSection('lighting', lighting)}
|
||
/>
|
||
)}
|
||
|
||
{activeSection === 'climate' && (
|
||
<ClimateSection
|
||
value={value.climate}
|
||
onChange={(climate) => updateSection('climate', climate)}
|
||
/>
|
||
)}
|
||
|
||
{activeSection === 'location' && (
|
||
<LocationSection
|
||
value={value.location}
|
||
onChange={(location) => updateSection('location', location)}
|
||
/>
|
||
)}
|
||
|
||
{activeSection === 'container' && (
|
||
<ContainerSection
|
||
value={value.container}
|
||
onChange={(container) => updateSection('container', container)}
|
||
/>
|
||
)}
|
||
|
||
{activeSection === 'watering' && (
|
||
<WateringSection
|
||
value={value.watering}
|
||
onChange={(watering) => updateSection('watering', watering)}
|
||
/>
|
||
)}
|
||
|
||
{activeSection === 'surroundings' && (
|
||
<SurroundingsSection
|
||
value={value.surroundings}
|
||
onChange={(surroundings) => updateSection('surroundings', surroundings)}
|
||
/>
|
||
)}
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Soil Section Component
|
||
function SoilSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<SoilComposition>;
|
||
onChange: (soil: Partial<SoilComposition>) => void;
|
||
}) {
|
||
const soil = value || {};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Soil Composition</h3>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Soil Type *
|
||
</label>
|
||
<select
|
||
value={soil.type || 'loam'}
|
||
onChange={(e) => onChange({ ...soil, type: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="clay">Clay</option>
|
||
<option value="sand">Sand</option>
|
||
<option value="silt">Silt</option>
|
||
<option value="loam">Loam (balanced)</option>
|
||
<option value="peat">Peat</option>
|
||
<option value="chalk">Chalk</option>
|
||
<option value="custom">Custom Mix</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Soil pH * <span className="text-xs text-gray-500">(most plants: 6.0-7.0)</span>
|
||
</label>
|
||
<input
|
||
type="number"
|
||
step="0.1"
|
||
min="0"
|
||
max="14"
|
||
value={soil.pH || 6.5}
|
||
onChange={(e) => onChange({ ...soil, pH: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
<div className="mt-1 text-xs text-gray-600">
|
||
{soil.pH && soil.pH < 5.5 && '⚠️ Acidic - may need lime'}
|
||
{soil.pH && soil.pH >= 5.5 && soil.pH <= 7.5 && '✓ Good range'}
|
||
{soil.pH && soil.pH > 7.5 && '⚠️ Alkaline - may need sulfur'}
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Texture</label>
|
||
<select
|
||
value={soil.texture || 'medium'}
|
||
onChange={(e) => onChange({ ...soil, texture: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="heavy">Heavy (clay-rich)</option>
|
||
<option value="medium">Medium (balanced)</option>
|
||
<option value="light">Light (sandy)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Drainage</label>
|
||
<select
|
||
value={soil.drainage || 'good'}
|
||
onChange={(e) => onChange({ ...soil, drainage: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="poor">Poor (stays wet)</option>
|
||
<option value="moderate">Moderate</option>
|
||
<option value="good">Good</option>
|
||
<option value="excellent">Excellent (fast draining)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Organic Matter % <span className="text-xs text-gray-500">(ideal: 5-10%)</span>
|
||
</label>
|
||
<input
|
||
type="number"
|
||
min="0"
|
||
max="100"
|
||
value={soil.organicMatter || 5}
|
||
onChange={(e) => onChange({ ...soil, organicMatter: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600 mb-2">
|
||
💡 <strong>Tip:</strong> Test soil pH with a meter or test kit for accuracy. Most vegetables prefer pH 6.0-7.0.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Nutrients Section Component
|
||
function NutrientsSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<NutrientProfile>;
|
||
onChange: (nutrients: Partial<NutrientProfile>) => void;
|
||
}) {
|
||
const nutrients = value || {};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Nutrient Profile</h3>
|
||
|
||
<div>
|
||
<h4 className="font-medium text-gray-900 mb-3">Primary Nutrients (NPK)</h4>
|
||
<div className="grid grid-cols-3 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Nitrogen (N) %
|
||
</label>
|
||
<input
|
||
type="number"
|
||
step="0.1"
|
||
min="0"
|
||
value={nutrients.nitrogen || 0}
|
||
onChange={(e) => onChange({ ...nutrients, nitrogen: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Phosphorus (P) %
|
||
</label>
|
||
<input
|
||
type="number"
|
||
step="0.1"
|
||
min="0"
|
||
value={nutrients.phosphorus || 0}
|
||
onChange={(e) => onChange({ ...nutrients, phosphorus: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Potassium (K) %
|
||
</label>
|
||
<input
|
||
type="number"
|
||
step="0.1"
|
||
min="0"
|
||
value={nutrients.potassium || 0}
|
||
onChange={(e) => onChange({ ...nutrients, potassium: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<p className="mt-2 text-xs text-gray-600">
|
||
NPK ratio: {nutrients.nitrogen || 0}-{nutrients.phosphorus || 0}-{nutrients.potassium || 0}
|
||
</p>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600">
|
||
💡 <strong>Tip:</strong> Leave at 0 if unknown. Use soil test kit for accurate NPK values.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Lighting Section Component
|
||
function LightingSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<LightingConditions>;
|
||
onChange: (lighting: Partial<LightingConditions>) => void;
|
||
}) {
|
||
const lighting = value || { type: 'natural' };
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Lighting Conditions</h3>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Light Type</label>
|
||
<select
|
||
value={lighting.type || 'natural'}
|
||
onChange={(e) => onChange({ ...lighting, type: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="natural">Natural Sunlight</option>
|
||
<option value="artificial">Artificial Light Only</option>
|
||
<option value="mixed">Mixed (Natural + Artificial)</option>
|
||
</select>
|
||
</div>
|
||
|
||
{(lighting.type === 'natural' || lighting.type === 'mixed') && (
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 p-4 bg-yellow-50 rounded-lg">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Sun Exposure
|
||
</label>
|
||
<select
|
||
value={lighting.naturalLight?.exposure || 'full_sun'}
|
||
onChange={(e) =>
|
||
onChange({
|
||
...lighting,
|
||
naturalLight: {
|
||
...lighting.naturalLight,
|
||
exposure: e.target.value as any,
|
||
} as any,
|
||
})
|
||
}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="full_sun">Full Sun (6+ hours)</option>
|
||
<option value="partial_sun">Partial Sun (4-6 hours)</option>
|
||
<option value="partial_shade">Partial Shade (2-4 hours)</option>
|
||
<option value="full_shade">Full Shade (<2 hours)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Hours of Sunlight/Day
|
||
</label>
|
||
<input
|
||
type="number"
|
||
min="0"
|
||
max="24"
|
||
value={lighting.naturalLight?.hoursPerDay || 8}
|
||
onChange={(e) =>
|
||
onChange({
|
||
...lighting,
|
||
naturalLight: {
|
||
...lighting.naturalLight,
|
||
hoursPerDay: parseInt(e.target.value),
|
||
} as any,
|
||
})
|
||
}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
)}
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600">
|
||
💡 <strong>Tip:</strong> Most vegetables need 6+ hours of direct sunlight. Herbs can do well with 4-6 hours.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Climate Section Component
|
||
function ClimateSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<ClimateConditions>;
|
||
onChange: (climate: Partial<ClimateConditions>) => void;
|
||
}) {
|
||
const climate = value || {};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Climate Conditions</h3>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Day Temperature (°C) *
|
||
</label>
|
||
<input
|
||
type="number"
|
||
value={climate.temperatureDay || 22}
|
||
onChange={(e) => onChange({ ...climate, temperatureDay: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Night Temperature (°C) *
|
||
</label>
|
||
<input
|
||
type="number"
|
||
value={climate.temperatureNight || 18}
|
||
onChange={(e) => onChange({ ...climate, temperatureNight: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Average Humidity (%) *
|
||
</label>
|
||
<input
|
||
type="number"
|
||
min="0"
|
||
max="100"
|
||
value={climate.humidityAverage || 50}
|
||
onChange={(e) => onChange({ ...climate, humidityAverage: parseFloat(e.target.value) })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Airflow</label>
|
||
<select
|
||
value={climate.airflow || 'moderate'}
|
||
onChange={(e) => onChange({ ...climate, airflow: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="none">None (still air)</option>
|
||
<option value="minimal">Minimal</option>
|
||
<option value="moderate">Moderate (good)</option>
|
||
<option value="strong">Strong/Windy</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600">
|
||
💡 <strong>Tip:</strong> Most plants thrive at 18-25°C. Good airflow prevents disease.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Location Section Component
|
||
function LocationSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<EnvironmentLocation>;
|
||
onChange: (location: Partial<EnvironmentLocation>) => void;
|
||
}) {
|
||
const location = value || {};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Growing Location</h3>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Location Type *</label>
|
||
<select
|
||
value={location.type || 'outdoor'}
|
||
onChange={(e) => onChange({ ...location, type: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="indoor">Indoor</option>
|
||
<option value="outdoor">Outdoor</option>
|
||
<option value="greenhouse">Greenhouse</option>
|
||
<option value="polytunnel">Polytunnel</option>
|
||
<option value="shade_house">Shade House</option>
|
||
<option value="window">Window</option>
|
||
<option value="balcony">Balcony</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600">
|
||
💡 <strong>Tip:</strong> Location type affects climate control and pest exposure.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Container Section Component
|
||
function ContainerSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<ContainerInfo>;
|
||
onChange: (container: Partial<ContainerInfo>) => void;
|
||
}) {
|
||
const container = value || {};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Container Information</h3>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Container Type</label>
|
||
<select
|
||
value={container.type || 'pot'}
|
||
onChange={(e) => onChange({ ...container, type: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="pot">Pot</option>
|
||
<option value="raised_bed">Raised Bed</option>
|
||
<option value="ground">In Ground</option>
|
||
<option value="fabric_pot">Fabric Pot</option>
|
||
<option value="hydroponic">Hydroponic</option>
|
||
<option value="hanging_basket">Hanging Basket</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Drainage * <span className="text-xs text-red-600">(CRITICAL!)</span>
|
||
</label>
|
||
<select
|
||
value={container.drainage || 'yes'}
|
||
onChange={(e) => onChange({ ...container, drainage: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="yes">Yes (has holes)</option>
|
||
<option value="no">No (sealed)</option>
|
||
</select>
|
||
{container.drainage === 'no' && (
|
||
<p className="mt-1 text-xs text-red-600 font-semibold">
|
||
⚠️ WARNING: No drainage will likely kill your plant!
|
||
</p>
|
||
)}
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Size (optional)
|
||
</label>
|
||
<input
|
||
type="text"
|
||
placeholder="e.g., 5 gallon, 30cm diameter"
|
||
value={container.size || ''}
|
||
onChange={(e) => onChange({ ...container, size: e.target.value })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600">
|
||
💡 <strong>Tip:</strong> Always ensure drainage! Sitting water = root rot = dead plant.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Watering Section Component
|
||
function WateringSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<WateringSchedule>;
|
||
onChange: (watering: Partial<WateringSchedule>) => void;
|
||
}) {
|
||
const watering = value || {};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Watering Schedule</h3>
|
||
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Method</label>
|
||
<select
|
||
value={watering.method || 'hand_water'}
|
||
onChange={(e) => onChange({ ...watering, method: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="hand_water">Hand Watering</option>
|
||
<option value="drip">Drip Irrigation</option>
|
||
<option value="soaker_hose">Soaker Hose</option>
|
||
<option value="sprinkler">Sprinkler</option>
|
||
<option value="self_watering">Self-Watering</option>
|
||
<option value="rain">Rain Fed</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Water Source</label>
|
||
<select
|
||
value={watering.waterSource || 'tap'}
|
||
onChange={(e) => onChange({ ...watering, waterSource: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="tap">Tap Water</option>
|
||
<option value="well">Well Water</option>
|
||
<option value="rain">Rain Water</option>
|
||
<option value="filtered">Filtered</option>
|
||
<option value="distilled">Distilled</option>
|
||
<option value="RO">Reverse Osmosis (RO)</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">
|
||
Frequency (optional)
|
||
</label>
|
||
<input
|
||
type="text"
|
||
placeholder="e.g., daily, every 2-3 days"
|
||
value={watering.frequency || ''}
|
||
onChange={(e) => onChange({ ...watering, frequency: e.target.value })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600">
|
||
💡 <strong>Tip:</strong> Water when top inch of soil is dry. Overwatering kills more plants than underwatering!
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
// Surroundings Section Component
|
||
function SurroundingsSection({
|
||
value,
|
||
onChange,
|
||
}: {
|
||
value?: Partial<SurroundingEnvironment>;
|
||
onChange: (surroundings: Partial<SurroundingEnvironment>) => void;
|
||
}) {
|
||
const surroundings = value || {};
|
||
|
||
return (
|
||
<div className="space-y-4">
|
||
<h3 className="text-lg font-semibold text-gray-900">Surrounding Environment</h3>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Ecosystem Type</label>
|
||
<select
|
||
value={surroundings.ecosystem || 'urban'}
|
||
onChange={(e) => onChange({ ...surroundings, ecosystem: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="urban">Urban</option>
|
||
<option value="suburban">Suburban</option>
|
||
<option value="rural">Rural</option>
|
||
<option value="forest">Forest</option>
|
||
<option value="desert">Desert</option>
|
||
<option value="coastal">Coastal</option>
|
||
<option value="mountain">Mountain</option>
|
||
<option value="tropical">Tropical</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-1">Wind Exposure</label>
|
||
<select
|
||
value={surroundings.windExposure || 'moderate'}
|
||
onChange={(e) => onChange({ ...surroundings, windExposure: e.target.value as any })}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="sheltered">Sheltered (protected)</option>
|
||
<option value="moderate">Moderate</option>
|
||
<option value="exposed">Exposed</option>
|
||
<option value="windy">Very Windy</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div className="pt-4 border-t border-gray-200">
|
||
<p className="text-sm text-gray-600">
|
||
💡 <strong>Tip:</strong> Track companion plants and pests to learn what works in your ecosystem.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|