| | import React, { useState, useEffect } from 'react'; |
| | import { X, Zap, Timer } from 'lucide-react'; |
| | import { Language, WeddingStyle, AdminConfig, UserAccount } from '../types'; |
| | import { TRANSLATIONS } from '../constants/translations'; |
| |
|
| | interface SlashModalProps { |
| | isVisible: boolean; |
| | onClose: () => void; |
| | language: Language; |
| | style: WeddingStyle | null; |
| | onUnlock: () => void; |
| | adminConfig: AdminConfig; |
| | user?: UserAccount; |
| | onUpdateUser: (progress: Record<string, number>) => void; |
| | onOpenShare: () => void; |
| | } |
| |
|
| | export const SlashModal: React.FC<SlashModalProps> = ({ |
| | isVisible, onClose, language, style, onUnlock, adminConfig, user, onUpdateUser, onOpenShare |
| | }) => { |
| | const t = TRANSLATIONS[language]; |
| | const [progress, setProgress] = useState(98); |
| |
|
| | useEffect(() => { |
| | if (isVisible && style) { |
| | if (user?.slashProgress && user.slashProgress[style.id]) { |
| | setProgress(user.slashProgress[style.id]); |
| | } else { |
| | setProgress(98); |
| | } |
| | } |
| | }, [isVisible, style, user]); |
| |
|
| | if (!isVisible || !style) return null; |
| | |
| | const styleName = (t.styles as any)[style.id] || style.name; |
| |
|
| | const handleInvite = () => { |
| | onOpenShare(); |
| | |
| | |
| | const remaining = 100 - progress; |
| | |
| | const cutPercent = 0.5 + Math.random() * 0.3; |
| | let newProgress = progress + (remaining * cutPercent); |
| | |
| | |
| | if (newProgress > 99.5) { |
| | newProgress = 100; |
| | } |
| |
|
| | setProgress(newProgress); |
| |
|
| | if (user) { |
| | const newMap = { ...(user.slashProgress || {}), [style.id]: newProgress }; |
| | onUpdateUser(newMap); |
| | } |
| | |
| | if (newProgress >= 100) { |
| | onUnlock(); |
| | } |
| | |
| | onClose(); |
| | }; |
| |
|
| | return ( |
| | <div className="fixed inset-0 z-[110] flex items-center justify-center p-4 bg-black/70 backdrop-blur-sm animate-fade-in"> |
| | <div className="bg-gradient-to-b from-orange-500 to-red-600 rounded-2xl shadow-2xl w-full max-w-sm overflow-hidden relative text-white"> |
| | <button onClick={onClose} className="absolute top-2 right-2 p-1.5 bg-black/20 rounded-full hover:bg-black/40 z-20"> |
| | <X className="w-4 h-4" /> |
| | </button> |
| | |
| | <div className="p-6 text-center"> |
| | <div className="inline-block px-3 py-1 bg-black/30 rounded-full text-xs font-bold mb-4 flex items-center gap-1 mx-auto border border-white/20"> |
| | <Timer className="w-3 h-3" /> Ends in 23:59:10 |
| | </div> |
| | |
| | <h2 className="text-2xl font-bold italic drop-shadow-md">{t.pddSlashTitle}</h2> |
| | <p className="text-orange-100 text-sm mt-1">{t.pddSlashDesc}</p> |
| | |
| | {/* Product Card */} |
| | <div className="bg-white text-gray-900 rounded-xl p-3 mt-6 flex items-center gap-3 shadow-lg"> |
| | <div className="w-16 h-16 bg-gray-200 rounded-lg shrink-0 overflow-hidden relative"> |
| | <div className={`w-full h-full ${style.coverColor} opacity-50`}></div> |
| | <div className="absolute inset-0 flex items-center justify-center"> |
| | <span className="text-xs font-bold text-gray-500">IMG</span> |
| | </div> |
| | </div> |
| | <div className="text-left flex-1"> |
| | <p className="font-bold text-sm truncate">{styleName}</p> |
| | <p className="text-xs text-gray-500">VIP Premium Collection</p> |
| | <p className="text-red-500 font-bold text-sm mt-1">¥0.00 <span className="text-gray-400 line-through text-xs">¥199</span></p> |
| | </div> |
| | </div> |
| | |
| | {/* Progress Bar */} |
| | <div className="mt-6 relative"> |
| | <div className="flex justify-between text-xs font-bold mb-1"> |
| | <span className="text-yellow-200">{t.pddSlashProgress}</span> |
| | <span>{progress.toFixed(2)}%</span> |
| | </div> |
| | <div className="h-4 bg-black/30 rounded-full overflow-hidden border border-white/20"> |
| | <div className="h-full bg-gradient-to-r from-yellow-300 to-yellow-500 relative" style={{ width: `${progress}%` }}> |
| | <div className="absolute top-0 right-0 h-full w-2 bg-white/50 animate-pulse"></div> |
| | </div> |
| | </div> |
| | <div className="absolute -right-2 -top-2 bg-red-100 text-red-600 text-[10px] font-bold px-1.5 rounded-full border border-red-500 shadow-sm animate-bounce"> |
| | Only {(100 - progress).toFixed(2)}% left! |
| | </div> |
| | </div> |
| | |
| | <button |
| | onClick={handleInvite} |
| | className="w-full py-3 bg-yellow-400 hover:bg-yellow-300 text-red-700 font-extrabold text-lg rounded-full mt-6 shadow-xl shadow-orange-700/50 flex items-center justify-center gap-2 transform active:scale-95 transition-all" |
| | > |
| | <Zap className="w-5 h-5 fill-current" /> {t.pddSlashCta} |
| | </button> |
| | </div> |
| | |
| | {/* Social Proof List */} |
| | <div className="bg-white/10 p-4 border-t border-white/10"> |
| | <p className="text-xs text-white/60 mb-2 text-center">Friends who helped</p> |
| | <div className="flex justify-center -space-x-2"> |
| | {[1,2,3].map(i => ( |
| | <div key={i} className="w-8 h-8 rounded-full bg-gray-200 border-2 border-orange-500"></div> |
| | ))} |
| | <div className="w-8 h-8 rounded-full bg-gray-800 border-2 border-orange-500 flex items-center justify-center text-[10px] text-white"> |
| | +99 |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | </div> |
| | ); |
| | }; |