diff --git a/app/modules/project/templates/project/inbox.html b/app/modules/project/templates/project/inbox.html index 432bb47..da75de5 100644 --- a/app/modules/project/templates/project/inbox.html +++ b/app/modules/project/templates/project/inbox.html @@ -45,5 +45,5 @@ {% endif %} - + {% endblock %} diff --git a/app/modules/project/templates/project/index.html b/app/modules/project/templates/project/index.html index fbf9fe9..0205590 100644 --- a/app/modules/project/templates/project/index.html +++ b/app/modules/project/templates/project/index.html @@ -392,5 +392,5 @@ - + {% endblock %} diff --git a/app/modules/project/templates/project/project.html b/app/modules/project/templates/project/project.html index f89ff45..10e568d 100644 --- a/app/modules/project/templates/project/project.html +++ b/app/modules/project/templates/project/project.html @@ -405,5 +405,5 @@ - + {% endblock %} diff --git a/app/static/project.js b/app/static/project.js index 31ea712..4994800 100644 --- a/app/static/project.js +++ b/app/static/project.js @@ -211,6 +211,12 @@ const descEditor = initDescEditor(); const subtaskBlock = initSubtaskBlock(); const linksBlock = initLinksBlock(); + if (descEditor) { + descEditor.setOnImageRemoved(function () { + const tid = el("pj-t-id").value; + if (tid) loadHomeAttachments(tid); + }); + } // 닫을 때(배경 클릭·Esc·취소 버튼) 수정한 내용이 있으면 저장할지 물어본다. let homeTaskSnapshot = null; @@ -1631,6 +1637,12 @@ const descEditor = initDescEditor(); const subtaskBlock = initSubtaskBlock(); const linksBlock = initLinksBlock(); + if (descEditor) { + descEditor.setOnImageRemoved(function () { + const tid = el("pj-t-id").value; + if (tid) loadAttachments(tid); + }); + } // 닫을 때(배경 클릭·Esc·취소 버튼) 수정한 내용이 있으면 저장할지 물어본다. // 하위업무/첨부/댓글/연결은 각자 즉시 저장돼서 여기서 추적할 필요 없다 — @@ -2139,6 +2151,22 @@ const imgDelBtn = imgTools.querySelector(".pj-img-del"); const imgHandle = imgTools.querySelector(".pj-img-handle"); let selectedImg = null; + let onImageRemoved = null; + + // 설명란 이미지는 첨부파일 업로드로 들어간 실제 첨부 레코드다 — 에디터에서 + // 지우면 첨부파일 목록에도 안 남게 서버 첨부도 같이 삭제한다. + async function deleteSelectedImage() { + if (!selectedImg) return; + const img = selectedImg; + const m = (img.getAttribute("src") || "").match(/\/attachments\/(\d+)\/inline/); + img.remove(); + selectImg(null); + if (!m) return; + try { + await api("DELETE", "/project/api/attachments/" + m[1]); + } catch (_) { /* 이미 지워졌을 수 있음 — 에디터에서는 이미 빠졌으니 조용히 무시 */ } + if (onImageRemoved) onImageRemoved(); + } function positionImgTools() { if (!selectedImg || !selectedImg.isConnected) { selectedImg = null; imgTools.hidden = true; return; } @@ -2161,10 +2189,7 @@ document.addEventListener("click", function (e) { if (selectedImg && !wrap.contains(e.target)) selectImg(null); }); - imgDelBtn.addEventListener("click", function () { - if (selectedImg) selectedImg.remove(); - selectImg(null); - }); + imgDelBtn.addEventListener("click", function () { deleteSelectedImage(); }); let dragging = false, dragStartX = 0, dragStartW = 0; imgHandle.addEventListener("mousedown", function (e) { if (!selectedImg) return; @@ -2190,8 +2215,7 @@ const active = document.activeElement; if (active && ["INPUT", "TEXTAREA", "SELECT"].indexOf(active.tagName) >= 0) return; e.preventDefault(); - selectedImg.remove(); - selectImg(null); + deleteSelectedImage(); }); // 업로드 버튼 클릭 → 파일 선택창이 뜨는 동안 contenteditable의 선택 범위가 @@ -2322,6 +2346,7 @@ setHtml: function (html) { selectImg(null); lastRange = null; box.innerHTML = html || ""; }, getHtml: function () { return box.innerHTML; }, isUploading: function () { return pendingUploads > 0; }, + setOnImageRemoved: function (fn) { onImageRemoved = fn; }, }; }