refactor(FilterManager): simplify notification handling and improve layout structure for enhanced user experience
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m0s

This commit is contained in:
2026-02-16 21:19:05 +07:00
parent 8ad4f23a49
commit 5af5abb02d
+60 -22
View File
@@ -1740,14 +1740,7 @@ function FilterManager() {
return (
<div className="page-wrapper">
<PageHeader
title="Управление фильтрами frouting"
icon={<IconFilter size={24} />}
/>
<div className="page-body">
<div className="container-xl">
<>
{/* Уведомления */}
{error && (
<div className="alert alert-danger alert-dismissible" role="alert">
@@ -1764,6 +1757,13 @@ function FilterManager() {
</div>
)}
<PageHeader
title="Управление фильтрами frouting"
icon={<IconFilter size={24} />}
/>
<div className="row">
<div className="col-12">
{/* Селектор серверов на одной странице (Tabler UI) */}
<div className="card mb-3">
<div className="card-header">
@@ -2054,10 +2054,14 @@ function FilterManager() {
<div className="alert alert-info mb-3">
<div className="d-flex align-items-center">
<IconGripVertical size={18} className="me-2" />
<span>Перетащите строки за иконку <IconGripVertical size={14} className="mx-1" /> чтобы изменить приоритет</span>
<span>
Перетащите строки за иконку{' '}
<IconGripVertical size={14} className="mx-1" /> чтобы изменить приоритет
</span>
</div>
<div className="text-muted small mt-1">
<strong>Важно:</strong> В RouterOS правила внизу списка имеют <strong>высший приоритет</strong> (переопределяют верхние)
<strong>Важно:</strong> В RouterOS правила внизу списка имеют{' '}
<strong>высший приоритет</strong> (переопределяют верхние)
</div>
</div>
<DndContext
@@ -2080,27 +2084,58 @@ function FilterManager() {
/>
</th>
<th style={{ width: '60px' }}>#</th>
<th style={{ width: '22%' }} className="cursor-pointer" onClick={() => toggleSort('community')}>
<th
style={{ width: '22%' }}
className="cursor-pointer"
onClick={() => toggleSort('community')}
>
<span className="me-1">COMMUNITY</span>
{sortBy === 'community' && (sortDir === 'asc' ? <IconSortAscending size={16} /> : <IconSortDescending size={16} />)}
{sortBy === 'community' &&
(sortDir === 'asc' ? (
<IconSortAscending size={16} />
) : (
<IconSortDescending size={16} />
))}
</th>
<th style={{ width: '22%' }} className="cursor-pointer" onClick={() => toggleSort('gateway')}>
<th
style={{ width: '22%' }}
className="cursor-pointer"
onClick={() => toggleSort('gateway')}
>
<span className="me-1">GATEWAY</span>
{sortBy === 'gateway' && (sortDir === 'asc' ? <IconSortAscending size={16} /> : <IconSortDescending size={16} />)}
{sortBy === 'gateway' &&
(sortDir === 'asc' ? (
<IconSortAscending size={16} />
) : (
<IconSortDescending size={16} />
))}
</th>
<th style={{ width: '30%' }} className="cursor-pointer" onClick={() => toggleSort('description')}>
<th
style={{ width: '30%' }}
className="cursor-pointer"
onClick={() => toggleSort('description')}
>
<span className="me-1">ОПИСАНИЕ</span>
{sortBy === 'description' && (sortDir === 'asc' ? <IconSortAscending size={16} /> : <IconSortDescending size={16} />)}
{sortBy === 'description' &&
(sortDir === 'asc' ? (
<IconSortAscending size={16} />
) : (
<IconSortDescending size={16} />
))}
</th>
<th style={{ width: '120px' }} className="text-end">
ДЕЙСТВИЯ
</th>
<th style={{ width: '120px' }} className="text-end">ДЕЙСТВИЯ</th>
</tr>
</thead>
<SortableContext
items={serverFilters.filter(f => f != null).map(f => filterKey(f))}
items={serverFilters.filter((f) => f != null).map((f) => filterKey(f))}
strategy={verticalListSortingStrategy}
>
<tbody>
{serverFilters.filter(f => f != null).map((filter, index) => {
{serverFilters
.filter((f) => f != null)
.map((filter, index) => {
const key = filterKey(filter);
const isSelected = selectedFilterKeys.has(key);
// Показываем только если совпадает с поиском
@@ -2118,7 +2153,7 @@ function FilterManager() {
key={key}
filter={filter}
index={index}
totalCount={serverFilters.filter(f => f != null).length}
totalCount={serverFilters.filter((f) => f != null).length}
filterKey={key}
isSelected={isSelected}
onToggleSelect={() => toggleSelectFilter(key)}
@@ -2145,7 +2180,10 @@ function FilterManager() {
<button className="btn btn-outline-danger btn-sm" onClick={deleteSelectedFilters}>
<IconTrash className="me-2" /> Удалить выбранные
</button>
<button className="btn btn-outline-secondary btn-sm" onClick={() => setSelectedFilterKeys(new Set())}>
<button
className="btn btn-outline-secondary btn-sm"
onClick={() => setSelectedFilterKeys(new Set())}
>
<IconX className="me-2" /> Отменить выбор
</button>
</div>
@@ -2635,7 +2673,7 @@ function FilterManager() {
onCancel={() => setConfirmState({ open: false, text: '', onConfirm: null })}
onConfirm={() => confirmState.onConfirm?.()}
/>
</div>
</>
);
}