feat(project): 업무 모달 2단 레이아웃 + 프로젝트연결 즉시선택 + 하위업무 즉시배정
연결된 프로젝트: "+추가" 버튼 없이 프로젝트/세션 셀렉트를 바로 노출, 모달 열릴 때 목록 미리 로드. 하위업무 추가행에 담당자/마감일 입력 추가해 생성과 동시에 배정 가능. 모달을 넓히고(780px) 메타/설명(좌)·하위업무/첨부/연결/ 댓글(우) 2단 그리드로 재배치, 우측 칼럼만 자체 스크롤 시켜 전체 스크롤 최소화. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
+21
-11
@@ -1983,6 +1983,8 @@
|
||||
const listEl = el("pj-subtask-list");
|
||||
if (!listEl) return null;
|
||||
const input = el("pj-subtask-input");
|
||||
const addAssignee = el("pj-subtask-add-assignee");
|
||||
const addDue = el("pj-subtask-add-due");
|
||||
const addBtn = el("pj-subtask-add");
|
||||
let taskId = null, projectId = null, canDeleteFn = function () { return false; }, onChange = null;
|
||||
|
||||
@@ -2056,10 +2058,17 @@
|
||||
const title = (input.value || "").trim();
|
||||
if (!title) return;
|
||||
if (!taskId) { alert("먼저 업무를 저장한 뒤 하위 업무를 추가할 수 있습니다."); return; }
|
||||
const assigneeOpt = addAssignee ? addAssignee.selectedOptions[0] : null;
|
||||
try {
|
||||
const res = await api("POST", "/project/api/projects/" + projectId + "/tasks",
|
||||
{ title: title, parent_task_id: taskId });
|
||||
const res = await api("POST", "/project/api/projects/" + projectId + "/tasks", {
|
||||
title: title, parent_task_id: taskId,
|
||||
assignee_email: addAssignee ? addAssignee.value : "",
|
||||
assignee_name: assigneeOpt ? assigneeOpt.textContent : "",
|
||||
due_date: addDue ? (addDue.value || null) : null,
|
||||
});
|
||||
input.value = "";
|
||||
if (addAssignee) addAssignee.value = "";
|
||||
if (addDue) addDue.value = "";
|
||||
const optionsHtml = await membersOptionsHtml(projectId);
|
||||
const empty = listEl.querySelector(".pj-empty-sm");
|
||||
if (empty) empty.remove();
|
||||
@@ -2076,6 +2085,11 @@
|
||||
taskId = opts.taskId; projectId = opts.projectId;
|
||||
canDeleteFn = opts.canDelete || function () { return false; };
|
||||
onChange = opts.onChange || null;
|
||||
if (addAssignee && projectId) {
|
||||
membersOptionsHtml(projectId).then(function (html) {
|
||||
addAssignee.innerHTML = "<option value=''>(미지정)</option>" + html;
|
||||
});
|
||||
}
|
||||
},
|
||||
render: render,
|
||||
};
|
||||
@@ -2085,12 +2099,9 @@
|
||||
function initLinksBlock() {
|
||||
const chipsEl = el("pj-link-chips");
|
||||
if (!chipsEl) return null;
|
||||
const addBtn = el("pj-link-add-btn");
|
||||
const form = el("pj-link-form");
|
||||
const projSel = el("pj-link-project");
|
||||
const stageSel = el("pj-link-stage");
|
||||
const saveBtn = el("pj-link-save");
|
||||
const cancelBtn = el("pj-link-cancel");
|
||||
let taskId = null, homeProjectId = null, onChange = null;
|
||||
|
||||
function chipEl(link) {
|
||||
@@ -2117,8 +2128,7 @@
|
||||
links.forEach(function (l) { chipsEl.appendChild(chipEl(l)); });
|
||||
}
|
||||
|
||||
if (addBtn) addBtn.addEventListener("click", async function () {
|
||||
form.hidden = false;
|
||||
async function loadProjects() {
|
||||
projSel.innerHTML = "<option value=''>불러오는 중…</option>";
|
||||
stageSel.innerHTML = "<option value=''>프로젝트를 먼저 선택하세요</option>";
|
||||
try {
|
||||
@@ -2130,8 +2140,7 @@
|
||||
return '<option value="' + p.id + '">' + escapeHtml(p.name) + "</option>";
|
||||
}).join("");
|
||||
} catch (e) { projSel.innerHTML = "<option value=''>불러오기 실패</option>"; }
|
||||
});
|
||||
if (cancelBtn) cancelBtn.addEventListener("click", function () { form.hidden = true; });
|
||||
}
|
||||
if (projSel) projSel.addEventListener("change", async function () {
|
||||
const pid = parseInt(projSel.value, 10);
|
||||
if (!pid) { stageSel.innerHTML = "<option value=''>프로젝트를 먼저 선택하세요</option>"; return; }
|
||||
@@ -2150,7 +2159,8 @@
|
||||
try {
|
||||
await api("POST", "/project/api/tasks/" + taskId + "/links",
|
||||
{ project_id: pid, stage_id: stageSel.value ? parseInt(stageSel.value, 10) : null });
|
||||
form.hidden = true;
|
||||
projSel.value = "";
|
||||
stageSel.innerHTML = "<option value=''>프로젝트를 먼저 선택하세요</option>";
|
||||
if (onChange) onChange();
|
||||
} catch (e) { alert("추가 실패: " + e.message); }
|
||||
});
|
||||
@@ -2158,7 +2168,7 @@
|
||||
return {
|
||||
setContext: function (opts) {
|
||||
taskId = opts.taskId; homeProjectId = opts.projectId; onChange = opts.onChange || null;
|
||||
form.hidden = true;
|
||||
loadProjects();
|
||||
},
|
||||
render: render,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user