garden_tracker/src/components/Dashboard.tsx
2025-07-28 22:22:46 +03:00

220 lines
8.3 KiB
TypeScript

import React from 'react';
import { Plant, Task, MaintenanceRecord, HarvestRecord } from '../types';
import { Calendar, AlertTriangle, CheckCircle, Sprout, Scissors, Droplets } from 'lucide-react';
interface DashboardProps {
plants: Plant[];
tasks: Task[];
maintenanceRecords: MaintenanceRecord[];
harvestRecords: HarvestRecord[];
onNavigate: (tab: string) => void;
}
const Dashboard: React.FC<DashboardProps> = ({
plants,
tasks,
maintenanceRecords,
harvestRecords,
onNavigate
}) => {
const upcomingTasks = tasks
.filter(task => !task.completed && new Date(task.deadline) >= new Date())
.sort((a, b) => new Date(a.deadline).getTime() - new Date(b.deadline).getTime())
.slice(0, 5);
const plantsNeedingAttention = plants.filter(plant => plant.healthStatus === 'needs-attention');
const recentMaintenanceRecords = maintenanceRecords
.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime())
.slice(0, 5);
const stats = [
{
label: 'Total Plants',
value: plants.length,
icon: Sprout,
color: 'text-green-600',
bgColor: 'bg-green-100'
},
{
label: 'Pending Tasks',
value: tasks.filter(task => !task.completed).length,
icon: Calendar,
color: 'text-blue-600',
bgColor: 'bg-blue-100'
},
{
label: 'Need Attention',
value: plantsNeedingAttention.length,
icon: AlertTriangle,
color: 'text-yellow-600',
bgColor: 'bg-yellow-100'
},
{
label: 'This Year Harvests',
value: harvestRecords.filter(record =>
new Date(record.date).getFullYear() === new Date().getFullYear()
).length,
icon: CheckCircle,
color: 'text-purple-600',
bgColor: 'bg-purple-100'
}
];
const getMaintenanceIcon = (type: string) => {
switch (type) {
case 'pruning': return Scissors;
case 'watering': return Droplets;
default: return Calendar;
}
};
return (
<div className="space-y-8">
<div>
<h2 className="text-3xl font-bold text-green-800 mb-2">Garden Dashboard</h2>
<p className="text-green-600">Welcome to your garden management center</p>
</div>
{/* Stats Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{stats.map((stat, index) => {
const Icon = stat.icon;
return (
<div key={index} className="bg-white rounded-lg shadow-sm border border-green-100 p-6 hover:shadow-md transition-shadow">
<div className="flex items-center justify-between">
<div>
<p className="text-sm font-medium text-gray-600">{stat.label}</p>
<p className="text-3xl font-bold text-gray-900 mt-1">{stat.value}</p>
</div>
<div className={`${stat.bgColor} p-3 rounded-lg`}>
<Icon className={`h-6 w-6 ${stat.color}`} />
</div>
</div>
</div>
);
})}
</div>
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
{/* Upcoming Tasks */}
<div className="bg-white rounded-lg shadow-sm border border-green-100">
<div className="p-6 border-b border-green-100">
<div className="flex justify-between items-center">
<h3 className="text-lg font-semibold text-green-800">Upcoming Tasks</h3>
<button
onClick={() => onNavigate('tasks')}
className="text-sm text-green-600 hover:text-green-700 transition-colors"
>
View all
</button>
</div>
</div>
<div className="p-6">
{upcomingTasks.length > 0 ? (
<div className="space-y-4">
{upcomingTasks.map((task) => {
const plant = plants.find(p => p.id === task.plantId);
const isOverdue = new Date(task.deadline) < new Date();
return (
<div key={task.id} className="flex items-start space-x-3 p-3 bg-green-50 rounded-lg">
<Calendar className={`h-5 w-5 mt-0.5 ${isOverdue ? 'text-red-500' : 'text-green-600'}`} />
<div className="flex-1 min-w-0">
<p className="font-medium text-gray-900">{task.title}</p>
{plant && (
<p className="text-sm text-gray-600">{plant.variety}</p>
)}
<p className={`text-sm ${isOverdue ? 'text-red-600' : 'text-gray-500'}`}>
Due: {new Date(task.deadline).toLocaleDateString()}
</p>
</div>
</div>
);
})}
</div>
) : (
<p className="text-gray-500 text-center py-8">No upcoming tasks</p>
)}
</div>
</div>
{/* Plants Needing Attention */}
<div className="bg-white rounded-lg shadow-sm border border-green-100">
<div className="p-6 border-b border-green-100">
<div className="flex justify-between items-center">
<h3 className="text-lg font-semibold text-green-800">Plants Needing Attention</h3>
<button
onClick={() => onNavigate('plants')}
className="text-sm text-green-600 hover:text-green-700 transition-colors"
>
View all
</button>
</div>
</div>
<div className="p-6">
{plantsNeedingAttention.length > 0 ? (
<div className="space-y-4">
{plantsNeedingAttention.map((plant) => (
<div key={plant.id} className="flex items-start space-x-3 p-3 bg-yellow-50 rounded-lg">
<AlertTriangle className="h-5 w-5 text-yellow-600 mt-0.5" />
<div className="flex-1 min-w-0">
<p className="font-medium text-gray-900">{plant.variety}</p>
<p className="text-sm text-gray-600 capitalize">{plant.type}</p>
{plant.notes && (
<p className="text-sm text-gray-500 mt-1">{plant.notes}</p>
)}
</div>
</div>
))}
</div>
) : (
<p className="text-gray-500 text-center py-8">All plants are healthy!</p>
)}
</div>
</div>
</div>
{/* Recent Maintenance */}
<div className="bg-white rounded-lg shadow-sm border border-green-100">
<div className="p-6 border-b border-green-100">
<div className="flex justify-between items-center">
<h3 className="text-lg font-semibold text-green-800">Recent Maintenance</h3>
<button
onClick={() => onNavigate('maintenance')}
className="text-sm text-green-600 hover:text-green-700 transition-colors"
>
View all
</button>
</div>
</div>
<div className="p-6">
{recentMaintenanceRecords.length > 0 ? (
<div className="space-y-4">
{recentMaintenanceRecords.map((record) => {
const plant = plants.find(p => p.id === record.plantId);
const Icon = getMaintenanceIcon(record.type);
return (
<div key={record.id} className="flex items-start space-x-3 p-3 bg-green-50 rounded-lg">
<Icon className="h-5 w-5 text-green-600 mt-0.5" />
<div className="flex-1 min-w-0">
<p className="font-medium text-gray-900 capitalize">{record.type}</p>
<p className="text-sm text-gray-600">{plant?.variety}</p>
<p className="text-sm text-gray-500">{record.description}</p>
<p className="text-xs text-gray-400 mt-1">
{new Date(record.date).toLocaleDateString()}
</p>
</div>
</div>
);
})}
</div>
) : (
<p className="text-gray-500 text-center py-8">No maintenance records yet</p>
)}
</div>
</div>
</div>
);
};
export default Dashboard;