Skip to content

Commit 7d263e9

Browse files
committed
feat: introduce custom ECharts candlestick chart for stock history, displayed conditionally for logged-in users.
1 parent 99407f5 commit 7d263e9

File tree

3 files changed

+387
-12
lines changed

3 files changed

+387
-12
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import { useMemo, useState } from "react";
2+
import { useGetStockHistory } from "@/api/stock";
3+
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
4+
import CandlestickChart from "@/components/valuecell/charts/candlestick-chart";
5+
import { TimeUtils } from "@/lib/time";
6+
import { cn } from "@/lib/utils";
7+
import type { StockInterval } from "@/types/stock";
8+
9+
interface StockHistoryChartProps {
10+
ticker: string;
11+
className?: string;
12+
}
13+
14+
const INTERVALS: { label: string; value: StockInterval }[] = [
15+
{ label: "1H", value: "1h" },
16+
{ label: "1D", value: "1d" },
17+
{ label: "1W", value: "1w" },
18+
];
19+
20+
export const StockHistoryChart = ({
21+
ticker,
22+
className,
23+
}: StockHistoryChartProps) => {
24+
const [interval, setInterval] = useState<StockInterval>("1d");
25+
26+
// Calculate date range based on interval
27+
const { startDate, endDate } = useMemo(() => {
28+
const now = TimeUtils.now();
29+
30+
let start = now;
31+
switch (interval) {
32+
case "1h":
33+
start = now.subtract(6, "month");
34+
break;
35+
case "1d":
36+
start = now.subtract(3, "year");
37+
break;
38+
case "1w":
39+
start = now.subtract(10, "year");
40+
break;
41+
default:
42+
start = now.subtract(3, "year");
43+
}
44+
45+
return {
46+
startDate: start.format("YYYY-MM-DD"),
47+
endDate: now.format("YYYY-MM-DD"),
48+
};
49+
}, [interval]);
50+
51+
const { data: historyData, isLoading } = useGetStockHistory({
52+
ticker,
53+
interval,
54+
start_date: startDate,
55+
end_date: endDate,
56+
});
57+
58+
return (
59+
<div className={cn("flex flex-col gap-4", className)}>
60+
<Tabs
61+
value={interval}
62+
onValueChange={(value) => setInterval(value as StockInterval)}
63+
>
64+
<TabsList>
65+
{INTERVALS.map((item) => (
66+
<TabsTrigger key={item.value} value={item.value}>
67+
{item.label}
68+
</TabsTrigger>
69+
))}
70+
</TabsList>
71+
</Tabs>
72+
<CandlestickChart
73+
data={historyData ?? []}
74+
height={500}
75+
loading={isLoading}
76+
showVolume
77+
/>
78+
</div>
79+
);
80+
};

frontend/src/app/home/stock.tsx

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,18 @@ import { useGetStockDetail, useRemoveStockFromWatchlist } from "@/api/stock";
55
import TradingViewAdvancedChart from "@/components/tradingview/tradingview-advanced-chart";
66
import { Button } from "@/components/ui/button";
77
import LinkButton from "@/components/valuecell/button/link-button";
8+
import { useIsLoggedIn } from "@/store/system-store";
89
import type { Route } from "./+types/stock";
10+
import { StockHistoryChart } from "./components/stock-history-chart";
911

1012
function Stock() {
1113
const { stockId } = useParams<Route.LoaderArgs["params"]>();
1214
const navigate = useNavigate();
1315
// Use stockId as ticker to fetch real data from API
1416
const ticker = stockId || "";
1517

18+
const isLoggedIn = useIsLoggedIn();
19+
1620
// Fetch stock detail data
1721
const {
1822
data: stockDetailData,
@@ -73,22 +77,28 @@ function Stock() {
7377
</div>
7478
</div>
7579
<div className="w-full">
76-
<TradingViewAdvancedChart
77-
ticker={ticker}
78-
interval="D"
79-
minHeight={420}
80-
theme="light"
81-
locale="en"
82-
timezone="UTC"
83-
/>
80+
{isLoggedIn ? (
81+
<StockHistoryChart ticker={ticker} />
82+
) : (
83+
<TradingViewAdvancedChart
84+
ticker={ticker}
85+
interval="D"
86+
minHeight={420}
87+
theme="light"
88+
locale="en"
89+
timezone="UTC"
90+
/>
91+
)}
8492
</div>
8593

86-
<div className="flex flex-col gap-4">
94+
<div className="flex flex-col gap-2">
8795
<h2 className="font-bold text-lg">About</h2>
8896

89-
<p className="line-clamp-4 text-neutral-500 text-sm leading-6">
90-
{stockDetailData?.properties?.business_summary}
91-
</p>
97+
{stockDetailData?.properties.business_summary && (
98+
<p className="line-clamp-4 text-neutral-500 text-sm leading-6">
99+
{stockDetailData?.properties?.business_summary}
100+
</p>
101+
)}
92102

93103
{stockDetailData?.properties && (
94104
<div className="mt-4 grid grid-cols-2 gap-4 text-sm">

0 commit comments

Comments
 (0)