/
netsvvir
/
treeRepo
Обзор
Документация
Войти
/
netsvvir
/
treeRepo
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
admin-panel/src/pages/TreeTypes.tsx
240 строк
11 KB
netsvvir
Upload project
23 ноя 2025, 15:06
23 ноя 2025, 15:06
ce0a46d
Код
Авторство
О чём код?
import React, { useState, useEffect } from 'react'; import { TreeType, TreeTypeCreate } from '../types/treeTypes'; import { treeTypesService } from '../services/api'; import TreeTypeList from '../components/TreeTypeList'; import TreeTypeForm from '../components/TreeTypeForm'; import { Plus, Search, Trees } from 'lucide-react'; const TreeTypes: React.FC = () => { const [treeTypes, setTreeTypes] = useState<TreeType[]>([]); const [filteredTreeTypes, setFilteredTreeTypes] = useState<TreeType[]>([]); const [searchTerm, setSearchTerm] = useState(''); const [isFormOpen, setIsFormOpen] = useState(false); const [editingTreeType, setEditingTreeType] = useState<TreeType | undefined>(); const [loading, setLoading] = useState(true); const [error, setError] = useState<string | null>(null); // Загрузка типов деревьев const loadTreeTypes = async () => { try { setLoading(true); const data = await treeTypesService.getTreeTypes(); setTreeTypes(data); setFilteredTreeTypes(data); setError(null); } catch (err) { setError('Ошибка при загрузке типов деревьев'); console.error('Error loading tree types:', err); } finally { setLoading(false); } }; useEffect(() => { loadTreeTypes(); }, []); // Поиск useEffect(() => { const filtered = treeTypes.filter(treeType => treeType.type_name.toLowerCase().includes(searchTerm.toLowerCase()) || treeType.description?.toLowerCase().includes(searchTerm.toLowerCase()) ); setFilteredTreeTypes(filtered); }, [searchTerm, treeTypes]); // Создание типа дерева // В функции handleCreate добавьте более детальную обработку ошибок: const handleCreate = async (treeTypeData: TreeTypeCreate) => { try { setError(null); // Валидация на фронтенде if (!treeTypeData.type_name.trim()) { setError('Название типа дерева обязательно'); return; } if (treeTypeData.base_price < 0) { setError('Цена не может быть отрицательной'); return; } await treeTypesService.createTreeType(treeTypeData); await loadTreeTypes(); setIsFormOpen(false); // Показать сообщение об успехе setError(null); } catch (err: any) { console.error('Error creating tree type:', err); setError(err.message || 'Ошибка при создании типа дерева'); } }; // Редактирование типа дерева const handleEdit = async (treeTypeData: TreeTypeCreate) => { if (!editingTreeType) return; try { await treeTypesService.updateTreeType(editingTreeType.type_id, treeTypeData); await loadTreeTypes(); setIsFormOpen(false); setEditingTreeType(undefined); } catch (err) { setError('Ошибка при обновлении типа дерева'); console.error('Error updating tree type:', err); } }; // Удаление типа дерева const handleDelete = async (id: number) => { if (!window.confirm('Вы уверены, что хотите удалить этот тип дерева?')) return; try { await treeTypesService.deleteTreeType(id); await loadTreeTypes(); } catch (err) { setError('Ошибка при удалении типа дерева'); console.error('Error deleting tree type:', err); } }; // Открытие формы редактирования const handleEditClick = (treeType: TreeType) => { setEditingTreeType(treeType); setIsFormOpen(true); }; // Открытие формы создания const handleCreateClick = () => { setEditingTreeType(undefined); setIsFormOpen(true); }; // Закрытие формы const handleFormClose = () => { setIsFormOpen(false); setEditingTreeType(undefined); }; if (loading) { return ( <div className="flex items-center justify-center h-64"> <div className="text-lg text-gray-600">Загрузка типов деревьев...</div> </div> ); } return ( <div className="space-y-6"> {/* Заголовок и кнопка */} <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center space-y-4 sm:space-y-0"> <div> <h1 className="text-2xl font-bold text-gray-900">Управление типами деревьев</h1> <p className="text-gray-600 mt-1"> Всего типов: {treeTypes.length} | Средняя цена: {treeTypes.length > 0 ? (treeTypes.reduce((sum, type) => sum + type.base_price, 0) / treeTypes.length).toFixed(2) : 0} ₽ </p> </div> <button onClick={handleCreateClick} className="flex items-center space-x-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors" > <Plus className="w-4 h-4" /> <span>Добавить тип дерева</span> </button> </div> {/* Поиск и фильтры */} <div className="bg-white p-4 rounded-lg shadow-sm border border-gray-200"> <div className="flex flex-col sm:flex-row space-y-3 sm:space-y-0 sm:space-x-4"> <div className="flex-1 relative"> <Search className="w-4 h-4 absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400" /> <input type="text" placeholder="Поиск по названию или описанию..." value={searchTerm} onChange={(e) => setSearchTerm(e.target.value)} className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 focus:border-transparent" /> </div> </div> </div> {/* Сообщение об ошибке */} {error && ( <div className="bg-red-50 border border-red-200 rounded-lg p-4"> <p className="text-red-800">{error}</p> </div> )} {/* Список типов деревьев */} {filteredTreeTypes.length === 0 ? ( <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-12 text-center"> <div className="text-gray-400 mb-4"> <Trees className="w-12 h-12 mx-auto" /> </div> <h3 className="text-lg font-medium text-gray-900 mb-2"> Типы деревьев не найдены </h3> <p className="text-gray-600 mb-4"> {searchTerm ? 'Попробуйте изменить условия поиска' : 'Добавьте первый тип дерева для посадки'} </p> <button onClick={handleCreateClick} className="inline-flex items-center space-x-2 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors" > <Plus className="w-4 h-4" /> <span>Добавить тип дерева</span> </button> </div> ) : ( <TreeTypeList treeTypes={filteredTreeTypes} onEdit={handleEditClick} onDelete={handleDelete} /> )} {/* Форма */} <TreeTypeForm treeType={editingTreeType} isOpen={isFormOpen} onClose={handleFormClose} onSubmit={editingTreeType ? handleEdit : handleCreate} /> {/* Информационный блок */} <div className="bg-gradient-to-r from-green-50 to-blue-50 border border-green-200 rounded-lg p-6"> <h3 className="font-semibold text-green-900 mb-3"> 🌳 Рекомендуемые типы деревьев для Ростовской области: </h3> <div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-sm"> <div> <p className="font-medium text-gray-700">Лиственные:</p> <ul className="mt-2 text-gray-600 space-y-1"> <li>• Дуб черешчатый - от 1500 ₽</li> <li>• Ясень обыкновенный - от 1200 ₽</li> <li>• Клен полевой - от 1000 ₽</li> <li>• Липа мелколистная - от 1300 ₽</li> </ul> </div> <div> <p className="font-medium text-gray-700">Хвойные и другие:</p> <ul className="mt-2 text-gray-600 space-y-1"> <li>• Сосна обыкновенная - от 1100 ₽</li> <li>• Береза повислая - от 900 ₽</li> <li>• Ива белая - от 800 ₽</li> <li>• Тополь пирамидальный - от 700 ₽</li> </ul> </div> </div> </div> </div> ); }; export default TreeTypes;