refactor(NetworkConfigManager): replace action buttons with compact icon buttons wrapped in tooltips for improved UI clarity and user interaction
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m40s

This commit is contained in:
2026-01-25 14:15:39 +07:00
parent 4feb84338a
commit 335f7c9b58
+91 -74
View File
@@ -6,6 +6,7 @@ import FormField from './components/FormField.jsx';
import ConfirmModal from './components/ConfirmModal.jsx'; import ConfirmModal from './components/ConfirmModal.jsx';
import ServerAutocompleteInput from './components/ServerAutocompleteInput.jsx'; import ServerAutocompleteInput from './components/ServerAutocompleteInput.jsx';
import GatewayAutocompleteInput from './components/GatewayAutocompleteInput.jsx'; import GatewayAutocompleteInput from './components/GatewayAutocompleteInput.jsx';
import Tooltip from './components/Tooltip.jsx';
import { countryToFlag } from './utils/serverUtils.js'; import { countryToFlag } from './utils/serverUtils.js';
import { import {
IconNetwork, IconNetwork,
@@ -2464,22 +2465,26 @@ function NetworkConfigManager() {
</div> </div>
</div> </div>
{/* Правая часть: действия */} {/* Правая часть: действия - компактные иконки */}
<div className="d-flex align-items-center gap-2 px-3 border-start" style={{ flexShrink: 0 }}> <div className="d-flex align-items-center gap-1 px-2 border-start" style={{ flexShrink: 0 }}>
<button <Tooltip content="Изменить" position="top">
className="btn btn-outline-secondary btn-sm" <button
onClick={() => handleEditPool(pool)} className="btn btn-outline-secondary btn-icon btn-sm"
> onClick={() => handleEditPool(pool)}
<IconEdit size={16} className="me-1" /> aria-label="Изменить"
Изменить >
</button> <IconEdit size={16} />
<button </button>
className="btn btn-outline-danger btn-sm" </Tooltip>
onClick={() => handleDeletePool(pool)} <Tooltip content="Удалить" position="top">
> <button
<IconTrash size={16} className="me-1" /> className="btn btn-outline-danger btn-icon btn-sm"
Удалить onClick={() => handleDeletePool(pool)}
</button> aria-label="Удалить"
>
<IconTrash size={16} />
</button>
</Tooltip>
</div> </div>
</div> </div>
</div> </div>
@@ -2556,12 +2561,12 @@ function NetworkConfigManager() {
<div className="flex-grow-1 py-2 px-3" style={{ minWidth: 0, flex: '1 1 auto', overflow: 'hidden' }}> <div className="flex-grow-1 py-2 px-3" style={{ minWidth: 0, flex: '1 1 auto', overflow: 'hidden' }}>
{/* Верхняя строка: флаг, название */} {/* Верхняя строка: флаг, название */}
<div className="d-flex align-items-center mb-1"> <div className="d-flex align-items-center mb-1">
<div className="d-flex align-items-center" style={{ flex: '0 0 auto', minWidth: 0 }}> <div className="d-flex align-items-center flex-grow-1" style={{ minWidth: 0 }}>
{displayCountry && ( {displayCountry && (
<span className="me-2" style={{ fontSize: '1.25rem', flexShrink: 0 }}>{countryToFlag(displayCountry)}</span> <span className="me-2 flex-shrink-0" style={{ fontSize: '1.25rem' }}>{countryToFlag(displayCountry)}</span>
)} )}
<div> <div className="flex-grow-1" style={{ minWidth: 0, overflow: 'hidden' }}>
<span className="fw-semibold">{gateway.description || gateway.ip || '—'}</span> <span className="fw-semibold d-block text-truncate" style={{ maxWidth: '100%' }}>{gateway.description || gateway.ip || '—'}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -2569,7 +2574,7 @@ function NetworkConfigManager() {
{/* Маршрут ip->ip сразу под названием */} {/* Маршрут ip->ip сразу под названием */}
{displayText && ( {displayText && (
<div className="mb-1"> <div className="mb-1">
<span className="badge bg-success-lt text-success" style={{ whiteSpace: 'nowrap', fontSize: '0.875rem' }}> <span className="badge bg-success-lt text-success d-inline-block" style={{ fontSize: '0.875rem', maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{displayText} {displayText}
</span> </span>
</div> </div>
@@ -2578,36 +2583,42 @@ function NetworkConfigManager() {
{/* Нижняя строка: DNS */} {/* Нижняя строка: DNS */}
<div className="d-flex align-items-center gap-2 flex-wrap"> <div className="d-flex align-items-center gap-2 flex-wrap">
{server && ( {server && (
<code className="text-muted" style={{ fontSize: '0.8rem', wordBreak: 'break-all', overflowWrap: 'break-word' }}> <code className="text-muted d-block text-truncate" style={{ fontSize: '0.8rem', wordBreak: 'break-all', overflowWrap: 'break-word', maxWidth: '100%' }}>
{server.dns || server.ip || gateway.serverId} {server.dns || server.ip || gateway.serverId}
</code> </code>
)} )}
</div> </div>
</div> </div>
{/* Правая часть: действия */} {/* Правая часть: действия - компактные иконки */}
<div className="d-flex align-items-center gap-2 px-3 border-start" style={{ flexShrink: 0 }}> <div className="d-flex align-items-center gap-1 px-2 border-start" style={{ flexShrink: 0 }}>
<button <Tooltip content="Получить код MikroTik" position="top">
className="btn btn-outline-primary btn-sm" <button
onClick={() => handleGenerateMikrotikCodeForGateway(gateway)} className="btn btn-outline-primary btn-icon btn-sm"
> onClick={() => handleGenerateMikrotikCodeForGateway(gateway)}
<IconCode size={16} className="me-1" /> aria-label="Получить код MikroTik"
Код >
</button> <IconCode size={16} />
<button </button>
className="btn btn-outline-secondary btn-sm" </Tooltip>
onClick={() => handleEditGateway(gateway)} <Tooltip content="Изменить" position="top">
> <button
<IconEdit size={16} className="me-1" /> className="btn btn-outline-secondary btn-icon btn-sm"
Изменить onClick={() => handleEditGateway(gateway)}
</button> aria-label="Изменить"
<button >
className="btn btn-outline-danger btn-sm" <IconEdit size={16} />
onClick={() => handleDeleteGateway(gateway)} </button>
> </Tooltip>
<IconTrash size={16} className="me-1" /> <Tooltip content="Удалить" position="top">
Удалить <button
</button> className="btn btn-outline-danger btn-icon btn-sm"
onClick={() => handleDeleteGateway(gateway)}
aria-label="Удалить"
>
<IconTrash size={16} />
</button>
</Tooltip>
</div> </div>
</div> </div>
</div> </div>
@@ -2682,12 +2693,12 @@ function NetworkConfigManager() {
<div className="flex-grow-1 py-2 px-3" style={{ minWidth: 0, flex: '1 1 auto', overflow: 'hidden' }}> <div className="flex-grow-1 py-2 px-3" style={{ minWidth: 0, flex: '1 1 auto', overflow: 'hidden' }}>
{/* Верхняя строка: флаг, название */} {/* Верхняя строка: флаг, название */}
<div className="d-flex align-items-center mb-1"> <div className="d-flex align-items-center mb-1">
<div className="d-flex align-items-center" style={{ flex: '0 0 auto', minWidth: 0 }}> <div className="d-flex align-items-center flex-grow-1" style={{ minWidth: 0 }}>
{displayCountry && ( {displayCountry && (
<span className="me-2" style={{ fontSize: '1.25rem', flexShrink: 0 }}>{countryToFlag(displayCountry)}</span> <span className="me-2 flex-shrink-0" style={{ fontSize: '1.25rem' }}>{countryToFlag(displayCountry)}</span>
)} )}
<div> <div className="flex-grow-1" style={{ minWidth: 0, overflow: 'hidden' }}>
<span className="fw-semibold">{interfaceName}</span> <span className="fw-semibold d-block text-truncate" style={{ maxWidth: '100%' }}>{interfaceName}</span>
</div> </div>
</div> </div>
</div> </div>
@@ -2695,7 +2706,7 @@ function NetworkConfigManager() {
{/* Маршрут между Local IP и Remote IP сразу под названием */} {/* Маршрут между Local IP и Remote IP сразу под названием */}
{displayLocalIp !== '—' && displayRemoteIp !== '—' && ( {displayLocalIp !== '—' && displayRemoteIp !== '—' && (
<div className="mb-1"> <div className="mb-1">
<span className="badge bg-success-lt text-success" style={{ whiteSpace: 'nowrap', fontSize: '0.875rem' }}> <span className="badge bg-success-lt text-success d-inline-block" style={{ fontSize: '0.875rem', maxWidth: '100%', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{displayLocalIp} → {displayRemoteIp} {displayLocalIp} → {displayRemoteIp}
</span> </span>
</div> </div>
@@ -2704,36 +2715,42 @@ function NetworkConfigManager() {
{/* Нижняя строка: DNS */} {/* Нижняя строка: DNS */}
<div className="d-flex align-items-center gap-2 flex-wrap"> <div className="d-flex align-items-center gap-2 flex-wrap">
{displayServerName && ( {displayServerName && (
<code className="text-muted" style={{ fontSize: '0.8rem', wordBreak: 'break-all', overflowWrap: 'break-word' }}> <code className="text-muted d-block text-truncate" style={{ fontSize: '0.8rem', wordBreak: 'break-all', overflowWrap: 'break-word', maxWidth: '100%' }}>
{displayServerName} {displayServerName}
</code> </code>
)} )}
</div> </div>
</div> </div>
{/* Правая часть: действия */} {/* Правая часть: действия - компактные иконки */}
<div className="d-flex align-items-center gap-2 px-3 border-start" style={{ flexShrink: 0 }}> <div className="d-flex align-items-center gap-1 px-2 border-start" style={{ flexShrink: 0 }}>
<button <Tooltip content="Получить код MikroTik" position="top">
className="btn btn-outline-primary btn-sm" <button
onClick={() => handleGenerateMikrotikCodeForInterface(iface)} className="btn btn-outline-primary btn-icon btn-sm"
> onClick={() => handleGenerateMikrotikCodeForInterface(iface)}
<IconCode size={16} className="me-1" /> aria-label="Получить код MikroTik"
Код >
</button> <IconCode size={16} />
<button </button>
className="btn btn-outline-secondary btn-sm" </Tooltip>
onClick={() => handleEditInterface(iface)} <Tooltip content="Изменить" position="top">
> <button
<IconEdit size={16} className="me-1" /> className="btn btn-outline-secondary btn-icon btn-sm"
Изменить onClick={() => handleEditInterface(iface)}
</button> aria-label="Изменить"
<button >
className="btn btn-outline-danger btn-sm" <IconEdit size={16} />
onClick={() => handleDeleteInterface(iface)} </button>
> </Tooltip>
<IconTrash size={16} className="me-1" /> <Tooltip content="Удалить" position="top">
Удалить <button
</button> className="btn btn-outline-danger btn-icon btn-sm"
onClick={() => handleDeleteInterface(iface)}
aria-label="Удалить"
>
<IconTrash size={16} />
</button>
</Tooltip>
</div> </div>
</div> </div>
</div> </div>