localgreenchain/components/vertical-farm/ZoneDetailCard.tsx
Claude 2f7f22ca22
Add vertical farming UI components and pages
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
2025-11-22 18:35:57 +00:00

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>
);
}