feat(project): 홈 프로젝트 카드 수정 버튼 + 완료 프로젝트 비활성/숨기기

- 카드에 수정(연필) 버튼: 이름·설명·기간·색상·완료 토글 + 삭제 (관리자)
- 프로젝트 상태에 'completed' 추가, update_project status 허용
- 완료 프로젝트: 흐리게 비활성 + 홈 목록 맨 끝으로 정렬
- '완료 N개 숨기기' 토글 버튼(localStorage 기억)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 16:34:09 +09:00
parent e36fcb1a34
commit 17f2d56231
5 changed files with 165 additions and 35 deletions
+83 -19
View File
@@ -33,9 +33,9 @@
}
// ── 색상 팔레트 렌더 ───────────────────────────────────────
function renderColorPalette(container, onPick) {
function renderColorPalette(container, onPick, initial) {
const colors = window.PJ_COLORS || ["#4573d2"];
let selected = colors[0];
let selected = (initial && colors.indexOf(initial) >= 0) ? initial : colors[0];
container.innerHTML = "";
colors.forEach(function (c) {
const b = document.createElement("button");
@@ -60,27 +60,91 @@
// 홈 페이지 — 새 프로젝트
// ════════════════════════════════════════════════════════════
function initHome() {
const grid = el("pj-project-grid");
if (!grid && !el("pj-toggle-done")) return; // 홈 아님
// ── 완료 프로젝트 숨기기 토글 (localStorage 기억) ──
const toggleBtn = el("pj-toggle-done");
if (toggleBtn) {
const KEY = "pj_hide_done";
function apply(hide) {
if (grid) grid.classList.toggle("pj-hide-done", hide);
const n = toggleBtn.dataset.count;
toggleBtn.textContent = hide ? "완료 " + n + "개 보기" : "완료 " + n + "개 숨기기";
}
apply(localStorage.getItem(KEY) === "1");
toggleBtn.addEventListener("click", function () {
const hide = !(grid && grid.classList.contains("pj-hide-done"));
try { localStorage.setItem(KEY, hide ? "1" : "0"); } catch (_) {}
apply(hide);
});
}
// ── 새 프로젝트 모달 ──
const newBtn = el("pj-new-project");
const modal = el("pj-modal-project");
if (!newBtn || !modal) return;
wireModalClose(modal);
const getColor = renderColorPalette(el("pj-f-colors"));
if (newBtn && modal) {
wireModalClose(modal);
const getColor = renderColorPalette(el("pj-f-colors"));
newBtn.addEventListener("click", function () { openModal(modal); });
el("pj-save-project").addEventListener("click", async function () {
const name = el("pj-f-name").value.trim();
if (!name) { alert("이름을 입력하세요."); return; }
try {
await api("POST", "/project/api/projects", {
name: name,
description: el("pj-f-desc").value.trim(),
start_date: el("pj-f-start").value || null,
due_date: el("pj-f-due").value || null,
color: getColor(),
});
location.reload();
} catch (e) { alert("생성 실패: " + e.message); }
});
}
newBtn.addEventListener("click", function () { openModal(modal); });
el("pj-save-project").addEventListener("click", async function () {
const name = el("pj-f-name").value.trim();
if (!name) { alert("이름을 입력하세요."); return; }
try {
await api("POST", "/project/api/projects", {
name: name,
description: el("pj-f-desc").value.trim(),
start_date: el("pj-f-start").value || null,
due_date: el("pj-f-due").value || null,
color: getColor(),
// ── 프로젝트 수정 모달 ──
const editModal = el("pj-modal-edit");
if (editModal && grid) {
wireModalClose(editModal);
let getEditColor = null;
grid.querySelectorAll(".pj-card-edit").forEach(function (btn) {
btn.addEventListener("click", function (e) {
e.preventDefault(); e.stopPropagation();
const card = btn.closest(".pj-project-card");
el("pj-e-id").value = card.dataset.id;
el("pj-e-name").value = card.dataset.name || "";
el("pj-e-desc").value = card.dataset.desc || "";
el("pj-e-start").value = card.dataset.start || "";
el("pj-e-due").value = card.dataset.due || "";
el("pj-e-done").checked = card.dataset.status === "completed";
getEditColor = renderColorPalette(el("pj-e-colors"), null, card.dataset.color);
openModal(editModal);
});
location.reload();
} catch (e) { alert("생성 실패: " + e.message); }
});
});
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;
if (!confirm("이 프로젝트를 삭제할까요? 안의 업무도 모두 삭제됩니다.")) return;
try { await api("DELETE", "/project/api/projects/" + id); location.reload(); }
catch (e) { alert("삭제 실패: " + e.message); }
});
}
}
// ════════════════════════════════════════════════════════════