feat(NetworkConfigManager): implement code copying functionality for individual blocks and all generated MikroTik code; update server grouping display for improved clarity
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m39s

This commit is contained in:
2026-01-22 14:05:52 +07:00
parent 75a7778148
commit 0bd2a07dd2
+104 -12
View File
@@ -751,6 +751,19 @@ function NetworkConfigManager() {
}
};
// === Копирование отдельного блока кода ===
const handleCopyBlock = async (code) => {
await handleCopyMikrotikCode(code);
};
// === Копирование всего кода ===
const handleCopyAllMikrotikCode = async () => {
const fullCode = Array.isArray(generatedMikrotikCode) && generatedMikrotikCode.length > 0
? generatedMikrotikCode.map(block => block.code).join('\n\n')
: '';
await handleCopyMikrotikCode(fullCode);
};
// === Группировка блоков по серверам ===
const groupedMikrotikCode = useMemo(() => {
if (!Array.isArray(generatedMikrotikCode) || generatedMikrotikCode.length === 0) {
@@ -759,7 +772,7 @@ function NetworkConfigManager() {
const grouped = {};
generatedMikrotikCode.forEach(block => {
const serverName = block.serverName || '__unknown__';
const serverName = block.serverName || '__unassigned__';
if (!grouped[serverName]) {
grouped[serverName] = {
server: block.server,
@@ -2042,15 +2055,94 @@ function NetworkConfigManager() {
Конфигурация MikroTik RouterOS для рекурсивных маршрутов и IP адресов интерфейсов
</div>
{Array.isArray(generatedMikrotikCode) && generatedMikrotikCode.length > 0 ? (
<div className="d-flex flex-column gap-3">
{generatedMikrotikCode.map((block, index) => (
<div key={index} className="bg-dark text-white p-3 rounded">
<pre className="mb-0" style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
<code className="text-white">{block.code}</code>
</pre>
</div>
))}
{Object.keys(groupedMikrotikCode).length > 0 ? (
<div className="d-flex flex-column gap-4">
{Object.entries(groupedMikrotikCode)
.sort(([a], [b]) => a.localeCompare(b))
.map(([serverName, serverData]) => {
const server = serverData.server;
const isUnassigned = serverName === '__unassigned__';
return (
<div key={serverName} className="border rounded p-3">
{/* Заголовок сервера */}
<div className="d-flex align-items-center mb-3 pb-2 border-bottom">
<div className="d-flex align-items-center flex-fill">
{isUnassigned ? (
<>
<span className="avatar avatar-sm bg-secondary-lt me-2">
<IconServer size={16} className="text-muted" />
</span>
<span className="text-muted">Без привязки к серверу</span>
</>
) : (
<>
<span className="avatar avatar-sm bg-primary-lt me-2">
<IconServer size={16} className="text-primary" />
</span>
<div>
<div className="fw-bold">{serverName}</div>
{server && (
<div className="text-muted small">
{server.provider && <span>{server.provider}</span>}
{server.provider && server.country && <span className="mx-1"></span>}
{server.country && (
<span>{countryToFlag(server.country)} {server.country}</span>
)}
{server.ip && server.ip !== serverName && (
<>
{(server.provider || server.country) && <span className="mx-1"></span>}
<span>{server.ip}</span>
</>
)}
</div>
)}
</div>
</>
)}
</div>
</div>
{/* Блоки кода для этого сервера */}
<div className="d-flex flex-column gap-3">
{serverData.blocks.map((block, blockIndex) => {
const blockTitle = block.type === 'recursive-routes'
? 'Рекурсивные маршруты'
: block.type === 'interface-addresses'
? 'IP адреса интерфейсов'
: 'Конфигурация';
return (
<div key={blockIndex} className="border rounded overflow-hidden">
{/* Заголовок блока */}
<div className="bg-secondary-lt px-3 py-2 d-flex align-items-center justify-content-between">
<div className="d-flex align-items-center">
<IconCode size={18} className="text-secondary me-2" />
<span className="fw-medium">{blockTitle}</span>
</div>
<button
className="btn btn-sm btn-outline-secondary"
onClick={() => handleCopyBlock(block.code)}
title="Копировать этот блок"
>
<IconCopy size={16} className="me-1" />
Копировать
</button>
</div>
{/* Код блока */}
<div className="bg-dark text-white p-3">
<pre className="mb-0" style={{ whiteSpace: 'pre-wrap', wordBreak: 'break-word', fontSize: '0.875rem' }}>
<code className="text-white">{block.code}</code>
</pre>
</div>
</div>
);
})}
</div>
</div>
);
})}
</div>
) : (
<div className="alert alert-warning mb-0">
@@ -2075,11 +2167,11 @@ function NetworkConfigManager() {
<button
type="button"
className="btn btn-primary"
onClick={handleCopyMikrotikCode}
onClick={handleCopyAllMikrotikCode}
disabled={!Array.isArray(generatedMikrotikCode) || generatedMikrotikCode.length === 0}
>
<IconCopy className="me-2" />
Копировать в буфер
Копировать всё
</button>
</div>
</div>