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);