From ecd4bd829ea9321db7b5a851a652762d3e8c08a8 Mon Sep 17 00:00:00 2001 From: king Date: Tue, 15 Sep 2026 23:08:52 +0900 Subject: [PATCH] =?UTF-8?q?feat(project):=20=EB=82=B4=20=EC=97=85=EB=AC=B4?= =?UTF-8?q?=20=ED=8C=A8=EB=84=90=20=EC=8A=A4=ED=81=AC=EB=A1=A4=20+=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20=ED=8F=B4=EB=8D=94=20?= =?UTF-8?q?=ED=8E=BC=EC=B9=98=EA=B8=B0/=EC=A0=91=EA=B8=B0=20=ED=86=A0?= =?UTF-8?q?=EA=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - .pj-home-side 에 max-height+overflow-y:auto 추가 — 목록이 길어지면 패널 안에서만 스크롤(프로젝트 상세 화면은 기존 height:100% 우선 적용). - "내 업무"의 프로젝트별 폴더 아이콘을 별도 토글 버튼으로 분리(이름은 계속 프로젝트로 이동하는 링크) — 클릭 시 하위 업무 목록 펼침/접힘, 아이콘도 folder_open/folder 로 전환. localStorage로 접힘 상태 기억. Co-Authored-By: Claude Sonnet 5 --- .../project/templates/project/inbox.html | 4 +-- .../project/templates/project/index.html | 18 ++++++----- .../project/templates/project/project.html | 18 ++++++----- app/static/project.css | 16 ++++++++-- app/static/project.js | 30 +++++++++++++++++++ 5 files changed, 67 insertions(+), 19 deletions(-) diff --git a/app/modules/project/templates/project/inbox.html b/app/modules/project/templates/project/inbox.html index 44db589..fd547d7 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 c6b3dcb..3db85b5 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 %} @@ -150,11 +150,15 @@ {% else %}
{% for p in my_projects %} -
- - folder - {{ p.name }} - +
+
+ + + {{ p.name }} + +
    {% for t in p.tasks %}
  • @@ -376,5 +380,5 @@ - + {% endblock %} diff --git a/app/modules/project/templates/project/project.html b/app/modules/project/templates/project/project.html index 451219d..bf8eb2c 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 %} - + @@ -175,11 +175,15 @@ {% else %}
    {% for p in my_projects %} -
    - - folder - {{ p.name }} - +
    +
    + + + {{ p.name }} + +
      {% for t in p.tasks %}
    • @@ -388,5 +392,5 @@ - + {% endblock %} diff --git a/app/static/project.css b/app/static/project.css index f7036ed..32a4092 100644 --- a/app/static/project.css +++ b/app/static/project.css @@ -116,7 +116,10 @@ .pj-section-head-done { margin-top: 22px; } .pj-section-head-done h2 { font-size: 14px; color: #6b7280; } -.pj-home-side { width: 300px; border-left: 1px solid #eef0f2; padding-left: 24px; } +/* max-height 는 항상 값을 갖는 안전한 fallback(홈 화면처럼 부모가 고정 + 높이가 아닌 곳에서도 동작) — 프로젝트 상세 화면은 .pj-project-mytasks 가 + 더 구체적인 height:100% 로 덮어써서 화면 꽉 채움 레이아웃을 그대로 쓴다. */ +.pj-home-side { width: 300px; border-left: 1px solid #eef0f2; padding-left: 24px; max-height: calc(100vh - 160px); overflow-y: auto; } @media (max-width: 980px) { .pj-home-side { border-left: none; padding-left: 0; } } .pj-mytask-list { list-style: none; margin: 0; padding: 0; } .pj-mytask a { display: flex; align-items: center; gap: 8px; padding: 9px 6px; border-radius: 8px; text-decoration: none; color: inherit; } @@ -294,11 +297,18 @@ .pj-th-resize { position: absolute; top: 0; right: -3px; width: 8px; height: 100%; cursor: col-resize; z-index: 2; } .pj-th-resize:hover { background: linear-gradient(90deg, transparent 40%, #c9d4ea 40%, #c9d4ea 60%, transparent 60%); } -/* ── 내 업무 트리(홈) ── */ +/* ── 내 업무 트리(홈) — 폴더 아이콘은 토글 버튼(열림/닫힘), 이름은 별도 링크 ── */ .pj-mytree-group { margin-bottom: 14px; } -.pj-mytree-proj { display: flex; align-items: center; gap: 7px; text-decoration: none; color: #1e1f21; font-weight: 700; font-size: 13.5px; padding: 4px 2px; border-radius: 7px; } +.pj-mytree-head { display: flex; align-items: center; gap: 2px; } +.pj-mytree-toggle { + flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; + width: 26px; height: 26px; padding: 0; border: none; background: transparent; border-radius: 7px; cursor: pointer; +} +.pj-mytree-toggle:hover { background: #f1f2f4; } +.pj-mytree-proj { display: flex; align-items: center; gap: 7px; text-decoration: none; color: #1e1f21; font-weight: 700; font-size: 13.5px; padding: 4px 2px; border-radius: 7px; flex: 1 1 auto; min-width: 0; } .pj-mytree-proj:hover { background: #f6f7f8; } .pj-mytree-tasks { list-style: none; margin: 2px 0 0; padding: 0 0 0 8px; border-left: 2px solid #eef0f2; margin-left: 10px; } +.pj-mytree-group.is-collapsed .pj-mytree-tasks { display: none; } .pj-mytree-task a { display: flex; align-items: flex-start; gap: 7px; padding: 6px 6px; border-radius: 7px; text-decoration: none; color: inherit; } .pj-mytree-task a:hover { background: #f6f7f8; } /* 댓글 아이콘 자리 — 댓글이 없어도 폭을 고정해 제목이 항상 같은 위치에서 시작(세로 정렬)되게 한다. */ diff --git a/app/static/project.js b/app/static/project.js index bebd263..a3c27ce 100644 --- a/app/static/project.js +++ b/app/static/project.js @@ -2441,11 +2441,41 @@ setTimeout(poll, 8000); } + // ── "내 업무" 패널 — 프로젝트별 폴더 아이콘 클릭으로 하위 업무 목록 + // 펼치기/접기. 홈 화면·프로젝트 상세 화면 둘 다 같은 마크업이라 공용으로 + // 한 번만 처리한다(각 initXxx 와 무관, 서버 렌더 마크업 그대로 사용). + function wireMyTreeToggles() { + const KEY = "pj_mytree_collapsed"; + let collapsed; + try { collapsed = new Set(JSON.parse(localStorage.getItem(KEY) || "[]")); } + catch (_) { collapsed = new Set(); } + function save() { + try { localStorage.setItem(KEY, JSON.stringify(Array.prototype.slice.call(collapsed))); } catch (_) {} + } + document.querySelectorAll(".pj-mytree-group").forEach(function (group) { + const gid = group.dataset.groupId; + const btn = group.querySelector(".pj-mytree-toggle"); + const icon = btn ? btn.querySelector(".material-symbols-outlined") : null; + function apply() { + const isCollapsed = collapsed.has(gid); + group.classList.toggle("is-collapsed", isCollapsed); + if (icon) icon.textContent = isCollapsed ? "folder" : "folder_open"; + } + apply(); + if (btn) btn.addEventListener("click", function () { + if (collapsed.has(gid)) collapsed.delete(gid); else collapsed.add(gid); + save(); + apply(); + }); + }); + } + document.addEventListener("DOMContentLoaded", function () { initHome(); initProject(); initBell(); initInbox(); initLiveRefresh(); + wireMyTreeToggles(); }); })();