diff --git a/app/modules/project/router.py b/app/modules/project/router.py index 9dda316..0488983 100644 --- a/app/modules/project/router.py +++ b/app/modules/project/router.py @@ -389,11 +389,10 @@ async def index(request: Request) -> HTMLResponse: member_email=None if admin else user["email"], ) tree = _build_tree(projects) - # 완료 프로젝트는 별도 섹션 대신, 같은 대시보드 안에서 반투명 + 맨 - # 끝으로만 보낸다(세션 완료 처리와 같은 원칙). sorted() 는 안정 정렬이라 - # tree 가 이미 sort_order 순이면 완료 여부로만 다시 나눠도 각 그룹 - # 내부 순서는 그대로 유지된다 — 완료 해제 시 원래 자리로 자동 복귀. - home_projects = sorted(tree, key=lambda p: p.get("status") == "completed") + # 진행중/완료 프로젝트를 별도 섹션으로 분류. list_projects 가 이미 + # sort_order 순으로 내려주므로 필터링만 해도 각 섹션 내부 순서는 그대로. + active_projects = [p for p in tree if p.get("status") != "completed"] + done_projects = [p for p in tree if p.get("status") == "completed"] # 아바타 맵: 이메일(소문자) → 구글 프로필 이미지 URL (홈 카드 업무 담당자 표시용) from app.main import user_store # noqa: WPS433 @@ -475,7 +474,8 @@ async def index(request: Request) -> HTMLResponse: "page_title": "프로젝트 관리", "page_subtitle": "달력·타임라인·보드로 프로젝트를 관리하세요.", "force_sidebar_collapsed": True, - "home_projects": home_projects, + "active_projects": active_projects, + "done_projects": done_projects, "my_projects": my_projects, "avatars": avatars, "home_tasks": home_tasks, diff --git a/app/modules/project/templates/project/inbox.html b/app/modules/project/templates/project/inbox.html index 3099e75..74bddda 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 0a81305..bfad721 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 %} @@ -125,19 +125,26 @@ - {% if not home_projects %} + {% if not active_projects and not done_projects %}
아직 프로젝트가 없습니다. 상단의 새 프로젝트 버튼으로 만들어 보세요.
+ {% elif not active_projects %} +
진행중인 프로젝트가 없습니다.
{% endif %} -
- {% for p in home_projects %}{{ project_card(p) }}{% endfor %} + {% for p in active_projects %}{{ project_card(p) }}{% endfor %}
+ + {% if done_projects %} +
+

완료 프로젝트

+
+
+ {% for p in done_projects %}{{ project_card(p) }}{% endfor %} +
+ {% endif %} @@ -421,5 +428,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 8133d86..aa8d770 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 %} - + @@ -544,5 +544,5 @@ - + {% endblock %} diff --git a/app/static/project.css b/app/static/project.css index bb50bee..9b50b70 100644 --- a/app/static/project.css +++ b/app/static/project.css @@ -35,6 +35,8 @@ .pj-icon-btn:hover { background: #eceef0; color: #1e1f21; } .pj-section-head { display: flex; align-items: center; justify-content: space-between; margin: 8px 0 14px; } +.pj-section-head-done { margin-top: 22px; } +.pj-section-head-done h2 { font-size: 14px; color: #6b7280; } .pj-section-head h2 { font-size: 16px; font-weight: 700; margin: 0; } .pj-empty { padding: 28px; text-align: center; color: #6b7280; background: #f7f8f9; border-radius: 12px; } diff --git a/app/static/project.js b/app/static/project.js index 8647c28..2512a6c 100644 --- a/app/static/project.js +++ b/app/static/project.js @@ -570,25 +570,29 @@ } } else if (dueChip) dueChip.remove(); } - // 완료 체크 시 반투명 + 대시보드 맨 끝으로, 해제하면 sort_order 기준 - // 원래 자리로(세션 완료 처리와 같은 원칙 — resortProjectGrid 가 매번 - // "완료 여부 → sort_order → id" 순으로 안정 재배치한다). - resortProjectGrid(); + // 완료 체크 시 "완료 프로젝트" 섹션으로, 해제하면 "진행중인 프로젝트"로 + // 되돌린다. 각 섹션 안에서는 sort_order 기준 안정 정렬 — 드래그로 손댄 + // 적 없으면 원래 순서 그대로 유지된다. + moveProjectCardToSection(card, p.status === "completed"); } - function resortProjectGrid() { - const grid = el("pj-project-grid"); + function sortGridByOrder(grid) { if (!grid) return; const cards = Array.prototype.slice.call(grid.querySelectorAll(".pj-project-card")); cards.sort(function (a, b) { - const da = a.dataset.status === "completed" ? 1 : 0; - const db = b.dataset.status === "completed" ? 1 : 0; - if (da !== db) return da - db; const oa = Number(a.dataset.sortOrder) || 0, ob = Number(b.dataset.sortOrder) || 0; if (oa !== ob) return oa - ob; return Number(a.dataset.id) - Number(b.dataset.id); }); cards.forEach(function (c) { grid.appendChild(c); }); } + function moveProjectCardToSection(card, isDone) { + // "완료 프로젝트" 섹션이 화면에 아직 없으면(이번이 첫 완료 프로젝트) + // 다음 새로고침까지 원래 자리에 남는다 — 드문 경우라 허용. + const targetGrid = el(isDone ? "pj-project-grid-done" : "pj-project-grid"); + if (!targetGrid) return; + targetGrid.appendChild(card); + sortGridByOrder(targetGrid); + } // 새 프로젝트 카드 DOM — project_card(p) 매크로(index.html)를 그대로 미러링. // 방금 만든 프로젝트라 멤버/업무는 아직 없다(멤버는 생성 직후 별도 배정, // 업무는 카드의 "업무 추가"로 나중에). 만든 사람은 자동 owner 라 관리 가능. @@ -1343,7 +1347,7 @@ // 프로젝트 카드 드래그 순서 변경 — 진행중/완료 그리드 각각 안에서만 // 재정렬(그리드 사이 드래그로 옮기면 상태가 바뀐 것처럼 보이니 막는다). function wireProjectGridDrag() { - const grids = [el("pj-project-grid")].filter(Boolean); + const grids = [el("pj-project-grid"), el("pj-project-grid-done")].filter(Boolean); if (!grids.length) return; let dragCard = null, sourceGrid = null; grids.forEach(function (grid) {