255 lines
8.0 KiB
TypeScript
255 lines
8.0 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Plus, Settings } from "lucide-react"
|
|
|
|
interface WatchlistProps {
|
|
onSelectStock: (symbol: string) => void
|
|
}
|
|
|
|
export function Watchlist({ onSelectStock }: WatchlistProps) {
|
|
const [activeTab, setActiveTab] = useState("active")
|
|
|
|
const watchlistStocks = [
|
|
{
|
|
symbol: "MSFT",
|
|
exchange: "NASDAQ",
|
|
name: "Microsoft Corporation",
|
|
price: 507.03,
|
|
changePercent: -0.61,
|
|
logo: "https://logo.clearbit.com/microsoft.com",
|
|
bgColor: "bg-orange-500",
|
|
},
|
|
{
|
|
symbol: "GOOG",
|
|
exchange: "NASDAQ",
|
|
name: "Alphabet Inc.",
|
|
price: 246.57,
|
|
changePercent: -0.51,
|
|
logo: "https://logo.clearbit.com/google.com",
|
|
bgColor: "bg-blue-500",
|
|
},
|
|
{
|
|
symbol: "AMZN",
|
|
exchange: "NASDAQ",
|
|
name: "Amazon.com, Inc.",
|
|
price: 218.15,
|
|
changePercent: -0.94,
|
|
logo: "https://logo.clearbit.com/amazon.com",
|
|
bgColor: "bg-orange-600",
|
|
},
|
|
{
|
|
symbol: "META",
|
|
exchange: "NASDAQ",
|
|
name: "Meta Platforms, Inc.",
|
|
price: 748.91,
|
|
changePercent: -1.54,
|
|
logo: "https://logo.clearbit.com/meta.com",
|
|
bgColor: "bg-blue-600",
|
|
},
|
|
]
|
|
|
|
const activeStocks = [
|
|
{
|
|
symbol: "OPEN",
|
|
exchange: "NASDAQ",
|
|
name: "Opendoor Technologies...",
|
|
price: 9.09,
|
|
changePercent: 10.45,
|
|
logo: "https://logo.clearbit.com/opendoor.com",
|
|
bgColor: "bg-purple-600",
|
|
},
|
|
{
|
|
symbol: "INTC",
|
|
exchange: "NASDAQ",
|
|
name: "Intel Corporation",
|
|
price: 33.99,
|
|
changePercent: 8.87,
|
|
logo: "https://logo.clearbit.com/intel.com",
|
|
bgColor: "bg-blue-700",
|
|
},
|
|
{
|
|
symbol: "SNAP",
|
|
exchange: "NYSE",
|
|
name: "Snap Inc.",
|
|
price: 8.33,
|
|
changePercent: 1.34,
|
|
logo: "https://logo.clearbit.com/snap.com",
|
|
bgColor: "bg-yellow-500",
|
|
},
|
|
{
|
|
symbol: "SOXS",
|
|
exchange: "AMEX",
|
|
name: "Direxion Daily Semicond...",
|
|
price: 5.34,
|
|
changePercent: 1.14,
|
|
logo: "",
|
|
bgColor: "bg-gray-600",
|
|
letter: "S",
|
|
},
|
|
]
|
|
|
|
const peersStocks = [
|
|
{
|
|
symbol: "TM",
|
|
exchange: "NYSE",
|
|
name: "Toyota Motor Corporation",
|
|
price: 197.28,
|
|
changePercent: -0.41,
|
|
logo: "https://logo.clearbit.com/toyota.com",
|
|
bgColor: "bg-red-600",
|
|
},
|
|
{
|
|
symbol: "BYDDY",
|
|
exchange: "OTC",
|
|
name: "BYD Company Limited",
|
|
price: 13.8,
|
|
changePercent: 2.53,
|
|
logo: "",
|
|
bgColor: "bg-gray-700",
|
|
letter: "B",
|
|
},
|
|
{
|
|
symbol: "GM",
|
|
exchange: "NYSE",
|
|
name: "General Motors Comp...",
|
|
price: 60.59,
|
|
changePercent: 0,
|
|
logo: "https://logo.clearbit.com/gm.com",
|
|
bgColor: "bg-blue-800",
|
|
},
|
|
]
|
|
|
|
const getCurrentStocks = () => {
|
|
switch (activeTab) {
|
|
case "gainers":
|
|
return activeStocks.filter((stock) => stock.changePercent > 0)
|
|
case "losers":
|
|
return watchlistStocks // All are negative in the watchlist
|
|
case "active":
|
|
default:
|
|
return activeStocks
|
|
}
|
|
}
|
|
|
|
const StockItem = ({ stock }: { stock: any }) => {
|
|
const isPositive = stock.changePercent > 0
|
|
|
|
return (
|
|
<div
|
|
className="flex items-center justify-between py-0.5 sm:py-1 hover:bg-accent/20 cursor-pointer transition-colors"
|
|
onClick={() => onSelectStock(stock.symbol)}
|
|
>
|
|
<div className="flex items-center space-x-2 sm:space-x-3">
|
|
<div className={`w-5 h-5 sm:w-6 sm:h-6 rounded-sm flex items-center justify-center ${stock.bgColor}`}>
|
|
{stock.logo ? (
|
|
<img src={stock.logo || "/placeholder.svg"} alt={stock.symbol} className="w-3 h-3 sm:w-4 sm:h-4" />
|
|
) : (
|
|
<span className="text-white font-bold text-xs">{stock.letter || stock.symbol[0]}</span>
|
|
)}
|
|
</div>
|
|
<div className="min-w-0">
|
|
<div className="text-xs sm:text-sm font-medium text-foreground truncate">{stock.name}</div>
|
|
<div className="text-xs text-muted-foreground">
|
|
{stock.symbol} · {stock.exchange}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="text-right flex items-center space-x-1 sm:space-x-2">
|
|
<div className="text-xs sm:text-sm text-foreground">US${stock.price.toFixed(2)}</div>
|
|
<Button size="sm" variant="ghost" className="h-5 w-5 sm:h-6 sm:w-6 p-0 hover:bg-accent/50">
|
|
<Plus className="w-2.5 h-2.5 sm:w-3 sm:h-3 text-muted-foreground" />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
const StockItemWithChange = ({ stock }: { stock: any }) => {
|
|
const isPositive = stock.changePercent > 0
|
|
|
|
return (
|
|
<div
|
|
className="flex items-center justify-between py-0.5 sm:py-1 hover:bg-accent/20 cursor-pointer transition-colors"
|
|
onClick={() => onSelectStock(stock.symbol)}
|
|
>
|
|
<div className="flex items-center space-x-2 sm:space-x-3">
|
|
<div className={`w-5 h-5 sm:w-6 sm:h-6 rounded-sm flex items-center justify-center ${stock.bgColor}`}>
|
|
{stock.logo ? (
|
|
<img src={stock.logo || "/placeholder.svg"} alt={stock.symbol} className="w-3 h-3 sm:w-4 sm:h-4" />
|
|
) : (
|
|
<span className="text-white font-bold text-xs">{stock.letter || stock.symbol[0]}</span>
|
|
)}
|
|
</div>
|
|
<div className="min-w-0">
|
|
<div className="text-xs sm:text-sm font-medium text-foreground truncate">{stock.name}</div>
|
|
<div className="text-xs text-muted-foreground">
|
|
{stock.symbol} · {stock.exchange}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="text-right">
|
|
<div className="text-xs sm:text-sm text-foreground">US${stock.price.toFixed(2)}</div>
|
|
<div className={`text-xs ${isPositive ? "text-green-400" : "text-red-400"}`}>
|
|
{isPositive ? "+" : ""}
|
|
{stock.changePercent.toFixed(2)}%
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-2 sm:space-y-4">
|
|
<div className="border border-border/50 rounded-lg p-2 sm:p-3 md:p-4 space-y-2 sm:space-y-3">
|
|
<div className="flex items-center justify-between mb-2 sm:mb-3">
|
|
<h2 className="text-xs sm:text-sm font-medium text-foreground">Create Watchlist</h2>
|
|
<Button size="sm" variant="ghost" className="h-5 w-5 sm:h-6 sm:w-6 p-0 hover:bg-accent/50">
|
|
<Settings className="w-3 h-3 sm:w-4 sm:h-4 text-muted-foreground" />
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="space-y-0.5 sm:space-y-1">
|
|
{watchlistStocks.map((stock) => (
|
|
<StockItemWithChange key={stock.symbol} stock={stock} />
|
|
))}
|
|
</div>
|
|
|
|
<div className="pt-2 sm:pt-3 border-t border-border/30">
|
|
<div className="flex border-b border-border">
|
|
{["Gainers", "Losers", "Active"].map((tab) => (
|
|
<button
|
|
key={tab}
|
|
onClick={() => setActiveTab(tab.toLowerCase())}
|
|
className={`px-2 sm:px-4 py-1 sm:py-2 text-xs sm:text-sm font-medium transition-colors relative ${
|
|
activeTab === tab.toLowerCase()
|
|
? "text-foreground border-b-2 border-foreground"
|
|
: "text-muted-foreground hover:text-foreground"
|
|
}`}
|
|
>
|
|
{tab}
|
|
</button>
|
|
))}
|
|
</div>
|
|
|
|
<div className="space-y-0.5 sm:space-y-1 mt-2 sm:mt-3">
|
|
{getCurrentStocks().map((stock) => (
|
|
<StockItemWithChange key={stock.symbol} stock={stock} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="pt-2 sm:pt-3 border-t border-border/30">
|
|
<h3 className="text-xs sm:text-sm font-medium text-foreground mb-1 sm:mb-2">Peers</h3>
|
|
<div className="space-y-0.5 sm:space-y-1">
|
|
{peersStocks.map((stock) => (
|
|
<StockItemWithChange key={stock.symbol} stock={stock} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|