!['frame','ts'].includes(k))
.reduce((a,[,v])=>a+(v||0),0);
objDat.push(cnt);
}
window.fch.data.labels = lbl;
window.fch.data.datasets[0].data = objDat;
// FPS dataset : valeur constante (fps de la scène)
window.fch.data.datasets[1].data = lbl.map(()=>lastScene.fps||0);
window.fch.update('none');
}
// ── Chart classification (vraies classes) ──
if(window.tch && Object.keys(uc).length){
const COLORS=['#3b82f6','#10b981','#f59e0b','#ef4444','#8b5cf6','#ec4899','#06b6d4','#f97316'];
window.tch.data.labels = Object.keys(uc);
window.tch.data.datasets[0].data = Object.values(uc);
window.tch.data.datasets[0].backgroundColor = Object.keys(uc).map((_,i)=>COLORS[i%COLORS.length]);
window.tch.update('none');
}
// ── Heatmap depuis vraies positions (déjà gérée par loadHeatmap) ──
}catch(err){ console.warn('[TrackIQ] Dashboard fetch:',err); }
}
}
/* ══ LOGS ══ */
if(PAGE==='logs'){
let _rows=[];
window.addEventListener('load',async()=>{
try{
const res=await fetch('/api/logs');
const scenes=await res.json();
if(!scenes.length)return;
_rows=[];
scenes.forEach(s=>{
Object.entries(s.unique_counts||{}).forEach(([cls,cnt])=>{
_rows.push({date:s.generated_at?new Date(s.generated_at).toLocaleString():'—',
scene:s.scene_id,video:s.video_name||'—',cls,cnt,total:s.total_unique||0,sid:s.scene_id});
});
});
const scvs=document.querySelectorAll('.sc-v');
if(scvs[0]) scvs[0].textContent=scenes.reduce((a,s)=>a+(s.total_unique||0),0);
if(scvs[1]) scvs[1].textContent=scenes.length;
renderTable(_rows);
hookFilters(scenes);
const expBtn=document.querySelector('.btn-exp');
if(expBtn) expBtn.onclick=()=>scenes.forEach((s,i)=>setTimeout(()=>window.open(`/api/logs/${s.scene_id}/csv`),i*300));
}catch(err){console.warn('[TrackIQ] Logs:',err);}
});
function renderTable(rows){
const tbody=document.getElementById('tbody');if(!tbody)return;
if(!rows.length){tbody.innerHTML=`| No data yet — run an analysis first |
`;return;}
const DOT={'Vehicle':'#3b82f6','Motorcycle':'#10b981','Truck':'#f59e0b','Bus':'#8b5cf6','Human':'#ec4899','Person':'#ec4899','Bicycle':'#06b6d4'};
const EMO={'Vehicle':'🚗','Motorcycle':'🏍','Truck':'🚚','Bus':'🚌','Human':'🚶','Person':'🚶','Bicycle':'🚲'};
tbody.innerHTML='';
rows.forEach(r=>{
const tr=document.createElement('tr');
tr.innerHTML=`${EMO[r.cls]||'📦'} |
${r.date} |
${r.scene} |
${r.video.length>18?r.video.slice(0,18)+'…':r.video} |
${r.cls} |
${r.cnt} |
CSV | `;
tbody.appendChild(tr);
});
}
function hookFilters(scenes){
const sIn=document.getElementById('sIn');
const camF=document.getElementById('camF');
const typeF=document.getElementById('typeF');
if(typeF){
const cls=[...new Set(_rows.map(r=>r.cls))];
typeF.innerHTML=''+cls.map(c=>``).join('');
}
if(camF) camF.innerHTML=''+
scenes.map(s=>``).join('');
const apply=()=>{
const s=(sIn?.value||'').toLowerCase(),sc=camF?.value||'',tp=typeF?.value||'';
renderTable(_rows.filter(r=>(!s||r.video.toLowerCase().includes(s)||r.scene.toLowerCase().includes(s))&&(!sc||r.scene===sc)&&(!tp||r.cls===tp)));
};
if(sIn) sIn.oninput=apply;
if(camF) camF.onchange=apply;
if(typeF) typeF.onchange=apply;
}
}
console.log(`[TrackIQ v5] page=${PAGE}`);