193 lines
12 KiB
HTML
193 lines
12 KiB
HTML
<!-- /data/gyber/apps/web/templates/base.html -->
|
|
{% load static %}
|
|
<!DOCTYPE html>
|
|
<html lang="ko" {% if is_dark_theme %}data-bs-theme="dark"{% endif %}>
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>{% block title %}Gyber 자산 관리{% endblock %}</title>
|
|
|
|
<!-- Bootstrap 5.3+ CSS -->
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
|
|
<!-- Select2 Core CSS -->
|
|
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
|
<!-- Select2 Bootstrap 5 Theme CSS -->
|
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/select2-bootstrap-5-theme@1.3.0/dist/select2-bootstrap-5-theme.min.css" />
|
|
<!-- Font Awesome CSS -->
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.2/css/all.min.css" integrity="sha512-SnH5WK+bZxgPHs44uWIX+LLJAJ9/2PkPKZ5QiAj6Ta86w+fsb2TkcmfRyVX3pBnMFcV7oQPJkl9QevSCWr3W6A==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
|
|
|
<!-- 커스텀 CSS (Bootstrap 및 라이브러리 CSS 이후에 로드) -->
|
|
<link rel="stylesheet" href="{% static 'css/custom_styles.css' %}">
|
|
|
|
{% block extra_head %}{% endblock %}
|
|
|
|
<style>
|
|
/* base.html 에 직접 작성하는 스타일은 최소화하고 custom_styles.css을 활용하자! */
|
|
</style>
|
|
</head>
|
|
<body>
|
|
{# Navbar #}
|
|
<nav class="navbar navbar-expand-lg bg-body-tertiary mb-4 shadow-sm">
|
|
<div class="container-fluid">
|
|
<a class="navbar-brand fw-bold" href="{% url 'gyber:dashboard' %}"><i class="fas fa-cubes me-1"></i>Gyber</a>
|
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
|
<span class="navbar-toggler-icon"></span>
|
|
</button>
|
|
<div class="collapse navbar-collapse" id="navbarNav">
|
|
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
|
|
{# ... (메뉴 항목은 이전과 동일) ... #}
|
|
{% if user.is_authenticated %}
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.resolver_match.app_name == 'gyber' and request.resolver_match.url_name == 'dashboard' %}active{% endif %}" href="{% url 'gyber:dashboard' %}"><i class="fas fa-tachometer-alt me-1"></i>대시보드</a>
|
|
</li>
|
|
{% if user_is_viewer_group_member %}
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.resolver_match.app_name == 'gyber' and request.resolver_match.url_name == 'resource_list' %}active{% endif %}" href="{% url 'gyber:resource_list' %}"><i class="fas fa-box me-1"></i>자산 목록</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.resolver_match.app_name == 'gyber' and request.resolver_match.url_name == 'user_list' %}active{% endif %}" href="{% url 'gyber:user_list' %}"><i class="fas fa-users me-1"></i>사용자 목록</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.resolver_match.app_name == 'gyber' and request.resolver_match.url_name == 'group_list' %}active{% endif %}" href="{% url 'gyber:group_list' %}"><i class="fas fa-sitemap me-1"></i>그룹(부서) 관리</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.resolver_match.app_name == 'gyber' and request.resolver_match.url_name == 'category_list' %}active{% endif %}" href="{% url 'gyber:category_list' %}"><i class="fas fa-tags me-1"></i>카테고리 관리</a>
|
|
</li>
|
|
{% endif %}
|
|
|
|
{% if user_is_admin_group_member %}
|
|
<li class="nav-item dropdown">
|
|
<a class="nav-link dropdown-toggle {% if 'log_list' in request.resolver_match.url_name %}active{% endif %}" href="#" id="navbarLogDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
<i class="fas fa-clipboard-list me-1"></i>로그 조회
|
|
</a>
|
|
<ul class="dropdown-menu" aria-labelledby="navbarLogDropdown">
|
|
<li><a class="dropdown-item {% if request.resolver_match.url_name == 'resource_log_list' %}active{% endif %}" href="{% url 'gyber:resource_log_list' %}">자산 로그</a></li>
|
|
<li><a class="dropdown-item {% if request.resolver_match.url_name == 'user_log_list' %}active{% endif %}" href="{% url 'gyber:user_log_list' %}">사용자 로그</a></li>
|
|
<li><hr class="dropdown-divider"></li>
|
|
<li><a class="dropdown-item {% if request.resolver_match.url_name == 'group_log_list' %}active{% endif %}" href="{% url 'gyber:group_log_list' %}">그룹(부서) 로그</a></li>
|
|
<li><a class="dropdown-item {% if request.resolver_match.url_name == 'category_log_list' %}active{% endif %}" href="{% url 'gyber:category_log_list' %}">카테고리 로그</a></li>
|
|
</ul>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="/admin/" target="_blank"><i class="fas fa-cog me-1"></i>Admin</a>
|
|
</li>
|
|
{% endif %}
|
|
{% endif %}
|
|
</ul>
|
|
<ul class="navbar-nav ms-auto align-items-center">
|
|
{# ... (사용자 정보, 로그아웃, 테마 버튼은 이전과 동일) ... #}
|
|
{% if user.is_authenticated %}
|
|
<li class="nav-item dropdown">
|
|
<a class="nav-link dropdown-toggle" href="#" id="navbarUserDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
|
|
<i class="fas fa-user-circle me-1"></i>{{ user.first_name|default:user.username }}
|
|
</a>
|
|
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="navbarUserDropdown">
|
|
<li>
|
|
<form action="{% url 'gyber:custom_logout' %}" method="post" class="d-inline">
|
|
{% csrf_token %}
|
|
<button type="submit" class="dropdown-item"><i class="fas fa-sign-out-alt me-1"></i>로그아웃</button>
|
|
</form>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
{% else %}
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="{% url 'oidc_authentication_init' %}">로그인</a>
|
|
</li>
|
|
{% endif %}
|
|
<li class="nav-item ms-2">
|
|
<button id="theme-toggle-button" class="btn btn-outline-secondary btn-sm" type="button" title="테마 전환">
|
|
<i id="theme-icon" class="fas"></i>
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
|
|
{# 메시지 표시 영역 (이전과 동일) #}
|
|
{% if messages %}
|
|
<div class="container mt-3 mb-3">
|
|
{% for message in messages %}
|
|
{% with message_icon_class=message.tags|lower %}
|
|
<div class="alert alert-{% if message_icon_class == 'error' %}danger{% elif message_icon_class == 'warning' %}warning{% elif message_icon_class == 'success' %}success{% else %}info{% endif %} alert-dismissible fade show" role="alert">
|
|
{% if message_icon_class == 'error' %}<i class="fas fa-times-circle me-2"></i>
|
|
{% elif message_icon_class == 'warning' %}<i class="fas fa-exclamation-triangle me-2"></i>
|
|
{% elif message_icon_class == 'success' %}<i class="fas fa-check-circle me-2"></i>
|
|
{% else %}<i class="fas fa-info-circle me-2"></i>
|
|
{% endif %}
|
|
{{ message }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
|
</div>
|
|
{% endwith %}
|
|
{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<main class="container">
|
|
{% block content %}
|
|
{% endblock %}
|
|
</main>
|
|
|
|
<footer class="mt-5 py-3 text-center text-body-secondary border-top">
|
|
<p class="mb-0">© <span id="footer-year">2023</span> Gyber Corp. All rights reserved.</p>
|
|
</footer>
|
|
|
|
<!-- JavaScript 파일들 (body 닫기 직전으로 이동) -->
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.3/dist/chart.umd.min.js"></script>
|
|
|
|
{# 테마 전환 JavaScript #}
|
|
<script>
|
|
(function() {
|
|
'use strict';
|
|
const htmlElement = document.documentElement; // html 태그 선택
|
|
const themeToggleButton = document.getElementById('theme-toggle-button');
|
|
const themeIcon = document.getElementById('theme-icon');
|
|
const storedTheme = localStorage.getItem('theme');
|
|
|
|
const getPreferredTheme = () => {
|
|
if (storedTheme) return storedTheme;
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
};
|
|
|
|
const setTheme = (theme) => {
|
|
if (theme === 'auto' && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
htmlElement.setAttribute('data-bs-theme', 'dark');
|
|
} else {
|
|
htmlElement.setAttribute('data-bs-theme', theme);
|
|
}
|
|
if (themeIcon) {
|
|
themeIcon.className = theme === 'dark' ? 'fas fa-sun' : 'fas fa-moon';
|
|
}
|
|
};
|
|
|
|
setTheme(getPreferredTheme()); // 초기 테마 설정
|
|
|
|
if (themeToggleButton) {
|
|
themeToggleButton.addEventListener('click', () => {
|
|
const currentTheme = htmlElement.getAttribute('data-bs-theme');
|
|
const newTheme = currentTheme === 'dark' ? 'light' : 'dark';
|
|
localStorage.setItem('theme', newTheme);
|
|
setTheme(newTheme);
|
|
});
|
|
}
|
|
|
|
// 시스템 테마 변경 감지 (localStorage에 저장된 테마가 없을 때만)
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
|
if (!localStorage.getItem('theme')) {
|
|
setTheme(e.matches ? 'dark' : 'light');
|
|
}
|
|
});
|
|
|
|
// Footer 연도 자동 업데이트
|
|
const footerYear = document.getElementById('footer-year');
|
|
if (footerYear) footerYear.textContent = new Date().getFullYear();
|
|
})();
|
|
</script>
|
|
|
|
{% block extra_js %}{% endblock %}
|
|
</body>
|
|
</html> |