diff --git a/app/modules/project/router.py b/app/modules/project/router.py index 6c988bc..0a5075e 100644 --- a/app/modules/project/router.py +++ b/app/modules/project/router.py @@ -419,6 +419,8 @@ async def index(request: Request) -> HTMLResponse: or store.norm_str(user["email"], lower=True) in member_emails or store.norm_str(p.get("owner_email"), lower=True) == store.norm_str(user["email"], lower=True) ) + # 세션이 하나도 없으면 업무를 넣을 곳이 없다 — "업무 추가" 버튼 비활성화용. + p["has_stages"] = bool(st.list_stages(project_id=p["id"])) my_tasks = st.list_tasks(assignee_email=user["email"], limit=500) for t in my_tasks: diff --git a/app/modules/project/templates/project/inbox.html b/app/modules/project/templates/project/inbox.html index 6caba30..7f01b78 100644 --- a/app/modules/project/templates/project/inbox.html +++ b/app/modules/project/templates/project/inbox.html @@ -1,7 +1,7 @@ {% extends "erp_base.html" %} {% block head_extra %} - + {% endblock %} @@ -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 9e41f4c..cfe57c2 100644 --- a/app/modules/project/templates/project/index.html +++ b/app/modules/project/templates/project/index.html @@ -1,7 +1,7 @@ {% extends "erp_base.html" %} {% block head_extra %} - + {% endblock %} @@ -20,7 +20,8 @@ data-due="{{ p.due_date or '' }}" data-color="{{ p.color }}" data-status="{{ p.status or 'active' }}" - data-creator="{{ p.created_by or '' }}"> + data-creator="{{ p.created_by or '' }}" + data-has-stages="{{ 'true' if p.has_stages else 'false' }}">
{{ p.name }} @@ -56,7 +57,9 @@ - {% endif %} @@ -421,5 +424,5 @@ window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }}; window.PJ_STAGE_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772","#e8398a","#a15c43"] | tojson }}; - + {% endblock %} diff --git a/app/modules/project/templates/project/project.html b/app/modules/project/templates/project/project.html index 14ff552..f39aabb 100644 --- a/app/modules/project/templates/project/project.html +++ b/app/modules/project/templates/project/project.html @@ -1,7 +1,7 @@ {% extends "erp_base.html" %} {% block head_extra %} - + @@ -538,5 +538,5 @@ - + {% endblock %} diff --git a/app/static/project.css b/app/static/project.css index 177e8c8..34b72df 100644 --- a/app/static/project.css +++ b/app/static/project.css @@ -94,6 +94,10 @@ .pj-card-actions .pj-icon-btn .material-symbols-outlined { font-size: 18px; } .pj-project-card:hover .pj-card-actions .pj-icon-btn { opacity: 1; } .pj-card-actions .pj-icon-btn:hover { background: rgba(255,255,255,.25); color: #fff; } +/* 세션이 없는 프로젝트는 업무를 넣을 곳이 없다 — 흐리게 + 클릭 불가 */ +.pj-project-card:hover .pj-card-actions .pj-icon-btn:disabled { opacity: .35; cursor: not-allowed; } +.pj-card-actions .pj-icon-btn:disabled { pointer-events: none; } +.pj-card-actions .pj-icon-btn:disabled:hover { background: none; } .pj-project-name { font-weight: 700; font-size: 15px; } .pj-project-desc { color: #6b7280; font-size: 12.5px; } /* 프로젝트 이름 아래 — 배정 멤버(아이콘+이름) */ diff --git a/app/static/project.js b/app/static/project.js index d89ecf2..6e308af 100644 --- a/app/static/project.js +++ b/app/static/project.js @@ -565,7 +565,8 @@ ' data-id="' + p.id + '" data-name="' + escapeHtml(p.name || "") + '"' + ' data-desc="' + escapeHtml(p.description || "") + '"' + ' data-start="' + (p.start_date || "") + '" data-due="' + (p.due_date || "") + '"' + - ' data-color="' + color + '" data-status="active" data-creator="' + escapeHtml(p.created_by || "") + '">' + + ' data-color="' + color + '" data-status="active" data-creator="' + escapeHtml(p.created_by || "") + '"' + + ' data-has-stages="false">' + '' + '
' + escapeHtml(p.name || "") + "
" + '
' + @@ -576,7 +577,7 @@ '
' + '' + - '' + '
' + @@ -610,6 +611,13 @@ try { await api("POST", "/project/api/projects/" + stageModalProjectId + "/stages", { name: name, color: color }); closeModal(stageModal); + // 이 프로젝트에 세션이 처음 생겼으면 "업무 추가" 버튼을 바로 활성화. + const targetCard = document.querySelector('.pj-project-card[data-id="' + stageModalProjectId + '"]'); + if (targetCard) { + targetCard.dataset.hasStages = "true"; + const addTaskBtn = targetCard.querySelector(".pj-card-addtask"); + if (addTaskBtn) { addTaskBtn.disabled = false; addTaskBtn.title = "업무 추가"; } + } showToast("세션이 추가되었습니다.", "success"); } catch (e) { showToast("추가 실패: " + e.message, "error"); } }); @@ -643,14 +651,18 @@ card.addEventListener("contextmenu", function (e) { if (!card.querySelector(".pj-card-edit")) return; // 관리 권한 없는 카드는 메뉴 없음 e.preventDefault(); - showContextMenu(e.clientX, e.clientY, [ + const items = [ { label: "열기", onClick: function () { location.href = "/project/p/" + card.dataset.id; } }, - { label: "업무 추가", onClick: function () { if (openNewTaskModalRef) openNewTaskModalRef(card.dataset.id, card.dataset.name); } }, - { label: "세션 추가", onClick: function () { openStageModalForProject(card.dataset.id, card.dataset.name); } }, - { label: "수정", onClick: function () { openCardEditModal(card); } }, - null, - { label: "삭제", danger: true, onClick: function () { deleteProjectCard(card); } }, - ]); + ]; + // 세션이 없으면 업무를 넣을 곳이 없다 — 메뉴에서도 뺀다(아이콘 버튼과 동일 원칙). + if (card.dataset.hasStages !== "false") { + items.push({ label: "업무 추가", onClick: function () { if (openNewTaskModalRef) openNewTaskModalRef(card.dataset.id, card.dataset.name); } }); + } + items.push({ label: "세션 추가", onClick: function () { openStageModalForProject(card.dataset.id, card.dataset.name); } }); + items.push({ label: "수정", onClick: function () { openCardEditModal(card); } }); + items.push(null); + items.push({ label: "삭제", danger: true, onClick: function () { deleteProjectCard(card); } }); + showContextMenu(e.clientX, e.clientY, items); }); }