feat(ui): complete Task 4 UI redesign — all sub-tasks 1-10
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 8m47s
Build and Publish Mana Loop Docker Image / build-and-publish (push) Successful in 8m47s
- Implemented complete design system with 40+ CSS custom properties - Created 9 UI primitives (GameCard, SectionHeader, StatRow, ManaBar, ElementBadge, ValueDisplay, ActionButton, SkillRow, TooltipInfo) - Redesigned all tabs: Spire, Skills, Stats, Equipment, Crafting, Attunements, Golemancy, Spells, Loot, Achievements, Lab, Debug - Added toast notification system (GameToast) with success/warning/error/info types - Added confirmation dialogs for destructive actions - Removed all dev artifacts and component name labels - Added empty states to all tabs - Replaced emoji icons with Lucide React icons - Added enchantPower placeholder to StatsTab and EquipmentTab - Mobile audit passed at 375px viewport - Build passes with 0 errors, lint passes with 0 errors Sub-tasks completed: - ST1: Design System Implementation - ST2: Global Layout & Header - ST3: Left Panel (Mana Display & Action Area) - ST4: Skills Tab - ST5: Spire Tab & Spire Mode UI - ST6: Stats Tab - ST7: Equipment & Crafting Tabs - ST8: Attunements Tab - ST9: Remaining Tabs - ST10: Toast System & Confirmation Dialogs Documentation: 15+ files in docs/task4/
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
'use client';
|
||||
|
||||
import { GameCard, ElementBadge, ActionButton } from '@/components/ui';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { ELEMENTS } from '@/lib/game/constants';
|
||||
|
||||
interface LabTabProps {
|
||||
@@ -24,11 +24,13 @@ export function LabTab({ store }: LabTabProps) {
|
||||
return (
|
||||
<div
|
||||
key={id}
|
||||
className="p-2 rounded border border-gray-700 bg-gray-800/50"
|
||||
className="p-2 rounded border border-[var(--border-subtle)] bg-[var(--bg-sunken)]"
|
||||
>
|
||||
<div className="text-lg text-center">{def?.sym}</div>
|
||||
<div className="text-xs font-semibold text-center" style={{ color: def?.color }}>{def?.name}</div>
|
||||
<div className="text-xs text-gray-400 game-mono text-center">{state.current}/{state.max}</div>
|
||||
<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>
|
||||
);
|
||||
})}
|
||||
@@ -44,41 +46,43 @@ export function LabTab({ store }: LabTabProps) {
|
||||
if (compositeElements.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Card className="bg-gray-900/80 border-gray-700">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-amber-400 text-sm">Composite Crafting</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<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-gray-700 bg-gray-800/50 flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-lg">{def.sym}</span>
|
||||
<span className="text-sm" style={{ color: def.color }}>{def.name}</span>
|
||||
<span className="text-xs text-gray-500">
|
||||
({recipe.map(r => ELEMENTS[r]?.sym).join(' + ')})
|
||||
</span>
|
||||
</div>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={!canCraft}
|
||||
onClick={() => store.craftComposite(id)}
|
||||
>
|
||||
Craft ({output})
|
||||
</Button>
|
||||
<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>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
disabled={!canCraft}
|
||||
onClick={() => store.craftComposite(id)}
|
||||
className={!canCraft ? 'opacity-50 cursor-not-allowed' : ''}
|
||||
>
|
||||
Craft ({output})
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</GameCard>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -87,27 +91,27 @@ export function LabTab({ store }: LabTabProps) {
|
||||
|
||||
if (!hasUnlockedElements) {
|
||||
return (
|
||||
<Card className="bg-gray-900/80 border-gray-700">
|
||||
<CardContent className="pt-6">
|
||||
<div className="text-center text-gray-500">
|
||||
<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>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</GameCard>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
{/* Elemental Mana Display */}
|
||||
<Card className="bg-gray-900/80 border-gray-700 lg:col-span-2">
|
||||
<CardHeader className="pb-2">
|
||||
<CardTitle className="text-amber-400 text-sm">Elemental Mana</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<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()}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</GameCard>
|
||||
|
||||
{/* Composite Crafting */}
|
||||
{renderCompositeCrafting()}
|
||||
|
||||
Reference in New Issue
Block a user