fix(ui): завершить адаптивность оптимизатора маршрутов

This commit is contained in:
Denozordec committed 2026-09-30 23:23:58 +07:00
1 parent 0ee7f02e87
commit fb70d80220
18 files changed
+173 -122

No files matched your search

@@ -37,7 +37,7 @@ function ProbChip({ prob, best }: { prob: number; best?: boolean }) {
<Chip
color={
best
? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20"
? "bg-success/10 text-success border-success/20"
: "bg-muted text-muted-foreground border-border"
}
>
@@ -105,12 +105,12 @@ function RouteOptimizerCommRecsDataGrid({
if (!r.current) return <span className="text-muted-foreground text-xs">—</span>
return (
<div className="text-xs flex items-center gap-1 flex-wrap">
<span className="font-mono font-medium text-sky-600 dark:text-sky-400">
<span className="font-mono font-medium text-info ">
{r.current.wan}
</span>
<ArrowRightIcon className="size-3 text-muted-foreground shrink-0" />
<ArrowRightIcon aria-hidden="true" className="size-3 text-muted-foreground shrink-0" />
<span>{r.current.jh}</span>
<ArrowRightIcon className="size-3 text-muted-foreground shrink-0" />
<ArrowRightIcon aria-hidden="true" className="size-3 text-muted-foreground shrink-0" />
<span className="text-muted-foreground">{r.current.exit}</span>
<span className="font-mono text-[10px] text-muted-foreground">
({r.current.gateway})
@@ -133,16 +133,16 @@ function RouteOptimizerCommRecsDataGrid({
<div
className={cn(
"text-xs flex items-center gap-1 flex-wrap",
r.shouldSwitch && !isPinned && "text-amber-600 dark:text-amber-400",
r.shouldSwitch && !isPinned && "text-warning ",
)}
>
<span className="font-mono font-medium">{r.recommended.wan}</span>
<ArrowRightIcon className="size-3 shrink-0 opacity-60" />
<ArrowRightIcon aria-hidden="true" className="size-3 shrink-0 opacity-60" />
<span>{r.recommended.jh}</span>
<ArrowRightIcon className="size-3 shrink-0 opacity-60" />
<ArrowRightIcon aria-hidden="true" className="size-3 shrink-0 opacity-60" />
<span>{r.recommended.exit}</span>
{r.shouldSwitch && !isPinned && (
<span className="ml-1 text-[10px] font-bold bg-amber-500/10 border border-amber-500/20 px-1.5 py-0.5 rounded">
<span className="ml-1 text-[10px] font-bold bg-warning/10 border border-warning/20 px-1.5 py-0.5 rounded">
+{(r.recommended.prob ?? 0) - (r.current?.prob ?? 0)} б.
</span>
)}
@@ -192,32 +192,32 @@ function RouteOptimizerCommRecsDataGrid({
const canApply = simulation && r.shouldSwitch && !isPinned && !isApplied
return (
<div className="flex items-center justify-end gap-1.5" onClick={(e) => e.stopPropagation()}>
{isPinned && <PinIcon className="size-3 text-sky-500 fill-sky-500" />}
<Button
{isPinned && <PinIcon aria-hidden="true" className="size-3 text-info fill-info" />}
<Button type="button"
variant="outline"
size="sm"
className={cn("h-7 text-xs", isPinned && "text-sky-600 dark:text-sky-400 border-sky-500/30")}
className={cn("h-7 text-xs", isPinned && "text-info border-info/30")}
onClick={() => onPin(pinKey)}
>
<PinIcon className={cn("size-3", isPinned && "fill-current")} />
<PinIcon aria-hidden="true" className={cn("size-3", isPinned && "fill-current")} />
{isPinned ? "Открепить" : "Закрепить"}
</Button>
{canApply && (
<Button
<Button type="button"
size="sm"
className="h-7 text-xs"
disabled={isApplying}
onClick={() => onApply(r.community, homeId)}
>
{isApplying ? (
<RefreshCwIcon className="size-3 animate-spin" />
<RefreshCwIcon aria-hidden="true" className="size-3 animate-spin" />
) : (
"Симулировать"
)}
</Button>
)}
{isApplied && (
<span className="text-[10px] text-emerald-600 dark:text-emerald-400 font-medium">
<span className="text-[10px] text-success font-medium">
✓ симуляция завершена
</span>
)}
@@ -249,8 +249,8 @@ function RouteOptimizerCommRecsDataGrid({
headerRow: "border-b bg-muted/20",
bodyRow: cn(
"group/row hover:bg-muted/30",
"[&:has([data-comm-switch=true])]:bg-amber-500/5",
"[&:has([data-comm-applied=true])]:bg-emerald-500/5",
"[&:has([data-comm-switch=true])]:bg-warning/5",
"[&:has([data-comm-applied=true])]:bg-success/5",
),
}}
tableLayout={{ dense: true }}
@@ -37,7 +37,7 @@ function ProbChip({ prob, best }: { prob: number; best?: boolean }) {
<Chip
color={
best
? "bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 border-emerald-500/20"
? "bg-success/10 text-success border-success/20"
: "bg-muted text-muted-foreground border-border"
}
>
@@ -48,9 +48,9 @@ function ProbChip({ prob, best }: { prob: number; best?: boolean }) {
function ConfChip({ conf }: { conf: string }) {
const map: Record<string, string> = {
HIGH: "bg-sky-500/10 text-sky-600 dark:text-sky-400 border-sky-500/20",
MEDIUM: "bg-amber-500/10 text-amber-600 dark:text-amber-400 border-amber-500/20",
LOW: "bg-red-500/10 text-red-600 dark:text-red-400 border-red-500/20",
HIGH: "bg-info/10 text-info border-info/20",
MEDIUM: "bg-warning/10 text-warning border-warning/20",
LOW: "bg-destructive/10 text-destructive border-destructive/20",
}
return <Chip color={map[conf] ?? map.LOW}>{conf}</Chip>
}
@@ -86,7 +86,7 @@ function RouteOptimizerFullRoutesDataGrid({
{row.original._index + 1}
</span>
{isBest && (
<span className="text-[9px] font-bold uppercase tracking-wide text-emerald-600 dark:text-emerald-400 bg-emerald-500/10 border border-emerald-500/20 px-1.5 py-0.5 rounded">
<span className="text-[9px] font-bold uppercase tracking-wide text-success bg-success/10 border border-success/20 px-1.5 py-0.5 rounded">
Лучший
</span>
)}
@@ -103,8 +103,8 @@ function RouteOptimizerFullRoutesDataGrid({
const r = row.original
return (
<div className="flex items-center gap-1.5 text-xs">
<span className="font-mono font-semibold text-sky-600 dark:text-sky-400">{r.wan.name}</span>
<ArrowRightIcon className="size-3 text-muted-foreground shrink-0" />
<span className="font-mono font-semibold text-info ">{r.wan.name}</span>
<ArrowRightIcon aria-hidden="true" className="size-3 text-muted-foreground shrink-0" />
<div>
<div className="font-medium">{r.jh.label}</div>
<div className="font-mono text-[10px] text-muted-foreground">
@@ -150,10 +150,10 @@ function RouteOptimizerFullRoutesDataGrid({
className={cn(
"font-mono text-xs text-center block",
totalPing < 40
? "text-emerald-600 dark:text-emerald-400"
? "text-success "
: totalPing < 80
? "text-amber-600 dark:text-amber-400"
: "text-red-500",
? "text-warning "
: "text-destructive",
)}
>
{totalPing} мс
@@ -263,7 +263,7 @@ function RouteOptimizerFullRoutesDataGrid({
recordCount={visibleRoutes.length}
tableClassNames={{
headerRow: "border-b bg-muted/20",
bodyRow: cn("group/row hover:bg-muted/30", "has-[[data-route-best=true]]:bg-emerald-500/5"),
bodyRow: cn("group/row hover:bg-muted/30", "has-[[data-route-best=true]]:bg-success/5"),
}}
tableLayout={{ dense: true }}
/>
@@ -275,12 +275,12 @@ function RouteOptimizerFullRoutesDataGrid({
>
{expanded ? (
<>
<ChevronUpIcon className="size-3" />
<ChevronUpIcon aria-hidden="true" className="size-3" />
Свернуть
</>
) : (
<>
<ChevronDownIcon className="size-3" />
<ChevronDownIcon aria-hidden="true" className="size-3" />
Показать все {routes.length} комбинаций
</>
)}
@@ -62,13 +62,13 @@ function RouteOptimizerOspfPreviewDataGrid({
const r = row.original
return (
<span className="font-mono">
<span className="text-sky-600 dark:text-sky-400">{r.currentCost}</span>
<span className="text-info ">{r.currentCost}</span>
{" → "}
<span
className={
r.currentCost === r.optimalCost
? "text-emerald-600 dark:text-emerald-400"
: "text-amber-600 dark:text-amber-400"
? "text-success "
: "text-warning "
}
>
{r.optimalCost}
@@ -127,7 +127,7 @@ function RouteOptimizerOspfPreviewDataGrid({
if (!loading && rows.length === 0) {
return (
<EmptyState
icon={<NetworkIcon className="size-4" />}
icon={<NetworkIcon aria-hidden="true" className="size-4" />}
title="Интерфейсы OSPF не найдены для выбранного сервера"
className="border-0 py-6 text-xs"
/>
@@ -19,9 +19,9 @@ import {
import { WifiIcon } from "lucide-react"
function LossChip({ loss }: { loss: number }) {
if (loss === 0) return <span className="text-emerald-600 dark:text-emerald-400 text-[11px] font-mono">0%</span>
if (loss === 0) return <span className="text-success text-[11px] font-mono">0%</span>
return (
<span className={cn("text-[11px] font-mono", loss > 1 ? "text-red-500" : "text-amber-500")}>
<span className={cn("text-[11px] font-mono", loss > 1 ? "text-destructive" : "text-warning")}>
{loss}%
</span>
)
@@ -50,7 +50,7 @@ function RouteOptimizerWanMatrixDataGrid({
const wan = row.original
return (
<div className="flex items-center gap-2">
<WifiIcon className="size-3.5 text-muted-foreground shrink-0" />
<WifiIcon aria-hidden="true" className="size-3.5 text-muted-foreground shrink-0" />
<div>
<p className="font-mono text-xs font-semibold">{wan.name}</p>
<p className="text-[10px] text-muted-foreground">{wan.iface}</p>
@@ -114,15 +114,15 @@ function RouteOptimizerWanMatrixDataGrid({
if (!leg) return <span className="text-center text-muted-foreground text-xs block">—</span>
const isBest = leg.score === bestScore
return (
<div className={cn("text-center", isBest && "bg-emerald-500/5")}>
<div className={cn("text-center", isBest && "bg-success/5")}>
<div
className={cn(
"flex flex-col items-center gap-0.5 rounded-md px-2 py-1.5 transition-colors",
isBest ? "border border-emerald-500/20 bg-emerald-500/8" : "border border-transparent",
isBest ? "border border-success/20 bg-success/8" : "border border-transparent",
)}
>
{isBest && (
<span className="text-[9px] font-bold uppercase tracking-wide text-emerald-600 dark:text-emerald-400 mb-0.5">
<span className="text-[9px] font-bold uppercase tracking-wide text-success mb-0.5">
★ ЛУЧШИЙ
</span>
)}
@@ -130,10 +130,10 @@ function RouteOptimizerWanMatrixDataGrid({
className={cn(
"font-mono text-xs font-semibold",
leg.pingMs < 10
? "text-emerald-600 dark:text-emerald-400"
? "text-success "
: leg.pingMs < 25
? "text-foreground"
: "text-amber-600 dark:text-amber-400",
: "text-warning ",
)}
>
{leg.pingMs} мс