Refactor large files into modular components
Build and Publish Mana Loop Docker Image / build-and-publish (push) Failing after 1m9s

- Refactored page.tsx (613→252 lines) with GameOverScreen and LeftPanel extracted
- Refactored StatsTab.tsx (584→92 lines) with section components
- Refactored SkillsTab.tsx (434→54 lines) with sub-components
- Created modular structure for GameContext, LootInventory, and other components
- All extracted components organized into feature directories
This commit is contained in:
Refactoring Agent
2026-05-02 17:35:03 +02:00
parent c9ae2576f4
commit d2d28887b1
194 changed files with 16862 additions and 15729 deletions
@@ -0,0 +1,252 @@
// ─── Crafting Slice Logic ─────────────────────────────────────────────────
import type { StateCreator } from 'zustand';
import type { CraftingStore } from './types';
import type {
DesignEffect,
EnchantmentDesign,
EquipmentInstance
} from '../../types';
import type { EquipmentSlot } from '../../data/equipment';
import { initialCraftingState } from './initial-state';
import {
generateInstanceId,
generateDesignId,
createEquipmentInstance,
calculateDesignTime,
calculatePreparationTime,
calculatePreparationManaCost,
calculateApplicationTime,
calculateApplicationManaPerHour
} from './utils';
import {
EQUIPMENT_SLOTS,
getEquipmentType
} from '../../data/equipment';
import { createSelectors } from './selectors';
import {
processDesignTick,
processPreparationTick,
processApplicationTick
} from './tick-processors';
// ─── Cached Skills Workaround ──────────────────────────────────────────────
// We need to access skills from the main store - this is a workaround
// The store will pass skills when calling these methods
let cachedSkills: Record<string, number> = {};
export function setCachedSkills(skills: Record<string, number>): void {
cachedSkills = skills;
}
// ─── Slice Creator ─────────────────────────────────────────────────────────
export const createCraftingSlice: StateCreator<CraftingStore, [], [], CraftingStore> = (set, get) => {
const selectors = createSelectors(get);
return {
...initialCraftingState,
// Equipment management
createEquipment: (typeId: string, slot?: EquipmentSlot) => {
const instance = createEquipmentInstance(typeId);
set((state) => ({
equipmentInstances: {
...state.equipmentInstances,
[instance.instanceId]: instance,
},
}));
// Auto-equip if slot provided
if (slot) {
get().equipInstance(instance.instanceId, slot);
}
return instance;
},
equipInstance: (instanceId: string, slot: EquipmentSlot) => {
const instance = get().equipmentInstances[instanceId];
if (!instance) return;
const typeDef = getEquipmentType(instance.typeId);
if (!typeDef) return;
// Check if equipment can go in this slot
if (typeDef.slot !== slot) {
// For accessories, both accessory1 and accessory2 are valid
if (typeDef.category !== 'accessory' || (slot !== 'accessory1' && slot !== 'accessory2')) {
return;
}
}
set((state) => ({
equippedInstances: {
...state.equippedInstances,
[slot]: instanceId,
},
}));
},
unequipSlot: (slot: EquipmentSlot) => {
set((state) => ({
equippedInstances: {
...state.equippedInstances,
[slot]: null,
},
}));
},
deleteInstance: (instanceId: string) => {
set((state) => {
const newInstanceMap = { ...state.equipmentInstances };
delete newInstanceMap[instanceId];
// Remove from equipped slots
const newEquipped = { ...state.equippedInstances };
for (const slot of EQUIPMENT_SLOTS) {
if (newEquipped[slot] === instanceId) {
newEquipped[slot] = null;
}
}
return {
equipmentInstances: newInstanceMap,
equippedInstances: newEquipped,
};
});
},
// Enchantment design
startDesign: (name: string, equipmentType: string, effects: DesignEffect[]) => {
const totalCapacity = effects.reduce((sum, e) => sum + e.capacityCost, 0);
const designTime = calculateDesignTime(effects);
const design: EnchantmentDesign = {
id: generateDesignId(),
name,
equipmentType,
effects,
totalCapacityUsed: totalCapacity,
designTime,
created: Date.now(),
};
set((state) => ({
enchantmentDesigns: [...state.enchantmentDesigns, design],
designProgress: {
designId: design.id,
progress: 0,
required: designTime,
name,
equipmentType,
effects,
},
}));
},
cancelDesign: () => {
const progress = get().designProgress;
if (!progress) return;
set((state) => ({
designProgress: null,
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== progress.designId),
}));
},
deleteDesign: (designId: string) => {
set((state) => ({
enchantmentDesigns: state.enchantmentDesigns.filter(d => d.id !== designId),
}));
},
// Equipment preparation
startPreparation: (instanceId: string) => {
const instance = get().equipmentInstances[instanceId];
if (!instance) return;
const prepTime = calculatePreparationTime(instance.typeId);
const manaCost = calculatePreparationManaCost(instance.typeId);
set({
preparationProgress: {
equipmentInstanceId: instanceId,
progress: 0,
required: prepTime,
manaCostPaid: 0,
},
});
},
cancelPreparation: () => {
set({ preparationProgress: null });
},
// Enchantment application
startApplication: (instanceId: string, designId: string) => {
const instance = get().equipmentInstances[instanceId];
const design = get().enchantmentDesigns.find(d => d.id === designId);
if (!instance || !design) return;
const appTime = calculateApplicationTime(design.effects, cachedSkills);
const manaPerHour = calculateApplicationManaPerHour(design.effects);
set({
applicationProgress: {
equipmentInstanceId: instanceId,
designId,
progress: 0,
required: appTime,
manaPerHour,
paused: false,
manaSpent: 0,
},
});
},
pauseApplication: () => {
const progress = get().applicationProgress;
if (!progress) return;
set({
applicationProgress: { ...progress, paused: true },
});
},
resumeApplication: () => {
const progress = get().applicationProgress;
if (!progress) return;
set({
applicationProgress: { ...progress, paused: false },
});
},
cancelApplication: () => {
set({ applicationProgress: null });
},
// Tick processing - delegated to tick-processors module
processDesignTick: (hours: number) => {
return processDesignTick(get(), set, hours);
},
processPreparationTick: (hours: number, manaAvailable: number) => {
return processPreparationTick(get(), set, hours, manaAvailable);
},
processApplicationTick: (hours: number, manaAvailable: number) => {
return processApplicationTick(get(), set, get, hours, manaAvailable, cachedSkills);
},
// Selectors - delegated to selectors module
getEquippedInstance: selectors.getEquippedInstance,
getAllEquipped: selectors.getAllEquipped,
getAvailableSpells: selectors.getAvailableSpells,
getEquipmentEffects: selectors.getEquipmentEffects,
};
};