diff --git a/app/modules/project/router.py b/app/modules/project/router.py
index aeb9929..43ca4cd 100644
--- a/app/modules/project/router.py
+++ b/app/modules/project/router.py
@@ -523,12 +523,18 @@ async def project_page(request: Request, project_id: int) -> HTMLResponse:
"start_date": p.get("start_date"),
"due_date": p.get("due_date"),
"created_by": p.get("created_by") or "",
+ "description": p.get("description") or "",
+ "status": p.get("status") or "active",
})
nav_projects.append({
"id": p["id"],
"name": p["name"],
"color": p.get("color") or "#4573d2",
"created_by": p.get("created_by") or "",
+ "description": p.get("description") or "",
+ "start_date": p.get("start_date"),
+ "due_date": p.get("due_date"),
+ "status": p.get("status") or "active",
"tasks": [
{"id": t["id"], "title": t["title"],
"color": t.get("project_color") or p.get("color") or "#4573d2",
diff --git a/app/modules/project/templates/project/inbox.html b/app/modules/project/templates/project/inbox.html
index bd9668b..56ca719 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 a13613a..71fb137 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 88b74de..d76ec4d 100644
--- a/app/modules/project/templates/project/project.html
+++ b/app/modules/project/templates/project/project.html
@@ -39,7 +39,11 @@
{% if nav_projects %}
{% for p in nav_projects %}
- -
+
-
+
+
+
@@ -388,6 +419,7 @@
{% endif %}
+
-
+
{% endblock %}
diff --git a/app/static/project.js b/app/static/project.js
index 4994800..f21d456 100644
--- a/app/static/project.js
+++ b/app/static/project.js
@@ -1979,16 +1979,24 @@
// ── 프로젝트 트리(사이드바) — 아사나처럼 프로젝트 이름만 나열한다(업무 목록은
// 안 보여준다 — 업무는 가운데 화면에서만 본다). 색상 원(첫 글자) 아이콘.
function treeProjLi(p) {
- const del = isAdmin
- ? '
'
- : "";
+ let acts = "";
+ if (isAdmin) {
+ acts = '
' +
+ '' +
+ '' +
+ "";
+ }
const initial = escapeHtml((p.name || "?").trim().charAt(0).toUpperCase() || "?");
return '
";
+ '
' + escapeHtml(p.name) + "" + acts + "
";
}
function wireTreeHandlers(rootUl) {
rootUl.querySelectorAll(".pj-tree-del").forEach(function (b) {
@@ -2003,6 +2011,12 @@
} catch (err) { alert("삭제 실패: " + err.message); }
});
});
+ rootUl.querySelectorAll(".pj-tree-edit").forEach(function (b) {
+ b.addEventListener("click", function (e) {
+ e.preventDefault(); e.stopPropagation();
+ openProjectEditModal(b.dataset);
+ });
+ });
}
function renderTree() {
const rootUl = el("pj-tree-root");
@@ -2024,6 +2038,52 @@
} catch (e) { alert("생성 실패: " + e.message); }
});
+ // ── 좌측 프로젝트 목록 ✎ 버튼 — 프로젝트 수정 모달(홈 화면과 동일).
+ // 트리는 renderTree() 로 매번 다시 그려지므로(라이브 새로고침 포함) 열기
+ // 로직을 재사용 가능한 함수로 두고, wireTreeHandlers 에서 매번 다시 건다.
+ const editModal = el("pj-modal-edit");
+ let getEditColor = null;
+ function openProjectEditModal(data) {
+ el("pj-e-id").value = data.id;
+ el("pj-e-name").value = data.name || "";
+ el("pj-e-desc").value = data.desc || "";
+ el("pj-e-start").value = data.start || "";
+ el("pj-e-due").value = data.due || "";
+ el("pj-e-done").checked = data.status === "completed";
+ getEditColor = renderColorPalette(el("pj-e-colors"), null, data.color);
+ openModal(editModal);
+ }
+ if (editModal) {
+ wireModalClose(editModal);
+ el("pj-save-edit").addEventListener("click", async function () {
+ const id = el("pj-e-id").value;
+ const name = el("pj-e-name").value.trim();
+ if (!name) { alert("이름을 입력하세요."); return; }
+ try {
+ await api("PUT", "/project/api/projects/" + id, {
+ name: name,
+ description: el("pj-e-desc").value.trim(),
+ start_date: el("pj-e-start").value || null,
+ due_date: el("pj-e-due").value || null,
+ color: getEditColor ? getEditColor() : undefined,
+ status: el("pj-e-done").checked ? "completed" : "active",
+ });
+ location.reload();
+ } catch (e) { alert("저장 실패: " + e.message); }
+ });
+ el("pj-delete-project").addEventListener("click", async function () {
+ const id = el("pj-e-id").value;
+ const p = projectsMeta.find(function (x) { return String(x.id) === String(id); });
+ if (p && !canDelete(p.created_by)) { alert(DELETE_DENIED); return; }
+ if (!confirm("이 프로젝트를 삭제할까요? 안의 업무도 모두 삭제됩니다.")) return;
+ try {
+ await api("DELETE", "/project/api/projects/" + id);
+ if (String(id) === String(projectId)) location.href = "/project/";
+ else location.reload();
+ } catch (e) { alert("삭제 실패: " + e.message); }
+ });
+ }
+
// ── 멤버 배정/제외 (관리자) ──
const memberModal = el("pj-modal-member");
wireModalClose(memberModal);