feat(project): 보드 4x2 그리드 + 세션추가 버튼 고정, 내 업무 패널 300px 고정
- .pj-board 를 flex 가로스크롤에서 4열(좁아지면 3/2/1열) 그리드로 변경, 세션이 많아지면 보드 영역 안에서만 세로 스크롤. "+ 세션 추가" 버튼을 보드 밖 pj-board-toolbar 로 옮겨 스크롤과 무관하게 항상 보이게 함. - 홈/프로젝트 상세 화면의 "내 업무" 패널 가로폭을 300px로 고정(그리드 트랙 + width 모두 지정). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916g" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916h" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script src="/static/project.js?v=20260916h" defer></script>
|
||||
<script src="/static/project.js?v=20260916i" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916g" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916h" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -376,5 +376,5 @@
|
||||
<script>
|
||||
window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }};
|
||||
</script>
|
||||
<script src="/static/project.js?v=20260916h" defer></script>
|
||||
<script src="/static/project.js?v=20260916i" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916g" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260916h" />
|
||||
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host -->
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
<!-- 타임라인 vis-timeline — self-host -->
|
||||
@@ -138,8 +138,13 @@
|
||||
<div class="pj-view" data-view="timeline" hidden>
|
||||
<div id="pj-timeline"></div>
|
||||
</div>
|
||||
<!-- 보드(칸반=세션) — 아사나처럼 프로젝트에 들어오면 기본으로 보이는 화면 -->
|
||||
<!-- 보드(칸반=세션) — 아사나처럼 프로젝트에 들어오면 기본으로 보이는 화면.
|
||||
4x2 그리드로 배치하고, 세션이 늘어나 보드 안에서 스크롤이 생겨도
|
||||
"+ 세션 추가" 버튼은 스크롤 영역 밖(고정 위치)에 있어 항상 보인다. -->
|
||||
<div class="pj-view" data-view="board">
|
||||
<div class="pj-board-toolbar">
|
||||
<button type="button" class="pj-add-stage" id="pj-board-addstage" hidden>+ 세션 추가</button>
|
||||
</div>
|
||||
<div class="pj-board" id="pj-board"></div>
|
||||
</div>
|
||||
<!-- 리스트 -->
|
||||
@@ -374,5 +379,5 @@
|
||||
</script>
|
||||
|
||||
<script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script>
|
||||
<script src="/static/project.js?v=20260916h" defer></script>
|
||||
<script src="/static/project.js?v=20260916i" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
+17
-8
@@ -44,7 +44,7 @@
|
||||
.pj-chip-sm { padding: 1px 6px; font-size: 10px; }
|
||||
|
||||
/* ── 홈 ── */
|
||||
.pj-home { display: grid; grid-template-columns: 1fr 320px; gap: 28px; align-items: start; }
|
||||
.pj-home { display: grid; grid-template-columns: 1fr 300px; gap: 28px; align-items: start; }
|
||||
@media (max-width: 980px) { .pj-home { grid-template-columns: 1fr; } }
|
||||
|
||||
.pj-project-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 14px; align-items: start; }
|
||||
@@ -116,7 +116,7 @@
|
||||
.pj-section-head-done { margin-top: 22px; }
|
||||
.pj-section-head-done h2 { font-size: 14px; color: #6b7280; }
|
||||
|
||||
.pj-home-side { border-left: 1px solid #eef0f2; padding-left: 24px; }
|
||||
.pj-home-side { width: 300px; border-left: 1px solid #eef0f2; padding-left: 24px; }
|
||||
@media (max-width: 980px) { .pj-home-side { border-left: none; padding-left: 0; } }
|
||||
.pj-mytask-list { list-style: none; margin: 0; padding: 0; }
|
||||
.pj-mytask a { display: flex; align-items: center; gap: 8px; padding: 9px 6px; border-radius: 8px; text-decoration: none; color: inherit; }
|
||||
@@ -130,9 +130,9 @@
|
||||
가운데 칸에 min-width:0 이 없으면 보드에 세션이 많이 늘어날 때 grid 트랙
|
||||
자체가 넓어져 페이지 전체가 가로 스크롤된다(overflow-x:auto 는 .pj-board
|
||||
안에서만 작동해야 한다) — 반드시 필요한 규칙. */
|
||||
.pj-layout { display: grid; grid-template-columns: 230px 1fr 260px; gap: 22px; align-items: start; }
|
||||
.pj-layout { display: grid; grid-template-columns: 230px 1fr 300px; gap: 22px; align-items: start; }
|
||||
.pj-main { min-width: 0; }
|
||||
.pj-project-mytasks { min-width: 0; }
|
||||
.pj-project-mytasks { min-width: 0; width: 300px; }
|
||||
@media (max-width: 1280px) {
|
||||
.pj-layout { grid-template-columns: 230px 1fr; }
|
||||
.pj-project-mytasks { display: none; }
|
||||
@@ -191,11 +191,21 @@
|
||||
transform-origin: top center;
|
||||
}
|
||||
|
||||
/* ── 보드(칸반) ── */
|
||||
.pj-board { display: flex; gap: 16px; overflow-x: auto; padding-bottom: 8px; align-items: flex-start; }
|
||||
/* ── 보드(칸반) — 4열 x 2행 그리드. 세션이 8개를 넘으면 이 영역 안에서만
|
||||
세로 스크롤되고, "+ 세션 추가" 버튼은 이 영역 밖(pj-board-toolbar)에
|
||||
있어 스크롤과 무관하게 항상 보인다. ── */
|
||||
.pj-board-toolbar { display: flex; justify-content: flex-end; margin-bottom: 10px; }
|
||||
.pj-board-toolbar .pj-add-stage { width: auto; padding: 8px 18px; }
|
||||
.pj-board {
|
||||
display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); grid-auto-rows: min-content;
|
||||
gap: 16px; align-content: start; max-height: 74vh; overflow-y: auto; padding-right: 4px; padding-bottom: 8px;
|
||||
}
|
||||
@media (max-width: 1400px) { .pj-board { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
|
||||
@media (max-width: 1100px) { .pj-board { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
|
||||
@media (max-width: 700px) { .pj-board { grid-template-columns: 1fr; } }
|
||||
/* 세션(보드 컬럼) — 배경이 페이지와 거의 같으면 구분이 안 되므로(#f6f5fb ≈
|
||||
흰 배경) 뚜렷한 회색 톤 + 테두리로 아사나처럼 컬럼 경계를 분명히 준다. */
|
||||
.pj-col { flex: 0 0 280px; background: #eef0f4; border: 1px solid #e2e4ea; border-radius: 14px; padding: 12px; }
|
||||
.pj-col { background: #eef0f4; border: 1px solid #e2e4ea; border-radius: 14px; padding: 12px; min-width: 0; }
|
||||
.pj-col-head {
|
||||
display: flex; align-items: center; gap: 6px; font-weight: 700; font-size: 13px;
|
||||
padding: 2px 6px 12px; margin-bottom: 4px; color: var(--pj-ink);
|
||||
@@ -476,7 +486,6 @@
|
||||
.pj-col:hover .pj-col-ctrls { display: inline-flex; }
|
||||
.pj-col-ctrls .pj-icon-btn { width: 20px; height: 20px; font-size: 13px; }
|
||||
.pj-icon-btn[disabled] { opacity: .3; cursor: default; }
|
||||
.pj-col-add { background: transparent; flex: 0 0 180px; }
|
||||
.pj-add-stage { width: 100%; padding: 10px; border: 1px dashed #c9cdd3; border-radius: 12px; background: #fff; color: #6b7280; font-size: 13px; font-weight: 600; cursor: pointer; }
|
||||
.pj-add-stage:hover { border-color: #1e1f21; color: #1e1f21; }
|
||||
|
||||
|
||||
+19
-17
@@ -1085,10 +1085,27 @@
|
||||
// 완료토글·삭제·추가·드래그 재정렬).
|
||||
// scope==="all" → 여러 프로젝트를 세션 '이름' 기준으로 합쳐 보여준다(기존
|
||||
// 동작 그대로 — 세션 하나를 특정할 수 없어 관리 컨트롤은 없다).
|
||||
// "+ 세션 추가" 버튼은 보드(스크롤 영역) 밖 고정 위치에 있어 세션이
|
||||
// 늘어나 스크롤이 생겨도 항상 보인다 — 한 번만 wiring, 표시 여부만 매
|
||||
// 렌더마다 토글.
|
||||
const boardAddStageBtn = el("pj-board-addstage");
|
||||
if (boardAddStageBtn) {
|
||||
boardAddStageBtn.addEventListener("click", async function () {
|
||||
const name = prompt("새 세션 이름");
|
||||
if (!name || !name.trim()) return;
|
||||
try {
|
||||
await api("POST", "/project/api/projects/" + projectId + "/stages", { name: name.trim() });
|
||||
await reloadStagesForCurrentProject();
|
||||
renderBoard();
|
||||
} catch (e) { alert("추가 실패: " + e.message); }
|
||||
});
|
||||
}
|
||||
|
||||
function renderBoard() {
|
||||
const board = el("pj-board");
|
||||
board.innerHTML = "";
|
||||
const vis = visibleTasks();
|
||||
if (boardAddStageBtn) boardAddStageBtn.hidden = !(viewScope === "project" && canManage);
|
||||
if (viewScope === "project") renderBoardByStage(board, vis);
|
||||
else renderBoardByName(board, vis);
|
||||
}
|
||||
@@ -1153,8 +1170,8 @@
|
||||
if (!list.length && !vis.length) {
|
||||
const empty = document.createElement("div");
|
||||
empty.className = "pj-empty";
|
||||
empty.style.flex = "1 1 auto";
|
||||
empty.textContent = "세션이 없습니다. 아래 \"+ 세션 추가\"로 만들어 보세요.";
|
||||
empty.style.gridColumn = "1 / -1";
|
||||
empty.textContent = "세션이 없습니다. 위 \"+ 세션 추가\"로 만들어 보세요.";
|
||||
board.appendChild(empty);
|
||||
}
|
||||
list.forEach(function (s) {
|
||||
@@ -1202,21 +1219,6 @@
|
||||
board.appendChild(col);
|
||||
}
|
||||
|
||||
if (canManage) {
|
||||
const addCol = document.createElement("div");
|
||||
addCol.className = "pj-col pj-col-add";
|
||||
addCol.innerHTML = '<button type="button" class="pj-add-stage">+ 세션 추가</button>';
|
||||
addCol.querySelector(".pj-add-stage").addEventListener("click", async function () {
|
||||
const name = prompt("새 세션 이름");
|
||||
if (!name || !name.trim()) return;
|
||||
try {
|
||||
await api("POST", "/project/api/projects/" + projectId + "/stages", { name: name.trim() });
|
||||
await reloadStagesForCurrentProject();
|
||||
renderBoard();
|
||||
} catch (e) { alert("추가 실패: " + e.message); }
|
||||
});
|
||||
board.appendChild(addCol);
|
||||
}
|
||||
}
|
||||
|
||||
async function reloadStagesForCurrentProject() {
|
||||
|
||||
Reference in New Issue
Block a user