156 lines
No EOL
6.1 KiB
TypeScript
156 lines
No EOL
6.1 KiB
TypeScript
import { cn } from '@/lib/utils';
|
|
import { Plus } from 'lucide-react';
|
|
|
|
interface StatsBoxProps {
|
|
stats: {
|
|
totalShares: number;
|
|
totalInvestment: number;
|
|
averageCostPerShare: number;
|
|
currentPrice?: number;
|
|
currentValue?: number;
|
|
profitLoss?: number;
|
|
profitLossPercentage?: number;
|
|
};
|
|
className?: string;
|
|
onAddPurchase?: () => void;
|
|
onAddMilestone?: () => void;
|
|
}
|
|
|
|
export default function StatsBox({
|
|
stats,
|
|
className,
|
|
onAddPurchase,
|
|
onAddMilestone
|
|
}: StatsBoxProps) {
|
|
const formatCurrency = (amount: number) => {
|
|
return new Intl.NumberFormat('de-DE', {
|
|
style: 'currency',
|
|
currency: 'EUR',
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
}).format(amount);
|
|
};
|
|
|
|
const formatCurrencyDetailed = (amount: number) => {
|
|
return new Intl.NumberFormat('de-DE', {
|
|
style: 'currency',
|
|
currency: 'EUR',
|
|
minimumFractionDigits: 4,
|
|
}).format(amount);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
"bg-black border-4 border-gray-800 rounded-lg",
|
|
"shadow-2xl shadow-red-500/20",
|
|
"p-6 space-y-4",
|
|
className
|
|
)}
|
|
>
|
|
{/* Current Price */}
|
|
{stats.currentPrice && (
|
|
<div className="text-center border-b border-red-500/30 pb-4">
|
|
<div className="text-red-400/70 text-sm font-medium tracking-wide mb-2">
|
|
current price
|
|
</div>
|
|
<div className="text-red-500 text-2xl md:text-3xl font-mono-display tracking-wider">
|
|
{formatCurrencyDetailed(stats.currentPrice)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Portfolio Stats Grid */}
|
|
<div className="grid grid-cols-2 gap-4 text-sm font-mono">
|
|
{/* Total Investment */}
|
|
<div className="space-y-1">
|
|
<div className="text-red-400/70 text-xs">Total Investment</div>
|
|
<div className="text-red-400">
|
|
{formatCurrency(stats.totalInvestment)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Current Value */}
|
|
{stats.currentValue && (
|
|
<div className="space-y-1">
|
|
<div className="text-red-400/70 text-xs">Current Value</div>
|
|
<div className="text-red-400">
|
|
{formatCurrency(stats.currentValue)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Average Cost */}
|
|
<div className="space-y-1">
|
|
<div className="text-red-400/70 text-xs">Avg Cost/Share</div>
|
|
<div className="text-red-400">
|
|
{formatCurrencyDetailed(stats.averageCostPerShare)}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Profit/Loss */}
|
|
{stats.profitLoss !== undefined && (
|
|
<div className="space-y-1">
|
|
<div className="text-red-400/70 text-xs">P&L</div>
|
|
<div className={cn(
|
|
"font-bold",
|
|
stats.profitLoss >= 0 ? "text-green-400" : "text-red-400"
|
|
)}>
|
|
{stats.profitLoss >= 0 ? '+' : ''}{formatCurrency(stats.profitLoss)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Withdrawal Estimates */}
|
|
{stats.currentValue && (
|
|
<div className="border-t border-red-500/30 pt-4">
|
|
<div className="text-red-400/70 text-xs mb-2">Annual Withdrawal (Safe)</div>
|
|
<div className="grid grid-cols-2 gap-4 text-sm font-mono">
|
|
<div className="space-y-1">
|
|
<div className="text-red-400/60 text-xs">3% Rule</div>
|
|
<div className="text-red-400">
|
|
{formatCurrency(stats.currentValue * 0.03)}
|
|
</div>
|
|
</div>
|
|
<div className="space-y-1">
|
|
<div className="text-red-400/60 text-xs">4% Rule</div>
|
|
<div className="text-red-400">
|
|
{formatCurrency(stats.currentValue * 0.04)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Action Buttons */}
|
|
<div className="border-t border-red-500/30 pt-4">
|
|
<div className="flex items-center justify-center space-x-4">
|
|
{/* Add Purchase Button */}
|
|
{onAddPurchase && (
|
|
<button
|
|
onClick={onAddPurchase}
|
|
className="flex items-center space-x-2 px-4 py-2 rounded bg-red-600/20 border border-red-500/50 text-red-400 hover:bg-red-600/40 hover:text-red-300 transition-colors text-sm"
|
|
aria-label="Add purchase"
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
<span className="font-mono">ADD PURCHASE</span>
|
|
</button>
|
|
)}
|
|
|
|
{/* Add Milestone Button */}
|
|
{onAddMilestone && (
|
|
<button
|
|
onClick={onAddMilestone}
|
|
className="flex items-center space-x-2 px-4 py-2 rounded bg-blue-600/20 border border-blue-500/50 text-blue-400 hover:bg-blue-600/40 hover:text-blue-300 transition-colors text-sm"
|
|
aria-label="Add milestone"
|
|
>
|
|
<Plus className="w-4 h-4" />
|
|
<span className="font-mono">ADD MILESTONE</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
} |