import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { TextField, Box, Typography, IconButton, Divider, CircularProgress, Alert, Collapse, Tooltip, Button, Select, MenuItem } from '@mui/material'; import DeleteIcon from '@mui/icons-material/Delete'; import AddIcon from '@mui/icons-material/Add'; import SearchIcon from '@mui/icons-material/Search'; import axios from 'axios'; import { statusConfig } from './statusConfig'; import { VariableSizeList as List } from 'react-window'; import AutoSizer from 'react-virtualized-auto-sizer'; const MetricItem = React.memo(({ metric, index, updateRange, addRange, deleteRange }) => { return ( {metric.name} {metric.ranges.map((r, j) => ( *': { flex: 1 } }} > updateRange(index, j, 'min', e.target.value)} size="small" variant="standard" /> updateRange(index, j, 'max', e.target.value)} size="small" variant="standard" /> deleteRange(index, j)} size="small" sx={{ flex: 'none', // Корректируем положение иконки marginBottom: '8px' }} > ))} ); }); const MetricRangeEditor = ({ onSave }) => { const [ranges, setRanges] = useState([]); const [filter, setFilter] = useState(''); const [newMetricName, setNewMetricName] = useState(''); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [hasChanges, setHasChanges] = useState(false); const [success, setSuccess] = useState(false); const loadRanges = useCallback(async () => { try { setLoading(true); const res = await axios.get(`${import.meta.env.VITE_BACK_URL}/api/ranges/list`); setRanges( Object.entries(res.data).map(([name, r]) => ({ name, ranges: Array.isArray(r) ? r : [] })) ); setError(null); } catch (err) { console.error('Ошибка при получении ranges:', err); setError('Не удалось загрузить данные'); } finally { setLoading(false); } }, []); useEffect(() => { loadRanges(); }, [loadRanges]); const updateRange = useCallback((metricIndex, rangeIndex, field, value) => { setRanges(prev => { const newRanges = [...prev]; newRanges[metricIndex] = { ...newRanges[metricIndex], ranges: [...newRanges[metricIndex].ranges] }; newRanges[metricIndex].ranges[rangeIndex] = { ...newRanges[metricIndex].ranges[rangeIndex], [field]: Number(value) }; return newRanges; }); setHasChanges(true); }, []); const getItemSize = (index) => { const baseHeight = 80; const rangeCount = filtered[index].ranges.length; return baseHeight + rangeCount * 56 + 40; }; const addRange = useCallback((metricIndex) => { setRanges(prev => { const newRanges = [...prev]; newRanges[metricIndex] = { ...newRanges[metricIndex], ranges: [...newRanges[metricIndex].ranges, { min: 0, max: 100, status: 1 }] }; return newRanges; }); setHasChanges(true); }, []); const deleteRange = useCallback((metricIndex, rangeIndex) => { setRanges(prev => { const newRanges = [...prev]; newRanges[metricIndex] = { ...newRanges[metricIndex], ranges: newRanges[metricIndex].ranges.filter((_, i) => i !== rangeIndex) }; return newRanges; }); setHasChanges(true); }, []); const saveChanges = useCallback(async () => { try { setLoading(true); await axios.post(`${import.meta.env.VITE_BACK_URL}/api/ranges/update`, ranges); setHasChanges(false); setSuccess(true); setTimeout(() => setSuccess(false), 3000); if (onSave) { onSave({ hasChanges: false, saveChanges: saveChanges }); } return true; } catch (err) { console.error('Ошибка при сохранении:', err); setError('Ошибка при сохранении'); return false; } finally { setLoading(false); } }, [ranges, onSave]); const addNewMetric = useCallback(() => { if (!newMetricName.trim()) { setError('Введите название метрики'); return; } if (ranges.some(r => r.name === newMetricName)) { setError('Метрика с таким именем уже существует'); return; } setRanges(prev => [...prev, { name: newMetricName, ranges: [{ min: 0, max: 100, status: 1 }] }]); setNewMetricName(''); setHasChanges(true); setError(null); }, [newMetricName, ranges]); const filtered = useMemo(() => { return filter ? ranges.filter(r => r.name.toLowerCase().includes(filter.toLowerCase())) : ranges; }, [filter, ranges]); useEffect(() => { if (onSave) { onSave({ hasChanges, saveChanges }); } }, [hasChanges, onSave, saveChanges]); return ( {loading && ( )} setError(null)}> {error} setSuccess(false)}> Изменения успешно сохранены! {!loading && ( <> setFilter(e.target.value)} variant="standard" /> setNewMetricName(e.target.value)} fullWidth variant="standard" /> {filtered.map((metric, index) => ( ))} {filtered.length === 0 && ( {filter ? 'Ничего не найдено' : 'Нет метрик для отображения'} )} )} ); }; export default React.memo(MetricRangeEditor);