1
0

ThroughputCard.tsx 3.3 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697
  1. import { useMemo } from 'react';
  2. import { useTranslation } from 'react-i18next';
  3. import { Card, theme } from 'antd';
  4. import { ArrowDownOutlined, ArrowUpOutlined } from '@ant-design/icons';
  5. import { SizeFormatter } from '@/utils';
  6. import { Sparkline } from '@/components/viz';
  7. import type { Status } from '@/models/status';
  8. import { mean, peak } from './useOverviewHistory';
  9. interface ThroughputCardProps {
  10. status: Status;
  11. up: number[];
  12. down: number[];
  13. labels: string[];
  14. isMobile: boolean;
  15. }
  16. export default function ThroughputCard({ status, up, down, labels, isMobile }: ThroughputCardProps) {
  17. const { t } = useTranslation();
  18. const { token } = theme.useToken();
  19. const accent = token.colorPrimary;
  20. const downColor = token.colorTextTertiary;
  21. const referenceLines = useMemo(
  22. () => [
  23. { y: status.netIO.down, color: downColor, dash: '2 4' },
  24. { y: status.netIO.up, color: accent, dash: '2 4' },
  25. ],
  26. [status.netIO.up, status.netIO.down, accent, downColor],
  27. );
  28. return (
  29. <Card hoverable styles={{ body: { padding: 0 } }}>
  30. <div className="ov-wide-head">
  31. <div>
  32. <div className="ov-kicker">{t('pages.index.overallSpeed')}</div>
  33. <div className="ov-sub">
  34. {`${t('pages.index.throughputSub')} · ${t('pages.index.peak')} ${SizeFormatter.speedFormat(peak(down))}`}
  35. </div>
  36. </div>
  37. <div className="ov-wide-legend">
  38. <div className="ov-legend-label">
  39. <ArrowUpOutlined style={{ color: accent }} />
  40. {t('pages.index.upload')}
  41. <span className="ov-legend-num">{SizeFormatter.speedFormat(status.netIO.up)}</span>
  42. </div>
  43. <div className="ov-legend-label">
  44. <ArrowDownOutlined style={{ color: downColor }} />
  45. {t('pages.index.download')}
  46. <span className="ov-legend-num">{SizeFormatter.speedFormat(status.netIO.down)}</span>
  47. </div>
  48. </div>
  49. </div>
  50. <div className="ov-wide-chart">
  51. <Sparkline
  52. data={up}
  53. data2={down}
  54. labels={labels}
  55. height={isMobile ? 140 : 186}
  56. strokeWidth={1.75}
  57. fillOpacity={0.24}
  58. showTooltip
  59. showLegend={false}
  60. valueMax={null}
  61. stroke={accent}
  62. stroke2={downColor}
  63. name1={t('pages.index.upload')}
  64. name2={t('pages.index.download')}
  65. yFormatter={SizeFormatter.speedFormat}
  66. referenceLines={referenceLines}
  67. />
  68. </div>
  69. <div className="ov-wide-foot">
  70. <div>
  71. <div className="ov-kicker">{t('pages.index.sent')}</div>
  72. <div className="ov-foot-value">{SizeFormatter.sizeFormat(status.netTraffic.sent)}</div>
  73. </div>
  74. <span className="ov-foot-sep" />
  75. <div>
  76. <div className="ov-kicker">{t('pages.index.received')}</div>
  77. <div className="ov-foot-value">{SizeFormatter.sizeFormat(status.netTraffic.recv)}</div>
  78. </div>
  79. <span className="ov-foot-sep" />
  80. <div>
  81. <div className="ov-kicker">{t('pages.index.avgWindow')}</div>
  82. <div className="ov-foot-value">
  83. <span className="ov-foot-part">{`↑ ${SizeFormatter.speedFormat(mean(up))}`}</span>{' '}
  84. <span className="ov-foot-part">{`↓ ${SizeFormatter.speedFormat(mean(down))}`}</span>
  85. </div>
  86. </div>
  87. </div>
  88. </Card>
  89. );
  90. }