refactor(NetworkConfigManager): improve MikroTik code copying functionality and group blocks by server; enhance interface card rendering for dual server support
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m38s
This commit is contained in:
@@ -718,25 +718,20 @@ function NetworkConfigManager() {
|
||||
};
|
||||
|
||||
// === Копирование кода MikroTik ===
|
||||
const handleCopyMikrotikCode = async () => {
|
||||
// Объединяем все блоки в одну строку для копирования
|
||||
const fullCode = Array.isArray(generatedMikrotikCode) && generatedMikrotikCode.length > 0
|
||||
? generatedMikrotikCode.map(block => block.code).join('\n\n')
|
||||
: '';
|
||||
|
||||
if (!fullCode) {
|
||||
const handleCopyMikrotikCode = async (codeToCopy) => {
|
||||
if (!codeToCopy) {
|
||||
notify.error('Нет кода для копирования');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||
await navigator.clipboard.writeText(fullCode);
|
||||
await navigator.clipboard.writeText(codeToCopy);
|
||||
notify.success('Код скопирован в буфер обмена!');
|
||||
} else {
|
||||
// Fallback для старых браузеров
|
||||
const textArea = document.createElement('textarea');
|
||||
textArea.value = fullCode;
|
||||
textArea.value = codeToCopy;
|
||||
textArea.style.position = 'fixed';
|
||||
textArea.style.left = '-999999px';
|
||||
document.body.appendChild(textArea);
|
||||
@@ -756,6 +751,40 @@ function NetworkConfigManager() {
|
||||
}
|
||||
};
|
||||
|
||||
// === Группировка блоков по серверам ===
|
||||
const groupedMikrotikCode = useMemo(() => {
|
||||
if (!Array.isArray(generatedMikrotikCode) || generatedMikrotikCode.length === 0) {
|
||||
return {};
|
||||
}
|
||||
|
||||
const grouped = {};
|
||||
generatedMikrotikCode.forEach(block => {
|
||||
const serverName = block.serverName || '__unknown__';
|
||||
if (!grouped[serverName]) {
|
||||
grouped[serverName] = {
|
||||
server: block.server,
|
||||
serverName: serverName,
|
||||
blocks: []
|
||||
};
|
||||
}
|
||||
grouped[serverName].blocks.push(block);
|
||||
});
|
||||
|
||||
return grouped;
|
||||
}, [generatedMikrotikCode]);
|
||||
|
||||
// === Получение названия типа блока ===
|
||||
const getBlockTypeLabel = (type) => {
|
||||
switch (type) {
|
||||
case 'recursive-routes':
|
||||
return 'Рекурсивные маршруты';
|
||||
case 'interface-addresses':
|
||||
return 'IP адреса интерфейсов';
|
||||
default:
|
||||
return 'Конфигурация';
|
||||
}
|
||||
};
|
||||
|
||||
// === Получение цвета провайдера ===
|
||||
const getProviderColor = (provider) => {
|
||||
if (!provider) return 'secondary';
|
||||
@@ -820,7 +849,7 @@ function NetworkConfigManager() {
|
||||
{/* Items */}
|
||||
<div className="row row-cards">
|
||||
{items.map(item => (
|
||||
type === 'gateway' ? renderGatewayCard(item) : renderInterfaceCard(item)
|
||||
type === 'gateway' ? renderGatewayCard(item) : renderInterfaceCard(item, serverId)
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
@@ -955,10 +984,38 @@ function NetworkConfigManager() {
|
||||
};
|
||||
|
||||
// === Рендер карточки Interface ===
|
||||
const renderInterfaceCard = (iface) => {
|
||||
const renderInterfaceCard = (iface, currentServerId) => {
|
||||
const server = getServerInfo(iface.serverId);
|
||||
const server2 = getServerInfo(iface.serverId2);
|
||||
|
||||
// Определяем, для какого сервера отображается карточка
|
||||
// Сравниваем currentServerId с serverId2 используя функцию getServerInfo для надежности
|
||||
const isForServer2 = currentServerId && iface.serverId2 && (() => {
|
||||
const currentServer = getServerInfo(currentServerId);
|
||||
const targetServer2 = getServerInfo(iface.serverId2);
|
||||
if (!currentServer || !targetServer2) return false;
|
||||
// Сравниваем по id, ip или dns
|
||||
return (
|
||||
currentServer.id === targetServer2.id ||
|
||||
currentServer.ip === targetServer2.ip ||
|
||||
currentServer.dns === targetServer2.dns ||
|
||||
currentServerId === iface.serverId2 ||
|
||||
currentServerId === targetServer2.id ||
|
||||
currentServerId === targetServer2.ip ||
|
||||
currentServerId === targetServer2.dns
|
||||
);
|
||||
})();
|
||||
|
||||
// Имя интерфейса зависит от того, для какого сервера отображается
|
||||
const interfaceName = isForServer2
|
||||
? (iface.name2 || iface.name || '—')
|
||||
: (iface.name || '—');
|
||||
|
||||
// IP адреса также зависят от того, для какого сервера отображается
|
||||
// Для второго сервера localIp и remoteIp меняются местами
|
||||
const displayLocalIp = isForServer2 ? (iface.remoteIp || '—') : (iface.localIp || '—');
|
||||
const displayRemoteIp = isForServer2 ? (iface.localIp || '—') : (iface.remoteIp || '—');
|
||||
|
||||
return (
|
||||
<div key={iface.id} className="col-sm-6 col-lg-4">
|
||||
<div className="card card-sm">
|
||||
@@ -968,7 +1025,7 @@ function NetworkConfigManager() {
|
||||
<IconRouter size={16} className={`text-${getInterfaceTypeColor(iface.type)}`} />
|
||||
</span>
|
||||
<div className="flex-fill">
|
||||
<div className="fw-medium">{iface.name || '—'}</div>
|
||||
<div className="fw-medium">{interfaceName}</div>
|
||||
<div className="text-muted small">
|
||||
<span className={`badge bg-${getInterfaceTypeColor(iface.type)}-lt text-${getInterfaceTypeColor(iface.type)}`}>
|
||||
{iface.type}
|
||||
@@ -994,11 +1051,11 @@ function NetworkConfigManager() {
|
||||
<div className="col-6">
|
||||
<div className="text-muted">Local IP</div>
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<code className="text-truncate">{iface.localIp || '—'}</code>
|
||||
{iface.localIp && (
|
||||
<code className="text-truncate">{displayLocalIp}</code>
|
||||
{displayLocalIp && displayLocalIp !== '—' && (
|
||||
<button
|
||||
className="btn btn-ghost-secondary btn-icon p-0"
|
||||
onClick={() => copyToClipboard(iface.localIp)}
|
||||
onClick={() => copyToClipboard(displayLocalIp)}
|
||||
style={{ width: 20, height: 20 }}
|
||||
>
|
||||
<IconCopy size={12} />
|
||||
@@ -1009,11 +1066,11 @@ function NetworkConfigManager() {
|
||||
<div className="col-6">
|
||||
<div className="text-muted">Remote IP</div>
|
||||
<div className="d-flex align-items-center gap-1">
|
||||
<code className="text-truncate">{iface.remoteIp || '—'}</code>
|
||||
{iface.remoteIp && (
|
||||
<code className="text-truncate">{displayRemoteIp}</code>
|
||||
{displayRemoteIp && displayRemoteIp !== '—' && (
|
||||
<button
|
||||
className="btn btn-ghost-secondary btn-icon p-0"
|
||||
onClick={() => copyToClipboard(iface.remoteIp)}
|
||||
onClick={() => copyToClipboard(displayRemoteIp)}
|
||||
style={{ width: 20, height: 20 }}
|
||||
>
|
||||
<IconCopy size={12} />
|
||||
|
||||
Reference in New Issue
Block a user