Files
Mana-Loop/docs/task5/subtask_12_context.md
T
Refactoring Agent 454195cdfb
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 3m16s
Update hooks and ignore markdown files in size check
2026-04-29 12:18:08 +02:00

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:

  1. A visual Stepper component showing phases: Design → Prepare → Apply → Craft
  2. Navigation buttons at the bottom to switch between stages
  3. Conditional rendering of content based on craftingStage state

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):

  1. Remove 1-4 progress bar - The Stepper component (lines 58-68) needs to be removed
  2. 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
  3. 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

  1. Stepper Removal: The CRAFTING_PHASES constant and Stepper component usage must be removed from CraftingTab

  2. State Management: The craftingStage state 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
  3. 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

  4. 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

  5. Helper Functions: The safeToFixed and calcPercent helpers are used for progress bars - these may no longer be needed in CraftingTab after restructuring