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
161 lines
5.9 KiB
TypeScript
161 lines
5.9 KiB
TypeScript
import { useState, useEffect } from 'react';
|
|
import Link from 'next/link';
|
|
import Head from 'next/head';
|
|
import FarmCard from '../../components/vertical-farm/FarmCard';
|
|
import { VerticalFarm } from '../../lib/vertical-farming/types';
|
|
|
|
export default function VerticalFarmDashboard() {
|
|
const [farms, setFarms] = useState<VerticalFarm[]>([]);
|
|
const [loading, setLoading] = useState(true);
|
|
const [stats, setStats] = useState({
|
|
totalFarms: 0,
|
|
totalPlants: 0,
|
|
upcomingHarvests: 0,
|
|
activeZones: 0,
|
|
});
|
|
|
|
useEffect(() => {
|
|
fetchFarms();
|
|
}, []);
|
|
|
|
const fetchFarms = async () => {
|
|
try {
|
|
const response = await fetch('/api/vertical-farm/list');
|
|
const data = await response.json();
|
|
if (data.success) {
|
|
setFarms(data.farms);
|
|
calculateStats(data.farms);
|
|
}
|
|
} catch (error) {
|
|
console.error('Error fetching farms:', error);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const calculateStats = (farmList: VerticalFarm[]) => {
|
|
const totalPlants = farmList.reduce((sum, f) => sum + f.specs.currentActivePlants, 0);
|
|
const activeZones = farmList.reduce(
|
|
(sum, f) => sum + f.zones.filter(z => z.status === 'growing' || z.status === 'planted').length,
|
|
0
|
|
);
|
|
const upcomingHarvests = farmList.reduce(
|
|
(sum, f) =>
|
|
sum +
|
|
f.zones.filter(z => {
|
|
if (!z.expectedHarvestDate) return false;
|
|
const daysUntil = (new Date(z.expectedHarvestDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24);
|
|
return daysUntil <= 7 && daysUntil >= 0;
|
|
}).length,
|
|
0
|
|
);
|
|
|
|
setStats({
|
|
totalFarms: farmList.length,
|
|
totalPlants,
|
|
upcomingHarvests,
|
|
activeZones,
|
|
});
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-green-50 to-emerald-100">
|
|
<Head>
|
|
<title>Vertical Farms - LocalGreenChain</title>
|
|
</Head>
|
|
|
|
{/* Header */}
|
|
<header className="bg-white shadow-sm">
|
|
<div className="max-w-7xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
|
|
<div className="flex items-center justify-between">
|
|
<Link href="/">
|
|
<a className="text-2xl font-bold text-green-800">
|
|
LocalGreenChain
|
|
</a>
|
|
</Link>
|
|
<nav className="flex gap-4">
|
|
<Link href="/vertical-farm/register">
|
|
<a className="px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition">
|
|
Register Farm
|
|
</a>
|
|
</Link>
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
|
|
<main className="max-w-7xl mx-auto px-4 py-12 sm:px-6 lg:px-8">
|
|
<div className="flex justify-between items-center mb-8">
|
|
<h1 className="text-3xl font-bold text-gray-900">Vertical Farm Dashboard</h1>
|
|
</div>
|
|
|
|
{/* Stats Overview */}
|
|
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 mb-8">
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<p className="text-sm text-gray-600">Total Farms</p>
|
|
<p className="text-3xl font-bold text-green-600">{stats.totalFarms}</p>
|
|
</div>
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<p className="text-sm text-gray-600">Active Plants</p>
|
|
<p className="text-3xl font-bold text-green-600">{stats.totalPlants.toLocaleString()}</p>
|
|
</div>
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<p className="text-sm text-gray-600">Active Zones</p>
|
|
<p className="text-3xl font-bold text-blue-600">{stats.activeZones}</p>
|
|
</div>
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<p className="text-sm text-gray-600">Harvests This Week</p>
|
|
<p className="text-3xl font-bold text-purple-600">{stats.upcomingHarvests}</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Farms Grid */}
|
|
{loading ? (
|
|
<div className="text-center py-12">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-green-600 mx-auto" />
|
|
<p className="text-gray-600 mt-4">Loading farms...</p>
|
|
</div>
|
|
) : farms.length > 0 ? (
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{farms.map(farm => (
|
|
<FarmCard key={farm.id} farm={farm} />
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="bg-white rounded-lg shadow-lg p-12 text-center">
|
|
<div className="text-6xl mb-4">🌱</div>
|
|
<h2 className="text-2xl font-bold text-gray-900 mb-2">No Vertical Farms Yet</h2>
|
|
<p className="text-gray-600 mb-6">
|
|
Start your controlled environment agriculture journey by registering your first vertical farm.
|
|
</p>
|
|
<Link href="/vertical-farm/register">
|
|
<a className="inline-block px-6 py-3 bg-green-600 text-white font-semibold rounded-lg hover:bg-green-700 transition">
|
|
Register Your First Farm
|
|
</a>
|
|
</Link>
|
|
</div>
|
|
)}
|
|
|
|
{/* Quick Actions */}
|
|
{farms.length > 0 && (
|
|
<div className="mt-8 bg-white rounded-lg shadow p-6">
|
|
<h2 className="text-xl font-bold text-gray-900 mb-4">Quick Actions</h2>
|
|
<div className="flex flex-wrap gap-4">
|
|
<Link href="/vertical-farm/register">
|
|
<a className="px-4 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition">
|
|
+ Add New Farm
|
|
</a>
|
|
</Link>
|
|
<button className="px-4 py-2 bg-blue-100 text-blue-700 rounded-lg hover:bg-blue-200 transition">
|
|
View All Batches
|
|
</button>
|
|
<button className="px-4 py-2 bg-purple-100 text-purple-700 rounded-lg hover:bg-purple-200 transition">
|
|
Export Analytics
|
|
</button>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|