feat(project): 내 업무 패널에 프로젝트별 전체/진행/완료 개수 배지
프로젝트 이름 오른쪽에 "전체5 진행2 완료3" 형식으로 표시, 숫자는 색깔 있는 동그라미 안에(전체=회색, 진행=파랑, 완료=초록). 서버 렌더용 카운트는 _group_my_tasks() 헬퍼로 통합(홈/프로젝트 화면 중복 제거), 실시간 갱신은 DOM의 실제 <li> 개수를 다시 세는 updateMyTreeCounts()를 기존 sync 함수 (syncMyTaskPanel/syncProjectMyTaskPanel) 안에서 호출해 처리. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -205,6 +205,31 @@ def _fmt_date_kr(d: str | None) -> str:
|
||||
return dd.strftime("%m/%d") + f"({_WEEKDAYS_KR[dd.weekday()]})"
|
||||
|
||||
|
||||
def _group_my_tasks(my_tasks: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""담당 업무를 프로젝트별로 묶고, "내 업무" 패널에 표시할 전체/진행/완료
|
||||
개수를 함께 센다(홈 화면·프로젝트 화면 양쪽에서 재사용)."""
|
||||
groups: dict[int, dict[str, Any]] = {}
|
||||
for t in my_tasks:
|
||||
pid = t.get("project_id")
|
||||
g = groups.get(pid)
|
||||
if g is None:
|
||||
g = {
|
||||
"id": pid,
|
||||
"name": t.get("project_name") or "프로젝트",
|
||||
"color": t.get("project_color") or "#4573d2",
|
||||
"tasks": [],
|
||||
}
|
||||
groups[pid] = g
|
||||
g["tasks"].append(t)
|
||||
for g in groups.values():
|
||||
total = len(g["tasks"])
|
||||
done = sum(1 for t in g["tasks"] if t.get("completed_at"))
|
||||
g["total"] = total
|
||||
g["done"] = done
|
||||
g["open"] = total - done
|
||||
return list(groups.values())
|
||||
|
||||
|
||||
def _build_tree(projects: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||
"""평면 프로젝트 목록 → parent_id 기준 트리(children 키)."""
|
||||
by_id: dict[int, dict[str, Any]] = {}
|
||||
@@ -426,21 +451,8 @@ async def index(request: Request) -> HTMLResponse:
|
||||
for t in my_tasks:
|
||||
t["start_label"] = _fmt_date_kr(t.get("start_date"))
|
||||
t["due_label"] = _fmt_date_kr(t.get("due_date"))
|
||||
# 내 업무를 프로젝트별로 묶어 트리로 표시
|
||||
my_groups: dict[int, dict[str, Any]] = {}
|
||||
for t in my_tasks:
|
||||
pid = t.get("project_id")
|
||||
g = my_groups.get(pid)
|
||||
if g is None:
|
||||
g = {
|
||||
"id": pid,
|
||||
"name": t.get("project_name") or "프로젝트",
|
||||
"color": t.get("project_color") or "#4573d2",
|
||||
"tasks": [],
|
||||
}
|
||||
my_groups[pid] = g
|
||||
g["tasks"].append(t)
|
||||
my_projects = list(my_groups.values())
|
||||
# 내 업무를 프로젝트별로 묶어 트리로 표시(전체/진행/완료 개수 포함)
|
||||
my_projects = _group_my_tasks(my_tasks)
|
||||
|
||||
# 홈 화면 카드/내업무에 노출된 업무 전체(id→업무) — 클릭 시 페이지 이동 없이
|
||||
# 팝업으로 편집하기 위해 필요한 원본 데이터를 그대로 클라이언트에 넘긴다.
|
||||
@@ -567,20 +579,7 @@ async def project_page(request: Request, project_id: int) -> HTMLResponse:
|
||||
for t in my_tasks:
|
||||
t["start_label"] = _fmt_date_kr(t.get("start_date"))
|
||||
t["due_label"] = _fmt_date_kr(t.get("due_date"))
|
||||
my_groups: dict[int, dict[str, Any]] = {}
|
||||
for t in my_tasks:
|
||||
pid = t.get("project_id")
|
||||
g = my_groups.get(pid)
|
||||
if g is None:
|
||||
g = {
|
||||
"id": pid,
|
||||
"name": t.get("project_name") or "프로젝트",
|
||||
"color": t.get("project_color") or "#4573d2",
|
||||
"tasks": [],
|
||||
}
|
||||
my_groups[pid] = g
|
||||
g["tasks"].append(t)
|
||||
my_projects = list(my_groups.values())
|
||||
my_projects = _group_my_tasks(my_tasks)
|
||||
|
||||
# 달력(휴가식 월간 그리드) — ?y=&m= 로 월 이동, 기본 이번 달
|
||||
today = today_kst()
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260917u" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260917v" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script src="/static/project.js?v=20260917w" defer></script>
|
||||
<script src="/static/project.js?v=20260917x" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260917u" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260917v" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -165,6 +165,11 @@
|
||||
<a class="pj-mytree-proj" href="/project/p/{{ p.id }}">
|
||||
<span class="pj-mytree-proj-name">{{ p.name }}</span>
|
||||
</a>
|
||||
<span class="pj-mytree-counts">
|
||||
<span class="pj-mtc">전체<span class="pj-mtc-num pj-mtc-total">{{ p.total }}</span></span>
|
||||
<span class="pj-mtc">진행<span class="pj-mtc-num pj-mtc-open">{{ p.open }}</span></span>
|
||||
<span class="pj-mtc">완료<span class="pj-mtc-num pj-mtc-done">{{ p.done }}</span></span>
|
||||
</span>
|
||||
</div>
|
||||
<ul class="pj-mytree-tasks">
|
||||
{% for t in p.tasks %}
|
||||
@@ -424,5 +429,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 }};
|
||||
</script>
|
||||
<script src="/static/project.js?v=20260917w" defer></script>
|
||||
<script src="/static/project.js?v=20260917x" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260917u" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260917v" />
|
||||
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host -->
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
<!-- 타임라인 vis-timeline — self-host -->
|
||||
@@ -207,6 +207,11 @@
|
||||
<a class="pj-mytree-proj" href="/project/p/{{ p.id }}">
|
||||
<span class="pj-mytree-proj-name">{{ p.name }}</span>
|
||||
</a>
|
||||
<span class="pj-mytree-counts">
|
||||
<span class="pj-mtc">전체<span class="pj-mtc-num pj-mtc-total">{{ p.total }}</span></span>
|
||||
<span class="pj-mtc">진행<span class="pj-mtc-num pj-mtc-open">{{ p.open }}</span></span>
|
||||
<span class="pj-mtc">완료<span class="pj-mtc-num pj-mtc-done">{{ p.done }}</span></span>
|
||||
</span>
|
||||
</div>
|
||||
<ul class="pj-mytree-tasks">
|
||||
{% for t in p.tasks %}
|
||||
@@ -538,5 +543,5 @@
|
||||
</script>
|
||||
|
||||
<script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script>
|
||||
<script src="/static/project.js?v=20260917w" defer></script>
|
||||
<script src="/static/project.js?v=20260917x" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user