-
{record.type}
+
{Maintenances[record.type]}
{plant?.variety}
{record.description}
diff --git a/src/components/FertilizerForm.tsx b/src/components/FertilizerForm.tsx
new file mode 100644
index 0000000..802d3f0
--- /dev/null
+++ b/src/components/FertilizerForm.tsx
@@ -0,0 +1,243 @@
+import React, { useState, useEffect } from 'react';
+import { Fertilizer } from '../types';
+import { X } from 'lucide-react';
+
+interface FertilizerFormProps {
+ fertilizer?: Fertilizer | null;
+ onSave: (fertilizer: Omit) => void;
+ onCancel: () => void;
+}
+
+const FertilizerForm: React.FC = ({ fertilizer, onSave, onCancel }) => {
+ const [formData, setFormData] = useState({
+ name: '',
+ brand: '',
+ type: 'organic' as Fertilizer['type'],
+ npkRatio: '',
+ description: '',
+ applicationRate: '',
+ frequency: '',
+ season: '',
+ notes: ''
+ });
+
+ useEffect(() => {
+ if (fertilizer) {
+ setFormData({
+ name: fertilizer.name,
+ brand: fertilizer.brand || '',
+ type: fertilizer.type,
+ npkRatio: fertilizer.npkRatio || '',
+ description: fertilizer.description || '',
+ applicationRate: fertilizer.applicationRate || '',
+ frequency: fertilizer.frequency || '',
+ season: fertilizer.season || '',
+ notes: fertilizer.notes || ''
+ });
+ }
+ }, [fertilizer]);
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault();
+ onSave({
+ ...formData,
+ brand: formData.brand || undefined,
+ npkRatio: formData.npkRatio || undefined,
+ description: formData.description || undefined,
+ applicationRate: formData.applicationRate || undefined,
+ frequency: formData.frequency || undefined,
+ season: formData.season || undefined,
+ notes: formData.notes || undefined
+ });
+ };
+
+ const handleChange = (e: React.ChangeEvent) => {
+ const { name, value } = e.target;
+ setFormData(prev => ({ ...prev, [name]: value }));
+ };
+
+ return (
+
+
+
+
+ {fertilizer ? 'Редактировать данные' : 'Новое удобрение'}
+
+
+
+
+
+
+
+ );
+};
+
+export default FertilizerForm;
\ No newline at end of file
diff --git a/src/components/FertilizerRegistry.tsx b/src/components/FertilizerRegistry.tsx
new file mode 100644
index 0000000..d42bdf8
--- /dev/null
+++ b/src/components/FertilizerRegistry.tsx
@@ -0,0 +1,254 @@
+import React, { useState, useEffect } from 'react';
+import { Fertilizer } from '../types';
+import { Plus, Search, Edit, Trash2, Sprout } from 'lucide-react';
+import FertilizerForm from './FertilizerForm';
+import { apiService } from '../services/api';
+
+const FertilizerRegistry: React.FC = () => {
+ const [fertilizers, setFertilizers] = useState([]);
+ const [showForm, setShowForm] = useState(false);
+ const [editingFertilizer, setEditingFertilizer] = useState(null);
+ const [searchTerm, setSearchTerm] = useState('');
+ const [filterType, setFilterType] = useState('all');
+ const [loading, setLoading] = useState(true);
+
+ useEffect(() => {
+ loadFertilizers();
+ }, []);
+
+ const loadFertilizers = async () => {
+ try {
+ setLoading(true);
+ const data = await apiService.getFertilizers();
+ setFertilizers(data);
+ } catch (error) {
+ console.error('Error loading fertilizers:', error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+ const filteredFertilizers = fertilizers.filter(fertilizer => {
+ const matchesSearch = fertilizer.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ (fertilizer.brand && fertilizer.brand.toLowerCase().includes(searchTerm.toLowerCase()));
+ const matchesType = filterType === 'all' || fertilizer.type === filterType;
+
+ return matchesSearch && matchesType;
+ });
+
+ const handleSaveFertilizer = async (fertilizerData: Omit) => {
+ try {
+ if (editingFertilizer) {
+ const updatedFertilizer = await apiService.updateFertilizer(editingFertilizer.id, fertilizerData);
+ setFertilizers(fertilizers.map(f => f.id === editingFertilizer.id ? updatedFertilizer : f));
+ } else {
+ const newFertilizer = await apiService.createFertilizer(fertilizerData);
+ setFertilizers([...fertilizers, newFertilizer]);
+ }
+ setShowForm(false);
+ setEditingFertilizer(null);
+ } catch (error) {
+ console.error('Error saving fertilizer:', error);
+ }
+ };
+
+ const handleDeleteFertilizer = async (fertilizerId: number) => {
+ if (window.confirm('Are you sure you want to delete this fertilizer?')) {
+ try {
+ await apiService.deleteFertilizer(fertilizerId);
+ setFertilizers(fertilizers.filter(f => f.id !== fertilizerId));
+ } catch (error) {
+ console.error('Error deleting fertilizer:', error);
+ }
+ }
+ };
+
+ const getTypeColor = (type: string) => {
+ switch (type) {
+ case 'organic': return 'bg-green-100 text-green-800';
+ case 'synthetic': return 'bg-blue-100 text-blue-800';
+ case 'liquid': return 'bg-cyan-100 text-cyan-800';
+ case 'granular': return 'bg-yellow-100 text-yellow-800';
+ case 'slow-release': return 'bg-purple-100 text-purple-800';
+ default: return 'bg-gray-100 text-gray-800';
+ }
+ };
+
+ if (loading) {
+ return (
+
+ );
+ }
+
+ return (
+
+
+
+
Реестр удобрений
+
Управляйте своими запасами удобрений
+
+
+
+
+ {/* Filters */}
+
+
+
+
+
+
+ setSearchTerm(e.target.value)}
+ className="pl-10 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"
+ />
+
+
+
+
+
+
+
+
+
+
+ {/* Fertilizers Grid */}
+
+ {filteredFertilizers.map((fertilizer) => (
+
+
+
+
+
+
+
+
+
{fertilizer.name}
+ {fertilizer.brand && (
+
{fertilizer.brand}
+ )}
+
+
+
+
+
+
+
+
+
+
+ Type:
+
+ {fertilizer.type}
+
+
+
+ {fertilizer.npkRatio && (
+
+ NPK Ratio:
+ {fertilizer.npkRatio}
+
+ )}
+
+ {fertilizer.applicationRate && (
+
+
Норма расхода:
+
{fertilizer.applicationRate}
+
+ )}
+
+ {fertilizer.frequency && (
+
+
Как часто применять:
+
{fertilizer.frequency}
+
+ )}
+
+ {fertilizer.season && (
+
+
Время года:
+
{fertilizer.season}
+
+ )}
+
+ {fertilizer.description && (
+
+
Описание:
+
{fertilizer.description}
+
+ )}
+
+ {fertilizer.notes && (
+
+
Заметки:
+
{fertilizer.notes}
+
+ )}
+
+
+
+ ))}
+
+
+ {filteredFertilizers.length === 0 && (
+
+
No fertilizers found.
+
+
+ )}
+
+ {/* Fertilizer Form Modal */}
+ {showForm && (
+
{
+ setShowForm(false);
+ setEditingFertilizer(null);
+ }}
+ />
+ )}
+
+ );
+};
+
+export default FertilizerRegistry;
\ No newline at end of file
diff --git a/src/components/HarvestForm.tsx b/src/components/HarvestForm.tsx
index a73d875..d0157e5 100644
--- a/src/components/HarvestForm.tsx
+++ b/src/components/HarvestForm.tsx
@@ -35,7 +35,7 @@ const HarvestForm: React.FC = ({ plant, onSave, onCancel }) =>
- Record Harvest: {plant.variety}
+ Урожай: {plant.variety}
@@ -183,7 +193,7 @@ const MaintenanceLog: React.FC
= ({
onClick={() => setShowMaintenanceForm(true)}
className="mt-4 text-green-600 hover:text-green-700 transition-colors"
>
- Запишите свое первое действие в журнал →
+ Записать свое первое действие в журнал →
)}
diff --git a/src/components/ObservationForm.tsx b/src/components/ObservationForm.tsx
index 59d5293..bad7d50 100644
--- a/src/components/ObservationForm.tsx
+++ b/src/components/ObservationForm.tsx
@@ -1,5 +1,5 @@
import React, { useState, useEffect } from 'react';
-import { Plant, PlantObservation } from '../types';
+import {Plant, PlantObservation, PlantTitles} from '../types';
import { X } from 'lucide-react';
interface ObservationFormProps {
@@ -82,10 +82,10 @@ const ObservationForm: React.FC = ({ observation, plants,
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"
>
-
+
{plants.map(plant => (
))}
diff --git a/src/components/ObservationJournal.tsx b/src/components/ObservationJournal.tsx
index 5d25b3a..00064e0 100644
--- a/src/components/ObservationJournal.tsx
+++ b/src/components/ObservationJournal.tsx
@@ -236,7 +236,7 @@ const ObservationJournal: React.FC = ({
onClick={() => setShowObservationForm(true)}
className="mt-4 text-green-600 hover:text-green-700 transition-colors"
>
- Добавте первое наблюдение →
+ Добавить первое наблюдение →
)}
diff --git a/src/components/PlantForm.tsx b/src/components/PlantForm.tsx
index 87b0a02..a8abb13 100644
--- a/src/components/PlantForm.tsx
+++ b/src/components/PlantForm.tsx
@@ -1,6 +1,7 @@
import React, { useState, useEffect } from 'react';
import { Plant } from '../types';
-import { X } from 'lucide-react';
+import { X, Upload, Image } from 'lucide-react';
+import { apiService } from '../services/api';
interface PlantFormProps {
plant?: Plant | null;
@@ -21,6 +22,8 @@ const PlantForm: React.FC