From cdf42aa589333d615bc328cc27d38d4389b47302 Mon Sep 17 00:00:00 2001 From: Denis Shatskiy Date: Tue, 12 Aug 2025 13:29:19 +0700 Subject: [PATCH] feat: Introduce cursor utility class for pointer interactions and refactor action buttons into PageHeaderActions component across multiple managers for improved UI consistency --- frontend/src/ASNsNewManager.jsx | 94 +++++-------------- frontend/src/App.css | 5 + frontend/src/BillingManager.jsx | 35 ++----- frontend/src/CommunitiesManager.jsx | 4 +- frontend/src/DataManager.jsx | 4 +- frontend/src/DomainsNewManager.jsx | 94 +++++-------------- frontend/src/FilterManager.jsx | 6 +- frontend/src/IPRangesManager.jsx | 94 +++++-------------- frontend/src/ServerManager.jsx | 12 +-- frontend/src/components/PageHeaderActions.jsx | 74 +++++++++++++++ 10 files changed, 162 insertions(+), 260 deletions(-) create mode 100644 frontend/src/components/PageHeaderActions.jsx diff --git a/frontend/src/ASNsNewManager.jsx b/frontend/src/ASNsNewManager.jsx index d239e29..ca67e0c 100644 --- a/frontend/src/ASNsNewManager.jsx +++ b/frontend/src/ASNsNewManager.jsx @@ -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'; @@ -393,9 +395,21 @@ function ASNsNewManager() {
Главная / Данные / ASNs
-
- -
+ setHistoryOpen(true)} + />
@@ -467,73 +481,7 @@ function ASNsNewManager() { - {/* Actions Card */} -
-
-

- - Действия -

-
-
-
- - - - - - -
-
-
+ {/* Карточка действий больше не нужна — действия перенесены в page-header */}
@@ -572,7 +520,7 @@ function ASNsNewManager() { - -
handleSort('asn')}> + handleSort('asn')}> Номер AS {sortField === 'asn' && ( @@ -580,7 +528,7 @@ function ASNsNewManager() { )} handleSort('community')}> + handleSort('community')}> Community {sortField === 'community' && ( diff --git a/frontend/src/App.css b/frontend/src/App.css index d892a78..6dd7f06 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -219,6 +219,11 @@ } } +/* Utility: pointer cursor */ +.cursor-pointer { + cursor: pointer; +} + /* Custom responsive adjustments */ @media (max-width: 992px) { .page-wrapper { diff --git a/frontend/src/BillingManager.jsx b/frontend/src/BillingManager.jsx index ebcb585..f9fb7ae 100644 --- a/frontend/src/BillingManager.jsx +++ b/frontend/src/BillingManager.jsx @@ -683,64 +683,43 @@ function BillingManager() { - - - - - - -
handleSort('hostName')} - > + handleSort('hostName')}> Имя хостера {sortField === 'hostName' && ( {sortOrder === 'asc' ? '↑' : '↓'} )} handleSort('purpose')} - > + handleSort('purpose')}> Назначение {sortField === 'purpose' && ( {sortOrder === 'asc' ? '↑' : '↓'} )} handleSort('country')} - > + handleSort('country')}> Страна {sortField === 'country' && ( {sortOrder === 'asc' ? '↑' : '↓'} )} handleSort('provider')} - > + handleSort('provider')}> Провайдер {sortField === 'provider' && ( {sortOrder === 'asc' ? '↑' : '↓'} )} handleSort('monthlyCost')} - > + handleSort('monthlyCost')}> Месячная стоимость {sortField === 'monthlyCost' && ( {sortOrder === 'asc' ? '↑' : '↓'} )} handleSort('nextPaymentDate')} - > + handleSort('nextPaymentDate')}> Следующий платеж {sortField === 'nextPaymentDate' && ( {sortOrder === 'asc' ? '↑' : '↓'} )} handleSort('status')} - > + handleSort('status')}> Статус {sortField === 'status' && ( {sortOrder === 'asc' ? '↑' : '↓'} diff --git a/frontend/src/CommunitiesManager.jsx b/frontend/src/CommunitiesManager.jsx index 2c93ce5..facda58 100644 --- a/frontend/src/CommunitiesManager.jsx +++ b/frontend/src/CommunitiesManager.jsx @@ -260,8 +260,8 @@ function CommunitiesManager() { - - + + diff --git a/frontend/src/DataManager.jsx b/frontend/src/DataManager.jsx index 8ba163b..a5a7c63 100644 --- a/frontend/src/DataManager.jsx +++ b/frontend/src/DataManager.jsx @@ -364,7 +364,7 @@ function DataManager({ entityName, entityKey, placeholder }) {
changeSort('value')}>Value {sortField==='value' && {sortOrder==='asc'?'▲':'▼'}}changeSort('name')}>Название {sortField==='name' && {sortOrder==='asc'?'▲':'▼'}}changeSort('value')}>Value {sortField==='value' && {sortOrder==='asc'?'▲':'▼'}}changeSort('name')}>Название {sortField==='name' && {sortOrder==='asc'?'▲':'▼'}} Описание Теги Gateway
- - - - - - - diff --git a/frontend/src/IPRangesManager.jsx b/frontend/src/IPRangesManager.jsx index 6f0508d..6172f8f 100644 --- a/frontend/src/IPRangesManager.jsx +++ b/frontend/src/IPRangesManager.jsx @@ -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() {
Главная / Данные / IP ranges
-
- -
+ setHistoryOpen(true)} + />
@@ -483,73 +497,7 @@ function IPRangesManager() { - {/* Actions Card */} -
-
-

- - Действия -

-
-
-
- - - - - - -
-
-
+ {/* Карточка действий больше не нужна — действия перенесены в page-header */}
@@ -588,7 +536,7 @@ function IPRangesManager() {
handleSort('domain')}> + handleSort('domain')}> Домен {sortField === 'domain' && ( @@ -372,7 +372,7 @@ function DataManager({ entityName, entityKey, placeholder }) { )} handleSort('type')}> + handleSort('type')}> Шлюз {sortField === 'type' && ( diff --git a/frontend/src/DomainsNewManager.jsx b/frontend/src/DomainsNewManager.jsx index f0acdf5..ea9c37c 100644 --- a/frontend/src/DomainsNewManager.jsx +++ b/frontend/src/DomainsNewManager.jsx @@ -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'; @@ -400,9 +402,21 @@ function DomainsNewManager() {
Главная / Данные / Домены
-
- -
+ setHistoryOpen(true)} + />
@@ -465,73 +479,7 @@ function DomainsNewManager() { - {/* Actions Card */} -
-
-

- - Действия -

-
-
-
- - - - - - -
-
-
+ {/* Карточка действий больше не нужна — действия перенесены в page-header */} {/* Drag & Drop импорт */}
handleSort('domain')}> + handleSort('domain')}> Домен {sortField === 'domain' && ( @@ -588,7 +536,7 @@ function DomainsNewManager() { )} handleSort('community')}> + handleSort('community')}> Community {sortField === 'community' && ( diff --git a/frontend/src/FilterManager.jsx b/frontend/src/FilterManager.jsx index 397a310..a38f9e7 100644 --- a/frontend/src/FilterManager.jsx +++ b/frontend/src/FilterManager.jsx @@ -1218,15 +1218,15 @@ function FilterManager() { onChange={toggleSelectAllProcessed} /> toggleSort('community')}> + toggleSort('community')}> COMMUNITY {sortBy === 'community' && (sortDir === 'asc' ? : )} toggleSort('gateway')}> + toggleSort('gateway')}> GATEWAY {sortBy === 'gateway' && (sortDir === 'asc' ? : )} toggleSort('description')}> + toggleSort('description')}> ОПИСАНИЕ {sortBy === 'description' && (sortDir === 'asc' ? : )}
- -
handleSort('ipRange')}> + handleSort('ipRange')}> IP-диапазон {sortField === 'ipRange' && ( @@ -596,7 +544,7 @@ function IPRangesManager() { )} handleSort('community')}> + handleSort('community')}> Community {sortField === 'community' && ( diff --git a/frontend/src/ServerManager.jsx b/frontend/src/ServerManager.jsx index 285006a..924cc15 100644 --- a/frontend/src/ServerManager.jsx +++ b/frontend/src/ServerManager.jsx @@ -558,7 +558,7 @@ function ServerManager() { - - - - - -
handleSort('ip')}> + handleSort('ip')}> IP адрес {sortField === 'ip' && ( @@ -566,7 +566,7 @@ function ServerManager() { )} handleSort('dns')}> + handleSort('dns')}> DNS имя {sortField === 'dns' && ( @@ -574,7 +574,7 @@ function ServerManager() { )} handleSort('country')}> + handleSort('country')}> Страна {sortField === 'country' && ( @@ -582,7 +582,7 @@ function ServerManager() { )} handleSort('provider')}> + handleSort('provider')}> Провайдер {sortField === 'provider' && ( @@ -590,7 +590,7 @@ function ServerManager() { )} handleSort('tunnel')}> + handleSort('tunnel')}> Туннель {sortField === 'tunnel' && ( @@ -598,7 +598,7 @@ function ServerManager() { )} handleSort('gateway')}> + handleSort('gateway')}> Основной шлюз {sortField === 'gateway' && ( diff --git a/frontend/src/components/PageHeaderActions.jsx b/frontend/src/components/PageHeaderActions.jsx new file mode 100644 index 0000000..98cc431 --- /dev/null +++ b/frontend/src/components/PageHeaderActions.jsx @@ -0,0 +1,74 @@ +import { + IconEye, + IconRefresh, + IconUpload, + IconDownload, + IconDeviceFloppy +} from '@tabler/icons-react'; + +function PageHeaderActions({ + loading = false, + onPreview, + disablePreview = false, + onRefresh, + disableRefresh = false, + onImport, + onExport, + disableExport = false, + onClear, + disableClear = false, + onSave, + disableSave = false, + onHistory, +}) { + return ( +
+ {onPreview && ( + + )} + {onRefresh && ( + + )} + {onImport && ( + + )} + {onExport && ( + + )} + {onClear && ( + + )} + {onSave && ( + + )} + {onHistory && ( + + )} +
+ ); +} + +export default PageHeaderActions; + +