feat: recreate Crafting Tab with Fabricator and Enchanter sub-tabs
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:
2026-05-20 02:32:37 +02:00
parent 9882578627
commit 1c7fc8c551
10 changed files with 840 additions and 2 deletions
+54
View File
@@ -0,0 +1,54 @@
'use client';
import { useState } from 'react';
import clsx from 'clsx';
import { Hammer, Sparkles } from 'lucide-react';
import { DebugName } from '@/components/game/debug/debug-context';
import { FabricatorSubTab } from './CraftingTab/FabricatorSubTab';
import { EnchanterSubTab } from './CraftingTab/EnchanterSubTab';
type CraftingAttunement = 'fabricator' | 'enchanter';
interface CraftingSubTab {
key: CraftingAttunement;
label: string;
icon: typeof Hammer;
}
const CRAFTING_SUB_TABS: CraftingSubTab[] = [
{ key: 'fabricator', label: 'Fabricator', icon: Hammer },
{ key: 'enchanter', label: 'Enchanter', icon: Sparkles },
];
export function CraftingTab() {
const [activeSubTab, setActiveSubTab] = useState<CraftingAttunement>('fabricator');
return (
<DebugName name="CraftingTab">
<div className="space-y-4">
{/* Sub-tab bar — same clsx pattern as DisciplinesTab */}
<div className="flex gap-2">
{CRAFTING_SUB_TABS.map(({ key, label, icon: Icon }) => (
<button
key={key}
onClick={() => setActiveSubTab(key)}
className={clsx('rounded px-3 py-1 text-sm font-medium flex items-center gap-1.5', {
'bg-amber-600 text-white': activeSubTab === key,
'text-gray-400 hover:text-gray-200': activeSubTab !== key,
})}
>
<Icon className="w-3.5 h-3.5" />
{label}
</button>
))}
</div>
{/* Sub-tab content */}
{activeSubTab === 'fabricator' && <FabricatorSubTab />}
{activeSubTab === 'enchanter' && <EnchanterSubTab />}
</div>
</DebugName>
);
}
CraftingTab.displayName = 'CraftingTab';