style(project): 업무 편집 모달 아사나식 레이아웃 (제목/메타/설명 순서 재배치)

제목 입력을 큰 무테 필드로, 담당자/세션/우선순위/일정을 컴팩트한 메타 칩
행으로 묶고, 하위업무→첨부→연결프로젝트→댓글 순서로 재배열. 관련 CSS
토큰(.pj-t-title-input, .pj-meta-row/field/label, .pj-modal-kicker,
.pj-extra-label) 추가.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-15 20:30:13 +09:00
parent 9c07aefb29
commit 1eae308b9e
4 changed files with 121 additions and 65 deletions
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260916b" /> <link rel="stylesheet" href="/static/project.css?v=20260916c" />
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" /> <link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
{% endblock %} {% endblock %}
@@ -45,5 +45,5 @@
{% endif %} {% endif %}
</div> </div>
<script src="/static/project.js?v=20260916d" defer></script> <script src="/static/project.js?v=20260916e" defer></script>
{% endblock %} {% endblock %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260916b" /> <link rel="stylesheet" href="/static/project.css?v=20260916c" />
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" /> <link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
{% endblock %} {% endblock %}
@@ -240,34 +240,41 @@
<!-- 업무 편집 모달 — 카드/내 업무에서 클릭 시 페이지 이동 없이 팝업으로 편집 --> <!-- 업무 편집 모달 — 카드/내 업무에서 클릭 시 페이지 이동 없이 팝업으로 편집 -->
<div class="pj-modal" id="pj-modal-task" hidden> <div class="pj-modal" id="pj-modal-task" hidden>
<div class="pj-modal-card"> <div class="pj-modal-card">
<h3 id="pj-task-modal-title">업무 편집</h3> <!-- 아사나처럼 큰 제목은 아래 무테 입력칸이 대신한다 — 이 표시는 작은
모드 안내(새 업무/업무 편집)만 한다. -->
<h3 id="pj-task-modal-title" class="pj-modal-kicker">업무 편집</h3>
<input type="hidden" id="pj-t-id" /> <input type="hidden" id="pj-t-id" />
<input type="hidden" id="pj-t-project-id" /> <input type="hidden" id="pj-t-project-id" />
<label>제목<input type="text" id="pj-t-title" placeholder="업무 제목" /></label> <input type="text" id="pj-t-title" class="pj-t-title-input" placeholder="업무 제목" />
<label>설명</label>
<div class="pj-desc-toolbar"> <!-- 메타 정보 — 담당자·세션·우선순위·일정을 작은 칩 형태로 한 줄에 -->
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label> <div class="pj-meta-row">
<span class="pj-muted-sm" id="pj-desc-hint"></span> <div class="pj-meta-field">
</div> <span class="pj-meta-label">담당자</span>
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
<div class="pj-form-row">
<label>담당자
<select id="pj-t-assignee"><option value="">불러오는 중…</option></select> <select id="pj-t-assignee"><option value="">불러오는 중…</option></select>
</label> </div>
<label>세션 <div class="pj-meta-field">
<span class="pj-meta-label">세션</span>
<select id="pj-t-stage"><option value="">불러오는 중…</option></select> <select id="pj-t-stage"><option value="">불러오는 중…</option></select>
</label> </div>
</div> <div class="pj-meta-field">
<div class="pj-form-row"> <span class="pj-meta-label">우선순위</span>
<label>우선순위
<select id="pj-t-priority"> <select id="pj-t-priority">
<option value="low">낮음</option> <option value="low">낮음</option>
<option value="normal" selected>보통</option> <option value="normal" selected>보통</option>
<option value="high">높음</option> <option value="high">높음</option>
</select> </select>
</label> </div>
<label>시작일<input type="date" id="pj-t-start" /></label> </div>
<label>마감일<input type="date" id="pj-t-due" /></label> <div class="pj-meta-row">
<div class="pj-meta-field">
<span class="pj-meta-label">시작일</span>
<input type="date" id="pj-t-start" />
</div>
<div class="pj-meta-field">
<span class="pj-meta-label">마감일</span>
<input type="date" id="pj-t-due" />
</div>
</div> </div>
<label class="pj-time-toggle"> <label class="pj-time-toggle">
<input type="checkbox" id="pj-t-usetime" /> 시간 지정 (체크 안 하면 종일) <input type="checkbox" id="pj-t-usetime" /> 시간 지정 (체크 안 하면 종일)
@@ -276,6 +283,15 @@
<label>시작 시간<input type="time" id="pj-t-stime" disabled /></label> <label>시작 시간<input type="time" id="pj-t-stime" disabled /></label>
<label>마감 시간<input type="time" id="pj-t-dtime" disabled /></label> <label>마감 시간<input type="time" id="pj-t-dtime" disabled /></label>
</div> </div>
<!-- 설명 -->
<div class="pj-desc-toolbar">
<span class="pj-extra-label">설명</span>
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label>
<span class="pj-muted-sm" id="pj-desc-hint"></span>
</div>
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
<div class="pj-task-extra" id="pj-subtask-block"> <div class="pj-task-extra" id="pj-subtask-block">
<div class="pj-extra-head"> <div class="pj-extra-head">
<span class="material-symbols-outlined pj-gicon pj-gicon-sm">checklist</span> 하위 업무 <span class="material-symbols-outlined pj-gicon pj-gicon-sm">checklist</span> 하위 업무
@@ -286,6 +302,15 @@
<button type="button" class="pj-btn pj-btn-primary" id="pj-subtask-add">추가</button> <button type="button" class="pj-btn pj-btn-primary" id="pj-subtask-add">추가</button>
</div> </div>
</div> </div>
<div class="pj-task-extra" id="pj-attach-block">
<div class="pj-extra-head">
<span class="material-symbols-outlined pj-gicon pj-gicon-sm">attach_file</span> 첨부파일
<label class="pj-upload-btn">
파일 추가<input type="file" id="pj-attach-input" hidden />
</label>
</div>
<ul class="pj-attach-list" id="pj-attach-list"></ul>
</div>
<div class="pj-task-extra" id="pj-links-block"> <div class="pj-task-extra" id="pj-links-block">
<div class="pj-extra-head"> <div class="pj-extra-head">
<span class="material-symbols-outlined pj-gicon pj-gicon-sm">link</span> 연결된 프로젝트 <span class="material-symbols-outlined pj-gicon pj-gicon-sm">link</span> 연결된 프로젝트
@@ -299,15 +324,7 @@
<button type="button" class="pj-btn" id="pj-link-cancel">취소</button> <button type="button" class="pj-btn" id="pj-link-cancel">취소</button>
</div> </div>
</div> </div>
<div class="pj-task-extra" id="pj-attach-block"> <!-- 댓글(활동) — 아사나처럼 맨 아래 -->
<div class="pj-extra-head">
<span class="material-symbols-outlined pj-gicon pj-gicon-sm">attach_file</span> 첨부파일
<label class="pj-upload-btn">
파일 추가<input type="file" id="pj-attach-input" hidden />
</label>
</div>
<ul class="pj-attach-list" id="pj-attach-list"></ul>
</div>
<div class="pj-task-extra" id="pj-comment-block"> <div class="pj-task-extra" id="pj-comment-block">
<div class="pj-extra-head"> <div class="pj-extra-head">
<span class="pj-cmt-emoji">💬</span> 댓글 <span class="pj-cmt-emoji">💬</span> 댓글
@@ -349,5 +366,5 @@
<script> <script>
window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }}; window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }};
</script> </script>
<script src="/static/project.js?v=20260916d" defer></script> <script src="/static/project.js?v=20260916e" defer></script>
{% endblock %} {% endblock %}
@@ -1,7 +1,7 @@
{% extends "erp_base.html" %} {% extends "erp_base.html" %}
{% block head_extra %} {% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260916b" /> <link rel="stylesheet" href="/static/project.css?v=20260916c" />
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host --> <!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host -->
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" /> <link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
<!-- 타임라인 vis-timeline — self-host --> <!-- 타임라인 vis-timeline — self-host -->
@@ -158,17 +158,16 @@
<!-- 업무 모달 --> <!-- 업무 모달 -->
<div class="pj-modal" id="pj-modal-task" hidden> <div class="pj-modal" id="pj-modal-task" hidden>
<div class="pj-modal-card"> <div class="pj-modal-card">
<h3 id="pj-task-modal-title">새 업무</h3> <!-- 아사나처럼 큰 제목은 아래 무테 입력칸이 대신한다 — 이 표시는 작은
모드 안내(새 업무/업무 편집)만 한다. -->
<h3 id="pj-task-modal-title" class="pj-modal-kicker">새 업무</h3>
<input type="hidden" id="pj-t-id" /> <input type="hidden" id="pj-t-id" />
<label>제목<input type="text" id="pj-t-title" placeholder="업무 제목" /></label> <input type="text" id="pj-t-title" class="pj-t-title-input" placeholder="업무 제목" />
<label>설명</label>
<div class="pj-desc-toolbar"> <!-- 메타 정보 — 담당자·세션·우선순위·일정을 작은 칩 형태로 한 줄에 -->
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label> <div class="pj-meta-row">
<span class="pj-muted-sm" id="pj-desc-hint"></span> <div class="pj-meta-field">
</div> <span class="pj-meta-label">담당자</span>
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
<div class="pj-form-row">
<label>담당자
<select id="pj-t-assignee"> <select id="pj-t-assignee">
<option value="">(미지정)</option> <option value="">(미지정)</option>
{% for m in members %} {% for m in members %}
@@ -177,25 +176,33 @@
</option> </option>
{% endfor %} {% endfor %}
</select> </select>
</label> </div>
<label>세션 <div class="pj-meta-field">
<span class="pj-meta-label">세션</span>
<select id="pj-t-stage"> <select id="pj-t-stage">
{% for s in stages %} {% for s in stages %}
<option value="{{ s.id }}">{{ s.name }}</option> <option value="{{ s.id }}">{{ s.name }}</option>
{% endfor %} {% endfor %}
</select> </select>
</label> </div>
</div> <div class="pj-meta-field">
<div class="pj-form-row"> <span class="pj-meta-label">우선순위</span>
<label>우선순위
<select id="pj-t-priority"> <select id="pj-t-priority">
<option value="low">낮음</option> <option value="low">낮음</option>
<option value="normal" selected>보통</option> <option value="normal" selected>보통</option>
<option value="high">높음</option> <option value="high">높음</option>
</select> </select>
</label> </div>
<label>시작일<input type="date" id="pj-t-start" /></label> </div>
<label>마감일<input type="date" id="pj-t-due" /></label> <div class="pj-meta-row">
<div class="pj-meta-field">
<span class="pj-meta-label">시작일</span>
<input type="date" id="pj-t-start" />
</div>
<div class="pj-meta-field">
<span class="pj-meta-label">마감일</span>
<input type="date" id="pj-t-due" />
</div>
</div> </div>
<label class="pj-time-toggle"> <label class="pj-time-toggle">
<input type="checkbox" id="pj-t-usetime" /> 시간 지정 (체크 안 하면 종일) <input type="checkbox" id="pj-t-usetime" /> 시간 지정 (체크 안 하면 종일)
@@ -205,6 +212,14 @@
<label>마감 시간<input type="time" id="pj-t-dtime" disabled /></label> <label>마감 시간<input type="time" id="pj-t-dtime" disabled /></label>
</div> </div>
<!-- 설명 -->
<div class="pj-desc-toolbar">
<span class="pj-extra-label">설명</span>
<label class="pj-upload-btn">이미지 추가<input type="file" id="pj-desc-img-input" accept="image/*" hidden /></label>
<span class="pj-muted-sm" id="pj-desc-hint"></span>
</div>
<div class="pj-desc-editor" id="pj-t-desc" contenteditable="true" data-placeholder="설명을 입력하세요… (이미지는 붙여넣기 또는 위 버튼으로 추가)"></div>
<!-- 하위 업무 (기존 업무 편집 시에만) --> <!-- 하위 업무 (기존 업무 편집 시에만) -->
<div class="pj-task-extra" id="pj-subtask-block" hidden> <div class="pj-task-extra" id="pj-subtask-block" hidden>
<div class="pj-extra-head"> <div class="pj-extra-head">
@@ -217,6 +232,17 @@
</div> </div>
</div> </div>
<!-- 첨부파일 (기존 업무 편집 시에만) -->
<div class="pj-task-extra" id="pj-attach-block" hidden>
<div class="pj-extra-head">
<span class="material-symbols-outlined pj-gicon pj-gicon-sm">attach_file</span> 첨부파일
<label class="pj-upload-btn">
파일 추가<input type="file" id="pj-attach-input" hidden />
</label>
</div>
<ul class="pj-attach-list" id="pj-attach-list"></ul>
</div>
<!-- 연결된 프로젝트 — 멀티호밍(아사나식). 기존 업무 편집 시에만 --> <!-- 연결된 프로젝트 — 멀티호밍(아사나식). 기존 업무 편집 시에만 -->
<div class="pj-task-extra" id="pj-links-block" hidden> <div class="pj-task-extra" id="pj-links-block" hidden>
<div class="pj-extra-head"> <div class="pj-extra-head">
@@ -232,18 +258,7 @@
</div> </div>
</div> </div>
<!-- 첨부파일 (기존 업무 편집 시에만) --> <!-- 댓글(활동) — 아사나처럼 맨 아래. 기존 업무 편집 시에만 -->
<div class="pj-task-extra" id="pj-attach-block" hidden>
<div class="pj-extra-head">
<span class="material-symbols-outlined pj-gicon pj-gicon-sm">attach_file</span> 첨부파일
<label class="pj-upload-btn">
파일 추가<input type="file" id="pj-attach-input" hidden />
</label>
</div>
<ul class="pj-attach-list" id="pj-attach-list"></ul>
</div>
<!-- 댓글 (기존 업무 편집 시에만) -->
<div class="pj-task-extra" id="pj-comment-block" hidden> <div class="pj-task-extra" id="pj-comment-block" hidden>
<div class="pj-extra-head"> <div class="pj-extra-head">
<span class="pj-cmt-emoji">💬</span> 댓글 <span class="pj-cmt-emoji">💬</span> 댓글
@@ -305,5 +320,5 @@
</script> </script>
<script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script> <script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script>
<script src="/static/project.js?v=20260916d" defer></script> <script src="/static/project.js?v=20260916e" defer></script>
{% endblock %} {% endblock %}
+24
View File
@@ -312,6 +312,30 @@
.pj-modal-card { background: #fff; border-radius: 16px; padding: 22px; width: min(480px, 92vw); box-shadow: 0 20px 60px rgba(0,0,0,.25); max-height: 90vh; overflow-y: auto; } .pj-modal-card { background: #fff; border-radius: 16px; padding: 22px; width: min(480px, 92vw); box-shadow: 0 20px 60px rgba(0,0,0,.25); max-height: 90vh; overflow-y: auto; }
.pj-modal-card h3 { margin: 0 0 16px; font-size: 17px; } .pj-modal-card h3 { margin: 0 0 16px; font-size: 17px; }
.pj-modal-hint { margin: -6px 0 14px; font-size: 12px; color: #6b7280; } .pj-modal-hint { margin: -6px 0 14px; font-size: 12px; color: #6b7280; }
/* 업무 편집 팝업 — 아사나식 레이아웃(큰 무테 제목 + 작은 메타 칩 한 줄). */
.pj-modal-kicker { margin: 0 0 2px; font-size: 11px; font-weight: 700; color: var(--pj-ink-soft);
text-transform: uppercase; letter-spacing: .04em; }
.pj-t-title-input {
display: block; width: 100%; border: none !important; background: transparent !important;
font-size: 19px !important; font-weight: 700; padding: 4px 4px !important; margin: 0 0 14px !important;
color: var(--pj-ink); box-sizing: border-box;
}
.pj-t-title-input:focus { outline: none; background: #f6f7f8 !important; border-radius: 8px; }
.pj-meta-row { display: flex; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
.pj-meta-field { flex: 1 1 110px; min-width: 100px; }
.pj-meta-label { display: block; font-size: 10.5px; font-weight: 700; color: var(--pj-ink-soft);
text-transform: uppercase; letter-spacing: .03em; margin-bottom: 4px; }
.pj-meta-field select, .pj-meta-field input {
display: block; width: 100%; margin: 0 !important; padding: 6px 8px !important;
border: 1px solid var(--pj-border) !important; border-radius: 8px !important;
font-size: 12.5px !important; font-family: inherit; background: #fafafa !important; box-sizing: border-box;
}
.pj-meta-field select:hover, .pj-meta-field input:hover,
.pj-meta-field select:focus, .pj-meta-field input:focus {
border-color: var(--pj-accent) !important; background: var(--pj-accent-tint) !important; outline: none;
}
.pj-extra-label { font-size: 12.5px; font-weight: 700; color: var(--pj-ink-soft); margin-right: auto; }
.pj-modal-card label { display: block; font-size: 12.5px; font-weight: 600; color: #525860; margin-bottom: 12px; } .pj-modal-card label { display: block; font-size: 12.5px; font-weight: 600; color: #525860; margin-bottom: 12px; }
.pj-modal-card input[type=text], .pj-modal-card input[type=date], .pj-modal-card textarea, .pj-modal-card select { .pj-modal-card input[type=text], .pj-modal-card input[type=date], .pj-modal-card textarea, .pj-modal-card select {
display: block; width: 100%; margin-top: 5px; padding: 8px 10px; border: 1px solid #d8dce2; border-radius: 8px; font-size: 13.5px; font-family: inherit; box-sizing: border-box; display: block; width: 100%; margin-top: 5px; padding: 8px 10px; border: 1px solid #d8dce2; border-radius: 8px; font-size: 13.5px; font-family: inherit; box-sizing: border-box;