Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 408x 408x 2x 2x 2x 2x 2x 10x 10x 60x 340x 340x 340x 340x 340x 60x 10x 10x 2x 2x 2x 2x 2x 2x 2x 2x 28x 28x 2x 2x 2x 2x 2x 2x 2x 2x 2x 2x 7x 7x 2x 2x 2x 2x 2x 2x | /**
* Skill categories and their human-readable names
*
* This is the single source of truth for skill groupings used in:
* - ManualSkillSelector (manage skills modal)
* - StudentSelector (student grouping by skill category)
* - Skill search and filtering
*
* Order determines priority for student grouping (first = highest level)
*/
export const SKILL_CATEGORIES = {
advanced: {
name: 'Advanced Multi-Column Operations',
/** Whether skill display names are concise math rules suitable for the practice banner */
hasMathSentence: false,
skills: {
cascadingCarry: 'Cascading Carry (e.g., 999 + 1 = 1000)',
cascadingBorrow: 'Cascading Borrow (e.g., 1000 - 1 = 999)',
},
},
tenComplementsSub: {
name: 'Ten Complements (Subtraction)',
hasMathSentence: true,
skills: {
'-9=+1-10': '-9 = +1 - 10',
'-8=+2-10': '-8 = +2 - 10',
'-7=+3-10': '-7 = +3 - 10',
'-6=+4-10': '-6 = +4 - 10',
'-5=+5-10': '-5 = +5 - 10',
'-4=+6-10': '-4 = +6 - 10',
'-3=+7-10': '-3 = +7 - 10',
'-2=+8-10': '-2 = +8 - 10',
'-1=+9-10': '-1 = +9 - 10',
},
},
tenComplements: {
name: 'Ten Complements (Addition)',
hasMathSentence: true,
skills: {
'9=10-1': '+9 = +10 - 1',
'8=10-2': '+8 = +10 - 2',
'7=10-3': '+7 = +10 - 3',
'6=10-4': '+6 = +10 - 4',
'5=10-5': '+5 = +10 - 5',
'4=10-6': '+4 = +10 - 6',
'3=10-7': '+3 = +10 - 7',
'2=10-8': '+2 = +10 - 8',
'1=10-9': '+1 = +10 - 9',
},
},
fiveComplementsSub: {
name: 'Five Complements (Subtraction)',
hasMathSentence: true,
skills: {
'-4=-5+1': '-4 = -5 + 1',
'-3=-5+2': '-3 = -5 + 2',
'-2=-5+3': '-2 = -5 + 3',
'-1=-5+4': '-1 = -5 + 4',
},
},
fiveComplements: {
name: 'Five Complements (Addition)',
hasMathSentence: true,
skills: {
'4=5-1': '+4 = +5 - 1',
'3=5-2': '+3 = +5 - 2',
'2=5-3': '+2 = +5 - 3',
'1=5-4': '+1 = +5 - 4',
},
},
basic: {
name: 'Basic Skills',
hasMathSentence: false,
skills: {
directAddition: 'Direct Addition (1-4)',
heavenBead: 'Heaven Bead (5)',
simpleCombinations: 'Simple Combinations (6-9)',
directSubtraction: 'Direct Subtraction (1-4)',
heavenBeadSubtraction: 'Heaven Bead Subtraction (5)',
simpleCombinationsSub: 'Simple Combinations Subtraction (6-9)',
},
},
} as const
export type SkillCategoryKey = keyof typeof SKILL_CATEGORIES
/**
* Priority order for skill categories (highest level first)
* This determines which category a student is grouped into
*/
export const CATEGORY_PRIORITY: SkillCategoryKey[] = [
'advanced',
'tenComplementsSub',
'tenComplements',
'fiveComplementsSub',
'fiveComplements',
'basic',
]
/**
* Convert a short skill key (e.g., "4=5-1") to a full skill ID (e.g., "fiveComplements.4=5-1")
*/
export function getFullSkillId(category: SkillCategoryKey, shortKey: string): string {
return `${category}.${shortKey}`
}
/**
* Build a map of full skill IDs to their categories for fast lookup
*/
export function buildSkillToCategoryMap(): Map<string, SkillCategoryKey> {
const map = new Map<string, SkillCategoryKey>()
for (const [categoryKey, category] of Object.entries(SKILL_CATEGORIES)) {
for (const shortKey of Object.keys(category.skills)) {
map.set(
getFullSkillId(categoryKey as SkillCategoryKey, shortKey),
categoryKey as SkillCategoryKey
)
}
}
return map
}
// Pre-built map for performance
const skillToCategoryMap = buildSkillToCategoryMap()
/**
* Get the category for a full skill ID
*/
export function getSkillCategory(skillId: string): SkillCategoryKey | null {
return skillToCategoryMap.get(skillId) ?? null
}
/**
* Get all skill IDs for a category
*/
export function getCategorySkillIds(category: SkillCategoryKey): string[] {
return Object.keys(SKILL_CATEGORIES[category].skills).map((shortKey) =>
getFullSkillId(category, shortKey)
)
}
/**
* Get the display name for a category
*/
export function getCategoryDisplayName(category: SkillCategoryKey): string {
return SKILL_CATEGORIES[category].name
}
/**
* Check if a skill's display name is a concise math sentence (e.g., "+4 = +5 - 1")
* suitable for showing in the practice focus banner.
*/
export function skillHasMathSentence(skillId: string): boolean {
const category = getSkillCategory(skillId)
if (!category) return false
return SKILL_CATEGORIES[category].hasMathSentence
}
|