import { useEffect, useState } from 'react'; import { supabase } from '../supabaseClient'; import { ExternalLink, Search, CheckCircle, XCircle, ArrowLeft, RefreshCw, Star, Info } from 'lucide-react'; const JOURNEY_SEQUENCE = [ 217, 242, 1, 49, 347, 271, 238, 36, 128, 125, 167, 15, 11, 42, 121, 3, 424, 567, 76, 239, 20, 155, 150, 739, 853, 84, 704, 74, 875, 153, 33, 981, 4, 206, 21, 141, 143, 19, 138, 2, 287, 146, 23, 25, 226, 104, 543, 110, 100, 572, 235, 102, 199, 1448, 98, 230, 105, 124, 297, 703, 1046, 973, 215, 621, 355, 295, 78, 39, 40, 46, 90, 22, 79, 131, 17, 51, 208, 211, 212, 200, 695, 133, 286, 994, 417, 130, 207, 210, 261, 323, 684, 127, 743, 332, 1584, 778, 269, 787, 70, 746, 198, 213, 5, 647, 91, 322, 152, 139, 300, 416, 62, 1143, 309, 518, 494, 97, 329, 115, 72, 312, 10, 53, 55, 45, 134, 846, 1899, 763, 678, 57, 56, 435, 252, 253, 1851, 48, 54, 73, 202, 66, 50, 43, 2013, 136, 191, 338, 190, 268, 371, 7 ]; interface JourneyPathProps { onSelectProblemForReview: (problemId: string) => void; } export default function JourneyPath({ onSelectProblemForReview }: JourneyPathProps) { const [completions, setCompletions] = useState>(new Set()); const [titles, setTitles] = useState>({}); const [loading, setLoading] = useState(true); const [selectedNum, setSelectedNum] = useState(null); const [details, setDetails] = useState([]); const [modalLoading, setModalLoading] = useState(false); const [toggleLoading, setToggleLoading] = useState(false); const fetchData = async () => { try { const { data: { session } } = await supabase.auth.getSession(); if (!session) return; const headers = { 'Authorization': `Bearer ${session.access_token}` }; const [progRes, titlesRes] = await Promise.all([ fetch(`${import.meta.env.VITE_API_BASE_URL}/api/journey-progress`, { headers }), fetch(`${import.meta.env.VITE_API_BASE_URL}/api/journey-progress/titles`, { headers }) ]); if (progRes.ok && titlesRes.ok) { const progData = await progRes.json(); const titlesData = await titlesRes.json(); setCompletions(new Set(progData)); setTitles(titlesData); } } catch (err) { console.error('Error fetching journey data:', err); } finally { setLoading(false); } }; useEffect(() => { fetchData(); }, []); const handleNodeClick = async (num: number) => { setSelectedNum(num); setDetails([]); setModalLoading(true); try { const { data: { session } } = await supabase.auth.getSession(); if (!session) return; const res = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/reviews/problems/${num}`, { headers: { 'Authorization': `Bearer ${session.access_token}` } }); if (res.ok) { const data = await res.json(); setDetails(data); } } catch (err) { console.error('Error fetching problem details:', err); } finally { setModalLoading(false); } }; const handleToggleCompletion = async (num: number) => { setToggleLoading(true); try { const { data: { session } } = await supabase.auth.getSession(); if (!session) return; const res = await fetch(`${import.meta.env.VITE_API_BASE_URL}/api/journey-progress/toggle`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${session.access_token}` }, body: JSON.stringify({ problem_number: num }) }); if (res.ok) { const updated = new Set(completions); if (updated.has(num)) { updated.delete(num); } else { updated.add(num); } setCompletions(updated); } } catch (err) { console.error('Error toggling progress:', err); } finally { setToggleLoading(false); } }; // Compute stats const total = JOURNEY_SEQUENCE.length; const completedCount = JOURNEY_SEQUENCE.filter(num => completions.has(num)).length; const percentage = total > 0 ? Math.round((completedCount / total) * 100) : 0; if (loading) { return (
COMPILING MAP...
); } // Determine active node: first node in sequence that is not completed const activeNode = JOURNEY_SEQUENCE.find(num => !completions.has(num)) || JOURNEY_SEQUENCE[0]; return (
{/* Header progress info */}

MAP PROGRESS STATUS

Leitner path completion metrics
{percentage}% DONE
{/* Milestone path visualization */}
{/* Timeline Center Sine Path via SVG */} {/* Nodes Container */}
{JOURNEY_SEQUENCE.map((num, index) => { const isCompleted = completions.has(num); const isSynced = num in titles; const title = titles[num] || 'Unsynced Note'; const isActive = num === activeNode; // Sine wave displacement const xOffset = Math.sin(index * 0.8) * 75; return (
{/* Tooltip */}
#{num} {title.toUpperCase()} {!isSynced && (LOCKED)}
{/* Circular / Rounded Square Node */}
); })}
{/* Modal Dialog (Styled as a Retro OS System Dialog) */} {selectedNum !== null && (
{/* Title Bar */}
info Problem_Detail.sys
{/* Dialog Content */}
Milestone Index

LeetCode Problem #{selectedNum}

{modalLoading ? (
FETCHING DATA...
) : details.length > 0 ? (
{details.map((prob) => (

{prob.name}

Pattern: {prob.pattern}
{prob.difficulty.toUpperCase()}
Time: {prob.optimal_time}
Space: {prob.optimal_space}
Google Doc
))}
) : (

No Local Note Synced

You haven't synced notes for LeetCode #{selectedNum} yet. Click "Sync Google Drive" to import your progress notes.

External Search Shortcuts: Search LeetCode Search Google
)} {/* Completion Toggle */}
Quick Action
)}
); }