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
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m30s
This commit is contained in:
@@ -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" />
|
||||
Копировать в буфер обмена
|
||||
|
||||
Reference in New Issue
Block a user