fix: SkillsTab barrel export, equipment store reads, LabTab re-export, debug null guards, GrimoireTab loaded state, spire tab switching
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m48s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 1m48s
This commit is contained in:
@@ -24,7 +24,7 @@ import { EnchantmentsPanel } from './EnchantmentsPanel';
|
||||
import { useGameToast } from '@/components/game/GameToast';
|
||||
import { ConfirmDialog } from '@/components/game/ConfirmDialog';
|
||||
import { equipItem, unequipItem, deleteEquipmentInstance } from '@/lib/game/crafting-actions';
|
||||
import { useCombatStore } from '@/lib/game/stores';
|
||||
import { useCombatStore, useCraftingStore } from '@/lib/game/stores';
|
||||
|
||||
// Rarity color mappings using design system tokens
|
||||
export const RARITY_BORDER_COLORS: Record<string, string> = {
|
||||
@@ -120,8 +120,8 @@ export function EquipmentTab() {
|
||||
const [deleteConfirm, setDeleteConfirm] = useState<{ instanceId: string; name: string } | null>(null);
|
||||
|
||||
// Use modular store directly - MUST be called before any conditional returns
|
||||
const equippedInstances = useCombatStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCombatStore((s) => s.equipmentInstances);
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
|
||||
// Get unequipped items - hooks must be called before conditional returns
|
||||
const equippedIds = useMemo(() =>
|
||||
|
||||
@@ -1,122 +1 @@
|
||||
'use client';
|
||||
|
||||
import { GameCard, ElementBadge, ActionButton } from '@/components/ui';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
interface LabTabProps {
|
||||
store: {
|
||||
rawMana: number;
|
||||
elements: Record<string, { current: number; max: number; unlocked: boolean }>;
|
||||
skills: Record<string, number>;
|
||||
craftComposite: (target: string) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export function LabTab({ store }: LabTabProps) {
|
||||
// Render elemental mana grid - only show elements with current > 0
|
||||
const renderElementsGrid = () => (
|
||||
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-6 lg:grid-cols-8 gap-2">
|
||||
{Object.entries(store.elements || {})
|
||||
.filter(([, state]) => state.unlocked && state.current > 0)
|
||||
.map(([id, state]) => {
|
||||
const def = ELEMENTS[id];
|
||||
return (
|
||||
<div
|
||||
key={id}
|
||||
className="p-2 rounded border border-[var(--border-subtle)] bg-[var(--bg-sunken)]"
|
||||
>
|
||||
<div className="text-lg text-center">
|
||||
<ElementBadge elementId={id} size="sm" />
|
||||
</div>
|
||||
<div className="text-xs font-semibold text-center" style={{ color: `var(--mana-${id})` }}>{def?.name}</div>
|
||||
<div className="text-xs text-[var(--text-secondary)] font-[var(--font-mono)] text-center">{state.current}/{state.max}</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
|
||||
// Render composite crafting
|
||||
const renderCompositeCrafting = () => {
|
||||
const compositeElements = Object.entries(ELEMENTS)
|
||||
.filter(([, def]) => def.recipe)
|
||||
.filter(([id]) => (store.elements || {})[id]?.unlocked);
|
||||
|
||||
if (compositeElements.length === 0) return null;
|
||||
|
||||
return (
|
||||
<GameCard>
|
||||
<div className="pb-2">
|
||||
<h3 className="text-sm font-semibold text-[var(--text-primary)]">Composite Crafting</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
{compositeElements.map(([id, def]) => {
|
||||
const recipe = def.recipe || [];
|
||||
const canCraft = recipe.every(r => ((store.elements || {})[r]?.current || 0) >= 1);
|
||||
const craftBonus = 1 + (store.skills.elemCrafting || 0) * 0.25;
|
||||
const output = Math.floor(craftBonus);
|
||||
|
||||
return (
|
||||
<div key={id} className="p-2 rounded border border-[var(--border-subtle)] bg-[var(--bg-sunken)] flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<ElementBadge elementId={id} size="md" />
|
||||
<span className="text-sm" style={{ color: `var(--mana-${id})` }}>{def.name}</span>
|
||||
<span className="text-xs text-[var(--text-muted)]">
|
||||
({recipe.map(r => {
|
||||
const rDef = ELEMENTS[r];
|
||||
return rDef?.sym || r;
|
||||
}).join(' + ')})
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={!canCraft}
|
||||
onClick={() => store.craftComposite(id)}
|
||||
className={!canCraft ? 'opacity-50 cursor-not-allowed' : ''}
|
||||
>
|
||||
Craft ({output})
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</GameCard>
|
||||
);
|
||||
};
|
||||
|
||||
// Check if there are any unlocked elements with current > 0
|
||||
const hasUnlockedElements = Object.values(store.elements || {}).some(e => e.unlocked && e.current > 0);
|
||||
|
||||
if (!hasUnlockedElements) {
|
||||
return (
|
||||
<GameCard>
|
||||
<div className="pt-6">
|
||||
<div className="text-center text-[var(--text-muted)]">
|
||||
No elemental mana available. Gather or convert mana to see elemental pools.
|
||||
</div>
|
||||
</div>
|
||||
</GameCard>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* Elemental Mana Display */}
|
||||
<GameCard className="lg:col-span-2">
|
||||
<div className="pb-2">
|
||||
<h3 className="text-sm font-semibold text-[var(--text-primary)]">Elemental Mana</h3>
|
||||
</div>
|
||||
<div>
|
||||
{renderElementsGrid()}
|
||||
</div>
|
||||
</GameCard>
|
||||
|
||||
{/* Composite Crafting */}
|
||||
{renderCompositeCrafting()}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
LabTab.displayName = "LabTab";
|
||||
export { LabTab } from '../LabTab';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
'use client';
|
||||
|
||||
import { useCombatStore, useManaStore, useSkillStore } from '@/lib/game/stores';
|
||||
import { useCombatStore, useCraftingStore, useManaStore, useSkillStore } from '@/lib/game/stores';
|
||||
import { GameCard, ElementBadge } from '@/components/ui';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { ELEMENTS, SPELLS_DEF } from '@/lib/game/constants';
|
||||
@@ -14,8 +14,8 @@ export function SpellsTab() {
|
||||
const activeSpell = useCombatStore((s) => s.activeSpell);
|
||||
const setSpell = useCombatStore((s) => s.setSpell);
|
||||
|
||||
const equippedInstances = useCombatStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCombatStore((s) => s.equipmentInstances);
|
||||
const equippedInstances = useCraftingStore((s) => s.equippedInstances);
|
||||
const equipmentInstances = useCraftingStore((s) => s.equipmentInstances);
|
||||
|
||||
const rawMana = useManaStore((s) => s.rawMana);
|
||||
const elements = useManaStore((s) => s.elements);
|
||||
|
||||
@@ -6,7 +6,7 @@ export { SpireTab } from './SpireTab';
|
||||
export { SpellsTab } from './SpellsTab';
|
||||
export { LabTab } from './LabTab';
|
||||
// SkillsTab is now exported from src/components/game/index.ts
|
||||
// export { SkillsTab } from './SkillsTab';
|
||||
export { SkillsTab } from '../SkillsTab';
|
||||
export { StatsTab } from './StatsTab';
|
||||
export { EquipmentTab } from './EquipmentTab';
|
||||
export { AttunementsTab } from './AttunementsTab';
|
||||
|
||||
Reference in New Issue
Block a user