pablogrois Claude Opus 4.8 commited on
Commit
ebbb798
·
1 Parent(s): 04168cd

Ejes: arreglar display de los selects; sección 6: pool de ligas

Browse files

- Los <select> de componentes de eje mostraban siempre la primera opción (bug de
timing de x-model/:value con opciones dinámicas anidadas): ahora usan :selected por
opción, así reflejan el eje real (X=Elaboración, no "Verticalidad").
- Sección 6 (Variables): agregado "Pool: sumar liga" (además de sumar equipo), como en
Recursos; vsDraw maneja pool de ligas + equipos.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

src/racing_reports/web/templates/home.html CHANGED
@@ -592,8 +592,8 @@
592
  <label class="block text-xs text-gray-400 mb-1">Eje X (ejes × peso)</label>
593
  <template x-for="(c,i) in ejXc" :key="'xc'+i">
594
  <div class="flex gap-1 mb-1">
595
- <select x-model="c.eje" @change="ejDraw()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm flex-1">
596
- <template x-for="(lbl,k) in ejLabels" :key="'x'+i+k"><option :value="k" x-text="lbl"></option></template>
597
  </select>
598
  <input type="number" min="0" max="9" step="1" x-model.number="c.w" @change="ejDraw()" title="peso" class="w-12 bg-ink-800 border border-ink-600 rounded-lg px-1.5 py-1.5 text-sm text-center">
599
  <button @click="ejXc.splice(i,1); ejDraw()" x-show="ejXc.length>1" class="text-red-300 px-1.5">×</button>
@@ -605,8 +605,8 @@
605
  <label class="block text-xs text-gray-400 mb-1">Eje Y (ejes × peso)</label>
606
  <template x-for="(c,i) in ejYc" :key="'yc'+i">
607
  <div class="flex gap-1 mb-1">
608
- <select x-model="c.eje" @change="ejDraw()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm flex-1">
609
- <template x-for="(lbl,k) in ejLabels" :key="'y'+i+k"><option :value="k" x-text="lbl"></option></template>
610
  </select>
611
  <input type="number" min="0" max="9" step="1" x-model.number="c.w" @change="ejDraw()" title="peso" class="w-12 bg-ink-800 border border-ink-600 rounded-lg px-1.5 py-1.5 text-sm text-center">
612
  <button @click="ejYc.splice(i,1); ejDraw()" x-show="ejYc.length>1" class="text-red-300 px-1.5">×</button>
@@ -867,6 +867,12 @@
867
  </select>
868
  </div>
869
  <button @click="vsPinned={}; vsDraw()" class="text-xs text-gray-300 border border-ink-600 rounded-lg px-3 py-2 hover:bg-ink-600">Limpiar marcas</button>
 
 
 
 
 
 
870
  <div>
871
  <label class="block text-xs text-gray-400 mb-1">Sumar y marcar equipo</label>
872
  <input list="vsteamall" x-model="vsPoolTeamIn" @change="vsAddPoolTeam()" placeholder="Equipo (de cualquier liga)…"
@@ -895,16 +901,16 @@
895
  <template x-if="ax==='B'"><label class="cursor-pointer inline-flex items-center gap-1.5"><input type="checkbox" x-model="vsB.on" @change="vsReload()"> Burbuja (tamaño)</label></template>
896
  </div>
897
  <div class="flex flex-col gap-2" :class="ax==='B'&&!vsB.on ? 'pointer-events-none':''">
898
- <select :value="vsAxis(ax).var" @change="vsAxis(ax).var=$event.target.value; vsReload()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm">
899
  <template x-for="tp in vsTypeList" :key="'vsg'+ax+tp">
900
  <optgroup :label="tp">
901
- <template x-for="c in (vsTypeCols[tp]||[])" :key="'vso'+ax+c"><option :value="c" x-text="vsColLabel[c]||c"></option></template>
902
  </optgroup>
903
  </template>
