feat(project): 담당자는 아이디만 표기 + 구글 머티리얼 아이콘

담당자 표시를 이메일 아이디(@ 앞)로 통일. 아바타를 letter 원형에서
구글 Material Symbols account_circle 아이콘으로 교체(멤버 목록·보드 카드·
모달 선택지·리스트 뷰).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-25 12:04:33 +09:00
parent 78f212fffd
commit d60ae9a210
3 changed files with 22 additions and 10 deletions
@@ -1,7 +1,9 @@
{% extends "erp_base.html" %}
{% block head_extra %}
<link rel="stylesheet" href="/static/project.css?v=20260625b" />
<link rel="stylesheet" href="/static/project.css?v=20260625c" />
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) -->
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:opsz,wght,FILL,GRAD@24,400,0,0" />
<!-- 달력은 휴가 모듈과 동일한 서버 렌더 그리드(project.css). 타임라인만 vis(CDN, 추후 self-host) -->
<link href="https://cdn.jsdelivr.net/npm/vis-timeline@7.7.3/styles/vis-timeline-graph2d.min.css" rel="stylesheet" />
{% endblock %}
@@ -49,8 +51,8 @@
<ul class="pj-member-list" id="pj-member-list">
{% for m in members %}
<li>
<span class="pj-avatar">{{ (m.user_name or m.user_email)[0] | upper }}</span>
<span class="pj-member-name">{{ m.user_name or m.user_email }}</span>
<span class="material-symbols-outlined pj-gicon">account_circle</span>
<span class="pj-member-name">{{ m.user_email.split('@')[0] }}</span>
{% if m.role == 'manager' %}<span class="pj-chip pj-chip-sm">관리</span>{% endif %}
{% if is_admin %}
<button type="button" class="pj-icon-btn pj-del-member" data-email="{{ m.user_email }}" title="제외">×</button>
@@ -154,8 +156,8 @@
<select id="pj-t-assignee">
<option value="">(미지정)</option>
{% for m in members %}
<option value="{{ m.user_email }}" data-name="{{ m.user_name or m.user_email }}">
{{ m.user_name or m.user_email }}
<option value="{{ m.user_email }}" data-name="{{ m.user_email.split('@')[0] }}">
{{ m.user_email.split('@')[0] }}
</option>
{% endfor %}
</select>
@@ -200,5 +202,5 @@
</script>
<script src="https://cdn.jsdelivr.net/npm/vis-timeline@7.7.3/standalone/umd/vis-timeline-graph2d.min.js"></script>
<script src="/static/project.js?v=20260625b" defer></script>
<script src="/static/project.js?v=20260625c" defer></script>
{% endblock %}
+5 -2
View File
@@ -67,8 +67,11 @@
.pj-sub-dot { width: 8px; height: 8px; border-radius: 50%; }
.pj-member-name { font-size: 13px; flex: 1; }
.pj-avatar { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; border-radius: 50%; background: #d6e0f5; color: #2b4a85; font-size: 11px; font-weight: 700; flex: 0 0 auto; }
.pj-avatar-sm { width: 20px; height: 20px; font-size: 10px; }
/* 구글 머티리얼 심볼 아이콘(담당자) */
.material-symbols-outlined { font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24; line-height: 1; vertical-align: middle; }
.pj-gicon { font-size: 24px; color: #5f6368; flex: 0 0 auto; }
.pj-gicon-sm { font-size: 20px; }
.pj-assignee { display: inline-flex; align-items: center; gap: 3px; font-size: 12px; color: #5f6368; }
/* ── 툴바 + 탭 ── */
.pj-toolbar { display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px; }
+9 -2
View File
@@ -204,7 +204,10 @@
(t.priority && t.priority !== "normal"
? '<span class="pj-pri pj-pri-' + t.priority + '">' + (priorityLabels[t.priority] || t.priority) + "</span>"
: "") +
(t.assignee_email ? '<span class="pj-avatar pj-avatar-sm">' + escapeHtml((t.assignee_name || t.assignee_email)[0].toUpperCase()) + "</span>" : "") +
(t.assignee_email
? '<span class="pj-assignee"><span class="material-symbols-outlined pj-gicon pj-gicon-sm">account_circle</span>' +
escapeHtml(idOf(t.assignee_email)) + "</span>"
: "") +
(t.due_date ? '<span class="pj-card-due">' + t.due_date + "</span>" : "") +
"</div>";
card.addEventListener("click", function () { if (canManage) openTaskModal(t); });
@@ -244,7 +247,7 @@
if (t.completed_at) tr.className = "is-done";
tr.innerHTML =
"<td>" + escapeHtml(t.title) + "</td>" +
"<td>" + escapeHtml(t.assignee_name || t.assignee_email || "-") + "</td>" +
"<td>" + (t.assignee_email ? escapeHtml(idOf(t.assignee_email)) : "-") + "</td>" +
"<td>" + escapeHtml(t.stage_name || "-") + "</td>" +
"<td>" + (priorityLabels[t.priority] || t.priority || "-") + "</td>" +
"<td>" + (t.start_date || "-") + "</td>" +
@@ -367,6 +370,10 @@
wireCalendar();
}
function idOf(email) {
return String(email == null ? "" : email).split("@")[0];
}
function escapeHtml(s) {
return String(s == null ? "" : s)
.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")