338 lines
13 KiB
TypeScript
338 lines
13 KiB
TypeScript
import React, { useState, useEffect } from 'react';
|
||
import { X, FileText, Sprout, Beaker } from 'lucide-react';
|
||
import {Task, Plant, Fertilizer, Chemical, PlantTitles, TaskDraft} from '../types';
|
||
import { apiService } from '../services/api';
|
||
|
||
interface TaskFormProps {
|
||
isOpen: boolean;
|
||
onClose: () => void;
|
||
onSubmit: (task: TaskDraft) => void;
|
||
task?: Task;
|
||
}
|
||
|
||
const taskTypes = [
|
||
{ value: 'general', label: 'Общая', icon: FileText, color: 'bg-gray-100 text-gray-800' },
|
||
{ value: 'fertilizer', label: 'Внесение удобрений', icon: Sprout, color: 'bg-green-100 text-green-800' },
|
||
{ value: 'chemical', label: 'Применение химикатов', icon: Beaker, color: 'bg-red-100 text-red-800' },
|
||
{ value: 'watering', label: 'Полив', icon: FileText, color: 'bg-blue-100 text-blue-800' },
|
||
{ value: 'pruning', label: 'Обрезка', icon: FileText, color: 'bg-purple-100 text-purple-800' },
|
||
{ value: 'transplanting', label: 'Пересадка', icon: FileText, color: 'bg-orange-100 text-orange-800' },
|
||
{ value: 'harvesting', label: 'Сбор урожая', icon: FileText, color: 'bg-yellow-100 text-yellow-800' },
|
||
{ value: 'other', label: 'Другое', icon: FileText, color: 'bg-gray-100 text-gray-800' }
|
||
];
|
||
|
||
export default function TaskForm({ isOpen, onClose, onSubmit, task }: TaskFormProps) {
|
||
const [formData, setFormData] = useState({
|
||
plantId: '',
|
||
type: 'general',
|
||
title: '',
|
||
description: '',
|
||
deadline: '',
|
||
completed: false,
|
||
fertilizerId: '',
|
||
chemicalId: ''
|
||
});
|
||
|
||
const [plants, setPlants] = useState<Plant[]>([]);
|
||
const [fertilizers, setFertilizers] = useState<Fertilizer[]>([]);
|
||
const [chemicals, setChemicals] = useState<Chemical[]>([]);
|
||
const [selectedFertilizer, setSelectedFertilizer] = useState<Fertilizer | null>(null);
|
||
const [selectedChemical, setSelectedChemical] = useState<Chemical | null>(null);
|
||
|
||
useEffect(() => {
|
||
if (isOpen) {
|
||
loadData();
|
||
}
|
||
}, [isOpen]);
|
||
|
||
useEffect(() => {
|
||
if (task) {
|
||
setFormData({
|
||
plantId: task.plantId?.toString() || '',
|
||
type: task.type || 'general',
|
||
title: task.title,
|
||
description: task.description || '',
|
||
deadline: task.deadline.split('T')[0],
|
||
completed: task.completed,
|
||
fertilizerId: task.fertilizerId?.toString() || '',
|
||
chemicalId: task.chemicalId?.toString() || ''
|
||
});
|
||
} else {
|
||
setFormData({
|
||
plantId: '',
|
||
type: 'general',
|
||
title: '',
|
||
description: '',
|
||
deadline: '',
|
||
completed: false,
|
||
fertilizerId: '',
|
||
chemicalId: ''
|
||
});
|
||
}
|
||
}, [task]);
|
||
|
||
const loadData = async () => {
|
||
try {
|
||
const [plantsData, fertilizersData, chemicalsData] = await Promise.all([
|
||
apiService.getPlants(),
|
||
apiService.getFertilizers(),
|
||
apiService.getChemicals()
|
||
]);
|
||
setPlants(plantsData);
|
||
setFertilizers(fertilizersData);
|
||
setChemicals(chemicalsData);
|
||
} catch (error) {
|
||
console.error('Error loading data:', error);
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
if (formData.fertilizerId) {
|
||
const fertilizer = fertilizers.find(f => f.id.toString() === formData.fertilizerId);
|
||
setSelectedFertilizer(fertilizer || null);
|
||
} else {
|
||
setSelectedFertilizer(null);
|
||
}
|
||
}, [formData.fertilizerId, fertilizers]);
|
||
|
||
useEffect(() => {
|
||
if (formData.chemicalId) {
|
||
const chemical = chemicals.find(c => c.id.toString() === formData.chemicalId);
|
||
setSelectedChemical(chemical || null);
|
||
} else {
|
||
setSelectedChemical(null);
|
||
}
|
||
}, [formData.chemicalId, chemicals]);
|
||
|
||
const handleSubmit = (e: React.FormEvent) => {
|
||
e.preventDefault();
|
||
|
||
const taskData = {
|
||
...formData,
|
||
plantId: formData.plantId ? parseInt(formData.plantId) : undefined,
|
||
fertilizerId: formData.fertilizerId ? parseInt(formData.fertilizerId) : undefined,
|
||
chemicalId: formData.chemicalId ? parseInt(formData.chemicalId) : undefined
|
||
};
|
||
|
||
onSubmit(taskData);
|
||
onClose();
|
||
};
|
||
|
||
const handleTaskTypeChange = (taskType: string) => {
|
||
setFormData(prev => ({
|
||
...prev,
|
||
type: taskType,
|
||
fertilizerId: taskType !== 'fertilizer' ? '' : prev.fertilizerId,
|
||
chemicalId: taskType !== 'chemical' ? '' : prev.chemicalId
|
||
}));
|
||
};
|
||
|
||
if (!isOpen) return null;
|
||
|
||
const selectedTaskType = taskTypes.find(type => type.value === formData.type);
|
||
|
||
return (
|
||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center p-4 z-50">
|
||
<div className="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[90vh] overflow-y-auto">
|
||
<div className="flex items-center justify-between p-6 border-b">
|
||
<h2 className="text-xl font-semibold text-gray-900">
|
||
{task ? 'Изменение работы' : 'Создание новой работы'}
|
||
</h2>
|
||
<button
|
||
onClick={onClose}
|
||
className="text-gray-400 hover:text-gray-600 transition-colors"
|
||
>
|
||
<X className="w-6 h-6" />
|
||
</button>
|
||
</div>
|
||
|
||
<form onSubmit={handleSubmit} className="p-6 space-y-6">
|
||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Наименование работы *
|
||
</label>
|
||
<input
|
||
type="text"
|
||
required
|
||
value={formData.title}
|
||
onChange={(e) => setFormData(prev => ({ ...prev, title: e.target.value }))}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
||
placeholder="Enter task title"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Тип *
|
||
</label>
|
||
<select
|
||
required
|
||
value={formData.type}
|
||
onChange={(e) => handleTaskTypeChange(e.target.value)}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
||
>
|
||
{taskTypes.map(type => (
|
||
<option key={type.value} value={type.value}>
|
||
{type.label}
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Крайний срок *
|
||
</label>
|
||
<input
|
||
type="date"
|
||
required
|
||
value={formData.deadline}
|
||
onChange={(e) => setFormData(prev => ({ ...prev, deadline: e.target.value }))}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
||
/>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Статус
|
||
</label>
|
||
<div className="flex items-center">
|
||
<input
|
||
type="checkbox"
|
||
id="completed"
|
||
checked={formData.completed}
|
||
onChange={(e) => setFormData(prev => ({ ...prev, completed: e.target.checked }))}
|
||
className="h-4 w-4 text-green-600 focus:ring-green-500 border-gray-300 rounded"
|
||
/>
|
||
<label htmlFor="completed" className="ml-2 block text-sm text-gray-700">
|
||
Завершена
|
||
</label>
|
||
</div>
|
||
</div>
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Растение (не обязательно)
|
||
</label>
|
||
<select
|
||
value={formData.plantId}
|
||
onChange={(e) => setFormData(prev => ({ ...prev, plantId: e.target.value }))}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="">Выбор растения</option>
|
||
{plants.map(plant => (
|
||
<option key={plant.id} value={plant.id}>
|
||
{plant.variety} ({PlantTitles[plant.type]})
|
||
</option>
|
||
))}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
|
||
{formData.type === 'fertilizer' && (
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Выбор удобрения
|
||
</label>
|
||
<select
|
||
value={formData.fertilizerId}
|
||
onChange={(e) => setFormData(prev => ({ ...prev, fertilizerId: e.target.value }))}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="">Выбор удобрения</option>
|
||
{fertilizers.map(fertilizer => (
|
||
<option key={fertilizer.id} value={fertilizer.id}>
|
||
{fertilizer.name} - {fertilizer.brand}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
{selectedFertilizer && (
|
||
<div className="mt-3 p-3 bg-green-50 rounded-md">
|
||
<h4 className="font-medium text-green-800 mb-2">Fertilizer Information</h4>
|
||
<div className="text-sm text-green-700 space-y-1">
|
||
<p><strong>NPK:</strong> {selectedFertilizer.npkRatio}</p>
|
||
<p><strong>Application Rate:</strong> {selectedFertilizer.applicationRate}</p>
|
||
<p><strong>Frequency:</strong> {selectedFertilizer.frequency}</p>
|
||
{selectedFertilizer.notes && (
|
||
<p><strong>Заметки:</strong> {selectedFertilizer.notes}</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
{formData.type === 'chemical' && (
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Выбор химикатов
|
||
</label>
|
||
<select
|
||
value={formData.chemicalId}
|
||
onChange={(e) => setFormData(prev => ({ ...prev, chemicalId: e.target.value }))}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
||
>
|
||
<option value="">Выбор химикатов</option>
|
||
{chemicals.map(chemical => (
|
||
<option key={chemical.id} value={chemical.id}>
|
||
{chemical.name} - {chemical.brand}
|
||
</option>
|
||
))}
|
||
</select>
|
||
|
||
{selectedChemical && (
|
||
<div className="mt-3 p-3 bg-red-50 rounded-md">
|
||
<h4 className="font-medium text-red-800 mb-2">Chemical Information</h4>
|
||
<div className="text-sm text-red-700 space-y-1">
|
||
<p><strong>Active Ingredients:</strong> {selectedChemical.activeIngredient}</p>
|
||
<p><strong>Target Pests:</strong> {selectedChemical.targetPests}</p>
|
||
<p><strong>Application Rate:</strong> {selectedChemical.applicationMethod}</p>
|
||
{selectedChemical.safetyPeriod && (
|
||
<p className="bg-yellow-100 text-yellow-800 p-2 rounded">
|
||
<strong>Safety Period:</strong> {selectedChemical.safetyPeriod}
|
||
</p>
|
||
)}
|
||
{selectedChemical.notes && (
|
||
<p><strong>Заметки:</strong> {selectedChemical.notes}</p>
|
||
)}
|
||
</div>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
|
||
<div>
|
||
<label className="block text-sm font-medium text-gray-700 mb-2">
|
||
Описание
|
||
</label>
|
||
<textarea
|
||
value={formData.description}
|
||
onChange={(e) => setFormData(prev => ({ ...prev, description: e.target.value }))}
|
||
rows={4}
|
||
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500"
|
||
placeholder="Enter task description"
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex justify-end space-x-3 pt-4 border-t">
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="px-4 py-2 text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200 transition-colors"
|
||
>
|
||
Отмена
|
||
</button>
|
||
<button
|
||
type="submit"
|
||
className="px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors"
|
||
>
|
||
{task ? 'Изменить' : 'Создать'}
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
);
|
||
} |