904
  </select>
905
- <select :value="vsAxis(ax).dir" @change="vsAxis(ax).dir=+$event.target.value; vsDraw()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm">
906
- <option :value="1">Más es mejor (↑ arriba/derecha)</option>
907
- <option :value="-1">Menos es mejor (↑ arriba/derecha)</option>
908
  </select>
909
  </div>
910
  </div>
@@ -1181,7 +1187,7 @@
1181
  vsLoaded:false, vsLeagueList:[], vsSeasons:{}, vsLeague:'__all__', vsSeason:'__all__',
1182
  vsColLabel:{}, vsTypeCols:{}, vsTypeList:[], vsNeg:{},
1183
  vsX:{var:'', dir:1}, vsY:{var:'', dir:1}, vsB:{on:false, var:'', dir:1},
1184
- vsEscala:'liga', vsRows:[], vsError:'', vsPinned:{}, vsPool:[], vsPoolTeamIn:'', vsAllKeys:[],
1185
  // Recursos
1186
  lbLeagues: [], lbRecursos: {}, lbMetricas: {}, lbLeague: '__all__', lbRows: [], lbError: '', lbLoaded: false,
1187
  lbBloques: {}, lbBloqSel: ['bajo','medio','alto'], lbSeason: '__all__',
@@ -1974,10 +1980,16 @@
1974
  await this.$nextTick(); this.vsDraw();
1975
  } catch(e){ this.vsError='Error: '+e; }
1976
  },
 
 
 
 
 
 
1977
  vsAddPoolTeam(){
1978
  const k=(this.vsPoolTeamIn||'').trim(); const m=k.match(/^(.*) \((.*)\)$/);
1979
- if (m && this.vsAllKeys.includes(k) && !this.vsPool.some(p=>p.equipo===m[1]&&p.liga===m[2]))
1980
- this.vsPool.push({equipo:m[1], liga:m[2], label:k});
1981
  this.vsPoolTeamIn=''; this.vsDraw();
1982
  },
