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
105 lines
3.5 KiB
TypeScript
105 lines
3.5 KiB
TypeScript
import { CropBatch, GrowingRecipe } from '../../lib/vertical-farming/types';
|
|
|
|
interface BatchProgressProps {
|
|
batch: CropBatch;
|
|
recipe?: GrowingRecipe;
|
|
showDetails?: boolean;
|
|
}
|
|
|
|
export default function BatchProgress({ batch, recipe, showDetails = true }: BatchProgressProps) {
|
|
const progressPercent = recipe
|
|
? Math.min(100, (batch.currentDay / recipe.expectedDays) * 100)
|
|
: 0;
|
|
|
|
const statusColors: Record<string, string> = {
|
|
germinating: 'bg-yellow-500',
|
|
growing: 'bg-green-500',
|
|
ready: 'bg-blue-500',
|
|
harvesting: 'bg-purple-500',
|
|
completed: 'bg-gray-500',
|
|
failed: 'bg-red-500',
|
|
};
|
|
|
|
const healthColor =
|
|
batch.healthScore >= 80
|
|
? 'text-green-600'
|
|
: batch.healthScore >= 60
|
|
? 'text-yellow-600'
|
|
: 'text-red-600';
|
|
|
|
return (
|
|
<div className="bg-white rounded-lg shadow p-4">
|
|
<div className="flex justify-between items-start mb-3">
|
|
<div>
|
|
<h4 className="font-semibold text-gray-900">
|
|
{batch.cropType}
|
|
{batch.variety && <span className="text-gray-600"> - {batch.variety}</span>}
|
|
</h4>
|
|
<p className="text-sm text-gray-600">Batch: {batch.id.slice(0, 12)}...</p>
|
|
</div>
|
|
<div className="text-right">
|
|
<span className={`inline-block px-2 py-1 rounded-full text-xs font-semibold text-white ${statusColors[batch.status]}`}>
|
|
{batch.status}
|
|
</span>
|
|
<p className={`text-sm font-bold mt-1 ${healthColor}`}>
|
|
Health: {batch.healthScore}%
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mb-3">
|
|
<div className="flex justify-between text-sm mb-1">
|
|
<span className="text-gray-600">Progress</span>
|
|
<span className="font-medium">
|
|
Day {batch.currentDay} {recipe && `/ ${recipe.expectedDays}`}
|
|
</span>
|
|
</div>
|
|
<div className="w-full bg-gray-200 rounded-full h-3">
|
|
<div
|
|
className={`h-3 rounded-full transition-all ${statusColors[batch.status] || 'bg-green-500'}`}
|
|
style={{ width: `${progressPercent}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{showDetails && (
|
|
<div className="grid grid-cols-2 gap-3 text-sm">
|
|
<div>
|
|
<p className="text-gray-600">Current Stage</p>
|
|
<p className="font-medium">{batch.currentStage}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-gray-600">Plants</p>
|
|
<p className="font-medium">{batch.plantCount.toLocaleString()}</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-gray-600">Expected Yield</p>
|
|
<p className="font-medium">{batch.expectedYieldKg.toFixed(1)} kg</p>
|
|
</div>
|
|
<div>
|
|
<p className="text-gray-600">Harvest Date</p>
|
|
<p className="font-medium">
|
|
{new Date(batch.expectedHarvestDate).toLocaleDateString()}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{batch.issues.length > 0 && (
|
|
<div className="mt-3 pt-3 border-t border-gray-200">
|
|
<p className="text-sm font-medium text-red-600 mb-1">
|
|
{batch.issues.filter(i => !i.resolvedAt).length} Active Issue(s)
|
|
</p>
|
|
{batch.issues
|
|
.filter(i => !i.resolvedAt)
|
|
.slice(0, 2)
|
|
.map(issue => (
|
|
<p key={issue.id} className="text-xs text-gray-600">
|
|
- {issue.type}: {issue.description}
|
|
</p>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|