452 lines
14 KiB
Markdown
452 lines
14 KiB
Markdown
# 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:**
|
|
```typescript
|
|
'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:**
|
|
```typescript
|
|
// Crafting phases for the stepper
|
|
const CRAFTING_PHASES = ['Design', 'Prepare', 'Apply', 'Craft'];
|
|
```
|
|
|
|
**Component Props:**
|
|
```typescript
|
|
export interface CraftingTabProps {
|
|
store: GameStore;
|
|
}
|
|
```
|
|
|
|
**State and Stepper Mapping:**
|
|
```typescript
|
|
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):**
|
|
```tsx
|
|
{/* 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):**
|
|
```tsx
|
|
{/* 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):**
|
|
```tsx
|
|
{/* 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):**
|
|
```tsx
|
|
{/* 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`):**
|
|
```typescript
|
|
// 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:**
|
|
```typescript
|
|
interface StepperProps extends React.HTMLAttributes<HTMLDivElement> {
|
|
steps: string[];
|
|
currentStep: number; // 0-indexed
|
|
orientation?: "horizontal" | "vertical";
|
|
}
|
|
```
|
|
|
|
**Full Implementation (100 lines):**
|
|
```typescript
|
|
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:**
|
|
```typescript
|
|
export interface EquipmentCrafterProps {
|
|
store: GameStore;
|
|
}
|
|
```
|
|
|
|
**EnchantmentDesignerProps:**
|
|
```typescript
|
|
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:**
|
|
```typescript
|
|
export interface EnchantmentPreparerProps {
|
|
store: GameStore;
|
|
selectedEquipmentInstance: string | null;
|
|
setSelectedEquipmentInstance: (id: string | null) => void;
|
|
}
|
|
```
|
|
|
|
**EnchantmentApplierProps:**
|
|
```typescript
|
|
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
|