Fix 3 files: migrate to useSkillStore, remove parallel study UI
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 33s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 33s
- SkillsTab.tsx: Replace useGameStore with useSkillStore for currentStudyTarget - SkillUpgradeDialog.tsx: Migrate to useSkillStore, fix commitSkillUpgrades call - SkillRow.tsx: Remove parallel study feature (startParallelStudySkill doesn't exist in skillStore), remove hasParallelStudy reference - Update index.ts export to point to new SkillsTab location - Delete old legacy tabs/SkillsTab.tsx file
This commit is contained in:
@@ -129,7 +129,6 @@ Mana-Loop/
|
|||||||
│ │ │ │ ├── SkillCategoryHeader.tsx
|
│ │ │ │ ├── SkillCategoryHeader.tsx
|
||||||
│ │ │ │ ├── SkillMultipliers.tsx
|
│ │ │ │ ├── SkillMultipliers.tsx
|
||||||
│ │ │ │ ├── SkillRow.tsx
|
│ │ │ │ ├── SkillRow.tsx
|
||||||
│ │ │ │ ├── SkillsTab.tsx
|
|
||||||
│ │ │ │ ├── SpellsTab.tsx
|
│ │ │ │ ├── SpellsTab.tsx
|
||||||
│ │ │ │ ├── SpireHeader.tsx
|
│ │ │ │ ├── SpireHeader.tsx
|
||||||
│ │ │ │ ├── SpireTab.tsx
|
│ │ │ │ ├── SpireTab.tsx
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ export function SkillRow({ skillId, onUpgradeClick }: SkillRowProps) {
|
|||||||
variant={canStudy ? 'default' : 'outline'}
|
variant={canStudy ? 'default' : 'outline'}
|
||||||
disabled={!canStudy}
|
disabled={!canStudy}
|
||||||
className={canStudy ? 'bg-purple-600 hover:bg-purple-700' : 'opacity-50'}
|
className={canStudy ? 'bg-purple-600 hover:bg-purple-700' : 'opacity-50'}
|
||||||
onClick={() => startStudyingSkill(tieredSkillId)}
|
onClick={() => startStudyingSkill(tieredSkillId, rawMana)}
|
||||||
>
|
>
|
||||||
Study ({fmt(cost)})
|
Study ({fmt(cost)})
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export function SkillUpgradeDialog({ skillId, milestone, onClose }: SkillUpgrade
|
|||||||
|
|
||||||
const handleDone = () => {
|
const handleDone = () => {
|
||||||
if (currentSelections.length === 2 && skillId) {
|
if (currentSelections.length === 2 && skillId) {
|
||||||
commitSkillUpgrades(skillId, currentSelections, milestone);
|
commitSkillUpgrades(skillId, currentSelections);
|
||||||
}
|
}
|
||||||
setPendingUpgradeSelections([]);
|
setPendingUpgradeSelections([]);
|
||||||
onClose();
|
onClose();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export { CraftingTab } from './tabs/CraftingTab';
|
|||||||
export { SpireTab } from './tabs/SpireTab';
|
export { SpireTab } from './tabs/SpireTab';
|
||||||
export { SpellsTab } from './tabs/SpellsTab';
|
export { SpellsTab } from './tabs/SpellsTab';
|
||||||
export { LabTab } from './tabs/LabTab';
|
export { LabTab } from './tabs/LabTab';
|
||||||
export { SkillsTab } from './tabs/SkillsTab';
|
export { SkillsTab } from './SkillsTab';
|
||||||
export { StatsTab } from './tabs/StatsTab';
|
export { StatsTab } from './tabs/StatsTab';
|
||||||
|
|
||||||
// UI components
|
// UI components
|
||||||
|
|||||||
@@ -1,252 +0,0 @@
|
|||||||
// ─── 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, useAttunementStore } 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 { useSkillStore, usePrestigeStore } from '@/lib/game/stores';
|
|
||||||
|
|
||||||
export function SkillsTab() {
|
|
||||||
const showToast = useGameToast();
|
|
||||||
const [upgradeDialogSkill, setUpgradeDialogSkill] = useState<string | null>(null);
|
|
||||||
const [upgradeDialogMilestone, setUpgradeDialogMilestone] = useState<5 | 10>(5);
|
|
||||||
const [collapsedCategories, setCollapsedCategories] = useState<Set<string>>(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 = useSkillStore((s) => s.currentStudyTarget);
|
|
||||||
const parallelStudyTarget = useSkillStore((s) => s.parallelStudyTarget);
|
|
||||||
const startStudyingSkill = useSkillStore((s) => s.startStudyingSkill);
|
|
||||||
const startParallelStudySkill = useSkillStore((s) => s.startParallelStudySkill);
|
|
||||||
const cancelStudy = useSkillStore((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 = useAttunementStore((s) => s.attunements);
|
|
||||||
const availableCategories = getAvailableSkillCategories(attunements || {});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="space-y-4">
|
|
||||||
{/* Upgrade Selection Dialog */}
|
|
||||||
<UpgradeDialog
|
|
||||||
open={!!upgradeDialogSkill}
|
|
||||||
skillId={upgradeDialogSkill}
|
|
||||||
milestone={upgradeDialogMilestone}
|
|
||||||
pendingSelections={pendingSelections.length > 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 && (
|
|
||||||
<ConfirmDialog
|
|
||||||
open={!!cancelStudyConfirm}
|
|
||||||
onOpenChange={() => 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' && (
|
|
||||||
<Card className="bg-gray-900/80 border-purple-600/50">
|
|
||||||
<CardContent className="pt-4">
|
|
||||||
<StudyProgress
|
|
||||||
currentStudyTarget={currentStudyTarget}
|
|
||||||
skills={skills}
|
|
||||||
studySpeedMult={studySpeedMult}
|
|
||||||
cancelStudy={handleCancelStudy}
|
|
||||||
/>
|
|
||||||
</CardContent>
|
|
||||||
</Card>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Skill Categories */}
|
|
||||||
{SKILL_CATEGORIES.filter((cat) => availableCategories.includes(cat.id)).map((cat) => (
|
|
||||||
<CategorySkillsList
|
|
||||||
key={cat.id}
|
|
||||||
category={cat}
|
|
||||||
availableCategories={availableCategories}
|
|
||||||
isCollapsed={collapsedCategories.has(cat.id)}
|
|
||||||
onToggleCategory={toggleCategory}
|
|
||||||
skills={skills}
|
|
||||||
skillUpgrades={skillUpgrades}
|
|
||||||
skillTiers={skillTiers}
|
|
||||||
prestigeUpgrades={prestigeUpgrades}
|
|
||||||
studySpeedMult={studySpeedMult}
|
|
||||||
upgradeEffects={upgradeEffects}
|
|
||||||
currentStudyTarget={currentStudyTarget}
|
|
||||||
onStartStudying={handleStartStudying}
|
|
||||||
onParallelStudy={handleParallelStudy}
|
|
||||||
onCancelStudy={handleCancelStudy}
|
|
||||||
onOpenUpgradeDialog={(skillId, milestone) => {
|
|
||||||
setUpgradeDialogSkill(skillId);
|
|
||||||
setUpgradeDialogMilestone(milestone);
|
|
||||||
setPendingSelections([]);
|
|
||||||
}}
|
|
||||||
onTierUp={(skillId) => tierUpSkill(skillId)}
|
|
||||||
pendingSelections={pendingSelections}
|
|
||||||
setPendingSelections={setPendingSelections}
|
|
||||||
/>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
SkillsTab.displayName = "SkillsTab";
|
|
||||||
Reference in New Issue
Block a user