function getNote(tid) {return notesCache[tid]?.text||'';} function getNoteBy(tid) {return notesCache[tid]?.by||null;} function getNoteAt(tid) {return notesCache[tid]?.at||null;} // Status marks are mutually exclusive — only one at a time const STATUS_MARKS = MARKS_CONFIG.map(m => m.key); async function setMark(taskId, markType, e) { e&&e.stopPropagation(); const tid = String(taskId); if(!marksCache[tid]) marksCache[tid]={}; const now = new Date().toISOString(); const wasActive = marksCache[tid][markType]?.active; // Clear all status marks first STATUS_MARKS.forEach(m=>{ marksCache[tid][m]={...(marksCache[tid][m]||{}),active:false}; }); // Toggle: if was active → unset; else → set this one if(!wasActive){ marksCache[tid][markType]={active:true,by:currentUser.username,at:now}; } renderTable(); // Patch side panel mark buttons in-place without full re-render if(currentModalIdx!==null){ const btns=document.querySelectorAll('.sp-marks .mark-btn[data-mark]'); if(btns.length){ const tid2=String(allData[currentModalIdx].task.id); const states = Object.fromEntries(MARKS_CONFIG.map(m => [m.key, isMark(tid2, m.key)])); btns.forEach(btn=>{ btn.classList.toggle('mbtn-active', !!states[btn.dataset.mark]); }); // Also update sp-header badge const badge=document.querySelector('.sp-header .sp-status-badge'); if(badge) badge.outerHTML=internalStatusBadge(tid2); } else { renderSidePanel(currentModalIdx); } } // Один запрос — сервер сам сбрасывает остальные статусы и шлёт одно SSE const newVal = !wasActive; apiPost('/api/marks',{task_id:tid,mark_type:markType,value:newVal,clear_others:newVal}); } function toggleMark(taskId, markType, e){ return setMark(taskId, markType, e); } // Именованные алиасы — генерируются из MARKS_CONFIG, плюс ручные для совместимости MARKS_CONFIG.forEach(m => { const name = 'toggle' + m.key.replace(/_([a-z])/g, (_, c) => c.toUpperCase()).replace(/^(.)/, c => c.toUpperCase()); window[name] = (id, e) => setMark(id, m.key, e); }); // Явные алиасы для обратной совместимости с inline-атрибутами HTML function toggleResolved(id,e) { return setMark(id,'resolved',e); } function toggleNeedsReply(id,e) { return setMark(id,'needs_reply',e); } function toggleWaitClient(id,e) { return setMark(id,'wait_client',e); } function toggleVyezdnye(id,e) { return setMark(id,'vyezdnye',e); } function toggleOborudovanie(id,e) { return setMark(id,'oborudovanie',e); } function toggleHidden(id,e) { return setMark(id,'hidden',e); }