garden_tracker/src/components/Dashboard.tsx

220 lines
8.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React from 'react';
import {Plant, Task, MaintenanceRecord, HarvestRecord, PlantTitles, Maintenances} 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: 'Всего растений',
value: plants.length,
icon: Sprout,
color: 'text-green-600',
bgColor: 'bg-green-100'
},
{
label: 'Отложенные работы',
value: tasks.filter(task => !task.completed).length,
icon: Calendar,
color: 'text-blue-600',
bgColor: 'bg-blue-100'
},
{
label: 'Требует внимания',
value: plantsNeedingAttention.length,
icon: AlertTriangle,
color: 'text-yellow-600',
bgColor: 'bg-yellow-100'
},
{
label: ' Урожай этого года',
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">Сводка по саду</h2>
<p className="text-green-600">Добро пожаловать в мой центр управления садом</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">Предстоящие работы</h3>
<button
onClick={() => onNavigate('tasks')}
className="text-sm text-green-600 hover:text-green-700 transition-colors"
>
Посмотреть все
</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'}`}>
Срок: {new Date(task.deadline).toLocaleDateString()}
</p>
</div>
</div>
);
})}
</div>
) : (
<p className="text-gray-500 text-center py-8">Предстоящих работ нет</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">Растения, требующие внимания</h3>
<button
onClick={() => onNavigate('plants')}
className="text-sm text-green-600 hover:text-green-700 transition-colors"
>
Посмотреть все
</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">{PlantTitles[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">Недавние работы</h3>
<button
onClick={() => onNavigate('maintenance')}
className="text-sm text-green-600 hover:text-green-700 transition-colors"
>
Посмотреть все
</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">{Maintenances[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">Записей о выполненных работах пока нет</p>
)}
</div>
</div>
</div>
);
};
export default Dashboard;