14 KiB
Task 12 Context: Restructure CraftingTab
Task Description
Restructure CraftingTab (remove 1-4 progress bar, split Fabricate/Enchant, top sub-tabs) (PRIORITY 3a)
Source Files
1. /src/components/game/tabs/CraftingTab.tsx (268 lines)
Imports and Dependencies:
'use client';
import { useState } from 'react';
import { Button } from '@/components/ui/button';
import { Progress } from '@/components/ui/progress';
import { GameCard } from '@/components/ui/game-card';
import { SectionHeader } from '@/components/ui/section-header';
import { ActionButton } from '@/components/ui/action-button';
import { Stepper } from '@/components/ui/stepper';
import { Scroll, Hammer, Sparkles, Anvil } from 'lucide-react';
import type { EquipmentInstance, EnchantmentDesign, DesignEffect, AppliedEnchantment, LootInventory, EquipmentCraftingProgress } from '@/lib/game/types';
import { fmt, type GameStore } from '@/lib/game/store';
import {
EnchantmentDesigner,
EnchantmentPreparer,
EnchantmentApplier,
EquipmentCrafter,
} from '@/components/game/crafting';
import { useGameToast } from '@/components/game/GameToast';
Crafting Phases Constant:
// Crafting phases for the stepper
const CRAFTING_PHASES = ['Design', 'Prepare', 'Apply', 'Craft'];
Component Props:
export interface CraftingTabProps {
store: GameStore;
}
State and Stepper Mapping:
export function CraftingTab({ store }: CraftingTabProps) {
const showToast = useGameToast();
const currentAction = store.currentAction;
const designProgress = store.designProgress;
const preparationProgress = store.preparationProgress;
const applicationProgress = store.applicationProgress;
const equipmentCraftingProgress = store.equipmentCraftingProgress;
const pauseApplication = store.pauseApplication;
const resumeApplication = store.resumeApplication;
const [craftingStage, setCraftingStage] = useState<'design' | 'prepare' | 'apply' | 'craft'>('craft');
// Map crafting stage to stepper index
const getStepperIndex = (stage: string): number => {
switch (stage) {
case 'design': return 0;
case 'prepare': return 1;
case 'apply': return 2;
case 'craft': return 3;
default: return 0;
}
};
Stepper Component (lines 58-68):
{/* Visual Stepper - Requirement: show Design, Prepare, Apply phases as visual stepper */}
<GameCard variant="default" className="p-4">
<Stepper
steps={CRAFTING_PHASES}
currentStep={getStepperIndex(craftingStage)}
className="px-4"
/>
</GameCard>
Stage Content Conditional Rendering (lines 71-97):
{/* Stage Content - Without unlabeled Tabs, using conditional rendering instead */}
<div className="mt-4">
{craftingStage === 'craft' && (
<EquipmentCrafter store={store} />
)}
{craftingStage === 'design' && (
<EnchantmentDesigner
store={store}
selectedEquipmentType={null}
setSelectedEquipmentType={() => {}}
selectedEffects={[]}
setSelectedEffects={() => {}}
designName={''}
setDesignName={() => {}}
selectedDesign={null}
setSelectedDesign={() => {}}
/>
)}
{craftingStage === 'prepare' && (
<EnchantmentPreparer
store={store}
selectedEquipmentInstance={null}
setSelectedEquipmentInstance={() => {}}
/>
)}
{craftingStage === 'apply' && (
<EnchantmentApplier
store={store}
selectedEquipmentInstance={null}
setSelectedEquipmentInstance={() => {}}
selectedDesign={null}
setSelectedDesign={() => {}}
onEnchantmentApplied={handleEnchantmentApplied}
onCapacityExceeded={handleCapacityExceeded}
/>
)}
</div>
Stage Navigation Buttons (lines 99-131):
{/* Stage Navigation Buttons */}
<GameCard variant="default" className="p-4">
<div className="flex justify-center gap-2 flex-wrap">
<ActionButton
variant={craftingStage === 'craft' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('craft')}
className={craftingStage === 'craft' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Anvil size={14} className="mr-1" />
Craft
</ActionButton>
<ActionButton
variant={craftingStage === 'design' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('design')}
className={craftingStage === 'design' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Scroll size={14} className="mr-1" />
Design
</ActionButton>
<ActionButton
variant={craftingStage === 'prepare' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('prepare')}
className={craftingStage === 'prepare' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Hammer size={14} className="mr-1" />
Prepare
</ActionButton>
<ActionButton
variant={craftingStage === 'apply' ? 'primary' : 'secondary'}
size="sm"
onClick={() => setCraftingStage('apply')}
className={craftingStage === 'apply' ? 'ring-2 ring-[var(--interactive-primary)]' : ''}
>
<Sparkles size={14} className="mr-1" />
Apply
</ActionButton>
</div>
</GameCard>
Current Activity Indicators (Progress Bars to be removed - lines 133-236):
{/* Current Activity Indicator */}
{currentAction === 'craft' && equipmentCraftingProgress && (
<GameCard variant="default" className="border-[var(--mana-water)]/60 bg-[var(--mana-water)]/10">
<SectionHeader
title="Crafting Equipment"
action={
<span className="text-sm text-[var(--text-muted)]">
{safeToFixed(calcPercent(equipmentCraftingProgress.progress, equipmentCraftingProgress.required), 0)}%
</span>
}
/>
<Progress
value={calcPercent(equipmentCraftingProgress.progress, equipmentCraftingProgress.required)}
className="h-3 bg-[var(--bg-sunken)]"
/>
{/* ... more content ... */}
</GameCard>
)}
{currentAction === 'design' && designProgress && (
<GameCard variant="default" className="border-[var(--mana-stellar)]/60 bg-[var(--mana-stellar)]/10">
{/* ... Progress bar and content ... */}
</GameCard>
)}
{currentAction === 'prepare' && preparationProgress && (
<GameCard variant="default" className="border-[var(--color-warning)]/60 bg-[var(--color-warning)]/10">
{/* ... Progress bar and content ... */}
</GameCard>
)}
{currentAction === 'enchant' && applicationProgress && (
<GameCard variant="default" className="border-[var(--mana-light)]/60 bg-[var(--mana-light)]/10">
{/* ... Progress bar and content ... */}
</GameCard>
)}
2. Files in /src/components/game/crafting/ Directory
| File | Size | Last Modified |
|---|---|---|
EnchantmentApplier.tsx |
12,206 bytes | 1777364523 |
EnchantmentDesigner.tsx |
19,568 bytes | 1777361558 |
EnchantmentPreparer.tsx |
14,816 bytes | 1777365343 |
EquipmentCrafter.tsx |
9,121 bytes | 1777205526 |
index.tsx |
396 bytes | 1777028644 |
Barrel File (index.tsx):
// Barrel file for crafting components
export { EnchantmentDesigner, type EnchantmentDesignerProps } from './EnchantmentDesigner';
export { EnchantmentPreparer, type EnchantmentPreparerProps } from './EnchantmentPreparer';
export { EnchantmentApplier, type EnchantmentApplierProps } from './EnchantmentApplier';
export { EquipmentCrafter, type EquipmentCrafterProps } from './EquipmentCrafter';
3. Stepper Component (/src/components/ui/stepper.tsx)
Interface:
interface StepperProps extends React.HTMLAttributes<HTMLDivElement> {
steps: string[];
currentStep: number; // 0-indexed
orientation?: "horizontal" | "vertical";
}
Full Implementation (100 lines):
import * as React from "react";
import { cn } from "@/lib/utils";
import { Check, Circle, ArrowRight } from "lucide-react";
interface StepperProps extends React.HTMLAttributes<HTMLDivElement> {
steps: string[];
currentStep: number; // 0-indexed
orientation?: "horizontal" | "vertical";
}
interface StepProps {
label: string;
stepNumber: number;
isActive: boolean;
isCompleted: boolean;
isLast: boolean;
orientation?: "horizontal" | "vertical";
}
const Step = ({ label, stepNumber, isActive, isCompleted, isLast, orientation = "horizontal" }: StepProps) => {
return (
<div
className={cn(
"flex items-center",
orientation === "vertical" ? "flex-col" : "flex-row",
orientation === "vertical" && "w-full"
)}
>
<div className="flex flex-col items-center">
<div
className={cn(
"flex items-center justify-center w-8 h-8 rounded-full border-2 transition-all duration-200",
isActive && "border-[var(--interactive-primary)] bg-[var(--interactive-primary)]/20 text-[var(--interactive-primary)]",
isCompleted && "border-[var(--color-success)] bg-[var(--color-success)]/20 text-[var(--color-success)]",
!isActive && !isCompleted && "border-[var(--border-default)] bg-[var(--bg-sunken)] text-[var(--text-muted)]"
)}
aria-current={isActive ? "step" : undefined}
>
{isCompleted ? (
<Check size={16} />
) : (
<span className="text-xs font-semibold">{stepNumber}</span>
)}
</div>
<span
className={cn(
"text-xs mt-1 font-medium",
isActive && "text-[var(--interactive-primary)]",
isCompleted && "text-[var(--color-success)]",
!isActive && !isCompleted && "text-[var(--text-muted)]"
)}
>
{label}
</span>
</div>
{!isLast && (
<div
className={cn(
"flex-1 mx-2",
orientation === "vertical" ? "h-8 w-px my-1" : "h-px",
isCompleted ? "bg-[var(--color-success)]" : "bg-[var(--border-default)]"
)}
/>
)}
</div>
);
};
export function Stepper({ steps, currentStep, orientation = "horizontal", className, ...props }: StepperProps) {
return (
<div
data-slot="stepper"
className={cn(
"flex w-full",
orientation === "horizontal" ? "flex-row items-center" : "flex-col",
className
)}
role="list"
aria-label="Progress steps"
{...props}
>
{steps.map((step, index) => (
<div
key={step}
className={cn("flex items-center", orientation === "vertical" && "w-full")}
role="listitem"
>
<Step
label={step}
stepNumber={index + 1}
isActive={index === currentStep}
isCompleted={index < currentStep}
isLast={index === steps.length - 1}
orientation={orientation}
/>
</div>
))}
</div>
);
}
4. Current Sub-Tab/Navigation Implementation Details
Current Structure: The CraftingTab currently uses a 4-stage linear workflow with:
- A visual Stepper component showing phases: Design → Prepare → Apply → Craft
- Navigation buttons at the bottom to switch between stages
- Conditional rendering of content based on
craftingStagestate
Current Stages:
design- EnchantmentDesigner component (Design enchantments)prepare- EnchantmentPreparer component (Prepare equipment)apply- EnchantmentApplier component (Apply enchantments)craft- EquipmentCrafter component (Craft equipment)
Issues to Address (Task Requirements):
- Remove 1-4 progress bar - The Stepper component (lines 58-68) needs to be removed
- Split Fabricate/Enchant - Currently "Craft" (EquipmentCrafter) is mixed in with enchantment workflow. Need to split into:
- "Fabricate" tab - for EquipmentCrafter (crafting equipment)
- "Enchant" tab - for the Design → Prepare → Apply workflow
- Top sub-tabs - Replace the bottom navigation buttons with proper top-level sub-tabs
Current Navigation Pattern:
- State:
craftingStage(useState with 4 possible values) - Navigation: 4 ActionButtons at the bottom of the tab
- Visual indicator: Stepper at the top showing progress through phases
Suggested New Structure (for implementation):
CraftingTab
├── Top Sub-Tabs: [Fabricate] [Enchant]
├── Fabricate Content: EquipmentCrafter
└── Enchant Content:
├── Sub-Navigation: [Design] [Prepare] [Apply]
├── Design: EnchantmentDesigner
├── Prepare: EnchantmentPreparer
└── Apply: EnchantmentApplier
5. Component Props Signatures
EquipmentCrafterProps:
export interface EquipmentCrafterProps {
store: GameStore;
}
EnchantmentDesignerProps:
export interface EnchantmentDesignerProps {
store: GameStore;
selectedEquipmentType: string | null;
setSelectedEquipmentType: (type: string | null) => void;
selectedEffects: DesignEffect[];
setSelectedEffects: (effects: DesignEffect[]) => void;
designName: string;
setDesignName: (name: string) => void;
selectedDesign: string | null;
setSelectedDesign: (id: string | null) => void;
}
EnchantmentPreparerProps:
export interface EnchantmentPreparerProps {
store: GameStore;
selectedEquipmentInstance: string | null;
setSelectedEquipmentInstance: (id: string | null) => void;
}
EnchantmentApplierProps:
export interface EnchantmentApplierProps {
store: GameStore;
selectedEquipmentInstance: string | null;
setSelectedEquipmentInstance: (id: string | null) => void;
selectedDesign: string | null;
setSelectedDesign: (id: string | null) => void;
onEnchantmentApplied?: () => void;
onCapacityExceeded?: (itemName: string, used: number, total: number) => void;
}
6. Key Observations for Restructuring
-
Stepper Removal: The
CRAFTING_PHASESconstant andSteppercomponent usage must be removed from CraftingTab -
State Management: The
craftingStagestate will need to be replaced with:- A top-level tab state (
fabricate|enchant) - An enchant sub-stage state (
design|prepare|apply) when in enchant mode
- A top-level tab state (
-
Progress Bars: The activity indicators with Progress components (lines 133-236) should potentially be moved into their respective components (EquipmentCrafter, EnchantmentDesigner, etc.) rather than being in CraftingTab
-
No Tab Component: Currently, the app doesn't use a Tab component - it uses conditional rendering with ActionButtons. The restructured version should implement proper tabs at the top level
-
Helper Functions: The
safeToFixedandcalcPercenthelpers are used for progress bars - these may no longer be needed in CraftingTab after restructuring