refactor(route-optimizer): enhance layout and structure of optimized routes table in RouteOptimizerPage component
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 1m53s

This commit is contained in:
2026-03-05 14:26:16 +07:00
parent 31ce1ae02a
commit 1dcdc0e6c2
+80 -74
View File
@@ -320,80 +320,86 @@ export default function RouteOptimizerPage() {
</div>
<div className="mt-4">
<h3 className="mb-3">Оптимизированные маршруты Home -&gt; JumpHost -&gt; Exit по BGP community</h3>
<div className="text-muted small mb-3">
Таблица показывает лучший полный маршрут для каждой community с учетом рекомендованного
gateway на Jumphost.
</div>
<div className="table-responsive mb-4">
<table className="table table-vcenter card-table align-middle" style={OPT_TABLE_STYLE}>
<OptimizerColgroup />
<thead>
<tr>
<th>Community</th>
<th>Маршрут (Home -&gt; JumpHost -&gt; Exit)</th>
<th>Сегменты</th>
<th>Gateway / Действие</th>
<th>Вероятность</th>
</tr>
</thead>
<tbody>
{communityFullRoutes.length === 0 ? (
<tr>
<td colSpan={5} className="text-muted">Недостаточно данных для построения полных маршрутов по community.</td>
</tr>
) : communityFullRoutes.map((r, idx) => (
<tr key={`${r.community}-${routeNodeKey(r.home)}-${routeNodeKey(r.jumphost)}-${routeNodeKey(r.exit)}-${idx}`}>
<td className="text-break">
<div className="fw-semibold">{r.community}</div>
{(r.communityInfo?.name || r.communityInfo?.description) && (
<div className="small text-muted">{r.communityInfo?.name || r.communityInfo?.description}</div>
)}
</td>
<td className="small text-break">
<span className="fw-semibold">{r.home?.label || r.home?.dns || r.home?.ip || 'home'}</span>
<span className="text-muted"> </span>
<span className="fw-semibold">{r.jumphost?.label || r.jumphost?.dns || r.jumphost?.ip || 'jumphost'}</span>
<span className="text-muted"> </span>
<span className="fw-semibold">{r.exit?.label || r.exit?.dns || r.exit?.ip || 'exit'}</span>
</td>
<td className="small text-break">
<div>
HJ: ping {r.homeToJumphost?.pingMs ?? '—'} ms, {fmtSpeed(
r.homeToJumphost?.speedDownloadMbps,
r.homeToJumphost?.speedUploadMbps,
r.homeToJumphost?.speedMbps
)}
</div>
<div className="text-muted">
JE: ping {r.jumphostToExit?.pingMs ?? '—'} ms, {fmtSpeed(
r.jumphostToExit?.speedDownloadMbps,
r.jumphostToExit?.speedUploadMbps,
r.jumphostToExit?.speedMbps
)}
</div>
</td>
<td className="small text-break">
<div>{r.recommendedGateway || '—'}</div>
{r.tunnelGateway && <div className="text-muted">{'->'} {r.tunnelGateway}</div>}
<div className="mt-1">
{r.pinnedBySettings ? (
<span className="badge bg-blue-lt text-blue">pin</span>
) : r.shouldSwitch ? (
<span className="badge bg-orange-lt text-orange">switch</span>
) : (
<span className="badge bg-green-lt text-green">ok</span>
)}
</div>
</td>
<td className="small">
<div>{fmtProb(r.fullRouteProbability)}</div>
<div className="text-muted">score {r.fullRouteScore} / conf {r.fullRouteConfidence ?? '—'}</div>
</td>
</tr>
))}
</tbody>
</table>
<div className="card">
<div className="card-header">
<h3 className="card-title mb-0">Оптимизированные маршруты Home -&gt; JumpHost -&gt; Exit по BGP community</h3>
</div>
<div className="card-body">
<div className="text-muted small mb-3">
Таблица показывает лучший полный маршрут для каждой community с учетом рекомендованного
gateway на Jumphost.
</div>
<div className="table-responsive mb-0">
<table className="table table-vcenter card-table align-middle" style={OPT_TABLE_STYLE}>
<OptimizerColgroup />
<thead>
<tr>
<th>Community</th>
<th>Маршрут (Home -&gt; JumpHost -&gt; Exit)</th>
<th>Сегменты</th>
<th>Gateway / Действие</th>
<th>Вероятность</th>
</tr>
</thead>
<tbody>
{communityFullRoutes.length === 0 ? (
<tr>
<td colSpan={5} className="text-muted">Недостаточно данных для построения полных маршрутов по community.</td>
</tr>
) : communityFullRoutes.map((r, idx) => (
<tr key={`${r.community}-${routeNodeKey(r.home)}-${routeNodeKey(r.jumphost)}-${routeNodeKey(r.exit)}-${idx}`}>
<td className="text-break">
<div className="fw-semibold">{r.community}</div>
{(r.communityInfo?.name || r.communityInfo?.description) && (
<div className="small text-muted">{r.communityInfo?.name || r.communityInfo?.description}</div>
)}
</td>
<td className="small text-break">
<span className="fw-semibold">{r.home?.label || r.home?.dns || r.home?.ip || 'home'}</span>
<span className="text-muted"> </span>
<span className="fw-semibold">{r.jumphost?.label || r.jumphost?.dns || r.jumphost?.ip || 'jumphost'}</span>
<span className="text-muted"> </span>
<span className="fw-semibold">{r.exit?.label || r.exit?.dns || r.exit?.ip || 'exit'}</span>
</td>
<td className="small text-break">
<div>
HJ: ping {r.homeToJumphost?.pingMs ?? '—'} ms, {fmtSpeed(
r.homeToJumphost?.speedDownloadMbps,
r.homeToJumphost?.speedUploadMbps,
r.homeToJumphost?.speedMbps
)}
</div>
<div className="text-muted">
JE: ping {r.jumphostToExit?.pingMs ?? '—'} ms, {fmtSpeed(
r.jumphostToExit?.speedDownloadMbps,
r.jumphostToExit?.speedUploadMbps,
r.jumphostToExit?.speedMbps
)}
</div>
</td>
<td className="small text-break">
<div>{r.recommendedGateway || '—'}</div>
{r.tunnelGateway && <div className="text-muted">{'->'} {r.tunnelGateway}</div>}
<div className="mt-1">
{r.pinnedBySettings ? (
<span className="badge bg-blue-lt text-blue">pin</span>
) : r.shouldSwitch ? (
<span className="badge bg-orange-lt text-orange">switch</span>
) : (
<span className="badge bg-green-lt text-green">ok</span>
)}
</div>
</td>
<td className="small">
<div>{fmtProb(r.fullRouteProbability)}</div>
<div className="text-muted">score {r.fullRouteScore} / conf {r.fullRouteConfidence ?? '—'}</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</div>
</div>
</div>