refactor(SettingsPage): streamline loading and success/error message display for improved clarity and user experience
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m56s

This commit is contained in:
2026-02-17 15:29:48 +07:00
parent 7b92d1eb70
commit 17cd5a8f43
+53 -77
View File
@@ -246,82 +246,58 @@ export default function SettingsPage() {
if (loading) {
return (
<div className="page-wrapper">
<div className="page-body">
<div className="container-xl">
<PageHeader
title="Настройки интерфейса"
icon={<IconSettings size={28} />}
pretitle="Интерфейс"
/>
<div className="card">
<div className="card-body text-center py-5">
<div className="spinner-border text-primary" role="status" />
<p className="mt-2 mb-0 text-muted">Загрузка настроек</p>
</div>
</div>
<>
<PageHeader title="Настройки интерфейса" icon={<IconSettings size={24} />} pretitle="Интерфейс" />
<div className="card">
<div className="card-body text-center py-5">
<div className="spinner-border text-primary" role="status" />
<p className="mt-2 mb-0 text-muted">Загрузка настроек</p>
</div>
</div>
</div>
</>
);
}
return (
<div className="page-wrapper">
<div className="page-body">
<div className="container-xl">
<PageHeader
title="Настройки интерфейса"
icon={<IconSettings size={28} />}
pretitle="Интерфейс"
meta="WebSocket, DoH, пинг, PTR зона"
actions={
<div className="btn-list">
<button
type="button"
className="btn btn-primary"
onClick={onSave}
disabled={saving}
>
{saving && (
<span className="spinner-border spinner-border-sm me-2" />
)}
<IconDeviceFloppy size={18} className="me-1" />
Сохранить
</button>
</div>
}
/>
<>
{error && (
<ErrorAlert message={error} onClose={() => setError('')} />
)}
{success && (
<div className="alert alert-success alert-dismissible mb-3" role="alert">
<div className="d-flex">
<div className="flex-grow-1">{success}</div>
</div>
<button type="button" className="btn-close" onClick={() => setSuccess('')} aria-label="Закрыть" />
</div>
)}
{error && (
<div className="mb-3">
<ErrorAlert message={error} onClose={() => setError('')} />
</div>
)}
{success && (
<div
className="alert alert-success alert-dismissible mb-3"
role="alert"
<PageHeader
title="Настройки интерфейса"
icon={<IconSettings size={24} />}
pretitle="Интерфейс"
meta="WebSocket, DoH, пинг, PTR зона"
actions={
<div className="btn-list">
<button
type="button"
className="btn btn-primary"
onClick={onSave}
disabled={saving}
>
<div className="d-flex">
<div className="flex-grow-1">{success}</div>
</div>
<button
type="button"
className="btn-close"
onClick={() => setSuccess('')}
aria-label="Закрыть"
/>
</div>
)}
{saving && <span className="spinner-border spinner-border-sm me-2" />}
<IconDeviceFloppy size={18} className="me-1" />
Сохранить
</button>
</div>
}
/>
{/* Как в Tabler Settings: одна card, row g-0, col-md-3 border-end + col-md-9 */}
<div className="card">
<div className="row g-0">
{/* Левая колонка — навигация (list-group list-group-transparent) */}
<div className="col-12 col-md-3 border-end">
<div className="card-body">
<div className="row">
{/* Сайдбар навигации — как блок в общем стиле страниц */}
<div className="col-12 col-md-3 mb-4 mb-md-0">
<div className="card card-flat">
<div className="card-body">
<div className="input-icon mb-3">
<span className="input-icon-addon">
<IconSearch size={18} className="text-muted" />
@@ -360,14 +336,16 @@ export default function SettingsPage() {
</div>
))}
{sidebarGroupsFiltered.length === 0 && (
<p className="text-muted small mb-0">Нет подходящих разделов</p>
)}
</div>
</div>
<p className="text-muted small mb-0">Нет подходящих разделов</p>
)}
</div>
</div>
</div>
{/* Правая колонка — контент раздела */}
<div className="col-12 col-md-9 d-flex flex-column">
<div className="card-body">
{/* Контент раздела — карточка как на других страницах */}
<div className="col-12 col-md-9">
<div className="card card-flat">
<div className="card-body">
{activeSection === 'live-doh' && (
<>
<SectionHeading title="BGP Live и DNS (DoH)" icon={IconPlugConnected} />
@@ -558,12 +536,10 @@ export default function SettingsPage() {
</div>
</>
)}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</>
);
}