Заметки
Переместить:
, bind here
var sBtn=document.getElementById('s-chat-send');
var sInp=document.getElementById('s-chat-input');
if(sBtn&&!sBtn._bound){
sBtn._bound=true;
sBtn.addEventListener('click',sndStudent);
if(sInp){
sInp.addEventListener('keydown',function(e){if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();sndStudent();}});
sInp.addEventListener('input',function(){
this.style.height='38px';
var h=Math.min(this.scrollHeight,120);
this.style.height=h+'px';
this.style.overflowY=this.scrollHeight>118?'auto':'hidden';
});
}
// Media attach
var mBtn=document.getElementById('s-media-btn');
var mInp=document.getElementById('s-media-inp');
if(mBtn&&mInp&&!mBtn._bound){
mBtn._bound=true;
mBtn.addEventListener('click',function(){mInp.click();});
mInp.addEventListener('change',function(){
var file=mInp.files[0];if(!file)return;
if(file.size>5*1024*1024){showToast('Файл > 5 МБ','err');return;}
var reader=new FileReader();
reader.onload=function(ev){
window._sChatPendingMedia={base64:ev.target.result,type:file.type};
var sBar=document.getElementById('s-media-bar');
if(!sBar)return;
sBar.innerHTML='';
if(file.type.startsWith('image')){var img=document.createElement('img');img.src=ev.target.result;img.style.cssText='width:48px;height:48px;object-fit:cover;border-radius:7px;';sBar.appendChild(img);}
else{var sp=document.createElement('span');sp.style.cssText='font-size:1.4rem;';sp.textContent='🎬';sBar.appendChild(sp);}
var inf=document.createElement('div');inf.className='tg-media-bar-info';inf.textContent=file.type.startsWith('image')?'Фото готово к отправке':'Видео готово';
var cx=document.createElement('button');cx.className='tg-mpcancel';cx.textContent='×';
cx.onclick=function(){window._sChatPendingMedia=null;sBar.innerHTML='';sBar.classList.remove('has-media');};
sBar.appendChild(inf);sBar.appendChild(cx);sBar.classList.add('has-media');
};
reader.readAsDataURL(file);mInp.value='';
});
}
}
}
bindStudentChat();
// Hook chat tab → init
document.querySelectorAll('.tab').forEach(function(t){
if(t.dataset.tab==='chat')t.addEventListener('click',function(){tgInit();});
});
// Load after unlock
var _origUnlock=unlock;
unlock=function(name,role){
_origUnlock(name,role);
if(role==='student'){setTimeout(function(){bindStudentChat();loadStudentChat();},500);}
if(role==='admin'){setTimeout(tgInit,1000);}
};
// ══════════════════════════════════════════════════════════
// DOCS TAB
// ══════════════════════════════════════════════════════════
function generateDocs(){
var el=document.getElementById('docs-body');
if(!el)return;
var h='';
// Sprints
h+='
🏃 Спринты'+_sprints.length+' всего
';
if(!_sprints.length){
h+='
Спринтов пока нет
';
} else {
_sprints.forEach(function(s){
var cnt=_cards.filter(function(c){return c.sprint_id===s.id;}).length;
h+='
'
+''+esc(s.name)+''
+''+(s.is_locked?'🔒 закрыт':'🟢 открыт')+''
+(s.description?''+esc(s.description.slice(0,65))+'':'')
+''+cnt+' карт.'
+'
';
});
}
h+='
';
// Cards
h+='
🗂 Карточки'+_cards.length+' всего
';
if(!_cards.length){
h+='
Карточек пока нет
';
} else {
_cards.forEach(function(c){
var links=[];try{links=JSON.parse(c.links||'[]');}catch(ex){}
h+='
'
+''+esc(c.title)+''
+''+esc(c.difficulty||'medium')+''
+''+esc(c.card_type||'practice')+''
+(c.sprint_name?''+esc(c.sprint_name)+'':'')
+(links.length?'📋 '+links.length+'':'')
+'
';
});
}
h+='
';
// Students
h+='
👥 Ученики'+_students.length+' всего
';
if(!_students.length){
h+='
Учеников пока нет
';
} else {
h+='
УченикВсегоВ работеDoneАпрув
';
_students.forEach(function(u){
h+='
'
+''+esc(u.display_name)+''
+''+(u.total_cards||0)+''
+''+(u.inprogress_count||0)+''
+''+(u.done_count||0)+''
+''+(u.approved_count||0)+''
+'
';
});
}
h+='
';
el.innerHTML=h;
var ts=document.getElementById('docs-ts');
if(ts)ts.textContent='Обновлено: '+new Date().toLocaleTimeString('ru',{hour:'2-digit',minute:'2-digit',second:'2-digit'});
}
function docsDownloadMd(){
var md='# Учебная лаборатория — Документация\n\n> Сгенерировано: '+new Date().toLocaleString('ru')+'\n\n';
md+='## Спринты\n\n';
_sprints.forEach(function(s){
var cnt=_cards.filter(function(c){return c.sprint_id===s.id;}).length;
md+='### '+s.name+' '+(s.is_locked?'🔒':'🟢')+'\n\n';
if(s.description)md+=s.description+'\n\n';
md+='**Карточек:** '+cnt+(s.start_date?' | **Период:** '+s.start_date+' — '+(s.end_date||'?'):'')+'\n\n';
var spCards=_cards.filter(function(c){return c.sprint_id===s.id;});
if(spCards.length){
md+='| Карточка | Сложность | Тип |\n|---|---|---|\n';
spCards.forEach(function(c){md+='| '+c.title+' | '+(c.difficulty||'medium')+' | '+(c.card_type||'practice')+' |\n';});
md+='\n';
}
});
md+='## Все карточки\n\n';
_cards.forEach(function(c){
md+='### '+c.title+'\n- **Сложность:** '+(c.difficulty||'medium')+'\n- **Тип:** '+(c.card_type||'practice')+'\n';
if(c.sprint_name)md+='- **Спринт:** '+c.sprint_name+'\n';
if(c.description)md+='\n'+c.description+'\n';
var links=[];try{links=JSON.parse(c.links||'[]');}catch(ex){}
if(links.length){md+='\n**Чеклист:**\n';links.forEach(function(l){md+='- '+(l.text||l.url||'')+(l.url&&l.url!==l.text?' ('+l.url+')':'')+'\n';});}
md+='\n';
});
md+='## Статистика учеников\n\n';
if(_students.length){
md+='| Ученик | Всего | В работе | Done | Апрув |\n|---|---|---|---|---|\n';
_students.forEach(function(u){md+='| '+u.display_name+' | '+(u.total_cards||0)+' | '+(u.inprogress_count||0)+' | '+(u.done_count||0)+' | '+(u.approved_count||0)+' |\n';});
}
var blob=new Blob([md],{type:'text/markdown;charset=utf-8'});
var url=URL.createObjectURL(blob);
var a=document.createElement('a');a.href=url;a.download='qa-lab-docs-'+new Date().toISOString().slice(0,10)+'.md';
document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);
}
function docsDownloadJson(){
var snapshot={generated_at:new Date().toISOString(),sprints:_sprints,cards:_cards,students:_students};
var blob=new Blob([JSON.stringify(snapshot,null,2)],{type:'application/json'});
var url=URL.createObjectURL(blob);
var a=document.createElement('a');a.href=url;a.download='qa-lab-snapshot-'+new Date().toISOString().slice(0,10)+'.json';
document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url);
}
document.addEventListener('keydown', function(e){
if(e.key==='Escape'){
document.getElementById('mat-modal').classList.remove('open');
document.getElementById('modal').classList.remove('open');
var openKcard=document.querySelector('.kcard.kcd-open');
if(openKcard){
openKcard.classList.remove('kcd-open');
setTimeout(function(){var d=openKcard.querySelector('.kcard-detail');if(d)d.remove();},230);
}
_activeCard=null;
}
});
}());
Пока вопросов нет. Напиши первый! 👇