mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-17 15:47:14 +00:00
c3fa73d5a0
Replace the ten-small-cards overview with an action bar, four vitals
tiles carrying 72-sample sparklines seeded from /server/history, a
two-series throughput chart, a TCP/UDP connections chart, and a
grouped system strip (uptime xray|os, panel ram|threads, ip
addresses). StatusCard and XrayStatusCard are deleted; every modal
stays reachable from the action bar, the Xray error message moves
into a tooltip on the state pill, and the panel version text keeps
opening the update modal (the dev-channel switch lives there) even
when no update is available. Live values sit beside the
upload/download and tcp/udp legends, a health sentence appears only
when a vital crosses the shared warn/crit thresholds now exported
from models/status, and load average is left to System History.
The sidebar becomes an auto-collapsed 72px icon rail that expands as
an overlay on hover: rail width, brand-row height and menu paddings
are pinned so nothing shifts during the transition, the collapsed-menu
tooltips are disabled, hover state survives the per-page sidebar
remounts (with a matches(':hover') resync), and the manual collapse
trigger is gone.
Sparkline gains rgb()/rgba() support in its fill gradient, a
showLegend prop so pages stop reaching into its internals, and loses
a dependency-less repaint effect that doubled canvas paints. Chart
tooltips show clock time via the new TimeFormatter.formatClock;
accents come from theme tokens instead of status.cpu.color. Verified
by screenshot at 390/800/1150/1280/1400/1600px in light and dark,
en and fa-IR, plus programmatic geometry checks on the sidebar.
Locale files gain 8 keys and lose 9 dead ones across all 13
languages.
76 lines
1.8 KiB
TypeScript
76 lines
1.8 KiB
TypeScript
import { useMemo } from 'react';
|
|
import type { ReactNode } from 'react';
|
|
import { Card, theme } from 'antd';
|
|
|
|
import { Sparkline } from '@/components/viz';
|
|
import { mean, peak } from './useOverviewHistory';
|
|
|
|
interface VitalTileProps {
|
|
icon: ReactNode;
|
|
label: string;
|
|
percent: number;
|
|
statusColor: string;
|
|
detail: string;
|
|
footLeft: string;
|
|
footRight: string;
|
|
data: number[];
|
|
isMobile: boolean;
|
|
}
|
|
|
|
export default function VitalTile({
|
|
icon,
|
|
label,
|
|
percent,
|
|
statusColor,
|
|
detail,
|
|
footLeft,
|
|
footRight,
|
|
data,
|
|
isMobile,
|
|
}: VitalTileProps) {
|
|
const { token } = theme.useToken();
|
|
const meanColor = token.colorTextTertiary;
|
|
|
|
const referenceLines = useMemo(
|
|
() => (data.length > 1 ? [{ y: mean(data), dash: '3 4', color: meanColor }] : []),
|
|
[data, meanColor],
|
|
);
|
|
|
|
return (
|
|
<Card hoverable className="ov-tile" styles={{ body: { padding: 0 } }}>
|
|
<div className="ov-tile-head">
|
|
<span className="ov-tile-icon">{icon}</span>
|
|
<span className="ov-kicker">{label}</span>
|
|
</div>
|
|
|
|
<div className="ov-tile-value">
|
|
<span className="ov-tile-number">{percent.toFixed(1)}</span>
|
|
<span className="ov-tile-unit">%</span>
|
|
</div>
|
|
|
|
<div className="ov-tile-detail">{detail}</div>
|
|
|
|
<div className="ov-tile-foot">
|
|
<span>{footLeft}</span>
|
|
<span>{footRight}</span>
|
|
</div>
|
|
|
|
<div className="ov-tile-chart">
|
|
<Sparkline
|
|
data={data}
|
|
height={isMobile ? 48 : 62}
|
|
strokeWidth={1.5}
|
|
fillOpacity={0.3}
|
|
showGrid={false}
|
|
showMarker={false}
|
|
valueMax={peak(data) > 0 ? null : 100}
|
|
stroke={statusColor}
|
|
referenceLines={referenceLines}
|
|
yFormatter={(v) => `${v.toFixed(0)}%`}
|
|
name1={label}
|
|
/>
|
|
</div>
|
|
</Card>
|
|
);
|
|
}
|