incr/resources/js/components/Display/StatsBox.tsx

156 lines
6.1 KiB
TypeScript
Raw Normal View History

2025-07-12 19:59:22 +02:00
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>
);
}