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
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 2m0s
This commit is contained in:
@@ -1740,14 +1740,7 @@ function FilterManager() {
|
|||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-wrapper">
|
<>
|
||||||
<PageHeader
|
|
||||||
title="Управление фильтрами frouting"
|
|
||||||
icon={<IconFilter size={24} />}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<div className="page-body">
|
|
||||||
<div className="container-xl">
|
|
||||||
{/* Уведомления */}
|
{/* Уведомления */}
|
||||||
{error && (
|
{error && (
|
||||||
<div className="alert alert-danger alert-dismissible" role="alert">
|
<div className="alert alert-danger alert-dismissible" role="alert">
|
||||||
@@ -1764,6 +1757,13 @@ function FilterManager() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
<PageHeader
|
||||||
|
title="Управление фильтрами frouting"
|
||||||
|
icon={<IconFilter size={24} />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-12">
|
||||||
{/* Селектор серверов на одной странице (Tabler UI) */}
|
{/* Селектор серверов на одной странице (Tabler UI) */}
|
||||||
<div className="card mb-3">
|
<div className="card mb-3">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
@@ -2054,10 +2054,14 @@ function FilterManager() {
|
|||||||
<div className="alert alert-info mb-3">
|
<div className="alert alert-info mb-3">
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center">
|
||||||
<IconGripVertical size={18} className="me-2" />
|
<IconGripVertical size={18} className="me-2" />
|
||||||
<span>Перетащите строки за иконку <IconGripVertical size={14} className="mx-1" /> чтобы изменить приоритет</span>
|
<span>
|
||||||
|
Перетащите строки за иконку{' '}
|
||||||
|
<IconGripVertical size={14} className="mx-1" /> чтобы изменить приоритет
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="text-muted small mt-1">
|
<div className="text-muted small mt-1">
|
||||||
<strong>Важно:</strong> В RouterOS правила внизу списка имеют <strong>высший приоритет</strong> (переопределяют верхние)
|
<strong>Важно:</strong> В RouterOS правила внизу списка имеют{' '}
|
||||||
|
<strong>высший приоритет</strong> (переопределяют верхние)
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<DndContext
|
<DndContext
|
||||||
@@ -2080,27 +2084,58 @@ function FilterManager() {
|
|||||||
/>
|
/>
|
||||||
</th>
|
</th>
|
||||||
<th style={{ width: '60px' }}>#</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>
|
<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>
|
||||||
<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>
|
<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>
|
||||||
<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>
|
<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>
|
||||||
<th style={{ width: '120px' }} className="text-end">ДЕЙСТВИЯ</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<SortableContext
|
<SortableContext
|
||||||
items={serverFilters.filter(f => f != null).map(f => filterKey(f))}
|
items={serverFilters.filter((f) => f != null).map((f) => filterKey(f))}
|
||||||
strategy={verticalListSortingStrategy}
|
strategy={verticalListSortingStrategy}
|
||||||
>
|
>
|
||||||
<tbody>
|
<tbody>
|
||||||
{serverFilters.filter(f => f != null).map((filter, index) => {
|
{serverFilters
|
||||||
|
.filter((f) => f != null)
|
||||||
|
.map((filter, index) => {
|
||||||
const key = filterKey(filter);
|
const key = filterKey(filter);
|
||||||
const isSelected = selectedFilterKeys.has(key);
|
const isSelected = selectedFilterKeys.has(key);
|
||||||
// Показываем только если совпадает с поиском
|
// Показываем только если совпадает с поиском
|
||||||
@@ -2118,7 +2153,7 @@ function FilterManager() {
|
|||||||
key={key}
|
key={key}
|
||||||
filter={filter}
|
filter={filter}
|
||||||
index={index}
|
index={index}
|
||||||
totalCount={serverFilters.filter(f => f != null).length}
|
totalCount={serverFilters.filter((f) => f != null).length}
|
||||||
filterKey={key}
|
filterKey={key}
|
||||||
isSelected={isSelected}
|
isSelected={isSelected}
|
||||||
onToggleSelect={() => toggleSelectFilter(key)}
|
onToggleSelect={() => toggleSelectFilter(key)}
|
||||||
@@ -2145,7 +2180,10 @@ function FilterManager() {
|
|||||||
<button className="btn btn-outline-danger btn-sm" onClick={deleteSelectedFilters}>
|
<button className="btn btn-outline-danger btn-sm" onClick={deleteSelectedFilters}>
|
||||||
<IconTrash className="me-2" /> Удалить выбранные
|
<IconTrash className="me-2" /> Удалить выбранные
|
||||||
</button>
|
</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" /> Отменить выбор
|
<IconX className="me-2" /> Отменить выбор
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -2635,7 +2673,7 @@ function FilterManager() {
|
|||||||
onCancel={() => setConfirmState({ open: false, text: '', onConfirm: null })}
|
onCancel={() => setConfirmState({ open: false, text: '', onConfirm: null })}
|
||||||
onConfirm={() => confirmState.onConfirm?.()}
|
onConfirm={() => confirmState.onConfirm?.()}
|
||||||
/>
|
/>
|
||||||
</div>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user