feat: recreate Crafting Tab with Fabricator and Enchanter sub-tabs
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m18s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 2m18s
- Add fabricator-recipes.ts with 12 recipes across earth/metal/crystal/sand mana types - Add FabricatorSubTab with mana-type filtering, recipe cards, materials inventory - Add EnchanterSubTab integrating existing 3-phase flow (Design → Prepare → Apply) - Add CraftingTab main component with clsx-based sub-tab system (matches DisciplinesTab pattern) - Wire into tabs barrel export and page.tsx with lazy loading + DebugName wrapper - Add 17 tests covering exports, displayNames, recipe data integrity, helpers, file sizes - All files under 400 lines
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
'use client';
|
||||
|
||||
import { useState } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { PenLine, FlaskConical, Sparkles } from 'lucide-react';
|
||||
import {
|
||||
EnchantmentDesigner,
|
||||
EnchantmentPreparer,
|
||||
EnchantmentApplier,
|
||||
} from '@/components/game/crafting';
|
||||
import { useCraftingStore } from '@/lib/game/stores';
|
||||
import type { DesignEffect } from '@/lib/game/types';
|
||||
|
||||
type EnchanterPhase = 'design' | 'prepare' | 'apply';
|
||||
|
||||
const PHASES: { key: EnchanterPhase; label: string; icon: typeof PenLine }[] = [
|
||||
{ key: 'design', label: 'Design', icon: PenLine },
|
||||
{ key: 'prepare', label: 'Prepare', icon: FlaskConical },
|
||||
{ key: 'apply', label: 'Apply', icon: Sparkles },
|
||||
];
|
||||
|
||||
export function EnchanterSubTab() {
|
||||
const [activePhase, setActivePhase] = useState<EnchanterPhase>('design');
|
||||
|
||||
// Shared state for the enchantment flow
|
||||
const [selectedEquipmentType, setSelectedEquipmentType] = useState<string | null>(null);
|
||||
const [selectedEffects, setSelectedEffects] = useState<DesignEffect[]>([]);
|
||||
const [designName, setDesignName] = useState('');
|
||||
const [selectedDesign, setSelectedDesign] = useState<string | null>(null);
|
||||
const [selectedEquipmentInstance, setSelectedEquipmentInstance] = useState<string | null>(null);
|
||||
|
||||
const resetEnchantmentSelection = useCraftingStore((s) => s.resetEnchantmentSelection);
|
||||
|
||||
const handlePhaseChange = (phase: EnchanterPhase) => {
|
||||
setActivePhase(phase);
|
||||
};
|
||||
|
||||
const handleEnchantmentApplied = () => {
|
||||
// Reset selection after successful application
|
||||
resetEnchantmentSelection();
|
||||
setSelectedEquipmentInstance(null);
|
||||
setSelectedDesign(null);
|
||||
// Go back to design phase
|
||||
setActivePhase('design');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
{/* Phase selector */}
|
||||
<div className="flex gap-2">
|
||||
{PHASES.map(({ key, label, icon: Icon }) => (
|
||||
<button
|
||||
key={key}
|
||||
onClick={() => handlePhaseChange(key)}
|
||||
className={clsx('rounded px-3 py-1 text-sm font-medium flex items-center gap-1.5', {
|
||||
'bg-purple-600 text-white': activePhase === key,
|
||||
'text-gray-400 hover:text-gray-200': activePhase !== key,
|
||||
})}
|
||||
>
|
||||
<Icon className="w-3.5 h-3.5" />
|
||||
{label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Phase content */}
|
||||
{activePhase === 'design' && (
|
||||
<EnchantmentDesigner
|
||||
selectedEquipmentType={selectedEquipmentType}
|
||||
setSelectedEquipmentType={setSelectedEquipmentType}
|
||||
selectedEffects={selectedEffects}
|
||||
setSelectedEffects={setSelectedEffects}
|
||||
designName={designName}
|
||||
setDesignName={setDesignName}
|
||||
selectedDesign={selectedDesign}
|
||||
setSelectedDesign={setSelectedDesign}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activePhase === 'prepare' && (
|
||||
<EnchantmentPreparer
|
||||
selectedEquipmentInstance={selectedEquipmentInstance}
|
||||
setSelectedEquipmentInstance={setSelectedEquipmentInstance}
|
||||
/>
|
||||
)}
|
||||
|
||||
{activePhase === 'apply' && (
|
||||
<EnchantmentApplier
|
||||
selectedEquipmentInstance={selectedEquipmentInstance}
|
||||
setSelectedEquipmentInstance={setSelectedEquipmentInstance}
|
||||
selectedDesign={selectedDesign}
|
||||
setSelectedDesign={setSelectedDesign}
|
||||
onEnchantmentApplied={handleEnchantmentApplied}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
EnchanterSubTab.displayName = 'EnchanterSubTab';
|
||||
Reference in New Issue
Block a user