import { useEffect, useState, useRef } from 'react'; import { motion, AnimatePresence, useReducedMotion } from 'motion/react'; import { Monitor, Shuffle, Server, Database, Zap, X, Gift, Lock, Sparkles } from 'lucide-react'; import type { LandingAuthMode } from './LandingPage'; type Feature = { icon: React.ElementType; title: string; desc: string; color: string; accent: string; image: string | null }; // ── 1. DSA Preview ───────────────────────────────────────────── function DSAPreview({ tier }: { tier: 'free' | 'pro' }) { const code = `def twoSum(nums, target):\n seen = {}\n for i, num in enumerate(nums):\n comp = target - num\n if comp in seen:\n return [seen[comp], i]\n seen[num] = i`; const [typed, setTyped] = useState(''); const [done, setDone] = useState(false); useEffect(() => { let i = 0; const t = setInterval(() => { i++; setTyped(code.slice(0, i)); if (i >= code.length) { clearInterval(t); setDone(true); } }, 30); return () => clearInterval(t); }, []); return (
Two Sum {tier === 'free' && 200 Problems Available} Easy
Amazon {tier === 'free' && }
{typed}
{done && ✓ Accepted}
); } // ── 2. AI Tutor Preview ───────────────────────────────────────── function AITutorPreview() { const [phase, setPhase] = useState(0); const words = "A HashMap stores key-value pairs using a hash function. When you call put(key, value), Java computes hashCode() of the key, maps it to a bucket index. Average O(1) for get/put!".split(' '); const [wordIdx, setWordIdx] = useState(0); useEffect(() => { const t1 = setTimeout(() => setPhase(1), 500); const t2 = setTimeout(() => setPhase(2), 1400); return () => { clearTimeout(t1); clearTimeout(t2); }; }, []); useEffect(() => { if (phase !== 2) return; if (wordIdx >= words.length) return; const t = setTimeout(() => setWordIdx(w => w + 1), 80); return () => clearTimeout(t); }, [phase, wordIdx]); return (
Explain how HashMap works in Java
{phase >= 1 && (
AI
{phase === 1 ? (
{[0,1,2].map(i => )}
) : (
{words.slice(0, wordIdx).join(' ')}
)}
)}
); } // ── 3. Quests Preview ─────────────────────────────────────────── function QuestsPreview() { const [xp, setXp] = useState(0); const [coins, setCoins] = useState(0); useEffect(() => { const t = setInterval(() => { setXp(v => Math.min(v + 2, 68)); setCoins(v => Math.min(v + 10, 340)); }, 30); return () => clearInterval(t); }, []); const nodes = [ { label: 'Arrays Basics', done: true }, { label: 'Sorting Algorithms', done: true }, { label: 'Binary Search', active: true }, { label: 'Trees & Graphs', locked: true }, { label: 'Dynamic Programming', locked: true } ]; return (
{nodes.map((n, i) => (
{n.done ? '✓' : n.active ? '▶' : '🔒'} {i < nodes.length - 1 &&
}
))}
{nodes.map((n, i) =>
{n.label}
)}
XP Progress{xp}%
🪙 {coins} coins earned
); } // ── 4. Daily Contest Preview ──────────────────────────────────── function DailyContestPreview() { const [secs, setSecs] = useState(2843); const [submitted, setSubmitted] = useState(false); const [live, setLive] = useState(1247); useEffect(() => { const t = setInterval(() => setSecs(s => Math.max(0, s - 1)), 1000); const t2 = setInterval(() => setLive(v => v + Math.floor(Math.random() * 3)), 2000); return () => { clearInterval(t); clearInterval(t2); }; }, []); const h = Math.floor(secs / 3600), m = Math.floor((secs % 3600) / 60), s = secs % 60; const fmt = (n: number) => String(n).padStart(2, '0'); const lb = [['#1','user_ace','320','12:45'],['#2','coder99','295','14:20'],['#3','ryp_star','280','16:55'],['#4','devpro','240','18:30'],['#5','you','180','22:10']]; const badges = ['🥇','🥈','🥉','','']; return (
{fmt(h)}:{fmt(m)}:{fmt(s)}
● {live.toLocaleString()} solving now
{lb.map(([rank, user, score, time], i) => ( {badges[i] || rank} {user} {score} {time} ))}
{submitted && ✓ Accepted! +100 pts}
); } // ── 5. Weekly Arena Preview ───────────────────────────────────── function WeeklyArenaPreview() { const prizes = [{ icon: '🥇', label: '1st Place', reward: '₹500 + Gold Badge' }, { icon: '🥈', label: '2nd Place', reward: '₹250' }, { icon: '🥉', label: '3rd Place', reward: '₹100' }]; const pts = [[1,850],[2,600],[3,340],[4,120]]; const maxR = 900; const w = 260, h = 80, padX = 20, padY = 10; const toX = (i: number) => padX + (i / (pts.length - 1)) * (w - 2 * padX); const toY = (r: number) => padY + ((r - 120) / (maxR - 120)) * (h - 2 * padY); const d = pts.map(([,r], i) => `${i === 0 ? 'M' : 'L'}${toX(i)},${toY(r)}`).join(' '); return (
{prizes.map((p, i) => (
{p.icon}
{p.label}
{p.reward}
))}
Your Global Rank (4 weeks)
{pts.map(([wk, r], i) => )}
#342↑ 18 places this week
); } // ── 6. Compiler Preview ───────────────────────────────────────── function CompilerPreview({ tier }: { tier: 'free' | 'pro' }) { const [running, setRunning] = useState(false); const [lines, setLines] = useState([]); const output = ['Hello, RYP!', 'Count: 0', 'Count: 1', 'Count: 2', 'Count: 3', 'Count: 4']; const run = () => { if (running) return; setRunning(true); setLines([]); output.forEach((line, i) => setTimeout(() => { setLines(prev => [...prev, line]); if (i === output.length - 1) setRunning(false); }, 800 + i * 200)); }; const code = `print("Hello, RYP!")\nfor i in range(5):\n print(f"Count: {i}")`; return (
{['Python', 'Java', 'C'].map(l => {l})} {['C++', 'Go', 'Rust'].map(l => ( {l} {tier === 'free' && } ))}
{code}
$ output
{lines.map((l, i) => $ {l})} {running && _}
); } // ── 7. System Design Preview ──────────────────────────────────── function SystemDesignPreview({ tier }: { tier: 'free' | 'pro' }) { const boxes = [ { label: 'Client', Icon: Monitor, x: 20, y: 80 }, { label: 'Load Balancer', Icon: Shuffle, x: 160, y: 80 }, { label: 'API Server 1', Icon: Server, x: 300, y: 30 }, { label: 'API Server 2', Icon: Server, x: 300, y: 130 }, { label: 'Redis Cache', Icon: Zap, x: 440, y: 30 }, { label: 'PostgreSQL', Icon: Database, x: 440, y: 130 }, ]; const arrows = [[0,1],[1,2],[1,3],[2,4],[3,5]]; return (
{arrows.map(([a,b], i) => { const from = boxes[a], to = boxes[b]; return ; })} {boxes.map((box, i) => ( {box.label} ))}
HLD: URL Shortener System
Available Modules
{['01. Client-Server Model', '02. Network Protocols', '03. Load Balancing', '04. Database Scaling'].map(m => (
{m}
))} {['05. Caching Strategies', '06. Message Queues', '07. Microservices'].map(m => (
{m} {tier === 'free' && }
))}
); } // ── 8. CS Fundamentals Preview ────────────────────────────────── function CSFundamentalsPreview({ tier }: { tier: 'free' | 'pro' }) { const [tab, setTab] = useState('OS'); const cards: Record = { OS: { q: 'What is a deadlock?', a: 'A state where processes wait for each other\'s resources indefinitely. Conditions: Mutual Exclusion, Hold & Wait, No Preemption, Circular Wait.' }, DBMS: { q: 'What is ACID?', a: 'Atomicity, Consistency, Isolation, Durability — properties guaranteeing reliable DB transactions.' }, CN: { q: 'TCP vs UDP?', a: 'TCP: reliable, ordered, connection-based. UDP: fast, connectionless, no guarantee.' }, }; return (
{['OS','DBMS','CN'].map(t => ( ))}
{cards[tab].q}
{cards[tab].a}
Card 1 of 24
Available Modules
{['01. OS Basics', '02. Process Management', '03. Memory Management', '04. File Systems'].map(m => (
{m}
))} {['05. Concurrency', '06. Deadlocks Deep Dive', '07. Networking Layers'].map(m => (
{m} {tier === 'free' && }
))}
); } // ── 9. Aptitude Preview ───────────────────────────────────────── function AptitudePreview({ tier }: { tier: 'free' | 'pro' }) { const [secs, setSecs] = useState(30); const [selected, setSelected] = useState(null); const [score, setScore] = useState(0); const opts = ['A) 20 m/s','B) 25 m/s','C) 30 m/s','D) 35 m/s']; const correct = 'B) 25 m/s'; useEffect(() => { if (selected) return; const t = setInterval(() => setSecs(s => Math.max(0, s - 1)), 1000); return () => clearInterval(t); }, [selected]); const pick = (opt: string) => { if (selected) return; setSelected(opt); if (opt === correct) setScore(10); }; return (
⏱ {secs}s remaining
0 ? { scale: [1,1.3,1] } : {}} className="text-sm font-bold text-amber-400">Score: {score > 0 ? `+${score}` : '0'}
If a train travels 360 km in 4 hours, what is its speed in m/s?
{opts.map(opt => { const isCorrect = opt === correct; const isSelected = selected === opt; let cls = 'border border-white/10 bg-white/5 text-slate-300 hover:border-white/30'; if (selected) cls = isCorrect ? 'border-emerald-500 bg-emerald-500/20 text-emerald-300 font-bold' : isSelected ? 'border-red-500 bg-red-500/10 text-red-400' : 'border-white/5 bg-white/[0.02] text-slate-600'; return ; })}
{selected && 💡 360 km/4h = 90 km/h = 90 × (1000/3600) = 25 m/s}
Available Modules
{['01. Time & Distance', '02. Percentages', '03. Profit & Loss', '04. Simple Interest'].map(m => (
{m}
))} {['05. Permutations', '06. Probability', '07. Logical Reasoning'].map(m => (
{m} {tier === 'free' && }
))}
); } // ── 10. Swag Store Preview ────────────────────────────────────── function SwagStorePreview({ tier }: { tier: 'free' | 'pro' }) { const [coins, setCoins] = useState(0); const items = [ { name: 'RYP Pen', price: 100, icon: '🖊️', bg: 'bg-indigo-500/10' }, { name: 'RYP T-Shirt', price: 1500, icon: '👕', bg: 'bg-blue-500/10' }, { name: 'Sticker Pack', price: 300, icon: '✨', bg: 'bg-purple-500/10', premium: true }, { name: 'Coffee Mug', price: 800, icon: '☕', bg: 'bg-orange-500/10', premium: true }, ]; useEffect(() => { const t = setInterval(() => { setCoins(prev => (prev >= 2000 ? 0 : prev + 50)); }, 100); return () => clearInterval(t); }, []); return (
Your RYP Balance
R {coins}
{items.map((item, i) => { const isLocked = item.premium && tier === 'free'; return (
{item.icon}
{item.name}
{item.price} Coins
{!isLocked && coins >= item.price && ( Redeemable! )} {!isLocked && = item.price ? '0%' : `${100 - (coins / item.price) * 100}%` }} transition={{ duration: 0.2 }} />} {isLocked && (
Pro Only
)}
); })}
); } // ── PREVIEW MAP ───────────────────────────────────────────────── const PREVIEW_MAP: Record> = { 'DSA Practice': DSAPreview, 'RYP AI Tutor': AITutorPreview, 'RYP Quests': QuestsPreview, 'Daily Contest': DailyContestPreview, 'Weekly Arena': WeeklyArenaPreview, 'Online Compiler': CompilerPreview, 'System Design': SystemDesignPreview, 'CS Fundamentals': CSFundamentalsPreview, 'Aptitude': AptitudePreview, 'RYP Swag Store': SwagStorePreview, }; // ── MODAL SHELL ───────────────────────────────────────────────── interface FeaturePreviewModalProps { feature: Feature | null; onClose: () => void; onGetStarted: (mode?: LandingAuthMode) => void; } export default function FeaturePreviewModal({ feature, onClose, onGetStarted }: FeaturePreviewModalProps) { const reduced = useReducedMotion(); const [tier, setTier] = useState<'free' | 'pro'>('free'); const PreviewComponent = feature ? PREVIEW_MAP[feature.title] : null; useEffect(() => { if (!feature) return; const handler = (e: KeyboardEvent) => { if (e.key === 'Escape') onClose(); }; window.addEventListener('keydown', handler); return () => window.removeEventListener('keydown', handler); }, [feature, onClose]); if (!feature) return null; const panelAnim = reduced ? {} : { initial: { opacity: 0, scale: 0.92, y: 30 }, animate: { opacity: 1, scale: 1, y: 0 }, exit: { opacity: 0, scale: 0.95, y: 20 } }; return (
e.stopPropagation()} className="relative w-full max-w-3xl mx-4 md:mx-auto bg-[#0a0a12] border border-white/10 rounded-t-[2rem] md:rounded-[2.5rem] shadow-[0_0_80px_rgba(0,0,0,0.8)] max-h-[85vh] md:max-h-[80vh] overflow-y-auto fixed bottom-0 md:relative md:bottom-auto" > {/* Close */} {/* Header with Toggle */}

{feature.title}

{feature.desc}

{/* Free vs Pro Toggle */}
{/* Preview */}
{PreviewComponent ? :
Preview coming soon.
}
{/* CTA */}
); }