diff --git a/app/modules/project/templates/project/inbox.html b/app/modules/project/templates/project/inbox.html
index 8b9281f..06c2697 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 0e17b32..cf115ec 100644
--- a/app/modules/project/templates/project/index.html
+++ b/app/modules/project/templates/project/index.html
@@ -393,5 +393,5 @@
-
+
{% endblock %}
diff --git a/app/modules/project/templates/project/project.html b/app/modules/project/templates/project/project.html
index 68c5be2..7213f61 100644
--- a/app/modules/project/templates/project/project.html
+++ b/app/modules/project/templates/project/project.html
@@ -461,5 +461,5 @@
-
+
{% endblock %}
diff --git a/app/static/project.js b/app/static/project.js
index ff5d25d..3d8dcad 100644
--- a/app/static/project.js
+++ b/app/static/project.js
@@ -362,6 +362,56 @@
if (prog) prog.remove();
}
}
+ // 업무 우클릭 메뉴 — 홈 카드 안 업무 행·"내 업무" 패널 행 공용(둘 다 같은
+ // .pj-task-popup-link 를 씀). 열기=프로젝트 페이지로 실제 이동, 수정=이
+ // 자리에서 팝업 편집 — 서로 다른 동작이라 항목을 나눴다. 카드 자체의
+ // 우클릭 메뉴와 겹치지 않도록 stopPropagation 필수(안 하면 이벤트가
+ // 버블링돼 카드 메뉴가 나중에 열려 덮어써 버린다).
+ function homeTaskContextItems(t) {
+ return [
+ { label: "열기", onClick: function () { location.href = "/project/p/" + t.project_id + "?task=" + t.id; } },
+ { label: "업무 추가", onClick: function () { if (openNewTaskModalRef) openNewTaskModalRef(t.project_id, t.project_name); } },
+ { label: "수정", onClick: function () { if (openHomeTaskModalRef) openHomeTaskModalRef(t); } },
+ {
+ label: t.completed_at ? "완료 취소" : "완료로 표시",
+ onClick: async function () {
+ try {
+ const res = await api("PUT", "/project/api/tasks/" + t.id, { completed: !t.completed_at });
+ upsertHomeTaskRow(res.task);
+ } catch (e) { showToast("처리 실패: " + e.message, "error"); }
+ },
+ },
+ null,
+ {
+ label: "삭제", danger: true,
+ onClick: async function () {
+ if (!confirm("이 업무를 삭제할까요?")) return;
+ try {
+ await api("DELETE", "/project/api/tasks/" + t.id);
+ removeHomeTaskRow(t.id, t.project_id);
+ } catch (e) { showToast("삭제 실패: " + e.message, "error"); }
+ },
+ },
+ ];
+ }
+ // .pj-task-popup-link 하나에 클릭(팝업 열기)+우클릭(메뉴) 배선 — 최초
+ // 로드된 행과 optimistic 하게 새로 그려진 행(upsertHomeTaskRow/
+ // syncMyTaskPanel) 모두 이 함수 하나로 통일.
+ function wireHomeTaskPopupLink(a, taskOverride) {
+ a.addEventListener("click", function (e) {
+ const task = taskOverride || homeTasks[a.dataset.taskId];
+ if (!task) return; // 정보 없으면 기존 링크 이동으로 폴백
+ e.preventDefault();
+ if (openHomeTaskModalRef) openHomeTaskModalRef(task);
+ });
+ a.addEventListener("contextmenu", function (e) {
+ const task = taskOverride || homeTasks[a.dataset.taskId];
+ if (!task) return;
+ e.preventDefault();
+ e.stopPropagation();
+ showContextMenu(e.clientX, e.clientY, homeTaskContextItems(task));
+ });
+ }
function upsertHomeTaskRow(t) {
const card = document.querySelector('.pj-project-card[data-id="' + t.project_id + '"]');
if (!card) { syncMyTaskPanel(t); return; }
@@ -377,10 +427,7 @@
const wrap = document.createElement("div");
wrap.innerHTML = homeTaskRowHtml(t);
const newLi = wrap.firstElementChild;
- newLi.querySelector(".pj-task-popup-link").addEventListener("click", function (e) {
- e.preventDefault();
- if (openHomeTaskModalRef) openHomeTaskModalRef(t);
- });
+ wireHomeTaskPopupLink(newLi.querySelector(".pj-task-popup-link"), t);
const badge = newLi.querySelector(".pj-cmt-badge");
if (badge) {
badge.addEventListener("click", function (e) { e.preventDefault(); e.stopPropagation(); });
@@ -444,10 +491,7 @@
const wrap = document.createElement("div");
wrap.innerHTML = html;
const newLi = wrap.firstElementChild;
- newLi.querySelector(".pj-task-popup-link").addEventListener("click", function (e) {
- e.preventDefault();
- if (openHomeTaskModalRef) openHomeTaskModalRef(t);
- });
+ wireHomeTaskPopupLink(newLi.querySelector(".pj-task-popup-link"), t);
if (existingLi) existingLi.replaceWith(newLi); else group.appendChild(newLi);
}
@@ -880,15 +924,7 @@
catch (e) { showToast("삭제 실패: " + e.message, "error"); }
});
- document.querySelectorAll(".pj-task-popup-link").forEach(function (a) {
- a.addEventListener("click", function (e) {
- const tid = a.dataset.taskId;
- const task = homeTasks[tid];
- if (!task) return; // 정보 없으면 기존 링크 이동으로 폴백
- e.preventDefault();
- openHomeTaskModal(task);
- });
- });
+ document.querySelectorAll(".pj-task-popup-link").forEach(function (a) { wireHomeTaskPopupLink(a); });
}
// ── 댓글 말풍선(카드/내 업무) — 더블클릭 시 댓글만 보는 채팅형 팝업 ──