feat(project): 아사나식 프로젝트/서브프로젝트 트리 사이드바
좌측 사이드바를 직속 서브 평면 목록 → 전체 프로젝트 재귀 트리로 교체. - 접기/펼치기 캐럿(localStorage 로 펼침 상태 저장, 활성 프로젝트 상위 자동 펼침) - 프로젝트 색상 아이콘, 들여쓰기(depth), 활성 프로젝트 강조 - hover 시 서브추가(+)/삭제(×) 액션(관리자·멤버, 서버 권한 검증) - 상단 '새 프로젝트'(관리자). Material Symbols 아이콘(self-host) 사용. Co-Authored-By: Claude Opus 4.8 <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=20260625h" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260625i" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -45,5 +45,5 @@
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<script src="/static/project.js?v=20260625h" defer></script>
|
||||
<script src="/static/project.js?v=20260625i" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260625h" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260625i" />
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
{% endblock %}
|
||||
|
||||
@@ -101,5 +101,5 @@
|
||||
<script>
|
||||
window.PJ_COLORS = {{ ["#4573d2","#37a3a3","#62a420","#e8a33d","#e8384f","#aa62e3","#f06a6a","#5a6772"] | tojson }};
|
||||
</script>
|
||||
<script src="/static/project.js?v=20260625h" defer></script>
|
||||
<script src="/static/project.js?v=20260625i" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,13 +1,49 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260625h" />
|
||||
<link rel="stylesheet" href="/static/project.css?v=20260625i" />
|
||||
<!-- 구글 머티리얼 심볼(담당자 아이콘 등) — self-host -->
|
||||
<link rel="stylesheet" href="/static/vendor/material-symbols/material-symbols.css" />
|
||||
<!-- 타임라인 vis-timeline — self-host -->
|
||||
<link href="/static/vendor/vis-timeline/vis-timeline-graph2d.min.css" rel="stylesheet" />
|
||||
{% endblock %}
|
||||
|
||||
{# ── 아사나식 프로젝트/서브프로젝트 재귀 트리 ── #}
|
||||
{% macro proj_tree(nodes, active_id, can_edit, depth) %}
|
||||
<ul class="pj-tree" {% if depth > 0 %}data-sub="1"{% endif %}>
|
||||
{% for n in nodes %}
|
||||
<li class="pj-tree-li" data-id="{{ n.id }}">
|
||||
<div class="pj-tree-row {% if n.id == active_id %}is-active{% endif %}" style="--pj-indent: {{ depth }}">
|
||||
{% if n.children %}
|
||||
<button type="button" class="pj-tree-caret" aria-expanded="false" title="펼치기/접기">
|
||||
<span class="material-symbols-outlined">chevron_right</span>
|
||||
</button>
|
||||
{% else %}
|
||||
<span class="pj-tree-caret-spacer"></span>
|
||||
{% endif %}
|
||||
<a class="pj-tree-link" href="/project/p/{{ n.id }}">
|
||||
<span class="pj-tree-icon material-symbols-outlined" style="color: {{ n.color }}">
|
||||
{% if n.children %}folder{% else %}check_circle{% endif %}
|
||||
</span>
|
||||
<span class="pj-tree-name">{{ n.name }}</span>
|
||||
</a>
|
||||
{% if can_edit %}
|
||||
<span class="pj-tree-acts">
|
||||
<button type="button" class="pj-icon-btn pj-tree-addsub" data-id="{{ n.id }}" title="서브프로젝트 추가">+</button>
|
||||
<button type="button" class="pj-icon-btn pj-tree-del" data-id="{{ n.id }}" data-name="{{ n.name }}" title="삭제">×</button>
|
||||
</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% if n.children %}
|
||||
<div class="pj-tree-children" hidden>
|
||||
{{ proj_tree(n.children, active_id, can_edit, depth + 1) }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endmacro %}
|
||||
|
||||
{% block content %}
|
||||
<div class="pj-wrap pj-project-view"
|
||||
data-project-id="{{ project.id }}"
|
||||
@@ -16,29 +52,20 @@
|
||||
data-me="{{ user.email }}">
|
||||
|
||||
<div class="pj-layout">
|
||||
<!-- ── 좌측: 서브프로젝트 + 멤버 ── -->
|
||||
<!-- ── 좌측: 프로젝트 트리 + 멤버 ── -->
|
||||
<aside class="pj-side">
|
||||
<div class="pj-side-block">
|
||||
<div class="pj-side-head">
|
||||
<span>서브프로젝트</span>
|
||||
{% if can_manage %}
|
||||
<button type="button" class="pj-icon-btn" id="pj-new-sub" title="서브프로젝트 추가">+</button>
|
||||
<span>프로젝트</span>
|
||||
{% if is_admin %}
|
||||
<button type="button" class="pj-icon-btn" id="pj-new-project-side" title="새 프로젝트">+</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<ul class="pj-sub-list" id="pj-sub-list">
|
||||
{% for s in subprojects %}
|
||||
<li>
|
||||
<a href="/project/p/{{ s.id }}">
|
||||
<span class="pj-sub-dot" style="background: {{ s.color }}"></span>{{ s.name }}
|
||||
</a>
|
||||
{% if can_manage %}
|
||||
<button type="button" class="pj-icon-btn pj-del-sub" data-id="{{ s.id }}" title="삭제">×</button>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% else %}
|
||||
<li class="pj-empty-sm">없음</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% if tree %}
|
||||
{{ proj_tree(tree, project.id, (is_admin or can_manage), 0) }}
|
||||
{% else %}
|
||||
<div class="pj-empty-sm">프로젝트 없음</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="pj-side-block">
|
||||
@@ -256,5 +283,5 @@
|
||||
</script>
|
||||
|
||||
<script src="/static/vendor/vis-timeline/vis-timeline-graph2d.min.js"></script>
|
||||
<script src="/static/project.js?v=20260625h" defer></script>
|
||||
<script src="/static/project.js?v=20260625i" defer></script>
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user