import React, { useState, useMemo, useCallback, useEffect } from 'react'; import PageWrapper from '../components/PageWrapper'; import Card from '../components/common/Card'; import images from '../assets/images'; import plannerRegions from '../data/planner/plannerRegions.json'; import plannerTasks from '../data/planner/plannerTasks.json'; import plannerItems from '../data/planner/plannerItems.json'; import plannerRelics from '../data/planner/plannerRelics.json'; // Constants const MAX_REGIONS = 5; // Map region names to their badge image keys const getRegionBadge = regionName => { const badgeKey = `${regionName}_Area_Badge.png`; return images[badgeKey]; }; // Get default region IDs const DEFAULT_REGION_IDS = new Set(plannerRegions.regions.filter(r => r.isDefault).map(r => r.id)); export default function Planner() { // Region selection state - default regions are pre-selected const [selectedRegions, setSelectedRegions] = useState(() => { const defaults = plannerRegions.regions .filter(r => r.isDefault) .map(r => r.id); return new Set(defaults); }); // Task completion state const [completedTasks, setCompletedTasks] = useState(new Set()); // Toast notification state const [toast, setToast] = useState(null); // Auto-hide toast after 3 seconds useEffect(() => { if (toast) { const timer = setTimeout(() => setToast(null), 3000); return () => clearTimeout(timer); } return undefined; }, [toast]); // Show toast notification const showToast = useCallback((message, type = 'error') => { setToast({ message, type, key: Date.now() }); }, []); // Toggle region selection const toggleRegion = regionId => { // Prevent removing default regions if (DEFAULT_REGION_IDS.has(regionId) && selectedRegions.has(regionId)) { return; } // Check if trying to add beyond max if (!selectedRegions.has(regionId) && selectedRegions.size >= MAX_REGIONS) { showToast(`You cannot select more than ${MAX_REGIONS} regions!`); return; } setSelectedRegions(prev => { const next = new Set(prev); if (next.has(regionId)) { next.delete(regionId); } else { next.add(regionId); } return next; }); }; // Get region names for filtering const selectedRegionNames = useMemo(() => { const names = new Set(['Global']); plannerRegions.regions.forEach(region => { if (selectedRegions.has(region.id)) { names.add(region.name); } }); return names; }, [selectedRegions]); // Filter tasks based on selected regions const availableTasks = useMemo( () => plannerTasks.tasks.filter(task => task.regions.some(region => selectedRegionNames.has(region))), [selectedRegionNames] ); // Filter items based on selected regions const availableItems = useMemo( () => plannerItems.items.filter(item => item.regions.some(region => selectedRegionNames.has(region))), [selectedRegionNames] ); // Handle task completion with cascading logic const toggleTaskCompletion = useCallback(taskId => { setCompletedTasks(prev => { const next = new Set(prev); const task = plannerTasks.tasks.find(t => t.id === taskId); if (!task) { return prev; } if (next.has(taskId)) { // Uncompleting a task - just remove it next.delete(taskId); } else { // Completing a task - also complete any tasks in autoCompletes next.add(taskId); if (task.autoCompletes && task.autoCompletes.length > 0) { task.autoCompletes.forEach(autoId => { next.add(autoId); }); } } return next; }); }, []); // Check if a task is locked (prerequisites not met) const isTaskLocked = useCallback(task => { if (!task.prerequisites || task.prerequisites.length === 0) { return false; } return task.prerequisites.some(prereqId => !completedTasks.has(prereqId)); }, [completedTasks]); // Calculate total points from completed tasks const totalPoints = useMemo( () => availableTasks.filter(task => completedTasks.has(task.id)).reduce((sum, task) => sum + task.points, 0), [availableTasks, completedTasks] ); // Get difficulty color class const getDifficultyColor = difficulty => { const colors = { Easy: 'text-green-400', Medium: 'text-yellow-400', Hard: 'text-orange-400', Elite: 'text-red-400', Master: 'text-purple-400', }; return colors[difficulty] || 'text-gray-400'; }; return ( {/* Toast Notification */} {toast && (
⚠️ {toast.message}
)} {/* Jiggle animation styles */}

League Planner

Select regions to see available tasks and items. Completing certain tasks will automatically mark related tasks as complete.

{/* Points Display */}
Total Points: {totalPoints}
Tasks Completed: {completedTasks.size} / {availableTasks.length}
{/* Region Selector - Right anchored card with badges, 1 per row */}
Select Regions
{selectedRegions.size} / {MAX_REGIONS} selected
{plannerRegions.regions.map(region => { const isSelected = selectedRegions.has(region.id); const { isDefault } = region; const badge = getRegionBadge(region.name); return ( ); })}
{/* Available Tasks */}

Available Tasks ({availableTasks.length})

{availableTasks.map(task => { const isCompleted = completedTasks.has(task.id); const isLocked = isTaskLocked(task); const hasAutoCompletes = task.autoCompletes && task.autoCompletes.length > 0; return (
toggleTaskCompletion(task.id)} className='mt-1 h-4 w-4 rounded' />
{task.name} {task.difficulty} {task.points} pts

{task.description}

{hasAutoCompletes && (

⚡ Auto-completes {task.autoCompletes.length} other task(s)

)} {isLocked && (

🔒 Requires prerequisite tasks

)}
{task.regions.map(region => ( {region} ))}
); })}
{/* Available Items */}

Available Items ({availableItems.length})

{availableItems.map(item => (
{item.name} {item.slot}

{item.obtainedFrom}

{item.requirements.length > 0 && (
{item.requirements.map((req, idx) => ( {req.skill} {req.level} ))}
)} {item.relicUnlock && (

🔮 Requires relic unlock

)}
{item.regions.map(region => ( {region} ))}
))}
{/* Relics Section */}

Relic Tiers

{plannerRelics.relicTiers.map(tier => { const relicsInTier = plannerRelics.relics.filter(r => r.tier === tier.tier); const isUnlocked = totalPoints >= tier.pointsRequired; return (
Tier {tier.tier} {tier.pointsRequired} pts

{tier.name}

{relicsInTier.slice(0, 3).map(relic => (
• {relic.name}
))}
{!isUnlocked && (

Need {tier.pointsRequired - totalPoints} more points

)}
); })}
); }