Initial commit: SAPIENS Stock service

This commit is contained in:
jungwoo choi
2025-10-22 09:31:15 +09:00
commit f0c0f3b8d6
93 changed files with 8369 additions and 0 deletions

136
components/stock-header.tsx Normal file
View File

@ -0,0 +1,136 @@
"use client"
import { Badge } from "@/components/ui/badge"
import { Button } from "@/components/ui/button"
import { TrendingUp, TrendingDown, Star } from "lucide-react"
interface StockHeaderProps {
symbol: string
activeTab: string
onTabChange: (tab: string) => void
}
export function StockHeader({ symbol, activeTab, onTabChange }: StockHeaderProps) {
// Mock data - in real app this would come from an API
const stockData = {
TSLA: {
name: "Tesla, Inc.",
symbol: "TSLA",
exchange: "NASDAQ",
price: 423.39,
afterHoursPrice: 417.13,
change: 18.4,
changePercent: 4.38,
afterHoursChange: -6.26,
afterHoursChangePercent: -1.48,
lastUpdate: "At close: Sep 25, 4:00 PM EDT",
afterHoursUpdate: "After hours: Sep 26, 6:08 AM EDT",
},
}
const stock = stockData[symbol as keyof typeof stockData] || stockData.TSLA
const isPositive = stock.change > 0
const isAfterHoursPositive = stock.afterHoursChange > 0
const tabs = [
{ id: "overview", label: "Overview" },
{ id: "historical", label: "Historical Data" },
{ id: "financials", label: "Financials" },
{ id: "earnings", label: "Earnings" },
{ id: "research", label: "Research" },
]
return (
<div className="bg-card border border-border rounded-lg">
<div className="p-3 sm:p-4 md:p-6">
<div className="flex items-start justify-between mb-3 sm:mb-4 md:mb-6">
<div className="flex items-center space-x-2 sm:space-x-3">
<div className="w-8 h-8 sm:w-10 sm:h-10 bg-destructive rounded-sm flex items-center justify-center">
<span className="text-destructive-foreground font-bold text-sm sm:text-base">T</span>
</div>
<div>
<h1 className="text-lg sm:text-xl md:text-2xl font-bold text-foreground leading-tight">{stock.name}</h1>
<div className="flex items-center space-x-1 sm:space-x-2 text-muted-foreground">
<span className="text-xs sm:text-sm">{stock.symbol}</span>
<span className="text-xs sm:text-sm"></span>
<span className="text-xs sm:text-sm">{stock.exchange}</span>
<Badge variant="secondary" className="ml-1 sm:ml-2 text-xs">
🇺🇸
</Badge>
</div>
</div>
</div>
<Button variant="outline" size="sm" className="text-xs sm:text-sm bg-transparent">
<Star className="w-3 h-3 sm:w-4 sm:h-4 mr-1 sm:mr-2" />
Add to Watchlist
</Button>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 sm:gap-6 md:gap-8">
{/* Regular Hours */}
<div>
<div className="flex items-baseline space-x-2 sm:space-x-3 md:space-x-4 mb-1 sm:mb-2">
<span className="text-2xl font-bold text-foreground leading-tight">US${stock.price.toFixed(2)}</span>
<div className={`flex items-center space-x-1 ${isPositive ? "text-success" : "text-destructive"}`}>
{isPositive ? (
<TrendingUp className="w-3 h-3 sm:w-4 sm:h-4" />
) : (
<TrendingDown className="w-3 h-3 sm:w-4 sm:h-4" />
)}
<span className="font-medium text-sm sm:text-base">US${Math.abs(stock.change).toFixed(2)}</span>
<span className="font-medium text-sm sm:text-base">
{isPositive ? "+" : ""}
{stock.changePercent.toFixed(2)}%
</span>
</div>
</div>
<p className="text-xs sm:text-sm text-muted-foreground leading-tight">{stock.lastUpdate}</p>
</div>
{/* After Hours */}
<div>
<div className="flex items-baseline space-x-2 sm:space-x-3 md:space-x-4 mb-1 sm:mb-2">
<span className="text-2xl font-bold text-foreground leading-tight">
US${stock.afterHoursPrice.toFixed(2)}
</span>
<div
className={`flex items-center space-x-1 ${isAfterHoursPositive ? "text-success" : "text-destructive"}`}
>
{isAfterHoursPositive ? (
<TrendingUp className="w-3 h-3 sm:w-4 sm:h-4" />
) : (
<TrendingDown className="w-3 h-3 sm:w-4 sm:h-4" />
)}
<span className="font-medium text-sm sm:text-base">
US${Math.abs(stock.afterHoursChange).toFixed(2)}
</span>
<span className="font-medium text-sm sm:text-base">
{isAfterHoursPositive ? "+" : ""}
{stock.afterHoursChangePercent.toFixed(2)}%
</span>
</div>
</div>
<p className="text-xs sm:text-sm text-muted-foreground leading-tight">{stock.afterHoursUpdate}</p>
</div>
</div>
</div>
<div className="flex items-center space-x-3 sm:space-x-4 md:space-x-6 border-t border-border px-3 sm:px-4 md:px-6 py-2 sm:py-3">
{tabs.map((tab) => (
<Button
key={tab.id}
variant="ghost"
onClick={() => onTabChange(tab.id)}
className={`rounded-none pb-2 sm:pb-3 text-xs sm:text-sm ${
activeTab === tab.id
? "text-foreground border-b-2 border-primary"
: "text-muted-foreground hover:text-foreground"
}`}
>
{tab.label}
</Button>
))}
</div>
</div>
)
}