diff --git a/frontend/src/components/HighLatencyBanner.tsx b/frontend/src/components/HighLatencyBanner.tsx new file mode 100644 index 00000000..0fdeab54 --- /dev/null +++ b/frontend/src/components/HighLatencyBanner.tsx @@ -0,0 +1,73 @@ +import React, { useState } from "react"; +import { useNetworkQuality, type NetworkQuality } from "../hooks/useNetworkQuality"; +import { AlertTriangle, Clock, Wifi, X } from "./icons"; + +const CONFIG: Record = { + fast: { color: "#22c55e", bg: "rgba(34, 197, 94, 0.1)", label: "Fast network", icon: }, + normal: { color: "var(--text-secondary)", bg: "transparent", label: "Normal network", icon: }, + slow: { + color: "#eab308", + bg: "rgba(234, 179, 8, 0.1)", + label: "Slow network — reduced refresh rate", + icon: , + }, + degraded: { + color: "#ef4444", + bg: "rgba(239, 68, 68, 0.1)", + label: "Degraded network — data may be stale", + icon: , + }, +}; + +const HighLatencyBanner: React.FC = () => { + const { quality, latencyMs, jitterMs } = useNetworkQuality(); + const [dismissed, setDismissed] = useState(false); + + if (quality === "fast" || quality === "normal" || dismissed) return null; + + const cfg = CONFIG[quality]; + + return ( +
+ {cfg.icon} + + {cfg.label} + + ({latencyMs}ms{quality === "degraded" ? `, jitter ${jitterMs}ms` : ""}) + + + +
+ ); +}; + +export default HighLatencyBanner; diff --git a/frontend/src/components/Skeleton.tsx b/frontend/src/components/Skeleton.tsx index 7c2a31c7..ae5cf1c0 100644 --- a/frontend/src/components/Skeleton.tsx +++ b/frontend/src/components/Skeleton.tsx @@ -165,5 +165,85 @@ export const ChartSkeleton: React.FC = () => { ); }; +export const SharePriceSkeleton: React.FC = () => ( + +); + +export const VaultStatSkeleton: React.FC = () => ( + +); + +export const TransactionRowSkeleton: React.FC = () => ( + +); + +export const PortfolioCardSkeleton: React.FC = () => ( +