feat: Introduce cursor utility class for pointer interactions and refactor action buttons into PageHeaderActions component across multiple managers for improved UI consistency
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m54s

This commit is contained in:
2025-08-12 13:29:19 +07:00
parent f06a433d77
commit cdf42aa589
10 changed files with 162 additions and 260 deletions
+21 -73
View File
@@ -15,11 +15,13 @@ import {
IconDeviceFloppy,
IconHash,
IconClock,
IconFileText
IconFileText,
IconEye
} from '@tabler/icons-react';
import LockBanner from './components/LockBanner.jsx';
import TableSkeleton from './components/TableSkeleton.jsx';
import S3MetaBar from './components/S3MetaBar.jsx';
import PageHeaderActions from './components/PageHeaderActions.jsx';
import ConfirmDiffModal from './components/ConfirmDiffModal.jsx';
import HistoryModal from './components/HistoryModal.jsx';
@@ -409,9 +411,21 @@ function IPRangesManager() {
<div className="page-pretitle">Главная / Данные / IP ranges</div>
</div>
<div className="col-auto ms-auto d-print-none">
<div className="btn-list">
<button className="btn btn-outline-secondary" onClick={() => setHistoryOpen(true)}>История</button>
</div>
<PageHeaderActions
loading={loading}
onPreview={handlePreviewDiff}
disablePreview={loading || items.length === 0}
onRefresh={fetchItems}
disableRefresh={loading}
onImport={handleImport}
onExport={handleExport}
disableExport={items.length === 0}
onClear={clearInvalid}
disableClear={items.length === 0}
onSave={handleSaveChanges}
disableSave={loading}
onHistory={() => setHistoryOpen(true)}
/>
</div>
</div>
</div>
@@ -483,73 +497,7 @@ function IPRangesManager() {
</div>
</div>
{/* Actions Card */}
<div className="card card-md">
<div className="card-header">
<h3 className="card-title">
<IconDatabase className="icon me-2" />
Действия
</h3>
</div>
<div className="card-body">
<div className="d-grid gap-2">
<button
className="btn btn-primary"
onClick={handleSaveChanges}
disabled={loading}
>
{loading ? (
<>
<span className="spinner-border spinner-border-sm me-2" role="status"></span>
Сохранение...
</>
) : (
<>
<IconDeviceFloppy className="icon me-2" />
Сохранить в S3
</>
)}
</button>
<button
className="btn btn-outline-secondary"
onClick={fetchItems}
disabled={loading}
>
<IconRefresh className="icon me-2" />
Обновить
</button>
<button
className="btn btn-outline-primary"
onClick={handleImport}
>
<IconUpload className="icon me-2" />
Импорт из буфера
</button>
<button
className="btn btn-outline-primary"
onClick={handleExport}
disabled={items.length === 0}
>
<IconDownload className="icon me-2" />
Экспорт CSV
</button>
<button
className="btn btn-outline-secondary"
onClick={handlePreviewDiff}
disabled={loading}
>
Предпросмотр изменений
</button>
<button
className="btn btn-outline-secondary"
onClick={clearInvalid}
disabled={items.length === 0}
>
Очистить пустые/невалидные
</button>
</div>
</div>
</div>
{/* Карточка действий больше не нужна — действия перенесены в page-header */}
</div>
<div className="col-lg-9">
@@ -588,7 +536,7 @@ function IPRangesManager() {
<table className="table card-table table-vcenter table-nowrap mb-0">
<thead>
<tr>
<th style={{cursor:'pointer'}} onClick={() => handleSort('ipRange')}>
<th className="cursor-pointer" onClick={() => handleSort('ipRange')}>
IP-диапазон
{sortField === 'ipRange' && (
<span className="ms-1">
@@ -596,7 +544,7 @@ function IPRangesManager() {
</span>
)}
</th>
<th style={{cursor:'pointer'}} onClick={() => handleSort('community')}>
<th className="cursor-pointer" onClick={() => handleSort('community')}>
Community
{sortField === 'community' && (
<span className="ms-1">