feat: Introduce simplified filter management mode in FilterManager with enhanced filter handling and deployment capabilities
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m21s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 6m21s
This commit is contained in:
@@ -55,6 +55,14 @@ function FilterManager() {
|
|||||||
const [editingServer, setEditingServer] = useState(null);
|
const [editingServer, setEditingServer] = useState(null);
|
||||||
const [editServerError, setEditServerError] = useState('');
|
const [editServerError, setEditServerError] = useState('');
|
||||||
|
|
||||||
|
// Новое состояние для режима
|
||||||
|
const [mode, setMode] = useState('simple'); // 'simple' | 'advanced'
|
||||||
|
const [simpleFilters, setSimpleFilters] = useState([]);
|
||||||
|
const [simpleAddFilter, setSimpleAddFilter] = useState({ community: '', gateway: '', description: '' });
|
||||||
|
const [simpleError, setSimpleError] = useState('');
|
||||||
|
const [simpleSuccess, setSimpleSuccess] = useState('');
|
||||||
|
const [simpleLoading, setSimpleLoading] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchServers();
|
fetchServers();
|
||||||
}, []);
|
}, []);
|
||||||
@@ -542,6 +550,55 @@ function FilterManager() {
|
|||||||
const enabledServers = servers.filter(s => s.enabled).length;
|
const enabledServers = servers.filter(s => s.enabled).length;
|
||||||
const serversWithFilters = servers.filter(s => s.id === selectedServer?.id ? serverFilters.length > 0 : false).length;
|
const serversWithFilters = servers.filter(s => s.id === selectedServer?.id ? serverFilters.length > 0 : false).length;
|
||||||
|
|
||||||
|
// Функция для применения фильтров из упрощённого режима на все сервера
|
||||||
|
const handleDeploySimpleFiltersToAll = async () => {
|
||||||
|
if (servers.length === 0) {
|
||||||
|
setSimpleError('Нет серверов для применения фильтров.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (simpleFilters.length === 0) {
|
||||||
|
setSimpleError('Нет фильтров для применения.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSimpleLoading(true);
|
||||||
|
try {
|
||||||
|
// Копируем фильтры и генерируем конфиг для каждого сервера
|
||||||
|
for (const server of servers) {
|
||||||
|
await axios.post(`${API_URL}/server-filters/${server.id}`, { filters: simpleFilters });
|
||||||
|
// Генерируем конфиг
|
||||||
|
const response = await axios.post(`${API_URL}/server-filters/generate-config`, { filters: simpleFilters });
|
||||||
|
if (response.data && response.data.config) {
|
||||||
|
await axios.post(`${API_URL}/server-configs/${server.id}`, { config: response.data.config });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setSimpleSuccess('Фильтры и конфигурация успешно применены ко всем серверам!');
|
||||||
|
setTimeout(() => setSimpleSuccess(''), 4000);
|
||||||
|
} catch (error) {
|
||||||
|
setSimpleError('Ошибка при применении фильтров: ' + (error.response?.data || error.message));
|
||||||
|
} finally {
|
||||||
|
setSimpleLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Функции для работы с фильтрами в упрощённом режиме
|
||||||
|
const handleSimpleAddFilter = () => {
|
||||||
|
if (!simpleAddFilter.community.trim() || !simpleAddFilter.gateway.trim()) {
|
||||||
|
setSimpleError('Поля Community и Gateway должны быть заполнены.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSimpleFilters([...simpleFilters, { ...simpleAddFilter }]);
|
||||||
|
setSimpleAddFilter({ community: '', gateway: '', description: '' });
|
||||||
|
setSimpleError('');
|
||||||
|
};
|
||||||
|
const handleSimpleDeleteFilter = (filter) => {
|
||||||
|
setSimpleFilters(simpleFilters.filter(f => f !== filter));
|
||||||
|
};
|
||||||
|
const handleSimpleEditFilter = (index, field, value) => {
|
||||||
|
const updated = [...simpleFilters];
|
||||||
|
updated[index][field] = value;
|
||||||
|
setSimpleFilters(updated);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container-xl">
|
<div className="container-xl">
|
||||||
<div className="page-header d-print-none mb-4">
|
<div className="page-header d-print-none mb-4">
|
||||||
@@ -1098,6 +1155,123 @@ function FilterManager() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* Модальное окно упрощённого режима */}
|
||||||
|
{mode === 'simple' && (
|
||||||
|
<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">Упрощённый режим управления фильтрами</h5>
|
||||||
|
<button type="button" className="btn-close" onClick={() => setMode('advanced')}></button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-body">
|
||||||
|
{simpleError && (
|
||||||
|
<div className="alert alert-danger py-2" role="alert">{simpleError}</div>
|
||||||
|
)}
|
||||||
|
{simpleSuccess && (
|
||||||
|
<div className="alert alert-success py-2" role="alert">{simpleSuccess}</div>
|
||||||
|
)}
|
||||||
|
<div className="table-responsive mb-3">
|
||||||
|
<table className="table card-table table-vcenter table-nowrap mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style={{ width: '25%' }}>COMMUNITY</th>
|
||||||
|
<th style={{ width: '25%' }}>GATEWAY</th>
|
||||||
|
<th style={{ width: '35%' }}>ОПИСАНИЕ</th>
|
||||||
|
<th style={{ width: '15%' }} className="text-end">ДЕЙСТВИЯ</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{simpleFilters.length === 0 ? (
|
||||||
|
<tr><td colSpan={4} className="text-center text-muted">Нет фильтров</td></tr>
|
||||||
|
) : (
|
||||||
|
simpleFilters.map((filter, idx) => (
|
||||||
|
<tr key={idx}>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
value={filter.community}
|
||||||
|
onChange={e => handleSimpleEditFilter(idx, 'community', e.target.value)}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
value={filter.gateway}
|
||||||
|
onChange={e => handleSimpleEditFilter(idx, 'gateway', e.target.value)}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
value={filter.description || ''}
|
||||||
|
onChange={e => handleSimpleEditFilter(idx, 'description', e.target.value)}
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
<td className="text-end">
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-danger btn-icon btn-sm"
|
||||||
|
onClick={() => handleSimpleDeleteFilter(filter)}
|
||||||
|
title="Удалить фильтр"
|
||||||
|
>
|
||||||
|
<IconTrash size={16} />
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div className="row g-2 align-items-end mb-3">
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Community"
|
||||||
|
value={simpleAddFilter.community}
|
||||||
|
onChange={e => setSimpleAddFilter({ ...simpleAddFilter, community: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-4">
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Gateway"
|
||||||
|
value={simpleAddFilter.gateway}
|
||||||
|
onChange={e => setSimpleAddFilter({ ...simpleAddFilter, gateway: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-3">
|
||||||
|
<input
|
||||||
|
className="form-control"
|
||||||
|
placeholder="Описание (необязательно)"
|
||||||
|
value={simpleAddFilter.description}
|
||||||
|
onChange={e => setSimpleAddFilter({ ...simpleAddFilter, description: e.target.value })}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-1">
|
||||||
|
<button className="btn btn-primary w-100" onClick={handleSimpleAddFilter}>
|
||||||
|
<IconPlus />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-success"
|
||||||
|
onClick={handleDeploySimpleFiltersToAll}
|
||||||
|
disabled={simpleFilters.length === 0 || simpleLoading}
|
||||||
|
>
|
||||||
|
<IconRocket className="me-2" /> Раскатать фильтры на все серверы
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div className="modal-footer">
|
||||||
|
<button type="button" className="btn btn-secondary" onClick={() => setMode('advanced')}>
|
||||||
|
Закрыть
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user