1983
  vsDraw(){
@@ -1985,12 +1997,15 @@
1985
  if (!svg || !this.vsRows.length) return;
1986
  svg.innerHTML='';
1987
  const esc=this.vsEscala, xa=this.vsX, ya=this.vsY, ba=this.vsB;
1988
- const poolKeys=new Set(this.vsPool.map(p=>p.liga+'|'+p.equipo));
 
 
1989
  let rows;
1990
- if (this.vsLeague!=='__all__') rows=this.vsRows.filter(r=>r.liga===this.vsLeague || poolKeys.has(r.liga+'|'+r.equipo));
 
1991
  else rows=this.vsRows;
1992
  if (this.vsLeague!=='__all__' && this.vsSeason!=='__all__')
1993
- rows=rows.filter(r=>String(r.temporada)===String(this.vsSeason) || poolKeys.has(r.liga+'|'+r.equipo));
1994
  const std=(vals)=>{const v=vals.filter(x=>x!=null&&!isNaN(x)); if(v.length<2) return {m:0,s:1};
1995
  const m=v.reduce((a,b)=>a+b,0)/v.length; const s=Math.sqrt(v.reduce((a,b)=>a+(b-m)*(b-m),0)/v.length)||1; return {m,s};};
1996
  const ligaStats=(v)=>{const map={}; for(const r of this.vsRows){const x=r[v]; if(x==null||isNaN(x))continue;(map[r.liga]=map[r.liga]||[]).push(+x);} const o={}; for(const lg in map)o[lg]=std(map[lg]); return o;};
 
592
  <label class="block text-xs text-gray-400 mb-1">Eje X (ejes × peso)</label>
593
  <template x-for="(c,i) in ejXc" :key="'xc'+i">
594
  <div class="flex gap-1 mb-1">
595
+ <select @change="c.eje=$event.target.value; ejDraw()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm flex-1">
596
+ <template x-for="(lbl,k) in ejLabels" :key="'x'+i+k"><option :value="k" :selected="k===c.eje" x-text="lbl"></option></template>
597
  </select>
598
  <input type="number" min="0" max="9" step="1" x-model.number="c.w" @change="ejDraw()" title="peso" class="w-12 bg-ink-800 border border-ink-600 rounded-lg px-1.5 py-1.5 text-sm text-center">
599
  <button @click="ejXc.splice(i,1); ejDraw()" x-show="ejXc.length>1" class="text-red-300 px-1.5">×</button>
 
605
  <label class="block text-xs text-gray-400 mb-1">Eje Y (ejes × peso)</label>
606
  <template x-for="(c,i) in ejYc" :key="'yc'+i">
607
  <div class="flex gap-1 mb-1">
608
+ <select @change="c.eje=$event.target.value; ejDraw()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm flex-1">
609
+ <template x-for="(lbl,k) in ejLabels" :key="'y'+i+k"><option :value="k" :selected="k===c.eje" x-text="lbl"></option></template>
610
  </select>
611
  <input type="number" min="0" max="9" step="1" x-model.number="c.w" @change="ejDraw()" title="peso" class="w-12 bg-ink-800 border border-ink-600 rounded-lg px-1.5 py-1.5 text-sm text-center">
612
  <button @click="ejYc.splice(i,1); ejDraw()" x-show="ejYc.length>1" class="text-red-300 px-1.5">×</button>
 
867
  </select>
868
  </div>
869
  <button @click="vsPinned={}; vsDraw()" class="text-xs text-gray-300 border border-ink-600 rounded-lg px-3 py-2 hover:bg-ink-600">Limpiar marcas</button>
870
+ <div>
871
+ <label class="block text-xs text-gray-400 mb-1">Pool: sumar liga</label>
872
+ <input list="vsligalist" x-model="vsPoolLigaIn" @change="vsAddPoolLiga()" placeholder="Liga…"
873
+ class="bg-ink-800 border border-ink-600 rounded-lg px-3 py-2 text-sm w-48">
874
+ <datalist id="vsligalist"><template x-for="l in vsLeagueList" :key="'vspl'+l"><option :value="l"></option></template></datalist>
875
+ </div>
876
  <div>
877
  <label class="block text-xs text-gray-400 mb-1">Sumar y marcar equipo</label>
878
  <input list="vsteamall" x-model="vsPoolTeamIn" @change="vsAddPoolTeam()" placeholder="Equipo (de cualquier liga)…"
 
901
  <template x-if="ax==='B'"><label class="cursor-pointer inline-flex items-center gap-1.5"><input type="checkbox" x-model="vsB.on" @change="vsReload()"> Burbuja (tamaño)</label></template>
902
  </div>
903
  <div class="flex flex-col gap-2" :class="ax==='B'&&!vsB.on ? 'pointer-events-none':''">
904
+ <select @change="vsAxis(ax).var=$event.target.value; vsReload()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm">
905
  <template x-for="tp in vsTypeList" :key="'vsg'+ax+tp">
906
  <optgroup :label="tp">
907
+ <template x-for="c in (vsTypeCols[tp]||[])" :key="'vso'+ax+c"><option :value="c" :selected="c===vsAxis(ax).var" x-text="vsColLabel[c]||c"></option></template>
908
  </optgroup>
909
  </template>
910
  </select>
911
+ <select @change="vsAxis(ax).dir=+$event.target.value; vsDraw()" class="bg-ink-800 border border-ink-600 rounded-lg px-2 py-1.5 text-sm">
912
+ <option :value="1" :selected="vsAxis(ax).dir===1">Más es mejor (↑ arriba/derecha)</option>
913
+ <option :value="-1" :selected="vsAxis(ax).dir===-1">Menos es mejor (↑ arriba/derecha)</option>
914
  </select>
915
  </div>
916
  </div>
 
1187
  vsLoaded:false, vsLeagueList:[], vsSeasons:{}, vsLeague:'__all__', vsSeason:'__all__',
1188
  vsColLabel:{}, vsTypeCols:{}, vsTypeList:[], vsNeg:{},
1189
  vsX:{var:'', dir:1}, vsY:{var:'', dir:1}, vsB:{on:false, var:'', dir:1},
1190
+ vsEscala:'liga', vsRows:[], vsError:'', vsPinned:{}, vsPool:[], vsPoolTeamIn:'', vsPoolLigaIn:'', vsAllKeys:[],
1191
  // Recursos
1192
  lbLeagues: [], lbRecursos: {}, lbMetricas: {}, lbLeague: '__all__', lbRows: [], lbError: '', lbLoaded: false,
1193
  lbBloques: {}, lbBloqSel: ['bajo','medio','alto'], lbSeason: '__all__',
 
1980
  await this.$nextTick(); this.vsDraw();
1981
  } catch(e){ this.vsError='Error: '+e; }
