refactor(MikrotikTools): replace gateway input with card-based target selection for improved UI and user interaction
Publish Fast Tabler Docker image / build-and-push-fast (push) Failing after 3m30s

This commit is contained in:
2026-02-13 00:09:50 +07:00
parent 34a52ed826
commit 79ca6e2d05
+19 -23
View File
@@ -1,7 +1,6 @@
import { useEffect, useMemo, useState } from 'react'; import { useEffect, useMemo, useState } from 'react';
import api from './lib/api.js'; import api from './lib/api.js';
import { useNotify } from './components/NotifyProvider.jsx'; import { useNotify } from './components/NotifyProvider.jsx';
import GatewayAutocompleteInput from './components/GatewayAutocompleteInput.jsx';
import PageHeader from './components/PageHeader.jsx'; import PageHeader from './components/PageHeader.jsx';
import TableSkeleton from './components/TableSkeleton.jsx'; import TableSkeleton from './components/TableSkeleton.jsx';
import EmptyState from './components/EmptyState.jsx'; import EmptyState from './components/EmptyState.jsx';
@@ -366,36 +365,33 @@ function MikrotikTools() {
})} })}
</div> </div>
)} )}
<GatewayAutocompleteInput
value={gatewayRef}
onChange={setGatewayRef}
gateways={gateways}
interfaces={interfaces}
serverId={serverId || null}
placeholder="IP gateway или интерфейс…"
onSelectMeta={handleSelectGatewayMeta}
/>
</> </>
)} )}
{/* Быстрые цели — чипы */} {/* Цели — карточки в стиле сервера/gateway */}
<div className="d-flex flex-wrap align-items-center gap-1 mt-2"> <div className="row row-cards g-1 mt-2">
<span className="text-muted small me-1">Цель:</span>
{POPULAR_TARGETS.map(({ id: tid, name, target: tgt, Icon, color }) => { {POPULAR_TARGETS.map(({ id: tid, name, target: tgt, Icon, color }) => {
const isSelected = target === tgt; const isSelected = target === tgt;
const bgClass = color === 'red' ? 'bg-red-lt' : color === 'blue' ? 'bg-blue-lt' : 'bg-orange-lt'; const bgClass = color === 'red' ? 'bg-red-lt' : color === 'blue' ? 'bg-blue-lt' : 'bg-orange-lt';
const textClass = color === 'red' ? 'text-red' : color === 'blue' ? 'text-blue' : 'text-orange'; const textClass = color === 'red' ? 'text-red' : color === 'blue' ? 'text-blue' : 'text-orange';
return ( return (
<button <div className="col-12 col-sm-6 col-md-4" key={tid}>
type="button" <div
key={tid} className={`card card-sm h-100 cursor-pointer ${isSelected ? 'border-primary card-hover' : 'card-hover'}`}
className={`btn btn-sm ${isSelected ? `${bgClass} ${textClass}` : 'btn-outline-secondary'}`} onClick={() => setTarget(tgt)}
onClick={() => setTarget(tgt)} title={tgt}
title={tgt} >
> <div className="card-body py-2 px-2 d-flex align-items-center gap-2">
<Icon size={14} stroke={1.5} className="me-1" /> <span className={`avatar avatar-sm ${bgClass} ${textClass}`}>
{name} <Icon size={20} stroke={1.5} />
</button> </span>
<div className="flex-grow-1 min-w-0">
<div className="fw-semibold text-truncate small">{name}</div>
<div className="text-muted small text-truncate">{tgt}</div>
</div>
</div>
</div>
</div>
); );
})} })}
</div> </div>