feat: Enhance FilterManager with advanced and simple preview modes for configuration, including improved modal display and copy functionality
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m30s

This commit is contained in:
2025-07-14 15:41:29 +07:00
parent c62666bd42
commit 3b852464d6
+117 -3
View File
@@ -34,6 +34,7 @@ function FilterManager() {
// Состояние для предварительного просмотра конфигурации
const [previewModalOpen, setPreviewModalOpen] = useState(false);
const [generatedConfig, setGeneratedConfig] = useState('');
const [previewMode, setPreviewMode] = useState(''); // 'simple' | 'advanced'
// Состояния для управления серверами
const [servers, setServers] = useState([]);
@@ -499,6 +500,7 @@ function FilterManager() {
const handlePreviewConfig = async () => {
const config = await generateMikrotikConfig();
setGeneratedConfig(config);
setPreviewMode('advanced');
setPreviewModalOpen(true);
};
@@ -1139,7 +1141,11 @@ function FilterManager() {
<PreviewConfigModal
show={previewModalOpen}
config={generatedConfig}
onClose={() => setPreviewModalOpen(false)}
mode={previewMode}
onClose={() => {
setPreviewModalOpen(false);
setPreviewMode('');
}}
onCopy={copyConfigToClipboard}
/>
@@ -1343,6 +1349,70 @@ function FilterManager() {
</div>
</div>
<div className="btn-list">
<button
className="btn btn-outline-primary"
onClick={async () => {
try {
const response = await axios.post(`${API_URL}/server-filters/generate-config`, { filters: simpleFilters });
if (response.data && response.data.config) {
setGeneratedConfig(response.data.config);
setPreviewMode('simple');
setPreviewModalOpen(true);
} else {
setSimpleError('Ошибка генерации конфигурации');
}
} catch (error) {
setSimpleError('Ошибка при генерации конфигурации: ' + (error.response?.data || error.message));
}
}}
disabled={simpleFilters.length === 0 || simpleLoading}
title={simpleFilters.length === 0 ? "Сначала добавьте фильтры" : "Предварительный просмотр конфигурации"}
>
<IconEye className="me-2" /> Предварительный просмотр
</button>
<button
className="btn btn-outline-primary"
onClick={async () => {
try {
const response = await axios.post(`${API_URL}/server-filters/generate-config`, { filters: simpleFilters });
if (response.data && response.data.config) {
const config = response.data.config;
if (navigator.clipboard) {
try {
await navigator.clipboard.writeText(config);
setSimpleSuccess('Конфигурация скопирована в буфер обмена!');
setTimeout(() => setSimpleSuccess(''), 3000);
} catch (err) {
setSimpleError('Не удалось скопировать конфигурацию');
}
} else {
// Fallback для старых браузеров
const textArea = document.createElement('textarea');
textArea.value = config;
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
setSimpleSuccess('Конфигурация скопирована в буфер обмена!');
setTimeout(() => setSimpleSuccess(''), 3000);
} catch (err) {
setSimpleError('Не удалось скопировать конфигурацию');
}
document.body.removeChild(textArea);
}
} else {
setSimpleError('Ошибка генерации конфигурации');
}
} catch (error) {
setSimpleError('Ошибка при генерации конфигурации: ' + (error.response?.data || error.message));
}
}}
disabled={simpleFilters.length === 0 || simpleLoading}
title={simpleFilters.length === 0 ? "Сначала добавьте фильтры" : "Копировать конфигурацию в буфер"}
>
<IconCopy className="me-2" /> Копировать конфигурацию
</button>
<button
className="btn btn-outline-success"
onClick={handleDeploySimpleFiltersToAll}
@@ -1589,23 +1659,67 @@ function DeleteFilterModal({ show, filter, onDelete, onClose }) {
}
// Модальное окно предварительного просмотра конфигурации
function PreviewConfigModal({ show, config, onClose, onCopy }) {
function PreviewConfigModal({ show, config, mode, onClose, onCopy }) {
if (!show) return null;
const getModalTitle = () => {
if (mode === 'simple') {
return 'Предварительный просмотр конфигурации MikroTik (упрощённый режим)';
} else if (mode === 'advanced') {
return 'Предварительный просмотр конфигурации MikroTik (расширенный режим)';
}
return 'Предварительный просмотр конфигурации MikroTik';
};
const getModeBadge = () => {
if (mode === 'simple') {
return <span className="badge bg-blue-lt text-blue ms-2">Упрощённый режим</span>;
} else if (mode === 'advanced') {
return <span className="badge bg-green-lt text-green ms-2">Расширенный режим</span>;
}
return null;
};
const getConfigInfo = () => {
if (config.includes('// Сначала выберите сервер') || config.includes('// Нет фильтров для генерации конфигурации')) {
return <div className="alert alert-warning mb-3">Нет данных для генерации конфигурации</div>;
}
// Подсчитываем количество фильтров из конфигурации
const communityMatches = config.match(/bgp-communities includes [^)]+/g);
const filterCount = communityMatches ? communityMatches.length : 0;
if (filterCount > 0) {
return (
<div className="alert alert-info mb-3">
<IconFilter className="me-2" />
Конфигурация сгенерирована на основе {filterCount} фильтр(ов)
</div>
);
}
return null;
};
return (
<div className="modal show d-block" style={{ backgroundColor: 'rgba(0,0,0,0.5)' }}>
<div className="modal-dialog modal-lg">
<div className="modal-content">
<div className="modal-header">
<h5 className="modal-title">Предварительный просмотр конфигурации MikroTik</h5>
<h5 className="modal-title">
{getModalTitle()}
{getModeBadge()}
</h5>
<button type="button" className="btn-close" onClick={onClose}></button>
</div>
<div className="modal-body">
{getConfigInfo()}
<div className="mb-3">
<div className="btn-list">
<button
className="btn btn-outline-primary"
onClick={onCopy}
disabled={config.includes('// Сначала выберите сервер') || config.includes('// Нет фильтров для генерации конфигурации')}
>
<IconCopy className="me-2" />
Копировать в буфер обмена