diff --git a/apps/web/src/app/tutorial-editor/page.tsx b/apps/web/src/app/tutorial-editor/page.tsx
index 2801aea3..ae382dc9 100644
--- a/apps/web/src/app/tutorial-editor/page.tsx
+++ b/apps/web/src/app/tutorial-editor/page.tsx
@@ -26,7 +26,19 @@ export default function TutorialEditorPage() {
editingTitle: false
})
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle')
- const [validationResult, setValidationResult] = useState(() => validateTutorialConversion())
+ const [validationResult, setValidationResult] = useState(() => {
+ const result = validateTutorialConversion()
+ return {
+ isValid: result.isValid,
+ errors: result.errors.map(error => ({
+ stepId: '',
+ field: 'general',
+ message: error,
+ severity: 'error' as const
+ })),
+ warnings: []
+ }
+ })
const [debugEvents, setDebugEvents] = useState([])
// Save tutorial (placeholder - would connect to actual backend)
@@ -179,15 +191,8 @@ export default function TutorialEditorPage() {
})
}
- // Error messages validation
- if (!step.errorMessages.wrongBead.trim() || !step.errorMessages.wrongAction.trim() || !step.errorMessages.hint.trim()) {
- warnings.push({
- stepId: step.id,
- field: 'errorMessages',
- message: `Step ${index + 1}: All error messages should be provided`,
- severity: 'warning'
- })
- }
+ // Error messages validation removed - errorMessages property no longer exists
+ // Bead diff tooltip provides better guidance instead
})
const validation: TutorialValidation = {
diff --git a/apps/web/src/components/EnhancedChampionArena.tsx b/apps/web/src/components/EnhancedChampionArena.tsx
index d81035fa..8f785f69 100644
--- a/apps/web/src/components/EnhancedChampionArena.tsx
+++ b/apps/web/src/components/EnhancedChampionArena.tsx
@@ -101,12 +101,11 @@ function ChampionCard({
})
return (
- {
+ onClick={(e: React.MouseEvent) => {
// Only handle click if not dragging and we have the toggle handler
if (!isDragging && onToggleArena) {
e.stopPropagation()
@@ -135,8 +134,9 @@ function ChampionCard({
boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)'
}
})}
+ style={cardStyle}
>
-
)}
-
{player.emoji}
-
+
{zone === 'arena' ? 'READY! 🔥' : `Level ${player.level}`}
-
+
)
}
@@ -294,7 +294,7 @@ function DroppableZone({
{title}
-
{isEmpty && (
-
{isOver ? `Drop to ${id === 'arena' ? 'enter the arena' : 'return to roster'}!` : subtitle}
-
+
)}
{children}
-
+
)
}
@@ -569,8 +569,8 @@ export function EnhancedChampionArena({ onGameModeChange, onConfigurePlayer, cla
Drag champions to experience the most tactile arena ever built!
- {/* Mode Indicator with Spring Animation */}
-
{arenaPlayers.length === 0 ? 'Select Champions' : gameMode === 'single' ? 'Solo Mode' : gameMode === 'battle' ? 'Battle Mode' : 'Tournament Mode'}
-
+
+ readonly?: boolean
+ showPlaceValues?: boolean
+ onBeadClick?: (placeValue: number, beadType: 'earth' | 'heaven', position: number) => void
+ }
+
+ export const Abacus: React.ComponentType
+
+ export interface BeadPosition {
+ placeValue: number
+ beadType: 'earth' | 'heaven'
+ position?: number
+ }
+
+ export interface AbacusState {
+ beads: BeadPosition[]
+ value: number
+ }
+
+ export function createAbacusState(value: number): AbacusState
+ export function calculateValue(state: AbacusState): number
+ export function addToAbacus(state: AbacusState, amount: number): AbacusState
+ export function resetAbacus(): AbacusState
+}
\ No newline at end of file
diff --git a/apps/web/src/types/vitest.d.ts b/apps/web/src/types/vitest.d.ts
new file mode 100644
index 00000000..6c57cb45
--- /dev/null
+++ b/apps/web/src/types/vitest.d.ts
@@ -0,0 +1,9 @@
+///
+
+// Extend vitest global types if needed
+declare global {
+ // Vitest globals are already included via the reference above
+ // This file ensures TypeScript recognizes describe, it, expect, etc.
+}
+
+export {}
\ No newline at end of file
diff --git a/apps/web/src/utils/skillConfiguration.ts b/apps/web/src/utils/skillConfiguration.ts
index 48357043..e38fc339 100644
--- a/apps/web/src/utils/skillConfiguration.ts
+++ b/apps/web/src/utils/skillConfiguration.ts
@@ -113,8 +113,8 @@ export function skillConfigurationToSkillSets(config: SkillConfiguration): {
}
}
- const target: Partial = { basic: {}, fiveComplements: {}, tenComplements: {} }
- const forbidden: Partial = { basic: {}, fiveComplements: {}, tenComplements: {} }
+ const target: Partial = {}
+ const forbidden: Partial = {}
// Basic skills
Object.entries(config.basic).forEach(([skill, mode]) => {
@@ -122,10 +122,12 @@ export function skillConfigurationToSkillSets(config: SkillConfiguration): {
required.basic[skill as keyof typeof required.basic] = true
}
if (mode === 'target') {
- target.basic![skill as keyof typeof target.basic] = true
+ if (!target.basic) target.basic = {} as any
+ target.basic[skill as keyof typeof required.basic] = true
}
if (mode === 'forbidden') {
- forbidden.basic![skill as keyof typeof forbidden.basic] = true
+ if (!forbidden.basic) forbidden.basic = {} as any
+ forbidden.basic[skill as keyof typeof required.basic] = true
}
})
@@ -135,10 +137,12 @@ export function skillConfigurationToSkillSets(config: SkillConfiguration): {
required.fiveComplements[skill as keyof typeof required.fiveComplements] = true
}
if (mode === 'target') {
- target.fiveComplements![skill as keyof typeof target.fiveComplements] = true
+ if (!target.fiveComplements) target.fiveComplements = {} as any
+ target.fiveComplements[skill as keyof typeof required.fiveComplements] = true
}
if (mode === 'forbidden') {
- forbidden.fiveComplements![skill as keyof typeof forbidden.fiveComplements] = true
+ if (!forbidden.fiveComplements) forbidden.fiveComplements = {} as any
+ forbidden.fiveComplements[skill as keyof typeof required.fiveComplements] = true
}
})
@@ -148,10 +152,12 @@ export function skillConfigurationToSkillSets(config: SkillConfiguration): {
required.tenComplements[skill as keyof typeof required.tenComplements] = true
}
if (mode === 'target') {
- target.tenComplements![skill as keyof typeof target.tenComplements] = true
+ if (!target.tenComplements) target.tenComplements = {} as any
+ target.tenComplements[skill as keyof typeof required.tenComplements] = true
}
if (mode === 'forbidden') {
- forbidden.tenComplements![skill as keyof typeof forbidden.tenComplements] = true
+ if (!forbidden.tenComplements) forbidden.tenComplements = {} as any
+ forbidden.tenComplements[skill as keyof typeof required.tenComplements] = true
}
})
diff --git a/apps/web/src/utils/test/instructionGenerator.test.ts b/apps/web/src/utils/test/instructionGenerator.test.ts
index 25bbd821..106c11c3 100644
--- a/apps/web/src/utils/test/instructionGenerator.test.ts
+++ b/apps/web/src/utils/test/instructionGenerator.test.ts
@@ -1024,7 +1024,7 @@ describe('Automatic Abacus Instruction Generator', () => {
expect(instruction.totalSteps).toBeGreaterThan(0)
// Verify pedagogical ordering if multi-step
- if (instruction.totalSteps > 1) {
+ if (instruction.totalSteps && instruction.totalSteps > 1) {
const stepBeads = instruction.stepBeadHighlights!
const placeValues = [...new Set(stepBeads.map(b => b.placeValue))].sort((a, b) => b - a)
diff --git a/apps/web/src/utils/tutorialConverter.ts b/apps/web/src/utils/tutorialConverter.ts
index ce18f44d..83fe681e 100644
--- a/apps/web/src/utils/tutorialConverter.ts
+++ b/apps/web/src/utils/tutorialConverter.ts
@@ -41,11 +41,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Adding earth beads',
explanation: 'Earth beads (bottom) are worth 1 each. Push them UP to activate them.'
- },
- errorMessages: {
- wrongBead: 'Click the highlighted earth bead at the bottom',
- wrongAction: 'Move the bead UP to add it',
- hint: 'Earth beads move up when adding numbers 1-4'
}
},
{
@@ -61,11 +56,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Building up earth beads',
explanation: 'Continue adding earth beads one by one for numbers 2, 3, and 4'
- },
- errorMessages: {
- wrongBead: 'Click the highlighted earth bead',
- wrongAction: 'Move the bead UP to add it',
- hint: 'You need 2 earth beads for the number 2'
}
},
{
@@ -81,11 +71,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Adding earth beads in sequence',
explanation: 'Continue adding earth beads one by one until you reach 4'
- },
- errorMessages: {
- wrongBead: 'Click the highlighted earth bead',
- wrongAction: 'Move the bead UP to add it',
- hint: 'You need 3 earth beads for the number 3'
}
},
{
@@ -101,11 +86,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Maximum earth beads',
explanation: 'Four earth beads is the maximum - next we need a different approach'
- },
- errorMessages: {
- wrongBead: 'Click the highlighted earth bead',
- wrongAction: 'Move the bead UP to add it',
- hint: 'Four earth beads represent the number 4'
}
},
@@ -123,11 +103,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Heaven bead = 5',
explanation: 'The single bead above the bar represents 5'
- },
- errorMessages: {
- wrongBead: 'Click the heaven bead at the top',
- wrongAction: 'Move the heaven bead DOWN to activate it',
- hint: 'The heaven bead is worth 5 points'
}
},
{
@@ -143,11 +118,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Heaven + Earth = 6',
explanation: 'When you have room in the earth section, simply add directly'
- },
- errorMessages: {
- wrongBead: 'Click the first earth bead',
- wrongAction: 'Move the earth bead UP to add it',
- hint: 'With the heaven bead active, add earth beads for 6, 7, 8, 9'
}
},
@@ -172,11 +142,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Five Complement: 4 = 5 - 1',
explanation: 'When you need to add 4 but only have 1 space, use: add 5, remove 1'
- },
- errorMessages: {
- wrongBead: 'Follow the two-step process: heaven bead first, then remove earth bead',
- wrongAction: 'Add heaven bead, then remove earth bead',
- hint: 'Complement thinking: 4 = 5 - 1, so add 5 and take away 1'
}
},
{
@@ -201,11 +166,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Five Complement: 3 = 5 - 2',
explanation: 'To add 3, think: 3 = 5 - 2, so add 5 and take away 2'
- },
- errorMessages: {
- wrongBead: 'Follow the multi-step process: add heaven, then remove earth beads',
- wrongAction: 'Add heaven bead first, then remove the necessary earth beads',
- hint: 'Complement: 3 = 5 - 2, so add heaven and remove 2 earth'
}
},
@@ -226,11 +186,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Direct addition when possible',
explanation: 'When you have space in the earth section, just add directly'
- },
- errorMessages: {
- wrongBead: 'Click the next earth beads in sequence',
- wrongAction: 'Move the earth beads UP to add them',
- hint: 'You have room for 2 more earth beads'
}
},
{
@@ -257,11 +212,6 @@ export const guidedAdditionSteps: ExistingTutorialStep[] = [
tooltip: {
content: 'Carrying to tens place',
explanation: '7 + 4 = 11, which needs the tens column heaven bead'
- },
- errorMessages: {
- wrongBead: 'First activate tens heaven, then clear all beads from ones place',
- wrongAction: 'Add tens heaven first, then remove heaven and earth beads from ones',
- hint: '7 + 4 = 11: add 10 (tens heaven), then subtract 6 by clearing ones place (7-1=6 remaining)'
}
}
]
diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json
index ddbb8237..d2809693 100644
--- a/apps/web/tsconfig.json
+++ b/apps/web/tsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"lib": ["dom", "dom.iterable", "es6"],
+ "types": ["vitest/globals", "@testing-library/jest-dom"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,