Components: - FarmCard: Farm summary display with status and metrics - ZoneGrid: Multi-level zone layout visualization - ZoneDetailCard: Individual zone details with environment readings - EnvironmentGauge: Real-time environmental parameter display - BatchProgress: Crop batch progress tracking with health scores - RecipeSelector: Growing recipe browser and selector - AlertPanel: Environment alerts display and management - GrowthStageIndicator: Visual growth stage progress tracker - ResourceUsageChart: Energy/water usage analytics visualization Pages: - /vertical-farm: Dashboard with farm listing and stats - /vertical-farm/register: Multi-step farm registration form - /vertical-farm/[farmId]: Farm detail view with zones and alerts - /vertical-farm/[farmId]/zones: Zone management with batch starting - /vertical-farm/[farmId]/batches: Batch management and harvesting - /vertical-farm/[farmId]/analytics: Farm analytics and performance metrics
112 lines
4.1 KiB
TypeScript
112 lines
4.1 KiB
TypeScript
import { GrowingZone } from '../../lib/vertical-farming/types';
|
|
import EnvironmentGauge from './EnvironmentGauge';
|
|
|
|
interface ZoneDetailCardProps {
|
|
zone: GrowingZone;
|
|
onClick?: () => void;
|
|
isSelected?: boolean;
|
|
expanded?: boolean;
|
|
}
|
|
|
|
export default function ZoneDetailCard({ zone, onClick, isSelected, expanded }: ZoneDetailCardProps) {
|
|
const statusColors: Record<string, string> = {
|
|
empty: 'bg-gray-100 text-gray-600 border-gray-300',
|
|
preparing: 'bg-yellow-100 text-yellow-800 border-yellow-400',
|
|
planted: 'bg-blue-100 text-blue-800 border-blue-400',
|
|
growing: 'bg-green-100 text-green-800 border-green-400',
|
|
harvesting: 'bg-purple-100 text-purple-800 border-purple-400',
|
|
cleaning: 'bg-orange-100 text-orange-800 border-orange-400',
|
|
};
|
|
|
|
const daysUntilHarvest = zone.expectedHarvestDate
|
|
? Math.max(0, Math.ceil((new Date(zone.expectedHarvestDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24)))
|
|
: null;
|
|
|
|
return (
|
|
<div
|
|
className={`rounded-lg p-4 border-2 transition cursor-pointer ${
|
|
statusColors[zone.status] || 'bg-gray-100 border-gray-300'
|
|
} ${isSelected ? 'ring-2 ring-green-500' : ''}`}
|
|
onClick={onClick}
|
|
>
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-semibold text-gray-900">{zone.name}</h4>
|
|
<p className="text-xs text-gray-600">{zone.growingMethod.replace('_', ' ')}</p>
|
|
</div>
|
|
<span className="text-xs font-medium uppercase">{zone.status}</span>
|
|
</div>
|
|
|
|
{zone.currentCrop && (
|
|
<div className="mb-3">
|
|
<p className="text-sm font-medium text-gray-900">{zone.currentCrop}</p>
|
|
{daysUntilHarvest !== null && (
|
|
<p className="text-xs text-gray-600">
|
|
Harvest in {daysUntilHarvest} days
|
|
</p>
|
|
)}
|
|
</div>
|
|
)}
|
|
|
|
<div className="grid grid-cols-2 gap-2 text-xs mb-3">
|
|
<div>
|
|
<span className="text-gray-600">Plants:</span>
|
|
<span className="ml-1 font-medium">{zone.plantIds.length}/{zone.plantPositions}</span>
|
|
</div>
|
|
<div>
|
|
<span className="text-gray-600">Area:</span>
|
|
<span className="ml-1 font-medium">{zone.areaSqm}m²</span>
|
|
</div>
|
|
</div>
|
|
|
|
{zone.currentEnvironment && (
|
|
<div className="space-y-2 pt-3 border-t border-gray-200">
|
|
<div className="grid grid-cols-4 gap-1 text-center">
|
|
<EnvironmentGauge
|
|
label="Temp"
|
|
value={zone.currentEnvironment.temperatureC}
|
|
unit="°C"
|
|
target={zone.environmentTargets.temperatureC.target}
|
|
min={zone.environmentTargets.temperatureC.min}
|
|
max={zone.environmentTargets.temperatureC.max}
|
|
compact
|
|
/>
|
|
<EnvironmentGauge
|
|
label="RH"
|
|
value={zone.currentEnvironment.humidityPercent}
|
|
unit="%"
|
|
target={zone.environmentTargets.humidityPercent.target}
|
|
min={zone.environmentTargets.humidityPercent.min}
|
|
max={zone.environmentTargets.humidityPercent.max}
|
|
compact
|
|
/>
|
|
<EnvironmentGauge
|
|
label="CO2"
|
|
value={zone.currentEnvironment.co2Ppm}
|
|
unit=""
|
|
target={zone.environmentTargets.co2Ppm.target}
|
|
min={zone.environmentTargets.co2Ppm.min}
|
|
max={zone.environmentTargets.co2Ppm.max}
|
|
compact
|
|
/>
|
|
<EnvironmentGauge
|
|
label="PPFD"
|
|
value={zone.currentEnvironment.ppfd}
|
|
unit=""
|
|
target={zone.environmentTargets.lightPpfd.target}
|
|
min={zone.environmentTargets.lightPpfd.min}
|
|
max={zone.environmentTargets.lightPpfd.max}
|
|
compact
|
|
/>
|
|
</div>
|
|
|
|
{zone.currentEnvironment.alerts.length > 0 && (
|
|
<div className="text-xs text-red-600 font-medium">
|
|
{zone.currentEnvironment.alerts.length} active alert(s)
|
|
</div>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|