125 lines
4.0 KiB
TypeScript
125 lines
4.0 KiB
TypeScript
"use client";
|
|
|
|
import { useMemo } from "react";
|
|
import {
|
|
AreaChart,
|
|
Area,
|
|
XAxis,
|
|
YAxis,
|
|
ResponsiveContainer,
|
|
Tooltip,
|
|
} from "recharts";
|
|
import Panel from "./ui/Panel";
|
|
|
|
function generatePriceData() {
|
|
const data = [];
|
|
let price = 870;
|
|
for (let i = 0; i < 60; i++) {
|
|
price += (Math.random() - 0.47) * 8;
|
|
data.push({
|
|
time: `${Math.floor(i / 4) + 9}:${String((i % 4) * 15).padStart(2, "0")}`,
|
|
price: Math.round(price * 100) / 100,
|
|
});
|
|
}
|
|
return data;
|
|
}
|
|
|
|
const stats = [
|
|
{ label: "Current Price", value: "$892.45", type: "up" as const },
|
|
{ label: "Day Change", value: "+$27.83 (+3.21%)", type: "up" as const },
|
|
{ label: "Volume", value: "48.2M", type: "neutral" as const },
|
|
{ label: "RSI (14)", value: "67.4", type: "neutral" as const },
|
|
{ label: "MACD Signal", value: "Bullish Cross", type: "up" as const },
|
|
{ label: "50 SMA", value: "$842.10", type: "up" as const },
|
|
{ label: "200 SMA", value: "$756.30", type: "up" as const },
|
|
{ label: "ATR (14)", value: "$18.92", type: "neutral" as const },
|
|
{ label: "Bollinger", value: "Upper Band", type: "down" as const },
|
|
];
|
|
|
|
const typeColors = {
|
|
up: "text-green border-l-green",
|
|
down: "text-red border-l-red",
|
|
neutral: "text-cyan border-l-cyan",
|
|
};
|
|
|
|
export default function MarketOverview() {
|
|
const data = useMemo(() => generatePriceData(), []);
|
|
|
|
return (
|
|
<Panel
|
|
title="Market Overview"
|
|
badge="Live"
|
|
badgeVariant="live"
|
|
className="col-span-2"
|
|
delay={0.15}
|
|
>
|
|
<div className="grid grid-cols-[1fr_280px] gap-4">
|
|
<div className="h-[200px] bg-bg-elevated rounded-lg overflow-hidden">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<AreaChart
|
|
data={data}
|
|
margin={{ top: 10, right: 10, left: 0, bottom: 0 }}
|
|
>
|
|
<defs>
|
|
<linearGradient id="priceGrad" x1="0" y1="0" x2="0" y2="1">
|
|
<stop offset="0%" stopColor="#4ecdc4" stopOpacity={0.2} />
|
|
<stop offset="100%" stopColor="#4ecdc4" stopOpacity={0} />
|
|
</linearGradient>
|
|
</defs>
|
|
<XAxis
|
|
dataKey="time"
|
|
tick={{ fill: "#5a5853", fontSize: 10 }}
|
|
axisLine={{ stroke: "rgba(255,255,255,0.04)" }}
|
|
tickLine={false}
|
|
interval={9}
|
|
/>
|
|
<YAxis
|
|
domain={["dataMin - 5", "dataMax + 5"]}
|
|
tick={{ fill: "#5a5853", fontSize: 10 }}
|
|
axisLine={false}
|
|
tickLine={false}
|
|
tickFormatter={(v: number) => `$${v.toFixed(0)}`}
|
|
width={48}
|
|
/>
|
|
<Tooltip
|
|
contentStyle={{
|
|
background: "#14161d",
|
|
border: "1px solid rgba(255,255,255,0.08)",
|
|
borderRadius: 8,
|
|
fontSize: 11,
|
|
color: "#e8e6e1",
|
|
}}
|
|
labelStyle={{ color: "#8a8780" }}
|
|
formatter={(value) => [`$${Number(value).toFixed(2)}`, "Price"]}
|
|
/>
|
|
<Area
|
|
type="monotone"
|
|
dataKey="price"
|
|
stroke="#4ecdc4"
|
|
strokeWidth={1.5}
|
|
fill="url(#priceGrad)"
|
|
/>
|
|
</AreaChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-2 overflow-y-auto max-h-[200px]">
|
|
{stats.map((s) => (
|
|
<div
|
|
key={s.label}
|
|
className={`flex justify-between items-center px-3 py-2 bg-bg-elevated rounded border-l-2 ${typeColors[s.type]}`}
|
|
>
|
|
<span className="text-[10px] text-text-tertiary uppercase tracking-[1px]">
|
|
{s.label}
|
|
</span>
|
|
<span className={`font-display font-semibold text-[13px] ${typeColors[s.type].split(" ")[0]}`}>
|
|
{s.value}
|
|
</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</Panel>
|
|
);
|
|
}
|