fc2ae9d1a3
- 프로젝트 관리(index/project/inbox) 진입 시 메인 좌측 사이드바를 전역 설정과 무관하게 항상 접어서 렌더(force_sidebar_collapsed) — 서버에서 바로 data-collapsed=true 로 그려 깜빡임도 없다. - 프로젝트 상세 화면(보드/리스트/달력/타임라인) 오른쪽에 홈 화면과 동일한 "내 업무" 패널 추가(프로젝트별 그룹, 클릭 시 팝업 편집). - 보드에 세션이 늘어나면 .pj-board 안에서만 가로 스크롤돼야 하는데 .pj-main 에 min-width:0 이 없어 grid 트랙 자체가 넓어져 페이지 전체가 가로 스크롤되던 문제 수정. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
183 lines
8.2 KiB
HTML
183 lines
8.2 KiB
HTML
<!doctype html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
<title>{{ page_title or "ERP" }} — DBX Corporation</title>
|
|
<link rel="stylesheet" href="/static/erp.css?v=20260615b" />
|
|
<link rel="stylesheet" href="/static/erp-shell.css?v=20260615b" />
|
|
<link rel="stylesheet" href="/static/erp-attach-viewer.css" />
|
|
<link rel="stylesheet" href="/static/erp-dialog.css?v=20260819a" />
|
|
{% block head_extra %}{% endblock %}
|
|
</head>
|
|
<body class="erp-body erp-app-body">
|
|
|
|
<div class="erp-app">
|
|
|
|
<!-- ── 좌측 사이드바 ── -->
|
|
<!-- force_sidebar_collapsed: 프로젝트 관리처럼 자체 좌측 패널(프로젝트
|
|
트리)을 가진 화면은 전역 접힘 설정과 무관하게 항상 접어서 시작한다
|
|
(서버에서 바로 true 로 렌더 — 깜빡임 자체가 없다). -->
|
|
<aside class="erp-sidebar" data-collapsed="{{ 'true' if force_sidebar_collapsed else 'false' }}">
|
|
{% if not force_sidebar_collapsed %}
|
|
<script>
|
|
// 접힘 상태를 렌더 직후(첫 페인트 전) 바로 반영 — 아래쪽 DOMContentLoaded
|
|
// 스크립트에서 뒤늦게 바꾸면 "펼쳐졌다가 접히는" 깜빡임이 보인다.
|
|
try {
|
|
if (localStorage.getItem("erp_sidebar_collapsed") === "true") {
|
|
document.currentScript.closest(".erp-sidebar").setAttribute("data-collapsed", "true");
|
|
}
|
|
} catch (_) {}
|
|
</script>
|
|
{% endif %}
|
|
|
|
<div class="erp-sidebar-head">
|
|
<a href="/" class="erp-sidebar-brand">
|
|
<img src="/static/dbx-logo.png" alt="DBX" class="erp-sidebar-logo" />
|
|
<span class="erp-sidebar-system">DBX ERP System</span>
|
|
</a>
|
|
<button type="button" class="erp-sidebar-toggle" id="erp-sidebar-toggle" aria-label="사이드바 접기">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<polyline points="15 18 9 12 15 6"/>
|
|
</svg>
|
|
</button>
|
|
</div>
|
|
|
|
<nav class="erp-sidebar-nav">
|
|
{% set last_group = namespace(value=None) %}
|
|
{% for item in nav_items %}
|
|
{% if item.group != last_group.value %}
|
|
<div class="erp-sidebar-group">{{ item.group }}</div>
|
|
{% set last_group.value = item.group %}
|
|
{% endif %}
|
|
|
|
{% set classes = "erp-sidebar-item" %}
|
|
{% if item.active %}{% set classes = classes + " is-active" %}{% endif %}
|
|
{% if item.disabled %}{% set classes = classes + " is-disabled" %}{% endif %}
|
|
|
|
{% if item.disabled %}
|
|
<div class="{{ classes }}" title="{{ item.disabled_reason or '준비중' }}">
|
|
{% else %}
|
|
<a class="{{ classes }}" href="{{ item.url }}"
|
|
{% if item.target == '_blank' %}target="_blank" rel="noopener noreferrer"{% endif %}>
|
|
{% endif %}
|
|
<span class="erp-sidebar-icon">{{ item.icon | safe }}</span>
|
|
<span class="erp-sidebar-label">{{ item.label }}</span>
|
|
{% if item.badge %}<span class="erp-sidebar-badge">{{ item.badge }}</span>{% endif %}
|
|
{% if item.target == '_blank' %}
|
|
<span class="erp-sidebar-ext" aria-hidden="true">
|
|
<svg width="11" height="11" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/>
|
|
<polyline points="15 3 21 3 21 9"/>
|
|
<line x1="10" y1="14" x2="21" y2="3"/>
|
|
</svg>
|
|
</span>
|
|
{% endif %}
|
|
{% if item.disabled %}
|
|
</div>
|
|
{% else %}
|
|
</a>
|
|
{% endif %}
|
|
{% endfor %}
|
|
</nav>
|
|
|
|
<div class="erp-sidebar-foot">
|
|
{% if user.is_super_admin %}
|
|
<a class="erp-sidebar-item" href="/modules" title="업무 모듈 선택 화면">
|
|
<span class="erp-sidebar-icon">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>
|
|
<rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/>
|
|
</svg>
|
|
</span>
|
|
<span class="erp-sidebar-label">업무 모듈</span>
|
|
</a>
|
|
{% endif %}
|
|
{% if is_admin %}
|
|
<a class="erp-sidebar-item" href="/admin">
|
|
<span class="erp-sidebar-icon">
|
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M12 2 4 6v6c0 5 3.4 9.4 8 10 4.6-.6 8-5 8-10V6l-8-4Z"/>
|
|
</svg>
|
|
</span>
|
|
<span class="erp-sidebar-label">관리자</span>
|
|
</a>
|
|
{% endif %}
|
|
</div>
|
|
</aside>
|
|
|
|
<!-- ── 우측 콘텐츠 ── -->
|
|
<div class="erp-content">
|
|
|
|
<header class="erp-topbar">
|
|
<div class="erp-topbar-left">
|
|
<button type="button" class="erp-sidebar-mobile-toggle" id="erp-sidebar-mobile-toggle" aria-label="메뉴 열기">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none"
|
|
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<line x1="3" y1="6" x2="21" y2="6"/>
|
|
<line x1="3" y1="12" x2="21" y2="12"/>
|
|
<line x1="3" y1="18" x2="21" y2="18"/>
|
|
</svg>
|
|
</button>
|
|
<div class="erp-topbar-title">
|
|
<h1>{{ page_title or "" }}</h1>
|
|
{% if page_subtitle %}<p>{{ page_subtitle }}</p>{% endif %}
|
|
</div>
|
|
</div>
|
|
|
|
<div class="erp-topbar-right">
|
|
{% block topbar_actions %}{% endblock %}
|
|
<div class="erp-user-chip">
|
|
{% if user.picture %}
|
|
<img class="erp-user-avatar" src="{{ user.picture }}" alt="{{ user.name }}" />
|
|
{% else %}
|
|
<span class="erp-user-avatar-fallback">{{ user.name[0] | upper }}</span>
|
|
{% endif %}
|
|
<div class="erp-user-info">
|
|
<span class="erp-user-name">{{ user.name }}</span>
|
|
<span class="erp-user-email">{{ user.email }}</span>
|
|
</div>
|
|
</div>
|
|
<a class="erp-btn erp-btn-outline" href="/logout">로그아웃</a>
|
|
</div>
|
|
</header>
|
|
|
|
<main class="erp-page">
|
|
{% block content %}{% endblock %}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
const sidebar = document.querySelector(".erp-sidebar");
|
|
const collapseBtn = document.getElementById("erp-sidebar-toggle");
|
|
const mobileBtn = document.getElementById("erp-sidebar-mobile-toggle");
|
|
if (collapseBtn) {
|
|
// 초기 접힘 상태는 위쪽 인라인 스크립트(첫 페인트 전)에서 이미 반영했다 — 여기선 클릭 시 토글/저장만.
|
|
collapseBtn.addEventListener("click", () => {
|
|
const next = sidebar.dataset.collapsed === "true" ? "false" : "true";
|
|
sidebar.dataset.collapsed = next;
|
|
try { localStorage.setItem("erp_sidebar_collapsed", next); } catch (_) {}
|
|
});
|
|
}
|
|
if (mobileBtn) {
|
|
mobileBtn.addEventListener("click", () => {
|
|
sidebar.classList.toggle("is-open");
|
|
});
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
{# 공용 확인/경고 창. defer 를 쓰지 않는다 — 아래 {% block scripts %} 의
|
|
인라인 스크립트가 곧바로 erpConfirm/erpAlert 을 쓸 수 있어야 한다. #}
|
|
<script src="/static/erp-dialog.js?v=20260819a"></script>
|
|
<script src="/static/erp-attach-viewer.js" defer></script>
|
|
{% block scripts %}{% endblock %}
|
|
</body>
|
|
</html>
|