refactor: Revise FilterManager layout to enhance server selection interface with improved button actions and card design, streamlining user interaction and visual presentation.
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m58s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m58s
This commit is contained in:
+92
-115
@@ -942,10 +942,10 @@ function FilterManager() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div className="row g-3">
|
<>
|
||||||
{/* Левая колонка: список серверов */}
|
{/* Тулбар: поиск + действия для выбранного сервера */}
|
||||||
<div className="col-12 col-xl-7">
|
<div className="d-flex flex-column flex-md-row gap-2 align-items-md-center mb-3">
|
||||||
<div className="input-icon mb-2">
|
<div className="input-icon flex-grow-1" style={{ minWidth: '240px' }}>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
className="form-control"
|
className="form-control"
|
||||||
@@ -957,131 +957,108 @@ function FilterManager() {
|
|||||||
<IconSearch />
|
<IconSearch />
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="list-group list-group-flush border rounded">
|
<div className="d-flex flex-wrap gap-2">
|
||||||
{activeServers.length > 0 && (
|
|
||||||
<div className="list-group-header px-3 pt-3 pb-2 text-uppercase text-muted small">Активные</div>
|
|
||||||
)}
|
|
||||||
{activeServers.map(server => (
|
|
||||||
<button
|
|
||||||
key={server.id}
|
|
||||||
className={`list-group-item list-group-item-action d-flex align-items-center ${selectedServer?.id === server.id ? 'active' : ''}`}
|
|
||||||
onClick={() => setSelectedServer(server)}
|
|
||||||
>
|
|
||||||
<span className="avatar bg-primary-lt me-3">
|
|
||||||
<IconServer size={16} />
|
|
||||||
</span>
|
|
||||||
<div className="flex-grow-1 text-start">
|
|
||||||
<div className="fw-bold">{server.name}</div>
|
|
||||||
{server.description && (
|
|
||||||
<div className="text-muted small text-truncate" style={{ maxWidth: '240px' }}>
|
|
||||||
{server.description}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<span className="badge bg-green-lt text-green">Активен</span>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
{inactiveServers.length > 0 && (
|
|
||||||
<div className="list-group-header px-3 pt-3 pb-2 text-uppercase text-muted small">Неактивные</div>
|
|
||||||
)}
|
|
||||||
{inactiveServers.map(server => (
|
|
||||||
<button
|
|
||||||
key={server.id}
|
|
||||||
className={`list-group-item list-group-item-action d-flex align-items-center ${selectedServer?.id === server.id ? 'active' : ''}`}
|
|
||||||
onClick={() => setSelectedServer(server)}
|
|
||||||
>
|
|
||||||
<span className="avatar bg-secondary-lt me-3">
|
|
||||||
<IconServer size={16} />
|
|
||||||
</span>
|
|
||||||
<div className="flex-grow-1 text-start">
|
|
||||||
<div className="fw-bold">{server.name}</div>
|
|
||||||
{server.description && (
|
|
||||||
<div className="text-muted small text-truncate" style={{ maxWidth: '240px' }}>
|
|
||||||
{server.description}
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<span className="badge bg-secondary-lt text-secondary">Выкл</span>
|
|
||||||
</button>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
<div className="mt-2 text-end">
|
|
||||||
<button
|
<button
|
||||||
className="btn btn-link px-0 text-decoration-none"
|
className="btn btn-outline-primary"
|
||||||
onClick={() => setServerListModalOpen(true)}
|
onClick={handlePreviewConfig}
|
||||||
|
disabled={!selectedServer || serverFilters.length === 0}
|
||||||
>
|
>
|
||||||
<IconSettings size={16} className="me-1" />
|
<IconEye className="icon" />
|
||||||
Управление серверами
|
Просмотр
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-outline-primary"
|
||||||
|
onClick={copyConfigToClipboard}
|
||||||
|
disabled={!selectedServer || serverFilters.length === 0}
|
||||||
|
>
|
||||||
|
<IconCopy className="icon" />
|
||||||
|
Копировать
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="btn btn-success"
|
||||||
|
onClick={() => selectedServer && handleSaveServerConfig(selectedServer.id)}
|
||||||
|
disabled={!selectedServer || loading || serverFilters.length === 0}
|
||||||
|
>
|
||||||
|
<IconDeviceFloppy className="icon" />
|
||||||
|
Сохранить
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Правая колонка: детали выбранного сервера / подсказка */}
|
{/* Горизонтальный список серверов */}
|
||||||
<div className="col-12 col-xl-5">
|
<div className="d-flex flex-wrap gap-2 mb-2">
|
||||||
{selectedServer ? (
|
{activeServers.map(server => (
|
||||||
<div className="card card-sm">
|
<div
|
||||||
<div className="card-body">
|
key={server.id}
|
||||||
<div className="d-flex align-items-start mb-2">
|
className={`card card-sm cursor-pointer ${selectedServer?.id === server.id ? 'border-primary' : ''}`}
|
||||||
<span className="avatar bg-primary-lt me-3">
|
style={{ minWidth: '220px', maxWidth: '260px' }}
|
||||||
<IconServer size={18} />
|
onClick={() => setSelectedServer(server)}
|
||||||
</span>
|
>
|
||||||
<div className="flex-grow-1">
|
<div className="card-body d-flex align-items-start gap-2">
|
||||||
<div className="d-flex align-items-center gap-2">
|
<span className="avatar bg-primary-lt">
|
||||||
<span className="fw-bold">{selectedServer.name}</span>
|
<IconServer size={16} />
|
||||||
<span className="badge bg-green-lt text-green">Активен</span>
|
</span>
|
||||||
</div>
|
<div className="flex-grow-1">
|
||||||
{selectedServer.description && (
|
<div className="d-flex align-items-center gap-2">
|
||||||
<div className="text-muted small mt-1">{selectedServer.description}</div>
|
<div className="fw-bold text-truncate" title={server.name}>{server.name}</div>
|
||||||
)}
|
<span className="badge bg-green-lt text-green">Активен</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
{server.description && (
|
||||||
|
<div className="text-muted small text-truncate" title={server.description}>
|
||||||
<div className="d-flex flex-wrap gap-3 text-muted small mb-2">
|
{server.description}
|
||||||
<span>Фильтров: <span className="badge bg-blue-lt text-blue">{serverFilters.length}</span></span>
|
</div>
|
||||||
<span>Поиск: <code className="text-blue">{filterSearch || '—'}</code></span>
|
)}
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="btn-list">
|
|
||||||
<button
|
|
||||||
className="btn btn-outline-primary"
|
|
||||||
onClick={handlePreviewConfig}
|
|
||||||
disabled={serverFilters.length === 0}
|
|
||||||
>
|
|
||||||
<IconEye className="icon" />
|
|
||||||
Просмотр
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="btn btn-outline-primary"
|
|
||||||
onClick={copyConfigToClipboard}
|
|
||||||
disabled={serverFilters.length === 0}
|
|
||||||
>
|
|
||||||
<IconCopy className="icon" />
|
|
||||||
Копировать
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
className="btn btn-success"
|
|
||||||
onClick={() => handleSaveServerConfig(selectedServer.id)}
|
|
||||||
disabled={loading || serverFilters.length === 0}
|
|
||||||
>
|
|
||||||
<IconDeviceFloppy className="icon" />
|
|
||||||
Сохранить
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
) : (
|
))}
|
||||||
<div className="card card-sm">
|
{inactiveServers.map(server => (
|
||||||
<div className="card-body d-flex align-items-start">
|
<div
|
||||||
<IconAlertTriangle className="me-3 text-warning" />
|
key={server.id}
|
||||||
<div>
|
className={`card card-sm cursor-pointer ${selectedServer?.id === server.id ? 'border-primary' : ''}`}
|
||||||
<div className="fw-bold">Сначала выберите сервер</div>
|
style={{ minWidth: '220px', maxWidth: '260px' }}
|
||||||
<div className="text-muted">Нажмите на сервер слева, чтобы открыть его фильтры.</div>
|
onClick={() => setSelectedServer(server)}
|
||||||
|
>
|
||||||
|
<div className="card-body d-flex align-items-start gap-2">
|
||||||
|
<span className="avatar bg-secondary-lt">
|
||||||
|
<IconServer size={16} />
|
||||||
|
</span>
|
||||||
|
<div className="flex-grow-1">
|
||||||
|
<div className="d-flex align-items-center gap-2">
|
||||||
|
<div className="fw-bold text-muted text-truncate" title={server.name}>{server.name}</div>
|
||||||
|
<span className="badge bg-secondary-lt text-secondary">Выкл</span>
|
||||||
|
</div>
|
||||||
|
{server.description && (
|
||||||
|
<div className="text-muted small text-truncate" title={server.description}>
|
||||||
|
{server.description}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
<div className="d-flex justify-content-between align-items-center mt-2">
|
||||||
|
<div className="text-muted small">
|
||||||
|
{selectedServer ? (
|
||||||
|
<>
|
||||||
|
Текущий сервер: <span className="fw-bold">{selectedServer.name}</span>
|
||||||
|
<span className="badge bg-blue-lt text-blue ms-2">{serverFilters.length} фильтров</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
'Выберите сервер для работы с фильтрами'
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
className="btn btn-link px-0 text-decoration-none"
|
||||||
|
onClick={() => setServerListModalOpen(true)}
|
||||||
|
>
|
||||||
|
<IconSettings size={16} className="me-1" />
|
||||||
|
Управление серверами
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user