garden_tracker/src/components/PlantForm.tsx

296 lines
11 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, { useState, useEffect } from 'react';
import { Plant } from '../types';
import { X } from 'lucide-react';
import { apiService } from '../services/api';
import ImageUpload from './ImageUpload';
interface PlantFormProps {
plant?: Plant | null;
onSave: (plant: Omit<Plant, 'id' | 'createdAt' | 'updatedAt'>) => void;
onCancel: () => void;
}
const PlantForm: React.FC<PlantFormProps> = ({ plant, onSave, onCancel }) => {
const [formData, setFormData] = useState({
type: '',
variety: '',
purchaseLocation: '',
seedlingAge: '',
seedlingHeight: '',
plantingDate: '',
healthStatus: 'good' as Plant['healthStatus'],
currentHeight: '',
photoUrl: '',
notes: ''
});
const [uploading, setUploading] = useState(false);
const [selectedFile, setSelectedFile] = useState<File | null>(null);
useEffect(() => {
if (plant) {
setFormData({
type: plant.type,
variety: plant.variety,
purchaseLocation: plant.purchaseLocation,
seedlingAge: plant.seedlingAge.toString(),
seedlingHeight: plant.seedlingHeight.toString(),
plantingDate: plant.plantingDate,
healthStatus: plant.healthStatus,
currentHeight: plant.currentHeight?.toString() || '',
photoUrl: plant.photoUrl || '',
notes: plant.notes
});
}
}, [plant]);
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
let finalPhotoUrl = formData.photoUrl;
// Upload image if a file is selected
if (selectedFile) {
try {
setUploading(true);
const result = await apiService.uploadPhoto(selectedFile);
finalPhotoUrl = result.photoUrl;
} catch (error) {
console.error('Error uploading photo:', error);
alert('Failed to upload photo. Please try again.');
setUploading(false);
return;
} finally {
setUploading(false);
}
}
onSave({
...formData,
seedlingAge: parseInt(formData.seedlingAge),
seedlingHeight: parseFloat(formData.seedlingHeight),
currentHeight: formData.currentHeight ? parseFloat(formData.currentHeight) : undefined,
photoUrl: finalPhotoUrl || undefined
});
};
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) => {
const { name, value } = e.target;
setFormData(prev => ({ ...prev, [name]: value }));
};
const handleFileSelect = (file: File) => {
setSelectedFile(file);
};
const handleImageChange = (imageUrl: string) => {
setFormData(prev => ({ ...prev, photoUrl: imageUrl }));
};
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-lg shadow-xl max-w-4xl w-full max-h-[85vh] overflow-y-auto">
<div className="flex justify-between items-center p-6 border-b border-gray-200">
<h3 className="text-lg font-semibold text-gray-900">
{plant ? 'Редактирование данных' : 'Новое растение'}
</h3>
<button
onClick={onCancel}
className="text-gray-400 hover:text-gray-600 transition-colors"
>
<X className="h-5 w-5" />
</button>
</div>
<form onSubmit={handleSubmit} className="p-6 space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="type" className="block text-sm font-medium text-gray-700 mb-1">
Тип растения *
</label>
<select
id="type"
name="type"
value={formData.type}
onChange={handleChange}
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
>
<option value="">Выбор типа</option>
<option value="tree">Дерево</option>
<option value="shrub">Кустарник</option>
<option value="herb">Зелень</option>
<option value="vegetable">Овощ</option>
<option value="flower">Цветы</option>
<option value="grass">Трава</option>
<option value="other">Другое</option>
</select>
</div>
<div>
<label htmlFor="variety" className="block text-sm font-medium text-gray-700 mb-1">
Вид/Сорт *
</label>
<input
type="text"
id="variety"
name="variety"
value={formData.variety}
onChange={handleChange}
placeholder="e.g., Apple - Honeycrisp"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div>
<label htmlFor="purchaseLocation" className="block text-sm font-medium text-gray-700 mb-1">
Место покупки *
</label>
<input
type="text"
id="purchaseLocation"
name="purchaseLocation"
value={formData.purchaseLocation}
onChange={handleChange}
placeholder="e.g., Local Nursery, Garden Center"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
/>
</div>
<div>
<label htmlFor="plantingDate" className="block text-sm font-medium text-gray-700 mb-1">
Дата посадки *
</label>
<input
type="date"
id="plantingDate"
name="plantingDate"
value={formData.plantingDate}
onChange={handleChange}
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
/>
</div>
</div>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div>
<label htmlFor="seedlingAge" className="block text-sm font-medium text-gray-700 mb-1">
Возраст саженца (м-цы) *
</label>
<input
type="number"
id="seedlingAge"
name="seedlingAge"
value={formData.seedlingAge}
onChange={handleChange}
min="0"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
/>
</div>
<div>
<label htmlFor="seedlingHeight" className="block text-sm font-medium text-gray-700 mb-1">
Высота саженца (м) *
</label>
<input
type="number"
id="seedlingHeight"
name="seedlingHeight"
value={formData.seedlingHeight}
onChange={handleChange}
step="0.1"
min="0"
required
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
/>
</div>
<div>
<label htmlFor="currentHeight" className="block text-sm font-medium text-gray-700 mb-1">
Текущая высота (м)
</label>
<input
type="number"
id="currentHeight"
name="currentHeight"
value={formData.currentHeight}
onChange={handleChange}
step="0.1"
min="0"
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
/>
</div>
<div>
<label htmlFor="healthStatus" className="block text-sm font-medium text-gray-700 mb-1">
Состояние
</label>
<select
id="healthStatus"
name="healthStatus"
value={formData.healthStatus}
onChange={handleChange}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent"
>
<option value="good">Хорошее</option>
<option value="needs-attention">Требует внимания</option>
<option value="dead">Погибло</option>
</select>
</div>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Plant Photo
</label>
<ImageUpload
currentImageUrl={formData.photoUrl}
onImageChange={handleImageChange}
onFileSelect={handleFileSelect}
uploading={uploading}
/>
</div>
<div>
<label htmlFor="type" className="block text-sm font-medium text-gray-700 mb-1">
Заметки
</label>
<textarea
id="notes"
name="notes"
value={formData.notes}
onChange={handleChange}
placeholder="Специальные заметки об этом растении..."
rows={3}
className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent resize-none"
/>
</div>
<div className="flex justify-end space-x-3 pt-4 border-t border-gray-200">
<button
type="button"
onClick={onCancel}
className="px-4 py-2 text-gray-700 bg-gray-100 hover:bg-gray-200 rounded-md transition-colors"
>
Отмена
</button>
<button
type="submit"
disabled={uploading}
className="px-4 py-2 bg-green-600 hover:bg-green-700 text-white rounded-md transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{uploading ? 'Загружается...' : (plant ? 'Изменить' : 'Добавить')}
</button>
</div>
</form>
</div>
</div>
);
};
export default PlantForm;