1982
  },
1983
+ vsAddPoolLiga(){
1984
+ const l=(this.vsPoolLigaIn||'').trim();
1985
+ if (l && this.vsLeagueList.includes(l) && !this.vsPool.some(p=>p.tipo==='liga'&&p.liga===l))
1986
+ this.vsPool.push({tipo:'liga', liga:l, label:'Liga: '+l});
1987
+ this.vsPoolLigaIn=''; this.vsDraw();
1988
+ },
1989
  vsAddPoolTeam(){
1990
  const k=(this.vsPoolTeamIn||'').trim(); const m=k.match(/^(.*) \((.*)\)$/);
1991
+ if (m && this.vsAllKeys.includes(k) && !this.vsPool.some(p=>p.tipo==='equipo'&&p.equipo===m[1]&&p.liga===m[2]))
1992
+ this.vsPool.push({tipo:'equipo', equipo:m[1], liga:m[2], label:k});
1993
  this.vsPoolTeamIn=''; this.vsDraw();
1994
  },
1995
  vsDraw(){
 
1997
  if (!svg || !this.vsRows.length) return;
1998
  svg.innerHTML='';
1999
  const esc=this.vsEscala, xa=this.vsX, ya=this.vsY, ba=this.vsB;
2000
+ const poolLigas=new Set(this.vsPool.filter(p=>p.tipo==='liga').map(p=>p.liga));
2001
+ const poolKeys=new Set(this.vsPool.filter(p=>p.tipo==='equipo').map(p=>p.liga+'|'+p.equipo));
2002
+ const inPool=r=>poolLigas.has(r.liga) || poolKeys.has(r.liga+'|'+r.equipo);
2003
  let rows;
2004
+ if (this.vsPool.length) rows=this.vsRows.filter(r=>inPool(r) || (this.vsLeague!=='__all__' && r.liga===this.vsLeague));
2005
+ else if (this.vsLeague!=='__all__') rows=this.vsRows.filter(r=>r.liga===this.vsLeague);
2006
  else rows=this.vsRows;
2007
  if (this.vsLeague!=='__all__' && this.vsSeason!=='__all__')
2008
+ rows=rows.filter(r=>String(r.temporada)===String(this.vsSeason) || inPool(r));
2009
  const std=(vals)=>{const v=vals.filter(x=>x!=null&&!isNaN(x)); if(v.length<2) return {m:0,s:1};
2010
  const m=v.reduce((a,b)=>a+b,0)/v.length; const s=Math.sqrt(v.reduce((a,b)=>a+(b-m)*(b-m),0)/v.length)||1; return {m,s};};
2011
  const ligaStats=(v)=>{const map={}; for(const r of this.vsRows){const x=r[v]; if(x==null||isNaN(x))continue;(map[r.liga]=map[r.liga]||[]).push(+x);} const o={}; for(const lg in map)o[lg]=std(map[lg]); return o;};