import React, { useState, useEffect, useCallback } from 'react';
import {
TextField, Box, Typography, IconButton, Divider,
CircularProgress, Alert, Collapse, Tooltip, Button,
Card, CardContent, Chip, Dialog, DialogTitle,
DialogContent, DialogActions, Snackbar, Table,
TableBody, TableCell, TableContainer, TableHead,
TableRow, Paper, Badge
} from '@mui/material';
import RefreshIcon from '@mui/icons-material/Refresh';
import SearchIcon from '@mui/icons-material/Search';
import EditIcon from '@mui/icons-material/Edit';
import SaveIcon from '@mui/icons-material/Save';
import WarningIcon from '@mui/icons-material/Warning';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import axios from 'axios';
const FormulaItem = React.memo(({ formula, onEdit }) => {
const getMetricStatusColor = (found) => {
return found ? 'success' : 'error';
};
const formatValue = (value) => {
if (value === undefined) return 'N/A';
return value.toFixed(2);
};
return (
{/* Заголовок с ID и статусом метрик */}
{formula.name}
ID: {formula.id}
}
onClick={() => onEdit(formula)}
variant="outlined"
size="small"
>
Редактировать
{/* Описание */}
{formula.description}
{/* Метрики */}
Метрики в формуле:
{formula.metadata?.missingMetrics > 0 && (
)}
Метрика
Описание
Значение
Статус
{formula.enrichedMetrics?.map((metric, index) => (
{metric.originalName}
{metric.prometheusName}
{metric.description}
{formatValue(metric.currentValue)}
: }
label={metric.found ? 'Найдена' : 'Не найдена'}
color={getMetricStatusColor(metric.found)}
size="small"
variant={metric.found ? "filled" : "outlined"}
/>
))}
{/* Формула */}
Формула с описанием метрик:
{formula.humanReadableFormula}
{/* Веса */}
Веса (warr):
{formula.values?.warr?.map((weight, index) => (
))}
);
});
const EditFormulaDialog = ({ open, formula, onClose, onSave }) => {
const [editedFormula, setEditedFormula] = useState('');
useEffect(() => {
if (formula) {
setEditedFormula(formula.formula || '');
}
}, [formula]);
const handleSave = () => {
if (formula && editedFormula.trim()) {
onSave(formula.id, editedFormula.trim());
}
};
return (
);
};
const FormulaEditor = () => {
const [formulas, setFormulas] = useState([]);
const [filter, setFilter] = useState('');
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const [refreshing, setRefreshing] = useState(false);
const [editingFormula, setEditingFormula] = useState(null);
const [saveLoading, setSaveLoading] = useState(false);
const [snackbar, setSnackbar] = useState({ open: false, message: '', severity: 'success' });
const showSnackbar = (message, severity = 'success') => {
setSnackbar({ open: true, message, severity });
};
const loadFormulas = useCallback(async () => {
try {
setLoading(true);
setError(null);
const response = await axios.get('http://192.168.2.39:3000/api/enriched-formulas');
if (Array.isArray(response.data)) {
setFormulas(response.data);
showSnackbar(`Загружено ${response.data.length} формул`);
} else {
throw new Error('Некорректный формат данных');
}
} catch (err) {
console.error('Ошибка при загрузке формул:', err);
const errorMessage = axios.isAxiosError(err)
? `Ошибка сервера: ${err.response?.status} - ${err.response?.data?.message || err.message}`
: `Ошибка загрузки: ${err.message}`;
setError(errorMessage);
showSnackbar(errorMessage, 'error');
} finally {
setLoading(false);
setRefreshing(false);
}
}, []);
const handleEditFormula = (formula) => {
setEditingFormula(formula);
};
const handleSaveFormula = async (formulaId, newFormula) => {
try {
setSaveLoading(true);
await axios.post(`http://192.168.2.39:3000/api/formula/${formulaId}/update`, {
formula: newFormula
});
setFormulas(prev => prev.map(formula =>
formula.id === formulaId
? { ...formula, formula: newFormula }
: formula
));
setEditingFormula(null);
showSnackbar('Формула успешно обновлена!');
} catch (err) {
console.error('Ошибка при сохранении формулы:', err);
showSnackbar('Ошибка при сохранении формулы', 'error');
} finally {
setSaveLoading(false);
}
};
const refreshData = useCallback(() => {
setRefreshing(true);
loadFormulas();
}, [loadFormulas]);
const filteredFormulas = formulas.filter(formula =>
formula.id.toLowerCase().includes(filter.toLowerCase()) ||
formula.name.toLowerCase().includes(filter.toLowerCase()) ||
formula.description.toLowerCase().includes(filter.toLowerCase()) ||
formula.formula.toLowerCase().includes(filter.toLowerCase())
);
const totalMetrics = formulas.reduce((sum, formula) => sum + (formula.metadata?.totalMetrics || 0), 0);
const foundMetrics = formulas.reduce((sum, formula) => sum + (formula.metadata?.foundMetrics || 0), 0);
const missingMetrics = formulas.reduce((sum, formula) => sum + (formula.metadata?.missingMetrics || 0), 0);
useEffect(() => {
loadFormulas();
}, [loadFormulas]);
return (
{/* Загрузка */}
{(loading || refreshing) && (
)}
{/* Ошибки */}
Повторить
}
>
{error}
{/* Панель управления */}
Редактор формул с метриками
}
disabled={refreshing}
>
Обновить
{/* Статистика */}
{missingMetrics > 0 && (
)}
{/* Поиск */}
setFilter(e.target.value)}
variant="outlined"
placeholder="Введите ID, название или описание..."
size="small"
/>
{/* Список формул */}
{filteredFormulas.map((formula) => (
))}
{filteredFormulas.length === 0 && !loading && (
{filter ? 'Формулы не найдены' : 'Нет загруженных формул'}
)}
{/* Статус бар */}
Всего формул: {formulas.length} • Отфильтровано: {filteredFormulas.length}
Метрики: {foundMetrics}/{totalMetrics} найдено
{/* Диалог редактирования */}
setEditingFormula(null)}
onSave={handleSaveFormula}
/>
{/* Уведомления */}
setSnackbar({ ...snackbar, open: false })}
>
setSnackbar({ ...snackbar, open: false })}
severity={snackbar.severity}
>
{snackbar.message}
);
};
export default React.memo(FormulaEditor);