import { useEffect, useMemo, useState } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { codingQuestions, type CodingQuestion } from '@/data/codingQuestions'; import { fetchDSAQuestions } from '@/lib/dsaQuestionsClient'; import { getQuestionReward } from '@/lib/codingProgress'; import type { ProgressLeaderboardEntry } from '@/lib/authClient'; import { cn } from '@/lib/utils'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { useSubscription } from '@/contexts/SubscriptionContext'; import ProgressHeader from './ProgressHeader'; import { ArrowLeft, BookOpen, CheckCircle2, ChevronLeft, ChevronRight, Circle, Code2, Coins, Filter, FolderOpen, Layers, Search, Terminal, Trophy, Zap, Lock } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from '@/components/ui/dialog'; interface CodingSectionProps { onSelectQuestion: (question: CodingQuestion, mode: 'code' | 'solution') => void; solvedQuestionIds: string[]; solvedCount?: number; userId?: string; leaderboard?: ProgressLeaderboardEntry[]; onViewFullLeaderboard?: () => void; } const BLIND_56_DATA: Record = { Array: ['Pair Sum Target', 'Optimal Stock Trading', 'Duplicate Finder', 'Array Product Exclusion', 'Maximum Sum Segment', 'Maximum Product Subarray', 'Find Minimum in Rotated Sorted Array', 'Search in Rotated Sorted Array'], Binary: ['Sum of Two Integers', 'Number of 1 Bits', 'Counting Bits', 'Missing Number', 'Reverse Bits'], DP: ['Climbing Stairs', 'Coin Change II', 'Longest Increasing Subsequence', 'Longest Common Subsequence', 'Word Break', 'Combination Sum', 'Maximum Sum of Non-Adjacent Elements', 'House Robber', 'Decode Ways'], Graph: ['Clone Graph', 'Course Schedule I', 'Pacific Atlantic Water Flow', 'Number of Islands', 'Longest Consecutive Sequence', 'Alien Dictionary', 'Graph Valid Tree'], Interval: ['Insert Interval', 'Merge Intervals', 'Non-overlapping Intervals', 'Repeating and Missing Number'], 'Linked List': ['Reverse a Linked List', 'Detect a Loop in Linked List', 'Merge Two Sorted Lists', 'Merge K Sorted Lists', 'Remove Nth Node From End of List'], Matrix: ['Set Matrix Zeroes', 'Spiral Matrix', 'Rotate Image'], Tree: ['Maximum Depth in BT', 'Check if two trees are identical', 'Invert/Flip Binary Tree', 'Maximum Path Sum', 'Level Order Traversal', 'Serialize and De-serialize BT'], String: ['Longest Substring Without Repeating Characters', 'Longest Repeating Character Replacement', 'Minimum Window Substring', 'Valid Anagram', 'Group Words by Anagrams', 'Balanced Paranthesis', 'Palindrome Number', 'Longest Palindromic Substring', 'Palindromic Substrings'], }; type SheetCategory = { name: string; count: number; completedCount: number; progress: number; }; const COMPANY_DOMAINS: Record = { accenture: 'accenture.com', adobe: 'adobe.com', airbnb: 'airbnb.com', amazon: 'amazon.com', apple: 'apple.com', atlassian: 'atlassian.com', baidu: 'baidu.com', bloomberg: 'bloomberg.com', bookingcom: 'booking.com', bytedance: 'bytedance.com', capgemini: 'capgemini.com', cisco: 'cisco.com', citadel: 'citadel.com', citrix: 'citrix.com', codeforces: 'codeforces.com', codesignal: 'codesignal.com', coursera: 'coursera.org', cred: 'cred.club', databricks: 'databricks.com', dhl: 'dhl.com', directi: 'directi.com', doordash: 'doordash.com', dropbox: 'dropbox.com', expedia: 'expedia.com', facebook: 'facebook.com', flipkart: 'flipkart.com', freshworks: 'freshworks.com', goldmansachs: 'goldmansachs.com', google: 'google.com', grab: 'grab.com', hackerrank: 'hackerrank.com', hcl: 'hcltech.com', ibm: 'ibm.com', infosys: 'infosys.com', intuit: 'intuit.com', janestreet: 'janestreet.com', jio: 'jio.com', jpmorgan: 'jpmorgan.com', juspay: 'juspay.in', leetcode: 'leetcode.com', linkedin: 'linkedin.com', lyft: 'lyft.com', meta: 'meta.com', microsoft: 'microsoft.com', mindtree: 'ltimindtree.com', morganstanley: 'morganstanley.com', netflix: 'netflix.com', nvidia: 'nvidia.com', ola: 'olacabs.com', oracle: 'oracle.com', palantir: 'palantir.com', paypal: 'paypal.com', paytm: 'paytm.com', pinterest: 'pinterest.com', qualcomm: 'qualcomm.com', quora: 'quora.com', razorpay: 'razorpay.com', robinhood: 'robinhood.com', salesforce: 'salesforce.com', samsung: 'samsung.com', sap: 'sap.com', shopee: 'shopee.com', shopify: 'shopify.com', snap: 'snap.com', snapchat: 'snapchat.com', snapdeal: 'snapdeal.com', spotify: 'spotify.com', stripe: 'stripe.com', swiggy: 'swiggy.com', tcs: 'tcs.com', tomtom: 'tomtom.com', twitch: 'twitch.tv', twitter: 'twitter.com', uber: 'uber.com', walmart: 'walmart.com', wipro: 'wipro.com', yahoo: 'yahoo.com', yelp: 'yelp.com', zenefits: 'zenefits.com', zillow: 'zillow.com', zoho: 'zoho.com', }; const COMPANY_ICON_SLUGS: Record = { adobe: 'adobe', airbnb: 'airbnb', amazon: 'amazon', apple: 'apple', atlassian: 'atlassian', baidu: 'baidu', bookingcom: 'bookingdotcom', bytedance: 'bytedance', cisco: 'cisco', codeforces: 'codeforces', coursera: 'coursera', databricks: 'databricks', dhl: 'dhl', doordash: 'doordash', dropbox: 'dropbox', expedia: 'expedia', facebook: 'facebook', flipkart: 'flipkart', freshworks: 'freshworks', google: 'google', hackerrank: 'hackerrank', ibm: 'ibm', infosys: 'infosys', intuit: 'intuit', leetcode: 'leetcode', linkedin: 'linkedin', lyft: 'lyft', meta: 'meta', microsoft: 'microsoft', netflix: 'netflix', nvidia: 'nvidia', oracle: 'oracle', paypal: 'paypal', pinterest: 'pinterest', qualcomm: 'qualcomm', quora: 'quora', robinhood: 'robinhood', salesforce: 'salesforce', samsung: 'samsung', sap: 'sap', shopify: 'shopify', snapchat: 'snapchat', spotify: 'spotify', stripe: 'stripe', twitch: 'twitch', twitter: 'x', uber: 'uber', walmart: 'walmart', wipro: 'wipro', yahoo: 'yahoo', yelp: 'yelp', zillow: 'zillow', zoho: 'zoho', }; const COMPANY_LOGO_OVERRIDES: Record = { apple: [ 'https://cdn.simpleicons.org/apple/white', ], google: [ 'https://www.google.com/images/branding/googleg/1x/googleg_standard_color_128dp.png', ], ibm: [ '/images/ibm-white.svg', ], ola: [ 'https://cdn.simpleicons.org/olacabs/white', ], paytm: [ 'https://upload.wikimedia.org/wikipedia/commons/2/24/Paytm_Logo_%28standalone%29.svg', ], samsung: [ 'https://cdn.simpleicons.org/samsung/white', ], uber: [ 'https://cdn.simpleicons.org/uber/white', ], wipro: [ 'https://cdn.simpleicons.org/wipro/white', ], }; export default function CodingSection({ onSelectQuestion, solvedQuestionIds, solvedCount = 0, userId = '', leaderboard = [], onViewFullLeaderboard, }: CodingSectionProps) { const [searchQuery, setSearchQuery] = useState(''); const [currentPage, setCurrentPage] = useState(1); const ITEMS_PER_PAGE = 30; const [selectedForDialog, setSelectedForDialog] = useState(null); const [selectedSheet, setSelectedSheet] = useState(null); const [showSheets, setShowSheets] = useState(false); const [sheetContext, setSheetContext] = useState<'all' | 'blind56' | 'rypquest'>('all'); const [questions, setQuestions] = useState([]); const [isLoadingQuestions, setIsLoadingQuestions] = useState(true); const [questionsError, setQuestionsError] = useState(null); const solvedSet = useMemo(() => new Set(solvedQuestionIds), [solvedQuestionIds]); useEffect(() => { let cancelled = false; async function loadQuestions() { setIsLoadingQuestions(true); setQuestionsError(null); try { const mongoQuestions = await fetchDSAQuestions(); if (cancelled) return; setQuestions(mongoQuestions.length > 0 ? mongoQuestions : codingQuestions); if (mongoQuestions.length === 0) { setQuestionsError('MongoDB returned no DSA questions, so local fallback data is shown.'); } } catch (error) { if (cancelled) return; console.error('Failed to load DSA questions from MongoDB:', error); setQuestions(codingQuestions); setQuestionsError(error instanceof Error ? error.message : 'Failed to load DSA questions from MongoDB.'); } finally { if (!cancelled) { setIsLoadingQuestions(false); } } } loadQuestions(); return () => { cancelled = true; }; }, []); const categories = useMemo(() => { if (sheetContext === 'blind56') { return Object.entries(BLIND_56_DATA).map(([name, titles]) => { const questionsInCategory = questions.filter((question) => titles.some((title) => matchesBlind56Question(question, title)), ); const completedCount = questionsInCategory.filter((question) => solvedSet.has(question.id)).length; return { name, count: questionsInCategory.length, completedCount, progress: questionsInCategory.length > 0 ? (completedCount / questionsInCategory.length) * 100 : 0, }; }); } return Array.from(new Set(questions.map((question) => question.category))).map((category) => { const questionsInCategory = questions.filter((question) => question.category === category); const completedCount = questionsInCategory.filter((question) => solvedSet.has(question.id)).length; return { name: category, count: questionsInCategory.length, completedCount, progress: questionsInCategory.length > 0 ? (completedCount / questionsInCategory.length) * 100 : 0, }; }); }, [questions, sheetContext, solvedSet]); const rootQuestions = useMemo(() => { const normalizedSearch = searchQuery.toLowerCase(); return questions.filter((question) => ( question.title.toLowerCase().includes(normalizedSearch) || question.category.toLowerCase().includes(normalizedSearch) || question.difficulty.toLowerCase().includes(normalizedSearch) || (question.companies ?? []).some((company) => company.toLowerCase().includes(normalizedSearch)) )); }, [questions, searchQuery]); const filteredQuestions = useMemo(() => { return questions.filter((question) => { const matchesSearch = question.title.toLowerCase().includes(searchQuery.toLowerCase()) || question.category.toLowerCase().includes(searchQuery.toLowerCase()) || (question.companies ?? []).some((company) => company.toLowerCase().includes(searchQuery.toLowerCase())); if (!matchesSearch) { return false; } if (selectedSheet) { if (sheetContext === 'blind56') { const titles = BLIND_56_DATA[selectedSheet] || []; return titles.some((title) => matchesBlind56Question(question, title)); } return question.category === selectedSheet; } if (sheetContext === 'blind56') { const blind56Titles = Object.values(BLIND_56_DATA).flat(); return blind56Titles.some((title) => matchesBlind56Question(question, title)); } return true; }); }, [questions, searchQuery, selectedSheet, sheetContext]); const progressStats = useMemo(() => { const easyQuestions = filteredQuestions.filter((question) => question.difficulty === 'Easy'); const mediumQuestions = filteredQuestions.filter((question) => question.difficulty === 'Medium'); const hardQuestions = filteredQuestions.filter((question) => question.difficulty === 'Hard'); return { totalQuestions: filteredQuestions.length, totalSolved: filteredQuestions.filter((question) => solvedSet.has(question.id)).length, categories: [ { label: 'EASY', solved: easyQuestions.filter((question) => solvedSet.has(question.id)).length, total: easyQuestions.length, color: 'text-emerald-400', barColor: 'bg-emerald-500', chartColor: '#10b981', }, { label: 'MED.', solved: mediumQuestions.filter((question) => solvedSet.has(question.id)).length, total: mediumQuestions.length, color: 'text-amber-400', barColor: 'bg-amber-500', chartColor: '#f59e0b', }, { label: 'HARD', solved: hardQuestions.filter((question) => solvedSet.has(question.id)).length, total: hardQuestions.length, color: 'text-rose-400', barColor: 'bg-rose-500', chartColor: '#ef4444', }, ], }; }, [filteredQuestions, solvedSet]); const leaderboardEntries = leaderboard; const visibleEntries = useMemo(() => leaderboardEntries.slice(0, 3), [leaderboardEntries]); const currentUserEntry = leaderboardEntries.find((entry) => entry.id === userId) || leaderboardEntries.find((entry) => entry.isCurrentUser); const leaderboardRank = currentUserEntry?.rank ?? leaderboardEntries.length + 1; const leaderboardGap = useMemo(() => { const currentIndex = leaderboardEntries.findIndex((entry) => entry.id === userId || entry.isCurrentUser); if (currentIndex <= 0) return 0; return Math.max(0, leaderboardEntries[currentIndex - 1].score - leaderboardEntries[currentIndex].score + 1); }, [leaderboardEntries, userId]); const codingSolvedIds = useMemo(() => solvedQuestionIds.filter(id => !id.startsWith('sd-') && !id.startsWith('cs-') && !id.startsWith('apt-')), [solvedQuestionIds]); const recordedSolvedCount = Math.max(solvedCount, codingSolvedIds.length); return (
{/* MAIN GRID: Content + Sidebar */}
{/* Header */}
{showSheets || selectedSheet ? ( ) : (
)}

{!showSheets ? 'Coding Hub' : selectedSheet ? selectedSheet : sheetContext === 'all' ? 'All Questions' : sheetContext === 'blind56' ? 'Blind 56 Sheets' : 'RYP Quest Sheet'}

{ setSearchQuery(event.target.value); setCurrentPage(1); }} className="w-full rounded-xl border border-zinc-800/80 bg-[#111] py-2.5 pl-10 pr-4 text-sm text-zinc-200 transition-all focus:border-cyan-500/50 focus:outline-none focus:ring-2 focus:ring-cyan-500/10 md:w-72 placeholder:text-zinc-600" />
{questionsError && (
{questionsError}
)} {!showSheets ? ( { setSheetContext('all'); setShowSheets(true); setCurrentPage(1); }} >

All Questions

{isLoadingQuestions ? 'Loading from MongoDB...' : `${questions.length} problems · All categories`}

{ setSheetContext('blind56'); setShowSheets(true); setSelectedSheet(null); setCurrentPage(1); }} >

Blind 56

Curated interview essentials

{ setSheetContext('rypquest'); setShowSheets(true); setSelectedSheet(null); setCurrentPage(1); }} >

