import { useState, useEffect, useRef } from 'react'; import axios from 'axios'; import { IconPlus, IconSearch, IconEdit, IconTrash, IconCheck, IconX, IconDatabase, IconRefresh } from '@tabler/icons-react'; const API_URL = '/api'; function IPRangesManager() { const [items, setItems] = useState([]); const [newItem, setNewItem] = useState({ ipRange: '', community: '' }); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [searchTerm, setSearchTerm] = useState(''); const [editingIp, setEditingIp] = useState(null); const [editingValue, setEditingValue] = useState(''); const [loading, setLoading] = useState(false); const [showDeleteModal, setShowDeleteModal] = useState(false); const [itemToDelete, setItemToDelete] = useState(null); const [currentPage, setCurrentPage] = useState(1); const [sortField, setSortField] = useState('ipRange'); const [sortOrder, setSortOrder] = useState('asc'); const [filterCommunity, setFilterCommunity] = useState(''); const editInputRef = useRef(null); const pageSize = 10; useEffect(() => { fetchItems(); }, []); const fetchItems = async () => { setLoading(true); try { const response = await axios.get(`${API_URL}/ip-ranges`); setItems(response.data); setError(''); } catch (error) { console.error('Error fetching ip-ranges:', error); setError('Не удалось загрузить IP-диапазоны. Проверьте, запущен ли бэкенд.'); } finally { setLoading(false); } }; const handleAddItem = () => { if (newItem.ipRange.trim() === '') { setError('IP-диапазон не может быть пустым.'); return; } if (newItem.community.trim() === '') { setError('Community не может быть пустым.'); return; } setError(''); setItems([...items, newItem]); setNewItem({ ipRange: '', community: '' }); }; const handleEdit = (item) => { setEditingIp(item.ipRange); setEditingValue(item.community); }; const handleSaveEdit = (ipRange) => { const updatedItems = items.map(i => i.ipRange === ipRange ? { ...i, community: editingValue } : i ); setItems(updatedItems); setEditingIp(null); }; const handleCancelEdit = () => { setEditingIp(null); }; const handleDeleteItem = (ipRangeToDelete) => { setItems(items.filter(i => i.ipRange !== ipRangeToDelete)); }; const confirmDelete = (item) => { setItemToDelete(item); setShowDeleteModal(true); }; const executeDelete = () => { if (itemToDelete) { handleDeleteItem(itemToDelete.ipRange); setShowDeleteModal(false); setItemToDelete(null); } }; const handleSaveChanges = async () => { setLoading(true); try { await axios.post(`${API_URL}/ip-ranges`, { ipRanges: items }); setSuccess('Изменения успешно сохранены!'); setTimeout(() => setSuccess(''), 3000); } catch (error) { console.error('Error saving changes:', error); setError('Не удалось сохранить изменения.'); } finally { setLoading(false); } }; // Сортировка const sortedItems = [...items].sort((a, b) => { let valA = a[sortField] || ''; let valB = b[sortField] || ''; if (typeof valA === 'string') valA = valA.toLowerCase(); if (typeof valB === 'string') valB = valB.toLowerCase(); if (valA < valB) return sortOrder === 'asc' ? -1 : 1; if (valA > valB) return sortOrder === 'asc' ? 1 : -1; return 0; }); // Фильтрация по community const filteredByCommunity = filterCommunity ? sortedItems.filter(i => i.community === filterCommunity) : sortedItems; // Поиск const filteredItems = filteredByCommunity.filter(i => i.ipRange.toLowerCase().includes(searchTerm.toLowerCase()) ); // Пагинация const totalPages = Math.ceil(filteredItems.length / pageSize); const paginatedItems = filteredItems.slice((currentPage - 1) * pageSize, currentPage * pageSize); // Для фильтра - список всех уникальных community const allCommunities = Array.from(new Set(items.map(i => i.community))); // Сортировка по клику const handleSort = (field) => { if (sortField === field) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortField(field); setSortOrder('asc'); } }; // Улучшенный инлайн-редакт useEffect(() => { if (editingIp && editInputRef.current) { editInputRef.current.focus(); } }, [editingIp]); const handleEditKeyDown = (e, ipRange) => { if (e.key === 'Enter') handleSaveEdit(ipRange); if (e.key === 'Escape') handleCancelEdit(); }; return ( <> {error && (
{error}
setError('')}>
)} {success && (
{success}
setSuccess('')}>
)}
{/* Add New Item Card */}

Добавить новый IP-диапазон

{ e.preventDefault(); handleAddItem(); }}>
setNewItem({ ...newItem, ipRange: e.target.value })} />
setNewItem({ ...newItem, community: e.target.value })} />
{/* Actions Card */}

Действия

Список IP-диапазонов

{/* Фильтр по community */} {/* Поиск */}
{ setSearchTerm(e.target.value); setCurrentPage(1); }} />
{paginatedItems.map((item) => ( ))}
handleSort('ipRange')}> IP-диапазон {sortField === 'ipRange' && ( {sortOrder === 'asc' ? '▲' : '▼'} )} handleSort('community')}> Community {sortField === 'community' && ( {sortOrder === 'asc' ? '▲' : '▼'} )}
{item.ipRange} {item.community} {editingIp === item.ipRange ? ( <> setEditingValue(e.target.value)} onKeyDown={e => handleEditKeyDown(e, item.ipRange)} style={{maxWidth: 120}} /> ) : ( <> )}
{/* Пагинация */} {totalPages > 1 && (
  • {Array.from({length: totalPages}, (_, i) => i + 1).map(page => (
  • ))}
)}
{/* Delete Confirmation Modal */} {showDeleteModal && (

Удалить IP-диапазон?

Вы уверены, что хотите удалить "{itemToDelete?.ipRange}"? Это действие необратимо.
)} ); } export default IPRangesManager;