import "./index.css"; import { useState, useMemo } from "react"; import { ChartConfig, ChartContainer, ChartTooltip, ChartTooltipContent, } from "@/components/ui/chart"; import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts"; import { Price } from "../../../types"; import { format } from "date-fns"; import { Button } from "@/components/ui/button"; import { cn } from "@/lib/utils"; const chartConfig = { price: { label: "Price", color: "hsl(var(--chart-1))", }, } satisfies ChartConfig; type DisplayRange = "1d" | "5d" | "1m"; function DisplayRangeSelector({ displayRange, setDisplayRange, }: { displayRange: DisplayRange; setDisplayRange: (range: DisplayRange) => void; }) { const sharedClass = " bg-transparent text-gray-500 hover:bg-gray-50 transition-colors ease-in-out duration-200 p-2 cursor-pointer"; const selectedClass = `text-black bg-gray-100 hover:bg-gray-50`; return (

|

|

); } function getPropsForDisplayRange( displayRange: DisplayRange, oneDayPrices: Price[], thirtyDayPrices: Price[], ) { const now = new Date(); const fiveDays = 5 * 24 * 60 * 60 * 1000; // 5 days in milliseconds switch (displayRange) { case "1d": return oneDayPrices; case "5d": return thirtyDayPrices.filter( (p) => new Date(p.time).getTime() >= now.getTime() - fiveDays, ); case "1m": return thirtyDayPrices; default: return []; } } export default function StockPrice(props: { ticker: string; oneDayPrices: Price[]; thirtyDayPrices: Price[]; }) { const { ticker } = props; const { oneDayPrices, thirtyDayPrices } = props; const [displayRange, setDisplayRange] = useState("1d"); const { currentPrice, openPrice, dollarChange, percentChange, highPrice, lowPrice, chartData, change, } = useMemo(() => { const prices = getPropsForDisplayRange( displayRange, oneDayPrices, thirtyDayPrices, ); const firstPrice = prices[0]; const lastPrice = prices[prices.length - 1]; const currentPrice = lastPrice?.close; const openPrice = firstPrice?.open; const dollarChange = currentPrice - openPrice; const percentChange = ((currentPrice - openPrice) / openPrice) * 100; const highPrice = prices.reduce( (acc, p) => Math.max(acc, p.high), -Infinity, ); const lowPrice = prices.reduce((acc, p) => Math.min(acc, p.low), Infinity); const chartData = prices.map((p) => ({ time: p.time, price: p.close, })); const change: "up" | "down" = dollarChange > 0 ? "up" : "down"; return { currentPrice, openPrice, dollarChange, percentChange, highPrice, lowPrice, chartData, change, }; }, [oneDayPrices, thirtyDayPrices, displayRange]); const formatDateByDisplayRange = (value: string, isTooltip?: boolean) => { if (displayRange === "1d") { return format(value, "h:mm a"); } if (isTooltip) { return format(value, "LLL do h:mm a"); } return format(value, "LLL do"); }; return (

{ticker}

${currentPrice}

${dollarChange.toFixed(2)} (${percentChange.toFixed(2)}%)

Open

High

Low

${openPrice}

${highPrice}

${lowPrice}

formatDateByDisplayRange(v)} /> `${value.toFixed(2)}`} /> formatDateByDisplayRange(v, true)} /> } />
); }