// ─── Skills Tab ─────────────────────────────────────────────────────────────── // SkillsTab - Displays all skills organized by category // Refactored: extracted components for better modularity (reduced from 400 lines) 'use client'; import { useState, useCallback } from 'react'; import { SKILLS_DEF, SKILL_CATEGORIES, getStudySpeedMultiplier, getStudyCostMultiplier, } from '@/lib/game/constants'; import { SKILL_EVOLUTION_PATHS, getUpgradesForSkillAtMilestone, getNextTierSkill, getTierMultiplier, } from '@/lib/game/skill-evolution'; import { getUnifiedEffects } from '@/lib/game/effects'; import { getAvailableSkillCategories } from '@/lib/game/data/attunements'; import { fmt, fmtDec } from '@/lib/game/stores'; import type { SkillUpgradeChoice } from '@/lib/game/types'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger, } from '@/components/ui/tooltip'; import { StudyProgress } from './StudyProgress'; import { UpgradeDialog } from './UpgradeDialog'; import { ConfirmDialog } from '@/components/game/ConfirmDialog'; import { useGameToast } from '@/components/game/GameToast'; import { ELEMENTS } from '@/lib/game/constants'; import { ChevronDown, ChevronRight } from 'lucide-react'; import { SkillRow } from './SkillRow'; import { useSkillUpgradeSelection } from '@/lib/game/hooks/useSkillUpgradeSelection'; import { CategorySkillsList } from './CategorySkillsList'; import { useGameStore, useSkillStore, usePrestigeStore } from '@/lib/game/stores'; export function SkillsTab() { const showToast = useGameToast(); const [upgradeDialogSkill, setUpgradeDialogSkill] = useState(null); const [upgradeDialogMilestone, setUpgradeDialogMilestone] = useState<5 | 10>(5); const [collapsedCategories, setCollapsedCategories] = useState>(new Set()); const [cancelStudyConfirm, setCancelStudyConfirm] = useState<{ skillId: string; skillName: string; } | null>(null); const skills = useSkillStore((s) => s.skills); const skillUpgrades = useSkillStore((s) => s.skillUpgrades); const skillTiers = useSkillStore((s) => s.skillTiers); const prestigeUpgrades = usePrestigeStore((s) => s.prestigeUpgrades); const currentStudyTarget = useGameStore((s) => s.currentStudyTarget); const parallelStudyTarget = useGameStore((s) => s.parallelStudyTarget); const startStudyingSkill = useSkillStore((s) => s.startStudyingSkill); const startParallelStudySkill = useSkillStore((s) => s.startParallelStudySkill); const cancelStudy = useGameStore((s) => s.cancelStudy); const commitSkillUpgrades = useSkillStore((s) => s.commitSkillUpgrades); const tierUpSkill = useSkillStore((s) => s.tierUpSkill); const studySpeedMult = getStudySpeedMultiplier({ skills, prestigeUpgrades, skillUpgrades, skillTiers }); const upgradeEffects = getUnifiedEffects({ skillUpgrades, skillTiers, equippedInstances: {}, equipmentInstances: {} }); // Upgrade selection hook const { pendingSelections, setPendingSelections, toggleUpgrade, handleConfirm: hookHandleConfirm, handleCancel: hookHandleCancel, } = useSkillUpgradeSelection(); // Toggle category collapse const toggleCategory = (categoryId: string) => { setCollapsedCategories((prev) => { const newSet = new Set(prev); if (newSet.has(categoryId)) { newSet.delete(categoryId); } else { newSet.add(categoryId); } return newSet; }); }; // Get upgrade choices for dialog const getUpgradeChoices = () => { if (!upgradeDialogSkill) return { available: [] as SkillUpgradeChoice[], selected: [] as string[] }; const skillDef = SKILLS_DEF[upgradeDialogSkill.includes('_t') ? upgradeDialogSkill.split('_t')[0] : upgradeDialogSkill]; if (!skillDef) return { available: [] as SkillUpgradeChoice[], selected: [] as string[] }; return { available: getUpgradesForSkillAtMilestone(upgradeDialogSkill, upgradeDialogMilestone), selected: skillUpgrades[upgradeDialogSkill] || [], }; }; const { available, selected: alreadySelected } = getUpgradeChoices(); // Handle upgrade dialog confirm const handleConfirm = () => { hookHandleConfirm( upgradeDialogSkill!, upgradeDialogMilestone, (skillId: string, selections: string[], milestone: 5 | 10) => { commitSkillUpgrades(skillId, selections, milestone); return () => setUpgradeDialogSkill(null); } ); }; // Handle upgrade dialog cancel const handleCancel = () => { hookHandleCancel(() => setUpgradeDialogSkill(null)); }; // Wrapper for upgrade toggle that matches UpgradeDialog's onToggle signature const handleUpgradeToggle = useCallback( (upgradeId: string) => { toggleUpgrade(upgradeId, available, alreadySelected); }, [toggleUpgrade, available, alreadySelected] ); // Handle study start with toast const handleStartStudying = (skillId: string) => { const skillDef = SKILLS_DEF[skillId.includes('_t') ? skillId.split('_t')[0] : skillId]; startStudyingSkill(skillId); showToast('info', 'Study Started', `Studying ${skillDef?.name || 'skill'}...`); }; // Handle parallel study start with toast const handleParallelStudy = (skillId: string) => { const skillDef = SKILLS_DEF[skillId.includes('_t') ? skillId.split('_t')[0] : skillId]; startParallelStudySkill(skillId); showToast('info', 'Parallel Study Started', `Studying ${skillDef?.name || 'skill'} in parallel (50% speed)...`); }; // Handle study cancel with confirmation const handleCancelStudy = () => { const currentTarget = currentStudyTarget; if (currentTarget?.type === 'skill') { const skillDef = SKILLS_DEF[currentTarget.id.includes('_t') ? currentTarget.id.split('_t')[0] : currentTarget.id]; setCancelStudyConfirm({ skillId: currentTarget.id, skillName: skillDef?.name || 'Unknown Skill', }); } }; const confirmCancelStudy = () => { if (cancelStudyConfirm) { cancelStudy(); showToast( 'warning', 'Study Cancelled', `${cancelStudyConfirm.skillName} study cancelled. Progress will be partially saved based on your Knowledge Retention skill.` ); setCancelStudyConfirm(null); } }; // Get available skill categories based on attunements const attunements = useGameStore((s) => s.attunements); const availableCategories = getAvailableSkillCategories(attunements || {}); return (
{/* Upgrade Selection Dialog */} 0 ? pendingSelections : alreadySelected} available={available} alreadySelected={alreadySelected} onToggle={handleUpgradeToggle} onConfirm={handleConfirm} onCancel={handleCancel} onOpenChange={(open) => { if (!open) { setPendingSelections([]); setUpgradeDialogSkill(null); } }} /> {/* Cancel Study Confirmation Dialog */} {cancelStudyConfirm && ( setCancelStudyConfirm(null)} title="Cancel Studying?" description={`Cancel studying ${cancelStudyConfirm.skillName}? Progress will be partially saved based on your Knowledge Retention skill.`} variant="warning" confirmText="Cancel Study" onConfirm={confirmCancelStudy} /> )} {/* Current Study Progress */} {currentStudyTarget && currentStudyTarget.type === 'skill' && ( )} {/* Skill Categories */} {SKILL_CATEGORIES.filter((cat) => availableCategories.includes(cat.id)).map((cat) => ( { setUpgradeDialogSkill(skillId); setUpgradeDialogMilestone(milestone); setPendingSelections([]); }} onTierUp={(skillId) => tierUpSkill(skillId)} pendingSelections={pendingSelections} setPendingSelections={setPendingSelections} /> ))}
); } SkillsTab.displayName = "SkillsTab";