/* ===== HOTPATCH Sprint 9+10: apply resume/cover fields + universal AI intake ===== */
/* ===== Sprint 9: поля «Резюме» + «Сопроводительное» на отклике + связь с генератором письма ===== */
/* Варианты резюме под вакансию (совпадают с ролями генератора письма) */
var RESUME_OPTS=["","Head of Digital / Performance","Senior / Performance-маркетолог","Руководитель интернет-маркетинга","CMO / Директор по маркетингу"];
/* Инжект двух полей в карточку отклика. Всегда видимы, сохраняются на изменении. */
function applyExtraFields(id){
var dlg=document.getElementById("dialog"); if(!dlg)return;
var grid=dlg.querySelector("#editscope .grid")||dlg.querySelector(".grid"); if(!grid)return;
if(grid.querySelector('[data-xk="resume"]'))return;
var rec=(typeof recOf==="function")?recOf("applies",id):null; if(!rec)return;
function saveField(k,v){ rec[k]=v; try{ if(typeof save==="function")save(); }catch(e){} }
/* Резюме под вакансию */
var f1=document.createElement("div"); f1.className="f fw"; f1.setAttribute("data-xrow","1");
var lab1=document.createElement("div"); lab1.className="lab"; lab1.textContent="Резюме под вакансию"; f1.appendChild(lab1);
var sel=document.createElement("select"); sel.setAttribute("data-xk","resume");
RESUME_OPTS.forEach(function(o){ var op=document.createElement("option"); op.value=o; op.textContent=o||"— не выбрано —"; if((rec.resume||"")===o)op.selected=true; sel.appendChild(op); });
sel.addEventListener("change",function(){ saveField("resume",sel.value); });
f1.appendChild(sel); grid.appendChild(f1);
/* Сопроводительное письмо */
var f2=document.createElement("div"); f2.className="f fw"; f2.setAttribute("data-xrow","1");
var lab2=document.createElement("div"); lab2.className="lab"; lab2.textContent="Сопроводительное письмо (что отправил)"; f2.appendChild(lab2);
var ta=document.createElement("textarea"); ta.setAttribute("data-xk","cover"); ta.style.minHeight="120px"; ta.style.width="100%"; ta.value=rec.cover||"";
ta.addEventListener("input",function(){ saveField("cover",ta.value); });
f2.appendChild(ta); grid.appendChild(f2);
}
/* Врезаем инжект в openEdit для откликов (после enhanceCard, чтобы поля осели внизу и не прятались) */
var _openEdit_i=openEdit;
openEdit=function(coll,id){
_openEdit_i(coll,id);
if(coll==="applies"){
window.__coverApplyId=id;
setTimeout(function(){ try{ applyExtraFields(id); }catch(e){} },170);
} else { window.__coverApplyId=null; }
};
/* «Вставить в отклик»: кладём сгенерированное письмо в поле cover открытого отклика + фиксируем резюме */
function coverSaveToApply(){
var id=window.__coverApplyId; if(!id){ toast("Открой отклик, потом жми письмо"); return; }
var rec=(typeof recOf==="function")?recOf("applies",id):null; if(!rec){ toast("Отклик не найден"); return; }
var out=document.getElementById("covout"); var txt=out?out.value.trim():"";
if(!txt){ toast("Сначала сгенерируй письмо"); return; }
rec.cover=txt;
var rs=document.getElementById("covrole"); var role=rs?rs.value:"";
if(role && role!=="auto"){ rec.resume=({Head:"Head of Digital / Performance",Senior:"Senior / Performance-маркетолог",Lead:"Руководитель интернет-маркетинга",CMO:"CMO / Директор по маркетингу"})[role]||rec.resume; }
try{ if(typeof save==="function")save(); }catch(e){}
var m=document.getElementById("covmodal"); if(m)m.style.display="none";
toast("Письмо сохранено в отклик");
try{ openEdit("applies",id); }catch(e){}
}
/* Добавляем кнопку «Вставить в отклик» в модалку письма (после её создания) */
var _coverOpen_i=(typeof coverOpen==="function")?coverOpen:null;
if(_coverOpen_i && !_coverOpen_i.__applybtn){
coverOpen=function(){
var r=_coverOpen_i.apply(this,arguments);
setTimeout(function(){
var modal=document.getElementById("covmodal"); if(!modal)return;
if(modal.querySelector("#covsaveapply"))return;
/* найдём панель с кнопкой Скопировать */
var copyBtn=null; var btns=modal.querySelectorAll("button");
[].slice.call(btns).forEach(function(b){ if(/Скопировать/.test(b.textContent||""))copyBtn=b; });
if(copyBtn && copyBtn.parentNode){
var b=document.createElement("button"); b.className="btn"; b.id="covsaveapply"; b.textContent="Вставить в отклик"; b.onclick=coverSaveToApply;
if(!window.__coverApplyId)b.style.display="none";
copyBtn.parentNode.insertBefore(b,copyBtn.nextSibling);
}
},60);
return r;
};
coverOpen.__applybtn=1;
}
/* ===== Sprint 10: универсальный ИИ-вход «Новый контакт» =====
Использует рабочий серверный ai_parse. Тип (отклик/клиент) и уточняющие вопросы — на клиенте.
Ничего не создаётся без подтверждения Александра. */
var INTAKE_SRC=["Звонок","HH.ru","Сайт","Telegram","Рекомендация","Исходящий контакт","Другое"];
function intakeEnsureModal(){
if(document.getElementById("inmodal"))return;
var d=document.createElement("div");d.id="inmodal";
d.style.cssText="position:fixed;inset:0;background:rgba(0,0,0,.55);display:none;z-index:10004;align-items:flex-start;justify-content:center;overflow:auto;padding:40px 16px";
var srcopts=INTAKE_SRC.map(function(s){return '';}).join("");
d.innerHTML='
'+
'
Новый контакт — разобрать через ИИ
'+
'
Опиши свободно, откуда контакт и что за запрос: расшифровку звонка, письмо, вакансию с hh, заявку с сайта. ИИ вытащит поля, определит отклик это или клиент и задаст уточняющие вопросы. Ничего не создастся без твоей проверки.
'+
'
Источник:'+
''+
'Тип:'+
''+
'
'+
''+
''+
'
'+
'
';
document.body.appendChild(d);
}
function intakeOpen(){
if(!window.__key){toast("Сначала войдите");return;}
intakeEnsureModal();
var m=document.getElementById("inmodal"); m.style.display="flex";
var q=document.getElementById("inq"); if(q)q.innerHTML="";
var st=document.getElementById("instatus"); if(st)st.textContent="";
var h=document.getElementById("inkindhint"); if(h)h.textContent="";
var t=document.getElementById("intext"); if(t)t.focus();
window.__intakeFields=null; window.__intakeColl=null;
}
function intakeClose(){var m=document.getElementById("inmodal");if(m)m.style.display="none";}
/* эвристика типа контакта */
function intakeGuessKind(text,src){
var t=(text||"").toLowerCase();
var jobHints=/(ваканси|резюме|отклик|должност|обязанност|требовани|мы предлагаем|график работ|оформлени|оклад|з\/п|заработн|штат|full-?time|hh\.ru|хедхантер)/;
var clientHints=/(хочу|нужен маркетолог|нужен директор|заявк|бюджет на рекламу|продвижен|подрядчик|реклама не работает|лиды дорог|привести клиент|запустить рекламу|аудит)/;
if(src==="HH.ru")return "applies";
var j=jobHints.test(t), c=clientHints.test(t);
if(j&&!c)return "applies";
if(c&&!j)return "leads";
return "leads";
}
/* какие уточняющие вопросы задать по пустым важным полям */
function intakeQuestions(coll,f){
var qs=[];
if(coll==="applies"){
if(!f.company)qs.push("Какая компания / работодатель?");
if(!f.vacancy&&!f.want)qs.push("Какая вакансия или позиция?");
if(!f.link)qs.push("Есть ссылка на вакансию?");
qs.push("Какой формат интересен: Fractional CMO, штат, проект?");
} else {
if(!f.company)qs.push("Как называется компания / клиент?");
if(!f.sphere)qs.push("Какая ниша или сфера бизнеса?");
if(!f.want)qs.push("Что именно хочет — какая задача?");
if(!f.budget)qs.push("Есть понимание бюджета на маркетинг / рекламу?");
if(!f.contact)qs.push("Контакт: имя, телеграм или телефон?");
}
return qs.slice(0,5);
}
function intakeRun(){
var t=document.getElementById("intext"); var text=t?t.value.trim():"";
var src=(document.getElementById("insrc")||{}).value||"Другое";
var kindSel=(document.getElementById("inkind")||{}).value||"auto";
var st=document.getElementById("instatus"); var b=document.getElementById("ingo");
if(!text){if(st)st.textContent="Опиши контакт";return;}
if(!window.__key){if(st)st.textContent="Войди заново";return;}
var answers="";
var qwrap=document.getElementById("inq");
if(qwrap){ [].slice.call(qwrap.querySelectorAll("[data-q]")).forEach(function(inp){ var qq=inp.getAttribute("data-q"); var av=inp.value.trim(); if(av)answers+="\n"+qq+" — "+av; }); }
var dialog="Источник контакта: "+src+".\n"+text+(answers?("\n\nУточнения:"+answers):"");
if(b)b.disabled=true; if(st)st.textContent="Разбираю… (5–15 сек)";
apiPost({action:"ai_parse",key:window.__key,dialog:dialog}).then(function(r){
if(b)b.disabled=false;
if(!r||!r.ok||!r.fields){
if(r&&r.error==="nokey"){ if(st)st.textContent="ИИ-ключ не настроен (окно «AI · Из переписки» → сохранить ключ)."; }
else if(r&&r.error==="auth"){ if(st)st.textContent="Сессия истекла, войди заново."; }
else { if(st)st.textContent="Не разобралось. Добавь деталей и попробуй ещё."; }
return;
}
var f=r.fields;
var coll=(kindSel!=="auto")?kindSel:intakeGuessKind(text,src);
window.__intakeFields=f; window.__intakeColl=coll; window.__intakeSrc=src;
var h=document.getElementById("inkindhint");
if(h)h.textContent="→ "+(coll==="applies"?"отклик на вакансию":"клиент / лид");
var qs=intakeQuestions(coll,f);
var already=qwrap?qwrap.querySelectorAll("[data-q]").length:0;
var qwrap2=document.getElementById("inq");
if(qs.length && already===0){
var html='
Уточни, чтобы карточка была полной (любой вопрос можно пропустить):
';
qs.forEach(function(q){ html+='
'+esc(q)+'
'; });
html+='';
if(qwrap2)qwrap2.innerHTML=html;
if(st)st.textContent="Ответь на вопросы или создавай как есть.";
} else {
if(qwrap2)qwrap2.innerHTML='';
if(st)st.textContent="Готово. Проверь и создай карточку.";
}
}).catch(function(){ if(b)b.disabled=false; if(st)st.textContent="Нет связи с сервером"; });
}
function intakeCreate(){
var coll=window.__intakeColl||"leads";
var f=window.__intakeFields||{};
var src=window.__intakeSrc||"Другое";
var st=document.getElementById("instatus");
var id=(coll==="applies"?"in":"il")+Date.now();
var rec={id:id, date:(typeof today==="function"?today():"")};
if(coll==="applies"){
rec.company=f.company||"Без названия";
rec.vacancy=f.vacancy||f.want||"";
rec.source=src; rec.channel=(src==="HH.ru"?"HH.ru":(src==="Telegram"?"Telegram":"Другой"));
rec.status="Новый";
if(f.concl)rec.reason=f.concl;
if(f.budget)rec.budget=f.budget;
} else {
["company","contact","sphere","want","budget","stage","next","nextdate","agreed","pain","quote","concl"].forEach(function(k){ if(f[k]!=null&&f[k]!=="")rec[k]=f[k]; });
if(!rec.company)rec.company="Без названия";
rec.source=src; if(!rec.stage)rec.stage="Новый контакт"; if(!rec.target)rec.target="Неясно";
}
try{ db[coll].unshift(rec); if(typeof save==="function")save(); }catch(e){ if(st)st.textContent="Не удалось сохранить"; return; }
intakeClose();
try{ TAB=coll; if(typeof renderTabs==="function")renderTabs(); if(typeof render==="function")render(); }catch(e){}
setTimeout(function(){ try{ openEdit(coll,id); }catch(e){} toast("Карточка создана — проверь и допиши"); },80);
}
/* Кнопка в шапке */
(function(){
function inject(){
var tools=document.querySelector("header.top .tools");
if(tools && !document.getElementById("intakebtn")){
var b=document.createElement("button");
b.className="btn sm primary"; b.id="intakebtn"; b.textContent="+ Новый контакт"; b.onclick=intakeOpen;
tools.insertBefore(b, tools.firstChild);
}
}
inject(); setTimeout(inject,300);
})();
/* HOTPATCH Sprint 11: mobile card fix */
(function(){
if(document.getElementById("crmmobfix2"))return;
var st=document.createElement("style");st.id="crmmobfix2";
st.textContent=
"@media(max-width:680px){"+
"#modal{background:var(--bg,#0d1117)!important;padding:0!important;align-items:flex-start!important}"+
"#modal .dialog,#modal .dialog.cardx{background:var(--card,#1c2128)!important;width:100%!important;max-width:100%!important;min-height:100vh!important;margin:0!important;border-radius:0!important;box-shadow:none!important;padding:14px 14px 40px!important}"+
"#modal .cardx .cardhead{position:static!important;background:transparent!important;border-bottom:1px solid var(--line,#2a2f37)!important}"+
"#modal .cardx .grid{display:block!important}"+
"#modal .cardx .grid .f{display:block!important;margin:0 0 14px 0!important}"+
"#modal .cardx .grid .f.hid{display:none!important}"+
"#modal .cardx .grid .f .lab{display:block!important;margin-bottom:4px!important}"+
"#modal .cardx .grid .f input,#modal .cardx .grid .f select,#modal .cardx .grid .f textarea{width:100%!important;box-sizing:border-box!important}"+
"#modal .cardx .actbar{flex-wrap:wrap!important;gap:6px!important}"+
"#modal .cardx .actbar .btn{font-size:12px!important;padding:8px 10px!important}"+
"}";
document.head.appendChild(st);
})();
/* ===== HOTPATCH Sprint 13: clickable rows ===== */
/* ===== Sprint 13: строки списка кликабельны целиком, убрать кнопку «Открыть» ===== */
(function(){
var list=document.getElementById("list");
if(!list)return;
var obs;
function clean(){
if(obs)obs.disconnect();
try{
var rows=list.querySelectorAll("tbody tr");
[].slice.call(rows).forEach(function(tr){
var btn=null;
[].slice.call(tr.querySelectorAll("button,a")).forEach(function(b){ if(/^\s*Открыть\s*$/.test((b.textContent||"").trim()))btn=b; });
if(btn){
var oc=btn.getAttribute("onclick");
if(oc && !tr.getAttribute("onclick")) tr.setAttribute("onclick",oc);
tr.style.cursor="pointer";
var cell=btn.parentNode;
if(cell && cell.removeChild) cell.removeChild(btn);
if(cell && cell.tagName==="TD" && !cell.textContent.trim() && !cell.querySelector("*")) cell.parentNode.removeChild(cell);
}
[].slice.call(tr.querySelectorAll("select,input,button,a,textarea")).forEach(function(c){
if(!c.__np){ c.__np=1; c.addEventListener("click",function(e){ e.stopPropagation(); }); }
});
if(!tr.__hov){ tr.__hov=1; tr.addEventListener("mouseenter",function(){ tr.style.background="rgba(255,255,255,.03)"; }); tr.addEventListener("mouseleave",function(){ tr.style.background=""; }); }
});
}catch(e){}
if(obs)obs.observe(list,{childList:true,subtree:true});
}
obs=new MutationObserver(function(){ clean(); });
obs.observe(list,{childList:true,subtree:true});
setTimeout(clean,60); setTimeout(clean,400);
})();
/* ===== HOTPATCH Sprint 14: chat + summary ===== */
/* ===== Sprint 14: переписка по отклику + ИИ-саммари ===== */
function applyChatFields(id){
var dlg=document.getElementById("dialog"); if(!dlg)return;
var grid=dlg.querySelector("#editscope .grid")||dlg.querySelector(".grid"); if(!grid)return;
if(grid.querySelector('[data-xk="chat"]'))return;
var rec=(typeof recOf==="function")?recOf("applies",id):null; if(!rec)return;
function save1(k,v){ rec[k]=v; try{ if(typeof save==="function")save(); }catch(e){} }
var f1=document.createElement("div"); f1.className="f fw";
var l1=document.createElement("div"); l1.className="lab"; l1.textContent="Переписка / о чём общались"; f1.appendChild(l1);
var ta=document.createElement("textarea"); ta.setAttribute("data-xk","chat"); ta.style.cssText="width:100%;min-height:140px;box-sizing:border-box"; ta.placeholder="Вставь переписку с работодателем или заметку о разговоре"; ta.value=rec.chat||"";
ta.addEventListener("input",function(){ save1("chat",ta.value); });
f1.appendChild(ta); grid.appendChild(f1);
var bar=document.createElement("div"); bar.className="f fw"; bar.style.cssText="flex-direction:row;align-items:center;gap:10px";
var btn=document.createElement("button"); btn.className="btn"; btn.textContent="Свернуть в саммари (ИИ)";
var stt=document.createElement("span"); stt.className="muted small";
btn.onclick=function(){
var chat=ta.value.trim();
if(!chat){ stt.textContent="Вставь переписку выше"; return; }
if(!window.__key){ stt.textContent="ИИ-ключ не настроен (окно «AI · Из переписки»)"; return; }
btn.disabled=true; stt.textContent="Свожу… (5–15 сек)";
apiPost({action:"ai_parse",key:window.__key,dialog:"Переписка по отклику на вакансию. Кратко: о чём договорились, что просили, следующий шаг.\n\n"+chat}).then(function(r){
btn.disabled=false;
if(r&&r.ok&&r.fields){
var f=r.fields, parts=[];
if(f.want)parts.push("Запрос: "+f.want);
if(f.agreed)parts.push("Договорились: "+f.agreed);
if(f.pain)parts.push("Важное: "+f.pain);
if(f.next)parts.push("Следующий шаг: "+f.next);
if(f.concl)parts.push(f.concl);
var sum=parts.join("\n")||(f.concl||"");
var out=grid.querySelector('[data-xk="chatsum"]'); if(out){ out.value=sum; save1("chatsum",sum); }
stt.textContent="Готово. Проверь саммари ниже.";
} else if(r&&r.error==="nokey"){ stt.textContent="ИИ-ключ не настроен."; }
else if(r&&r.error==="auth"){ stt.textContent="Сессия истекла, войди заново."; }
else { stt.textContent="Не разобралось, поправь текст."; }
}).catch(function(){ btn.disabled=false; stt.textContent="Нет связи с сервером"; });
};
bar.appendChild(btn); bar.appendChild(stt); grid.appendChild(bar);
var f2=document.createElement("div"); f2.className="f fw";
var l2=document.createElement("div"); l2.className="lab"; l2.textContent="Саммари (о чём договорились)"; f2.appendChild(l2);
var ta2=document.createElement("textarea"); ta2.setAttribute("data-xk","chatsum"); ta2.style.cssText="width:100%;min-height:90px;box-sizing:border-box"; ta2.value=rec.chatsum||"";
ta2.addEventListener("input",function(){ save1("chatsum",ta2.value); });
f2.appendChild(ta2); grid.appendChild(f2);
}
(function(){
if(typeof openEdit!=="function")return;
if(openEdit.__chatfields)return;
var _oe=openEdit;
openEdit=function(coll,id){
_oe(coll,id);
if(coll==="applies"){ setTimeout(function(){ try{ applyChatFields(id); }catch(e){} },240); }
};
openEdit.__chatfields=1;
})();
/* ===== HOTPATCH Sprint 15: tg-chat + date groups + field priority ===== */
/* ===== Sprint 15: телеграм-переписка + группировка откликов по датам + приоритет полей ===== */
/* дата в человеческий вид */
function crmFmtDate(d){
if(!d)return "Без даты";
var t=Date.parse(d); if(isNaN(t))return d;
var dd=new Date(t), now=new Date();
var today=new Date(now.getFullYear(),now.getMonth(),now.getDate());
var that=new Date(dd.getFullYear(),dd.getMonth(),dd.getDate());
var diff=Math.round((today-that)/86400000);
if(diff===0)return "Сегодня";
if(diff===1)return "Вчера";
var m=["января","февраля","марта","апреля","мая","июня","июля","августа","сентября","октября","ноября","декабря"];
return dd.getDate()+" "+m[dd.getMonth()]+" "+dd.getFullYear();
}
/* А. Переписка пузырями */
function crmChatBubbles(id){
var dlg=document.getElementById("dialog"); if(!dlg)return;
var grid=dlg.querySelector("#editscope .grid")||dlg.querySelector(".grid"); if(!grid)return;
var ta=grid.querySelector('[data-xk="chat"]'); if(!ta)return;
var wrap=ta.closest(".f"); if(!wrap)return;
if(wrap.querySelector(".tgchat"))return;
var rec=(typeof recOf==="function")?recOf("applies",id):null; if(!rec)return;
var box=document.createElement("div"); box.className="tgchat";
box.style.cssText="display:flex;flex-direction:column;gap:6px;padding:10px;border:1px solid var(--line,#2a2f37);border-radius:10px;background:rgba(0,0,0,.15);max-height:360px;overflow:auto";
function build(){
box.innerHTML="";
var t=(rec.chat||"");
if(!t.trim()){ box.innerHTML='
Переписки пока нет
'; return; }
var parts=t.split(/\n\n(?=(?:Я|Работодатель):\s)/);
parts.forEach(function(p){
var me=/^Я:\s/.test(p);
var body=p.replace(/^(Я|Работодатель):\s*/,"");
var b=document.createElement("div");
b.style.cssText="max-width:80%;padding:7px 11px;border-radius:12px;font-size:13px;line-height:1.4;white-space:pre-wrap;word-break:break-word;"+(me?"align-self:flex-end;background:#2b5278;color:#fff;border-bottom-right-radius:4px":"align-self:flex-start;background:var(--card,#232830);color:var(--fg,#cbd3dc);border:1px solid var(--line,#2a2f37);border-bottom-left-radius:4px");
b.textContent=body;
box.appendChild(b);
});
}
build();
ta.style.display="none";
var toggle=document.createElement("button"); toggle.type="button"; toggle.className="btn sm"; toggle.textContent="Править текстом"; toggle.style.marginTop="6px";
var editing=false;
toggle.onclick=function(){ editing=!editing; if(editing){ ta.style.display=""; box.style.display="none"; toggle.textContent="Показать чат"; } else { ta.style.display="none"; box.style.display=""; build(); toggle.textContent="Править текстом"; } };
ta.parentNode.insertBefore(box, ta);
ta.parentNode.appendChild(toggle);
if(!ta.__syncb){ ta.__syncb=1; ta.addEventListener("input",function(){ rec.chat=ta.value; }); }
}
/* В. Приоритет полей: важное вверх */
function crmApplyPriority(){
var dlg=document.getElementById("dialog"); if(!dlg)return;
var grid=dlg.querySelector("#editscope .grid")||dlg.querySelector(".grid"); if(!grid)return;
var pr="data-k:company,data-k:vacancy,data-k:status,data-k:next,data-k:nextdate,data-xk:resume,data-xk:cover,data-xk:chat".split(",").map(function(x){return x.split(":");});
for(var i=pr.length-1;i>=0;i--){
var el=grid.querySelector("["+pr[i][0]+'="'+pr[i][1]+'"]');
var f=el&&el.closest(".f");
if(f && f.parentNode===grid){ grid.insertBefore(f, grid.firstChild); }
}
}
var _openEdit_o=openEdit;
openEdit=function(coll,id){
_openEdit_o(coll,id);
if(coll==="applies"){
setTimeout(function(){ try{ crmChatBubbles(id); crmApplyPriority(); }catch(e){} },320);
}
};
/* Б. Группировка списка откликов по датам (поллер) */
(function(){
var lastSig="";
setInterval(function(){
try{
if((window.TAB||"")!=="applies"){ lastSig=""; return; }
var list=document.getElementById("list"); if(!list)return;
var tbody=list.querySelector("tbody"); if(!tbody)return;
var rows=[].slice.call(tbody.querySelectorAll("tr")).filter(function(tr){return !tr.classList.contains("dategrp");});
if(!rows.length)return;
var sig=rows.map(function(tr){var m=(tr.getAttribute("onclick")||"").match(/'applies','([^']+)'/);return m?m[1]:"";}).join(",");
if(sig===lastSig && tbody.querySelector(".dategrp"))return;
lastSig=sig;
[].slice.call(tbody.querySelectorAll("tr.dategrp")).forEach(function(x){x.parentNode.removeChild(x);});
var last=null;
rows.forEach(function(tr){
var m=(tr.getAttribute("onclick")||"").match(/'applies','([^']+)'/); if(!m)return;
var rec=(typeof recOf==="function")?recOf("applies",m[1]):null; if(!rec)return;
var d=rec.date||"";
if(d!==last){ last=d; var hr=document.createElement("tr");hr.className="dategrp";var td=document.createElement("td");td.colSpan=8;td.style.cssText="padding:12px 7px 4px;font-size:12px;text-transform:uppercase;letter-spacing:.04em;opacity:.5;font-weight:600";td.textContent=crmFmtDate(d);hr.appendChild(td);tbody.insertBefore(hr,tr);}
});
}catch(e){}
},500);
})();
/* ===== HOTPATCH Sprint 16: card as page ===== */
/* ===== Sprint 16: карточка как страница (не попап) ===== */
(function(){
if(document.getElementById("crmpagecss"))return;
var st=document.createElement("style");st.id="crmpagecss";
st.textContent=
"#modal.pagemode{background:var(--bg,#0d1117)!important;padding:0!important;align-items:flex-start!important;overflow:auto!important}"+
"#modal.pagemode .dialog,#modal.pagemode .dialog.cardx{max-width:920px!important;width:100%!important;margin:0 auto!important;background:transparent!important;border:none!important;box-shadow:none!important;min-height:auto!important;border-radius:0!important;padding:16px 20px 70px!important}"+
"#modal.pagemode .cardhead{background:var(--bg,#0d1117)!important;border-bottom:1px solid var(--line,#2a2f37)!important}"+
"#crmback{display:inline-flex;align-items:center;gap:6px;background:transparent;border:none;color:var(--fg,#8ea0b5);cursor:pointer;font-size:14px;padding:6px 0;margin-bottom:6px;opacity:.8}"+
"#crmback:hover{opacity:1}";
document.head.appendChild(st);
})();
function crmPageBack(){
var dlg=document.getElementById("dialog");
var close=null;
if(dlg){ [].slice.call(dlg.querySelectorAll("button")).forEach(function(b){ if(/^\s*Закрыть\s*$/.test((b.textContent||"").trim()))close=b; }); }
if(close){ close.click(); }
else { var m=document.getElementById("modal"); if(m){ m.style.display="none"; } }
}
function crmMakePage(){
var m=document.getElementById("modal"); if(!m)return;
m.classList.add("pagemode");
var dlg=document.getElementById("dialog"); if(!dlg)return;
if(dlg.querySelector("#crmback"))return;
var back=document.createElement("button"); back.type="button"; back.id="crmback";
back.innerHTML="← Назад к списку"; back.onclick=crmPageBack;
dlg.insertBefore(back, dlg.firstChild);
}
var _openEdit_q=openEdit;
openEdit=function(coll,id){
_openEdit_q(coll,id);
setTimeout(function(){ try{ crmMakePage(); }catch(e){} },90);
setTimeout(function(){ try{ crmMakePage(); }catch(e){} },360);
};
/* ===== HOTPATCH Sprint 17: read ticks + reply button ===== */
/* ===== Sprint 17: галочки прочтения + кнопка «Ответить на hh» ===== */
/* переопределяем рендер переписки: из chatj (со статусом прочтения), с галочками */
function crmChatBubbles(id){
var dlg=document.getElementById("dialog"); if(!dlg)return;
var grid=dlg.querySelector("#editscope .grid")||dlg.querySelector(".grid"); if(!grid)return;
var ta=grid.querySelector('[data-xk="chat"]'); if(!ta)return;
var wrap=ta.closest(".f"); if(!wrap)return;
if(wrap.querySelector(".tgchat"))return;
var rec=(typeof recOf==="function")?recOf("applies",id):null; if(!rec)return;
var msgs=[];
if(rec.chatj){ try{ msgs=JSON.parse(rec.chatj); }catch(e){} }
if(!msgs.length){
var t=(rec.chat||"");
if(t.trim()){ t.split(/\n\n(?=(?:Я|Работодатель):\s)/).forEach(function(p){ var me=/^Я:\s/.test(p); msgs.push({me:me, t:p.replace(/^(Я|Работодатель):\s*/,""), r:undefined}); }); }
}
var box=document.createElement("div"); box.className="tgchat";
box.style.cssText="display:flex;flex-direction:column;gap:6px;padding:10px;border:1px solid var(--line,#2a2f37);border-radius:10px;background:rgba(0,0,0,.15);max-height:420px;overflow:auto";
if(!msgs.length){ box.innerHTML='
Переписки пока нет
'; }
else msgs.forEach(function(m){
var me=m.me;
var b=document.createElement("div");
b.style.cssText="max-width:80%;padding:7px 11px;border-radius:12px;font-size:13px;line-height:1.4;white-space:pre-wrap;word-break:break-word;"+(me?"align-self:flex-end;background:#2b5278;color:#fff;border-bottom-right-radius:4px":"align-self:flex-start;background:var(--card,#232830);color:var(--fg,#cbd3dc);border:1px solid var(--line,#2a2f37);border-bottom-left-radius:4px");
b.textContent=m.t;
if(me && m.r!==undefined){
var tk=document.createElement("span");
tk.textContent=m.r?" ✓✓":" ✓";
tk.title=m.r?"Прочитано работодателем":"Доставлено, не прочитано";
tk.style.cssText="font-size:11px;opacity:.95;color:"+(m.r?"#8fe0ff":"#cfe0f0");
b.appendChild(tk);
}
box.appendChild(b);
});
ta.style.display="none";
var toggle=document.createElement("button"); toggle.type="button"; toggle.className="btn sm"; toggle.textContent="Править текстом"; toggle.style.marginTop="6px";
var editing=false;
toggle.onclick=function(){ editing=!editing; if(editing){ ta.style.display=""; box.style.display="none"; toggle.textContent="Показать чат"; } else { ta.style.display="none"; box.style.display=""; toggle.textContent="Править текстом"; } };
ta.parentNode.insertBefore(box, ta);
ta.parentNode.appendChild(toggle);
}
/* кнопка «Ответить на hh» — открывает нужный чат */
function crmReplyBtn(id){
var dlg=document.getElementById("dialog"); if(!dlg)return;
var rec=(typeof recOf==="function")?recOf("applies",id):null; if(!rec||!rec.chatid)return;
if(dlg.querySelector("#crmreply"))return;
var openVac=null;
[].slice.call(dlg.querySelectorAll("a,button")).forEach(function(b){ if(/Открыть вакансию/.test(b.textContent||""))openVac=b; });
var a=document.createElement("a"); a.id="crmreply"; a.className="btn"; a.href="https://hh.ru/chat/"+rec.chatid; a.target="_blank"; a.rel="noopener";
a.textContent="Ответить на hh ↗";
if(openVac && openVac.parentNode){ openVac.parentNode.insertBefore(a, openVac.nextSibling); }
else { var bar=dlg.querySelector(".actbar")||dlg.querySelector(".rowbtns"); if(bar)bar.appendChild(a); }
}
var _openEdit_r=openEdit;
openEdit=function(coll,id){
_openEdit_r(coll,id);
if(coll==="applies"){ setTimeout(function(){ try{ crmReplyBtn(id); }catch(e){} },380); }
};
/* ===== HOTPATCH Sprint 12: version badge ===== */
(function(){
var V="v19 · 04.09.2026";
window.__crmVer=V;
function inject(){
var brand=document.querySelector("header.top .brand");
if(!brand)return false;
if(document.getElementById("crmver"))return true;
var s=document.createElement("span");
s.id="crmver"; s.textContent=V; s.title="Версия загруженного приложения. Нет метки или дата старее — кэш, жми Cmd+Shift+R.";
s.style.cssText="display:inline-block;padding:2px 8px;border:1px solid var(--line,#2a2f37);border-radius:20px;font-size:11px;opacity:.55;vertical-align:middle;white-space:nowrap";
var sub=brand.querySelector("small");
if(sub){ sub.appendChild(document.createTextNode("\u2003\u00b7\u2003")); sub.appendChild(s); } else { brand.appendChild(s); }
return true;
}
if(!inject()){ var k=0; var t=setInterval(function(){ if(inject()||++k>25)clearInterval(t); },200); }
})();