RYP Quest Sheet

Platform-curated problem set

Problems

{isLoadingQuestions ? 'Loading questions...' : `${rootQuestions.length} questions`}

) : sheetContext === 'all' && !selectedSheet ? ( {categories.map((category, index) => ( setSelectedSheet(category.name)} >
{category.completedCount}/{category.count}

{category.name} Sheet

{category.count} Questions
Open Sheet
))} ) : ( {sheetContext === 'blind56' ? ( Object.entries(BLIND_56_DATA).map(([category, titles]) => { const categoryQuestions = filteredQuestions.filter((question) => titles.some((title) => matchesBlind56Question(question, title)), ); if (categoryQuestions.length === 0) { return null; } return (

{category}

{categoryQuestions.filter((question) => solvedSet.has(question.id)).length} / {categoryQuestions.length}
); }) ) : ( )} )} !open && setSelectedForDialog(null)}> {selectedForDialog?.title}
{/* Right Sidebar: Session Progress + Leaderboard */}
{/* Leaderboard Rank - Compact */}

Rank

#{leaderboardRank}

● LIVE
{leaderboardGap > 0 &&

{leaderboardGap} pts to next

} {/* Session Progress */} {/* Leaderboard */}

Leaderboard

Rank #{leaderboardRank}

● Live
{visibleEntries.map((entry, index) => (
#{entry.rank}

{entry.displayName}

{entry.solvedCount} solved · {entry.codingStreak ?? entry.currentStreak}d streak

{entry.score} pt
))}
{leaderboardGap > 0 && (

{leaderboardGap} more points to climb up

)} View Full Leaderboard
); } type QuestionTableProps = { questions: CodingQuestion[]; solvedSet: Set; onOpenQuestion: (question: CodingQuestion) => void; startIndex?: number; currentPage?: number; onPageChange?: (page: number) => void; itemsPerPage?: number; emptyMessage?: string; }; function QuestionTable({ questions, solvedSet, onOpenQuestion, startIndex = 0, currentPage = 1, onPageChange, itemsPerPage = 30, emptyMessage = 'No DSA questions match this view.', }: QuestionTableProps) { const { tier } = useSubscription(); const totalPages = Math.ceil(questions.length / itemsPerPage); const paginatedQuestions = onPageChange ? questions.slice((currentPage - 1) * itemsPerPage, currentPage * itemsPerPage) : questions; const pageStartIndex = onPageChange ? (currentPage - 1) * itemsPerPage : startIndex; return (
{paginatedQuestions.length === 0 && ( )} {paginatedQuestions.map((question, index) => { const isSolved = solvedSet.has(question.id); const reward = getQuestionReward(question.difficulty); const displayIndex = pageStartIndex + index + 1; const isLocked = tier === 'free' && displayIndex > 200; return ( { if (!isLocked) onOpenQuestion(question); }} > ); })}
# Status Title Difficulty Companies Reward
{emptyMessage}
{displayIndex}
{isSolved ? : }
{question.title} {question.difficulty} {isLocked &&
}
{reward}
{/* Pagination */} {onPageChange && totalPages > 1 && (

Showing {pageStartIndex + 1}-{Math.min(pageStartIndex + itemsPerPage, questions.length)} of {questions.length}

{Array.from({ length: Math.min(5, totalPages) }, (_, i) => { let page: number; if (totalPages <= 5) { page = i + 1; } else if (currentPage <= 3) { page = i + 1; } else if (currentPage >= totalPages - 2) { page = totalPages - 4 + i; } else { page = currentPage - 2 + i; } return ( ); })}
)}
); } function CompanyLogos({ companies }: { companies: string[] }) { const { tier } = useSubscription(); const uniqueCompanies = Array.from(new Set(companies.map((company) => company.trim()).filter(Boolean))); const visibleCompanies = uniqueCompanies.slice(0, 3); const extraCompanyCount = Math.max(uniqueCompanies.length - visibleCompanies.length, 0); if (uniqueCompanies.length === 0) { return (
NA
); } return (
{visibleCompanies.map((company, index) => ( ))} {extraCompanyCount > 0 && ( +{extraCompanyCount} )}
); } function CompanyLogo({ company, index }: { company: string; index: number }) { const logoUrls = useMemo(() => getCompanyLogoUrls(company), [company]); const [logoIndex, setLogoIndex] = useState(0); const initials = getCompanyInitials(company); const logoUrl = logoUrls[logoIndex]; return ( {logoUrl ? ( {`${company} setLogoIndex((current) => current + 1)} className="company-logo-icon h-full w-full object-contain" /> ) : ( {initials} )} ); } function getCompanyLogoUrls(company: string) { const normalizedCompany = normalizeCompanyName(company); const overrides = COMPANY_LOGO_OVERRIDES[normalizedCompany] ?? []; const domain = COMPANY_DOMAINS[normalizedCompany] ?? `${normalizedCompany}.com`; const simpleIconSlug = COMPANY_ICON_SLUGS[normalizedCompany] ?? normalizedCompany; return [ ...overrides, `https://cdn.simpleicons.org/${simpleIconSlug}`, `https://logo.clearbit.com/${domain}`, `https://www.google.com/s2/favicons?sz=64&domain=${domain}`, ]; } function normalizeCompanyName(company: string) { return company.toLowerCase().replace(/[^a-z0-9]/g, ''); } function getCompanyInitials(company: string) { const words = company.match(/[a-z0-9]+/gi) ?? []; const initials = words.slice(0, 2).map((word) => word[0]?.toUpperCase()).join(''); return initials || '?'; } function matchesBlind56Question(question: CodingQuestion, title: string) { const questionTitle = question.title.toLowerCase(); const blind56Title = title.toLowerCase(); return questionTitle.includes(blind56Title) || blind56Title.includes(questionTitle); }