incr/resources/js/components/Display/MilestoneProgressBar.tsx
2025-07-10 18:31:39 +02:00

167 lines
No EOL
6.4 KiB
TypeScript

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>
);
}