function TrainingSignal() { const [missing,setMissing]=React.useState(false); const src=window.TRAINING_STUDIO_CONFIG?.backgroundGif; return ; } function TrainingStudio({user, available, onStart, running, summary, onBack}) { const [activity,setActivity]=React.useState('zeroError'); const [path,setPath]=React.useState([]); const [basket,setBasket]=React.useState([]); const [notice,setNotice]=React.useState(''); const [fusion,setFusion]=React.useState(null); const [count,setCount]=React.useState(1); const [catalog,setCatalog]=React.useState({f:[],m:[],d:[]}); const [loading,setLoading]=React.useState(false); const [error,setError]=React.useState(''); const [refresh,setRefresh]=React.useState(0); React.useEffect(()=>{ let masters=JSON.stringify(window.MasterSpecialties?.selected(user?.id)||[]); const update=event=>{if(String(event.detail?.userId)===String(user?.id)){ const next=JSON.stringify(window.MasterSpecialties?.selected(user?.id)||[]); if(next!==masters){masters=next;setRefresh(value=>value+1);setBasket([]);} }}; window.addEventListener('tb:student-personalization-updated',update); return ()=>window.removeEventListener('tb:student-personalization-updated',update); },[user?.id]); const availableRef=React.useRef(available); availableRef.current=available; const vm=window.MyProgressEngine.getMyProgressViewModel(user?.id || ''); const groups=vm.structureGroups; const roots=[ {...groups.completeIdea,id:'completeIdea',label:'Estructuras',items:[groups.important,...groups.completeIdea.items]}, {...groups.extraInformation,id:'extraInformation',label:'Extra information'}, {id:'fusion',label:'Fusion',items:window.SpecialTechniques.list().map(item=>({...item,topicIds:item.practiceTopicIds,percentLabel:window.MyProgressEngine.formatPercent(item.practiceTopicIds.reduce((sum,id)=>sum+window.MyProgressEngine.getTopicProgress(user?.id||'',id).percent,0)/item.practiceTopicIds.length)}))} ]; const current=path.reduce((parent,node)=>(parent?.items || roots).find(item=>item.id===node.id),null); const leaf=current && !current.items?.length ? current : null; const base=fusion?.base || leaf; const primary=base?.topicIds || []; const extra=fusion?.selected || []; const activities=[{id:'zeroError',label:'ZERO ERROR',note:'3 preguntas · cero errores'}, {id:'reaction',label:'REACTION',note:'Reacción y precisión'}, {id:'mirror',label:'Mirror Reflection',note:'Respuestas completas'}, {id:'alien',label:'Alien Contact',note:'Construye con figuras'}]; const labels={f:'Fácil',m:'Medio',d:'Difícil'}; React.useEffect(()=>{ let live=true; setError('');setCatalog({f:[],m:[],d:[]}); if(!base){setLoading(false);return;} setLoading(true); Promise.all(['f','m','d'].map(diff=>window.TrainingStudioCatalog.groups({activity,primary,extra,diff,userId:user?.id,available:(...args)=>availableRef.current(...args)}))) .then(results=>{if(!live)return;setCatalog({f:results[0].rows,m:results[1].rows,d:results[2].rows});if(results.some(r=>r.errors.length))setError('No se pudo cargar parte del material. Puedes reintentar.');}) .catch(()=>{if(live)setError('No se pudo cargar el material. Reintenta.');}) .finally(()=>{if(live)setLoading(false);}); return ()=>{live=false;}; },[activity,base?.id,extra.join('|'),user?.id,refresh]); const rows=Object.values(catalog).flat(); const related=window.TrainingStudioCatalog.related(rows,[...primary,...extra]); const choosingFusion=Boolean(fusion?.target && extra.length{ if(node.items?.length)return node.items.some(intersects); return (node.topicIds||[]).some(id=>related.includes(id)); }; const showNodes=(window.MasterSpecialties?.prioritize(current?.items || roots,user?.id)||current?.items||roots).filter(node=>!choosingFusion || intersects(node)); const reset=()=>{setPath([]);setFusion(null);setError('');}; const goBack=()=>{ if(path.length){setPath(previous=>previous.slice(0,-1));return;} if(fusion?.selected.length){setFusion(previous=>({...previous,selected:previous.selected.slice(0,-1),names:previous.names.slice(0,-1)}));return;} if(fusion){setPath([fusion.base]);setFusion(null);} }; const choose=node=>{ if(node.items?.length){setPath(prev=>[...prev,node]);return;} if(choosingFusion){ const id=(node.topicIds||[]).find(id=>related.includes(id)); if(!id)return; setFusion(prev=>({...prev,selected:[...prev.selected,id],names:[...prev.names,node.label]})); setPath([]); } else setPath(prev=>[...prev,node]); }; const add=diff=>{ if(loading || !catalog[diff].length)return; const label=[base.label,...(fusion?.names||[])].join(' + '); const step=window.TrainingStudioCatalog.makeStep(catalog[diff],label,extra); setBasket(prev=>[...prev,step]); setNotice(`Se agregó un set de ${label} en ${labels[diff]}.`); }; const begin=()=>{if(!basket.length || running)return;onStart(basket.map(step=>({...step,id:`${step.id}-${Date.now()}`})));}; const status=node=>{const n=Number.parseFloat(node.percentLabel)||0;return n<=54?'low':n<=85?'mid':'high';}; const optionsVisible=base && (!fusion || (fusion.target && !choosingFusion)); return
Freestyle
ENGLISH LEVEL TRAINING

Freestyle

Todos Bilingües. Así de fácil.
{activities.map(item=>)}
a.id===activity).label}>
{path.map((node,i)=>/)}
{fusion &&
Fusión · {fusion.base.label}{fusion.names.join(' + ') || 'Combina temas dentro del mismo ejercicio.'}
} {fusion && !fusion.target ?

¿Cuántos temas adicionales?

{[1,2,3,4,5,6,7,8,9].map(n=>)}
: optionsVisible ?
{fusion?'FUSIÓN': 'ELIGE UN SET'}

{[base.label,...(fusion?.names||[])].join(' + ')}

{['f','m','d'].map(diff=>)}{!fusion && }
{!loading&&!rows.length&&

No hay sets disponibles. El tema principal debe estar desbloqueado en Paso a paso.

}
: <>

{choosingFusion?`Elige el tema ${extra.length+1} de ${fusion.target}`:current?.label||'¿Qué quieres practicar?'}

ReforzarEntendiéndoloDominado
{showNodes.map(node=>)}
{choosingFusion&&!loading&&!showNodes.length&&

No hay ejercicios que compartan esta combinación de temas.

}} {loading &&

Buscando ejercicios…

}{error&&
{error}
}
{summary}
; }