diff --git a/frontend/src/IPRangesManager.jsx b/frontend/src/IPRangesManager.jsx index 52731ae..0d04f72 100644 --- a/frontend/src/IPRangesManager.jsx +++ b/frontend/src/IPRangesManager.jsx @@ -1,41 +1,35 @@ -import { useState, useEffect } from 'react'; +import { useState, useEffect, useRef } from 'react'; import axios from 'axios'; -import { - IconPlus, - IconSearch, - IconEdit, - IconTrash, - IconCheck, - IconX, +import { + IconPlus, + IconSearch, + IconEdit, + IconTrash, + IconCheck, + IconX, IconDatabase, - IconRefresh, - IconDownload, - IconUpload, - IconAlertTriangle, - IconFilter, - IconChevronDown, - IconLink, - IconCopy, - IconEye, - IconServer, - IconSettings, - IconDeviceFloppy, - IconHash, - IconRocket, - IconWorld, - IconNetwork + IconRefresh } from '@tabler/icons-react'; const API_URL = '/api'; function IPRangesManager() { const [items, setItems] = useState([]); - const [loading, setLoading] = useState(false); + const [newItem, setNewItem] = useState({ ipRange: '', community: '' }); const [error, setError] = useState(''); const [success, setSuccess] = useState(''); const [searchTerm, setSearchTerm] = useState(''); - const [editingIndex, setEditingIndex] = useState(null); - const [editingItem, setEditingItem] = useState({ ipRange: '', community: '' }); + 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(); @@ -49,314 +43,311 @@ function IPRangesManager() { setError(''); } catch (error) { console.error('Error fetching ip-ranges:', error); - setItems([]); + setError('Не удалось загрузить IP-диапазоны. Проверьте, запущен ли бэкенд.'); } finally { setLoading(false); } }; - const handleAdd = () => { - setEditingIndex(null); - setEditingItem({ ipRange: '', community: '' }); - }; - - const handleEdit = (index) => { - setEditingIndex(index); - setEditingItem({ ...items[index] }); - }; - - const handleSave = () => { - if (!editingItem.ipRange.trim() || !editingItem.community.trim()) { - setError('IP-диапазон и Community обязательны для заполнения.'); + const handleAddItem = () => { + if (newItem.ipRange.trim() === '') { + setError('IP-диапазон не может быть пустым.'); return; } - - const updatedItems = [...items]; - if (editingIndex !== null) { - updatedItems[editingIndex] = { ...editingItem }; - } else { - updatedItems.push({ ...editingItem }); + 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); - setEditingIndex(null); - setEditingItem({ ipRange: '', community: '' }); + setEditingIp(null); }; - const handleCancel = () => { - setEditingIndex(null); - setEditingItem({ ipRange: '', community: '' }); + const handleCancelEdit = () => { + setEditingIp(null); }; - const handleDelete = (index) => { - const updatedItems = items.filter((_, i) => i !== index); - setItems(updatedItems); + const handleDeleteItem = (ipRangeToDelete) => { + setItems(items.filter(i => i.ipRange !== ipRangeToDelete)); }; - const handleSaveToS3 = async () => { + 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('IP-диапазоны успешно сохранены!'); + setSuccess('Изменения успешно сохранены!'); setTimeout(() => setSuccess(''), 3000); } catch (error) { - console.error('Error saving ip-ranges:', error); - setError('Не удалось сохранить IP-диапазоны.'); + console.error('Error saving changes:', error); + setError('Не удалось сохранить изменения.'); } finally { setLoading(false); } }; - const handleUpload = async (event) => { - const file = event.target.files[0]; - if (!file) return; + // Сортировка + 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; + }); - const reader = new FileReader(); - reader.onload = (e) => { - const content = e.target.result; - const lines = content.split('\n').filter(line => line.trim()); - const parsedItems = lines.map(line => { - const parts = line.trim().split(/\s+/); - return { - ipRange: parts[0] || '', - community: parts[1] || '' - }; - }); - setItems(parsedItems); - setSuccess(`Загружено ${parsedItems.length} IP-диапазонов!`); - setTimeout(() => setSuccess(''), 3000); - }; - reader.readAsText(file); - }; + // Фильтрация по community + const filteredByCommunity = filterCommunity + ? sortedItems.filter(i => i.community === filterCommunity) + : sortedItems; - const handleDownload = () => { - const content = items.map(item => `${item.ipRange} ${item.community}`).join('\n'); - const blob = new Blob([content], { type: 'text/plain' }); - const url = URL.createObjectURL(blob); - const a = document.createElement('a'); - a.href = url; - a.download = 'ips.txt'; - document.body.appendChild(a); - a.click(); - document.body.removeChild(a); - URL.revokeObjectURL(url); - }; - - const filteredItems = items.filter(item => - item.ipRange.toLowerCase().includes(searchTerm.toLowerCase()) || - item.community.toLowerCase().includes(searchTerm.toLowerCase()) + // Поиск + const filteredItems = filteredByCommunity.filter(i => + i.ipRange.toLowerCase().includes(searchTerm.toLowerCase()) ); - return ( -
Нет IP-диапазонов
- -| IP-ДИАПАЗОН | -COMMUNITY | -ДЕЙСТВИЯ | +handleSort('ipRange')}> + IP-диапазон + {sortField === 'ipRange' && ( + + {sortOrder === 'asc' ? '▲' : '▼'} + + )} + | +handleSort('community')}> + Community + {sortField === 'community' && ( + + {sortOrder === 'asc' ? '▲' : '▼'} + + )} + | +|
|---|---|---|---|---|---|
|
- {editingIndex === index ? (
- setEditingItem({ ...editingItem, ipRange: e.target.value })}
- placeholder="192.168.1.0/24"
- />
- ) : (
-
-
-
- )}
- {item.ipRange}
- |
-
- {editingIndex === index ? (
- setEditingItem({ ...editingItem, community: e.target.value })}
- placeholder="65001:100"
- />
- ) : (
-
-
-
- )}
- {item.community}
- |
+ {paginatedItems.map((item) => (
+ ||||
| {item.ipRange} | +{item.community} |
- {editingIndex === index ? (
-
-
-
-
+ {editingIp === item.ipRange ? (
+ <>
+ setEditingValue(e.target.value)}
+ onKeyDown={e => handleEditKeyDown(e, item.ipRange)}
+ style={{maxWidth: 120}}
+ />
+
+
+ >
) : (
-
-
-
-
+ <>
+
+
+ >
)}
|