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} {/* Описание */} {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 ( Редактирование формулы: {formula?.name} {formula?.description} Доступные переменные: setEditedFormula(e.target.value)} multiline rows={6} fullWidth variant="outlined" placeholder="Введите формулу..." sx={{ mt: 2 }} /> ); }; 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} {/* Панель управления */} Редактор формул с метриками {/* Статистика */} {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);