mirror of
https://github.com/hanzoai/shadcn.git
synced 2026-11-03 03:18:52 +00:00
- Enable all 15 finance component docs (from .disabled → .mdx) - Create stock-market.mdx, 8 chart docs, 8 block docs, 2 framework docs - Add direction.tsx component (RTL/LTR support from upstream shadcn) - Add xs/icon-xs button sizes, data-variant/data-size attributes - Fix build-registry.mts to handle default exports in namespace re-exports - Add default props to order-entry, positions-list, orders-history, trading-panel - Fix 18 navigation stubs → re-export from root implementations - Add 44 orphaned MDX files to nav (installation, dark-mode, desktop, guides, etc) - Migrate 6 framer-motion imports → motion/react - Bump React 19.2.0→19.2.4, Next.js 16.0.1→16.1.6, motion→12.34.0 - Align lucide-react, sonner v2, tailwind-merge v3, postcss-selector-parser v7 - Remove framer-motion dep (superseded by motion) - Fix @types/react-dom, TypeScript versions across workspaces
108 lines
3.4 KiB
TypeScript
108 lines
3.4 KiB
TypeScript
"use client"
|
|
|
|
import { TrendingDown, TrendingUp } from "lucide-react"
|
|
|
|
export interface Position {
|
|
symbol: string
|
|
shares: number
|
|
avgPrice: number
|
|
currentPrice: number
|
|
}
|
|
|
|
export interface PositionsListProps {
|
|
positions: Position[]
|
|
onPositionClick?: (position: Position) => void
|
|
}
|
|
|
|
export function PositionsList({
|
|
positions = [],
|
|
onPositionClick,
|
|
}: Partial<PositionsListProps>) {
|
|
const getTickerFromSymbol = (symbol: string) => {
|
|
return symbol.split(":")[1] || symbol
|
|
}
|
|
|
|
if (positions.length === 0) {
|
|
return (
|
|
<div className="p-4 text-center py-8 text-white/40">
|
|
<p className="text-sm">No open positions</p>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="p-4 space-y-2">
|
|
{positions.map((position) => {
|
|
const pl = (position.currentPrice - position.avgPrice) * position.shares
|
|
const plPercent =
|
|
((position.currentPrice - position.avgPrice) / position.avgPrice) *
|
|
100
|
|
const totalValue = position.currentPrice * position.shares
|
|
|
|
return (
|
|
<div
|
|
key={position.symbol}
|
|
className={`bg-white/5 rounded-lg p-3 ${onPositionClick ? "cursor-pointer hover:bg-white/10 transition-colors" : ""}`}
|
|
onClick={() => onPositionClick?.(position)}
|
|
>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<div className="font-semibold text-white">
|
|
{getTickerFromSymbol(position.symbol)}
|
|
</div>
|
|
<div
|
|
className={`flex items-center gap-1 text-sm ${pl >= 0 ? "text-success" : "text-danger"}`}
|
|
>
|
|
{pl >= 0 ? (
|
|
<TrendingUp size={14} />
|
|
) : (
|
|
<TrendingDown size={14} />
|
|
)}
|
|
{pl >= 0 ? "+" : ""}
|
|
{plPercent.toFixed(2)}%
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-2 text-xs">
|
|
<div>
|
|
<div className="text-white/60">Shares</div>
|
|
<div className="text-white font-medium">{position.shares}</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-white/60">Avg Price</div>
|
|
<div className="text-white font-medium">
|
|
${position.avgPrice.toFixed(2)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-2 text-xs mt-2 pt-2 border-t border-white/10">
|
|
<div>
|
|
<div className="text-white/60">Current Price</div>
|
|
<div className="text-white font-medium">
|
|
${position.currentPrice.toFixed(2)}
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<div className="text-white/60">Total Value</div>
|
|
<div className="text-white font-medium">
|
|
${totalValue.toFixed(2)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div className="mt-2 pt-2 border-t border-white/10">
|
|
<div className="flex justify-between items-center text-xs">
|
|
<span className="text-white/60">P&L</span>
|
|
<span
|
|
className={`font-semibold ${pl >= 0 ? "text-success" : "text-danger"}`}
|
|
>
|
|
{pl >= 0 ? "+" : ""}${pl.toFixed(2)}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
})}
|
|
</div>
|
|
)
|
|
}
|
|
|
|
export default PositionsList
|