feat(MikrotikTools): add traceroute tab state management for improved user navigation between results and analysis
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 3m35s
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 3m35s
This commit is contained in:
+144
-130
@@ -36,6 +36,7 @@ function MikrotikTools() {
|
|||||||
const [running, setRunning] = useState(false);
|
const [running, setRunning] = useState(false);
|
||||||
const [hops, setHops] = useState([]);
|
const [hops, setHops] = useState([]);
|
||||||
const [analysis, setAnalysis] = useState([]);
|
const [analysis, setAnalysis] = useState([]);
|
||||||
|
const [tracerouteTab, setTracerouteTab] = useState('table'); // 'table' | 'analysis'
|
||||||
const [pingResult, setPingResult] = useState(null);
|
const [pingResult, setPingResult] = useState(null);
|
||||||
const [useDns, setUseDns] = useState(true);
|
const [useDns, setUseDns] = useState(true);
|
||||||
|
|
||||||
@@ -508,11 +509,151 @@ function MikrotikTools() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-md-8">
|
<div className="col-md-8">
|
||||||
{mode === 'traceroute' && (
|
{mode === 'ping' && (
|
||||||
<div className="card mb-3">
|
<div className="card mb-3">
|
||||||
<div className="card-header">
|
<div className="card-header">
|
||||||
<h3 className="card-title mb-0">Результаты трассировки</h3>
|
<h3 className="card-title mb-0">Результаты ping</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<div className="card-body p-0">
|
||||||
|
{!pingResult ? (
|
||||||
|
<div className="p-3 text-muted small">
|
||||||
|
Результаты ping появятся здесь после запуска проверки.
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div className="table-responsive">
|
||||||
|
<table className="table table-vcenter card-table mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Цель</th>
|
||||||
|
<th style={{ width: 130 }}>Среднее, мс</th>
|
||||||
|
<th style={{ width: 140 }}>Мин / Макс, мс</th>
|
||||||
|
<th style={{ width: 110 }}>Потери</th>
|
||||||
|
<th style={{ width: 200 }}>Оценка</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{(() => {
|
||||||
|
const avg = typeof pingResult.avgMs === 'number' ? pingResult.avgMs : null;
|
||||||
|
const loss = typeof pingResult.loss === 'number' ? pingResult.loss : null;
|
||||||
|
|
||||||
|
let severity = 'ok';
|
||||||
|
if (loss != null && loss >= 50) severity = 'danger';
|
||||||
|
else if ((loss != null && loss > 0) || (avg != null && avg > 200)) severity = 'warn';
|
||||||
|
|
||||||
|
const rowClass =
|
||||||
|
severity === 'danger'
|
||||||
|
? 'table-danger'
|
||||||
|
: severity === 'warn'
|
||||||
|
? 'table-warning'
|
||||||
|
: '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<tr className={rowClass}>
|
||||||
|
<td>
|
||||||
|
<div className="d-flex flex-column">
|
||||||
|
<span className="fw-semibold">
|
||||||
|
{pingResult.target || target || '—'}
|
||||||
|
</span>
|
||||||
|
{gatewayMeta?.ip && (
|
||||||
|
<span className="text-muted small">
|
||||||
|
через gateway {gatewayMeta.ip}
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{avg != null ? (
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
avg > 200
|
||||||
|
? 'text-danger fw-semibold'
|
||||||
|
: avg > 120
|
||||||
|
? 'text-warning fw-semibold'
|
||||||
|
: ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{avg.toFixed(1)}
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
'—'
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{pingResult.minMs != null ? pingResult.minMs.toFixed(1) : '—'} /{' '}
|
||||||
|
{pingResult.maxMs != null ? pingResult.maxMs.toFixed(1) : '—'}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{loss != null ? (
|
||||||
|
<span
|
||||||
|
className={
|
||||||
|
loss >= 50
|
||||||
|
? 'badge bg-red-lt text-red'
|
||||||
|
: loss > 0
|
||||||
|
? 'badge bg-yellow-lt text-yellow'
|
||||||
|
: 'badge bg-green-lt text-green'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{loss}%
|
||||||
|
</span>
|
||||||
|
) : (
|
||||||
|
'—'
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{severity === 'danger' && (
|
||||||
|
<span className="badge bg-red-lt text-red me-1">Плохое качество</span>
|
||||||
|
)}
|
||||||
|
{severity === 'warn' && (
|
||||||
|
<span className="badge bg-yellow-lt text-yellow me-1">
|
||||||
|
Возможные проблемы
|
||||||
|
</span>
|
||||||
|
)}
|
||||||
|
<span className="text-muted small">
|
||||||
|
{loss === 0
|
||||||
|
? 'Все пакеты доставлены'
|
||||||
|
: loss != null
|
||||||
|
? `Потеряно пакетов: ${loss}%`
|
||||||
|
: 'Нет данных о потере пакетов'}
|
||||||
|
</span>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
);
|
||||||
|
})()}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{mode === 'traceroute' && (
|
||||||
|
<div className="card">
|
||||||
|
<div className="card-header">
|
||||||
|
<ul className="nav nav-tabs card-header-tabs" role="tablist">
|
||||||
|
<li className="nav-item" role="presentation">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`nav-link ${tracerouteTab === 'table' ? 'active' : ''}`}
|
||||||
|
onClick={() => setTracerouteTab('table')}
|
||||||
|
role="tab"
|
||||||
|
>
|
||||||
|
Результаты
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li className="nav-item" role="presentation">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`nav-link ${tracerouteTab === 'analysis' ? 'active' : ''}`}
|
||||||
|
onClick={() => setTracerouteTab('analysis')}
|
||||||
|
role="tab"
|
||||||
|
>
|
||||||
|
Расшифровка
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{tracerouteTab === 'table' ? (
|
||||||
<div className="card-body p-0">
|
<div className="card-body p-0">
|
||||||
{hops.length === 0 ? (
|
{hops.length === 0 ? (
|
||||||
<div className="p-3 text-muted small">
|
<div className="p-3 text-muted small">
|
||||||
@@ -643,135 +784,7 @@ function MikrotikTools() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{mode === 'ping' && (
|
|
||||||
<div className="card mb-3">
|
|
||||||
<div className="card-header">
|
|
||||||
<h3 className="card-title mb-0">Результаты ping</h3>
|
|
||||||
</div>
|
|
||||||
<div className="card-body p-0">
|
|
||||||
{!pingResult ? (
|
|
||||||
<div className="p-3 text-muted small">
|
|
||||||
Результаты ping появятся здесь после запуска проверки.
|
|
||||||
</div>
|
|
||||||
) : (
|
) : (
|
||||||
<div className="table-responsive">
|
|
||||||
<table className="table table-vcenter card-table mb-0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>Цель</th>
|
|
||||||
<th style={{ width: 130 }}>Среднее, мс</th>
|
|
||||||
<th style={{ width: 140 }}>Мин / Макс, мс</th>
|
|
||||||
<th style={{ width: 110 }}>Потери</th>
|
|
||||||
<th style={{ width: 200 }}>Оценка</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{(() => {
|
|
||||||
const avg = typeof pingResult.avgMs === 'number' ? pingResult.avgMs : null;
|
|
||||||
const loss = typeof pingResult.loss === 'number' ? pingResult.loss : null;
|
|
||||||
|
|
||||||
let severity = 'ok';
|
|
||||||
if (loss != null && loss >= 50) severity = 'danger';
|
|
||||||
else if ((loss != null && loss > 0) || (avg != null && avg > 200)) severity = 'warn';
|
|
||||||
|
|
||||||
const rowClass =
|
|
||||||
severity === 'danger'
|
|
||||||
? 'table-danger'
|
|
||||||
: severity === 'warn'
|
|
||||||
? 'table-warning'
|
|
||||||
: '';
|
|
||||||
|
|
||||||
return (
|
|
||||||
<tr className={rowClass}>
|
|
||||||
<td>
|
|
||||||
<div className="d-flex flex-column">
|
|
||||||
<span className="fw-semibold">
|
|
||||||
{pingResult.target || target || '—'}
|
|
||||||
</span>
|
|
||||||
{gatewayMeta?.ip && (
|
|
||||||
<span className="text-muted small">
|
|
||||||
через gateway {gatewayMeta.ip}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{avg != null ? (
|
|
||||||
<span
|
|
||||||
className={
|
|
||||||
avg > 200
|
|
||||||
? 'text-danger fw-semibold'
|
|
||||||
: avg > 120
|
|
||||||
? 'text-warning fw-semibold'
|
|
||||||
: ''
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{avg.toFixed(1)}
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
'—'
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{pingResult.minMs != null ? pingResult.minMs.toFixed(1) : '—'} /{' '}
|
|
||||||
{pingResult.maxMs != null ? pingResult.maxMs.toFixed(1) : '—'}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{loss != null ? (
|
|
||||||
<span
|
|
||||||
className={
|
|
||||||
loss >= 50
|
|
||||||
? 'badge bg-red-lt text-red'
|
|
||||||
: loss > 0
|
|
||||||
? 'badge bg-yellow-lt text-yellow'
|
|
||||||
: 'badge bg-green-lt text-green'
|
|
||||||
}
|
|
||||||
>
|
|
||||||
{loss}%
|
|
||||||
</span>
|
|
||||||
) : (
|
|
||||||
'—'
|
|
||||||
)}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{severity === 'danger' && (
|
|
||||||
<span className="badge bg-red-lt text-red me-1">Плохое качество</span>
|
|
||||||
)}
|
|
||||||
{severity === 'warn' && (
|
|
||||||
<span className="badge bg-yellow-lt text-yellow me-1">
|
|
||||||
Возможные проблемы
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
<span className="text-muted small">
|
|
||||||
{loss === 0
|
|
||||||
? 'Все пакеты доставлены'
|
|
||||||
: loss != null
|
|
||||||
? `Потеряно пакетов: ${loss}%`
|
|
||||||
: 'Нет данных о потере пакетов'}
|
|
||||||
</span>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
);
|
|
||||||
})()}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{mode === 'traceroute' && (
|
|
||||||
<div className="card">
|
|
||||||
<div className="card-header">
|
|
||||||
<h3 className="card-title d-flex align-items-center">
|
|
||||||
<IconAlertTriangle size={18} className="me-2 text-yellow" />
|
|
||||||
Расшифровка трассировки
|
|
||||||
</h3>
|
|
||||||
</div>
|
|
||||||
<div className="card-body">
|
<div className="card-body">
|
||||||
{analysis.length === 0 ? (
|
{analysis.length === 0 ? (
|
||||||
<div className="text-muted small">
|
<div className="text-muted small">
|
||||||
@@ -787,6 +800,7 @@ function MikrotikTools() {
|
|||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user