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

193 lines
9.6 KiB
TypeScript
Raw Normal View History

2025-07-10 18:04:58 +02:00
import { cn } from '@/lib/utils';
import { useState } from 'react';
interface StatsData {
totalShares: number;
totalInvestment: number;
averageCostPerShare: number;
currentPrice?: number;
currentValue?: number;
profitLoss?: number;
profitLossPercentage?: number;
}
interface StatsPanelProps {
stats: StatsData;
isVisible: boolean;
className?: string;
}
export default function StatsPanel({ stats, isVisible, className }: StatsPanelProps) {
const [withdrawalRate, setWithdrawalRate] = useState(0.03); // 3% default
const calculateWithdrawal = (rate: number) => {
if (!stats.currentValue) return 0;
return stats.currentValue * rate;
};
const formatCurrency = (amount: number) => {
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: 'EUR',
minimumFractionDigits: 2,
}).format(amount);
};
const formatPercentage = (percentage: number) => {
return `${percentage >= 0 ? '+' : ''}${percentage.toFixed(2)}%`;
};
return (
<div className={cn(
"fixed bottom-12 left-0 right-0",
"bg-black/95 backdrop-blur-sm",
"border-t border-red-500/30",
"transition-all duration-300 ease-in-out",
"transform",
isVisible ? "translate-y-0 opacity-100" : "translate-y-full opacity-0",
className
)}>
<div className="container mx-auto px-4 py-6">
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
{/* Portfolio Overview */}
<div className="space-y-3">
<h3 className="text-red-400 font-bold text-sm uppercase tracking-wide">
Portfolio
</h3>
<div className="space-y-2 font-mono text-sm">
<div className="flex justify-between">
<span className="text-red-300/70">Shares:</span>
<span className="text-red-400">{stats.totalShares.toFixed(6)}</span>
</div>
<div className="flex justify-between">
<span className="text-red-300/70">Invested:</span>
<span className="text-red-400">{formatCurrency(stats.totalInvestment)}</span>
</div>
<div className="flex justify-between">
<span className="text-red-300/70">Avg Cost:</span>
<span className="text-red-400">{formatCurrency(stats.averageCostPerShare)}</span>
</div>
</div>
</div>
{/* Current Value */}
{stats.currentPrice && (
<div className="space-y-3">
<h3 className="text-red-400 font-bold text-sm uppercase tracking-wide">
Current Value
</h3>
<div className="space-y-2 font-mono text-sm">
<div className="flex justify-between">
<span className="text-red-300/70">Price:</span>
<span className="text-red-400">{formatCurrency(stats.currentPrice)}</span>
</div>
<div className="flex justify-between">
<span className="text-red-300/70">Value:</span>
<span className="text-red-400">{formatCurrency(stats.currentValue || 0)}</span>
</div>
{stats.profitLoss !== undefined && (
<div className="flex justify-between">
<span className="text-red-300/70">P&L:</span>
<span className={cn(
"font-bold",
stats.profitLoss >= 0 ? "text-green-400" : "text-red-500"
)}>
{formatCurrency(stats.profitLoss)}
</span>
</div>
)}
{stats.profitLossPercentage !== undefined && (
<div className="flex justify-between">
<span className="text-red-300/70">Return:</span>
<span className={cn(
"font-bold",
stats.profitLossPercentage >= 0 ? "text-green-400" : "text-red-500"
)}>
{formatPercentage(stats.profitLossPercentage)}
</span>
</div>
)}
</div>
</div>
)}
{/* Withdrawal Estimates */}
{stats.currentValue && (
<div className="space-y-3">
<h3 className="text-red-400 font-bold text-sm uppercase tracking-wide">
Annual Withdrawal
</h3>
<div className="space-y-2 font-mono text-sm">
<div className="flex justify-between">
<span className="text-red-300/70">3%:</span>
<span className="text-red-400">{formatCurrency(calculateWithdrawal(0.03))}</span>
</div>
<div className="flex justify-between">
<span className="text-red-300/70">4%:</span>
<span className="text-red-400">{formatCurrency(calculateWithdrawal(0.04))}</span>
</div>
<div className="flex justify-between">
<span className="text-red-300/70">Custom:</span>
<div className="flex items-center space-x-1">
<input
type="number"
value={withdrawalRate * 100}
onChange={(e) => setWithdrawalRate(Number(e.target.value) / 100)}
className="w-12 bg-transparent text-red-400 text-right text-xs border-b border-red-500/30 focus:border-red-400 outline-none"
min="0"
max="10"
step="0.1"
/>
<span className="text-red-300/70 text-xs">%</span>
</div>
</div>
<div className="flex justify-between">
<span className="text-red-300/70"></span>
<span className="text-red-400">{formatCurrency(calculateWithdrawal(withdrawalRate))}</span>
</div>
</div>
</div>
)}
{/* Monthly Breakdown */}
{stats.currentValue && (
<div className="space-y-3">
<h3 className="text-red-400 font-bold text-sm uppercase tracking-wide">
Monthly Income
</h3>
<div className="space-y-2 font-mono text-sm">
<div className="flex justify-between">
<span className="text-red-300/70">3%:</span>
<span className="text-red-400">{formatCurrency(calculateWithdrawal(0.03) / 12)}</span>
</div>
<div className="flex justify-between">
<span className="text-red-300/70">4%:</span>
<span className="text-red-400">{formatCurrency(calculateWithdrawal(0.04) / 12)}</span>
</div>
<div className="flex justify-between">
<span className="text-red-300/70">Custom:</span>
<span className="text-red-400">{formatCurrency(calculateWithdrawal(withdrawalRate) / 12)}</span>
</div>
</div>
</div>
)}
</div>
</div>
</div>
);
}