Clean up old files
This commit is contained in:
parent
dce7571109
commit
896ad15321
4 changed files with 0 additions and 426 deletions
|
|
@ -1,33 +0,0 @@
|
||||||
import AddMilestoneForm from '@/components/Milestones/AddMilestoneForm';
|
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
||||||
|
|
||||||
interface MilestoneModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
onSuccess?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function MilestoneModal({ isOpen, onClose, onSuccess }: MilestoneModalProps) {
|
|
||||||
const handleSuccess = () => {
|
|
||||||
if (onSuccess) {
|
|
||||||
onSuccess();
|
|
||||||
}
|
|
||||||
onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
||||||
<DialogContent className="bg-black border-red-500/30 text-red-400 max-w-md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="text-red-500 font-mono tracking-wide">
|
|
||||||
ADD MILESTONE
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="mt-4">
|
|
||||||
<AddMilestoneForm onSuccess={handleSuccess} />
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,167 +0,0 @@
|
||||||
import { cn } from '@/lib/utils';
|
|
||||||
import { ChevronLeft, ChevronRight, Plus } from 'lucide-react';
|
|
||||||
import { useState } from 'react';
|
|
||||||
|
|
||||||
interface Milestone {
|
|
||||||
target: number;
|
|
||||||
label: string;
|
|
||||||
color: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface MilestoneProgressBarProps {
|
|
||||||
currentShares: number;
|
|
||||||
className?: string;
|
|
||||||
onStatsToggle?: () => void;
|
|
||||||
showStats?: boolean;
|
|
||||||
isVisible?: boolean;
|
|
||||||
onAddPurchase?: () => void;
|
|
||||||
onHover?: (isHovered: boolean) => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const milestones: Milestone[] = [
|
|
||||||
{ target: 1500, label: '1.5K', color: 'bg-blue-500' },
|
|
||||||
{ target: 3000, label: '3K', color: 'bg-green-500' },
|
|
||||||
{ target: 4500, label: '4.5K', color: 'bg-yellow-500' },
|
|
||||||
{ target: 6000, label: '6K', color: 'bg-red-500' },
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function MilestoneProgressBar({
|
|
||||||
currentShares,
|
|
||||||
className,
|
|
||||||
onStatsToggle,
|
|
||||||
showStats = false,
|
|
||||||
isVisible = false,
|
|
||||||
onAddPurchase,
|
|
||||||
onHover
|
|
||||||
}: MilestoneProgressBarProps) {
|
|
||||||
const [currentMilestoneIndex, setCurrentMilestoneIndex] = useState(0);
|
|
||||||
|
|
||||||
const currentMilestone = milestones[currentMilestoneIndex];
|
|
||||||
const progress = Math.min((currentShares / currentMilestone.target) * 100, 100);
|
|
||||||
const isCompleted = currentShares >= currentMilestone.target;
|
|
||||||
|
|
||||||
const nextMilestone = () => {
|
|
||||||
setCurrentMilestoneIndex((prev) =>
|
|
||||||
prev < milestones.length - 1 ? prev + 1 : 0
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const prevMilestone = () => {
|
|
||||||
setCurrentMilestoneIndex((prev) =>
|
|
||||||
prev > 0 ? prev - 1 : milestones.length - 1
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleBarClick = () => {
|
|
||||||
if (onStatsToggle) {
|
|
||||||
onStatsToggle();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"absolute bottom-0 left-0 right-0",
|
|
||||||
"bg-black/90 backdrop-blur-sm",
|
|
||||||
"border-t border-red-500/30",
|
|
||||||
"transition-all duration-300 transform",
|
|
||||||
isVisible ? "translate-y-0 opacity-100" : "translate-y-full opacity-0",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
onMouseEnter={() => onHover?.(true)}
|
|
||||||
onMouseLeave={() => onHover?.(false)}
|
|
||||||
>
|
|
||||||
<div className="relative">
|
|
||||||
{/* Progress Bar */}
|
|
||||||
<div
|
|
||||||
className="h-2 bg-gray-800 cursor-pointer relative overflow-hidden"
|
|
||||||
onClick={handleBarClick}
|
|
||||||
>
|
|
||||||
{/* Background pulse for completed milestones */}
|
|
||||||
{isCompleted && (
|
|
||||||
<div className="absolute inset-0 bg-gradient-to-r from-transparent via-white/10 to-transparent animate-pulse" />
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Progress fill */}
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"h-full transition-all duration-1000 ease-out",
|
|
||||||
currentMilestone.color,
|
|
||||||
"shadow-lg",
|
|
||||||
isCompleted ? "animate-pulse" : ""
|
|
||||||
)}
|
|
||||||
style={{ width: `${progress}%` }}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* Glow effect */}
|
|
||||||
<div
|
|
||||||
className={cn(
|
|
||||||
"absolute top-0 h-full transition-all duration-1000 ease-out",
|
|
||||||
"bg-gradient-to-r from-transparent to-white/30",
|
|
||||||
"blur-sm"
|
|
||||||
)}
|
|
||||||
style={{ width: `${Math.min(progress + 10, 100)}%` }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Milestone Info */}
|
|
||||||
<div className="flex items-center justify-between px-4 py-2">
|
|
||||||
{/* Left: Previous milestone button */}
|
|
||||||
<button
|
|
||||||
onClick={prevMilestone}
|
|
||||||
className="text-red-400 hover:text-red-300 transition-colors p-1"
|
|
||||||
aria-label="Previous milestone"
|
|
||||||
>
|
|
||||||
<ChevronLeft className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Center: Milestone info */}
|
|
||||||
<div className="flex items-center space-x-4 text-center">
|
|
||||||
<div className="text-red-400 text-sm font-mono">
|
|
||||||
{currentShares.toFixed(2)} / {currentMilestone.target}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-red-500 font-bold text-sm">
|
|
||||||
{currentMilestone.label}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="text-red-400 text-sm">
|
|
||||||
{isCompleted ? 'COMPLETED' : `${(100 - progress).toFixed(1)}% TO GO`}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right: Add Purchase, Next milestone button and stats toggle */}
|
|
||||||
<div className="flex items-center space-x-2">
|
|
||||||
{/* Add Purchase Button */}
|
|
||||||
{onAddPurchase && (
|
|
||||||
<button
|
|
||||||
onClick={onAddPurchase}
|
|
||||||
className="flex items-center space-x-1 px-2 py-1 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-xs"
|
|
||||||
aria-label="Add purchase"
|
|
||||||
>
|
|
||||||
<Plus className="w-3 h-3" />
|
|
||||||
<span className="font-mono">ADD</span>
|
|
||||||
</button>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<button
|
|
||||||
onClick={nextMilestone}
|
|
||||||
className="text-red-400 hover:text-red-300 transition-colors p-1"
|
|
||||||
aria-label="Next milestone"
|
|
||||||
>
|
|
||||||
<ChevronRight className="w-4 h-4" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{/* Stats indicator */}
|
|
||||||
<div className={cn(
|
|
||||||
"text-xs text-red-400/60 cursor-pointer transition-colors",
|
|
||||||
showStats && "text-red-400"
|
|
||||||
)}>
|
|
||||||
{showStats ? '▲' : '▼'}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import AddPurchaseForm from '@/components/Transactions/AddPurchaseForm';
|
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog';
|
|
||||||
|
|
||||||
interface PurchaseModalProps {
|
|
||||||
isOpen: boolean;
|
|
||||||
onClose: () => void;
|
|
||||||
onSuccess?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function PurchaseModal({ isOpen, onClose, onSuccess }: PurchaseModalProps) {
|
|
||||||
const handleSuccess = () => {
|
|
||||||
if (onSuccess) {
|
|
||||||
onSuccess();
|
|
||||||
}
|
|
||||||
onClose();
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
|
||||||
<DialogContent className="bg-black border-red-500/30 text-red-400 max-w-md">
|
|
||||||
<DialogHeader>
|
|
||||||
<DialogTitle className="text-red-500 font-mono tracking-wide">
|
|
||||||
ADD PURCHASE
|
|
||||||
</DialogTitle>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
<div className="mt-4">
|
|
||||||
<AddPurchaseForm onSuccess={handleSuccess} />
|
|
||||||
</div>
|
|
||||||
</DialogContent>
|
|
||||||
</Dialog>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
@ -1,193 +0,0 @@
|
||||||
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>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Loading…
Reference in a new issue