- Accessibility: dark ink (#00003d) on warning/success buttons, badges, and success alert - white text on #ff9500/#0ad64f failed WCAG AA (~2:1); dark-on-bright now passes. - Heading hierarchy: sidebar brand h1 -> div.brand-title (with matching CSS selector), and standardized all page-header titles to a single <h1> per page (8 templates were h2). - Prefix-safe modal JS: dashboard/backups/reports/enrollment delete+clone actions now build their form action from url_for(...'__K__'/'__F__').replace(...) instead of hardcoded paths, so they survive a URL-prefix mount.
179 lines
6.9 KiB
HTML
179 lines
6.9 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en" data-bs-theme="light">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<meta name="csrf-token" content="{{ csrf_token() }}">
|
|
<title>{% block title %}PXE Server Manager{% endblock %}</title>
|
|
<link rel="icon" href="{{ url_for('static', filename='favicon.ico') }}" type="image/x-icon">
|
|
<link href="{{ url_for('static', filename='bootstrap.min.css') }}" rel="stylesheet">
|
|
<link href="{{ url_for('static', filename='bootstrap-icons.min.css') }}" rel="stylesheet">
|
|
<!-- pxe-theme.css loads AFTER bootstrap so its token+component rules win -->
|
|
<link href="{{ url_for('static', filename='pxe-theme.css') }}" rel="stylesheet">
|
|
<script>
|
|
// Set the theme before first paint to avoid an unstyled flash.
|
|
(function () {
|
|
var KEY = 'pxe-theme';
|
|
var stored = null;
|
|
try { stored = localStorage.getItem(KEY); } catch (e) {}
|
|
var theme = (stored === 'light' || stored === 'dark')
|
|
? stored
|
|
: (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
document.documentElement.setAttribute('data-bs-theme', theme);
|
|
})();
|
|
</script>
|
|
{% block extra_head %}{% endblock %}
|
|
</head>
|
|
<body>
|
|
|
|
<!-- Sidebar -->
|
|
<nav class="sidebar d-flex flex-column">
|
|
<div class="sidebar-header">
|
|
<img class="sidebar-logo" src="{{ url_for('static', filename='ge-aerospace-logo.svg') }}" alt="GE Aerospace">
|
|
<div class="brand-title">PXE Manager</div>
|
|
</div>
|
|
|
|
<ul class="sidebar-nav nav flex-column mt-2">
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'dashboard' %}active{% endif %}"
|
|
href="{{ url_for('dashboard') }}">
|
|
<i class="bi bi-speedometer2"></i> Dashboard
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'images_import' %}active{% endif %}"
|
|
href="{{ url_for('images_import') }}">
|
|
<i class="bi bi-box-arrow-in-down"></i> Image Import
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'imaging_dashboard' %}active{% endif %}"
|
|
href="{{ url_for('imaging_dashboard') }}">
|
|
<i class="bi bi-activity"></i> Imaging Progress
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="nav-section">Tools</div>
|
|
<ul class="sidebar-nav nav flex-column">
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'startnet_editor' %}active{% endif %}"
|
|
href="{{ url_for('startnet_editor') }}">
|
|
<i class="bi bi-terminal"></i> startnet.cmd
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'clonezilla_backups' %}active{% endif %}"
|
|
href="{{ url_for('clonezilla_backups') }}">
|
|
<i class="bi bi-hdd-stack"></i> Clonezilla Backups
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'blancco_reports' %}active{% endif %}"
|
|
href="{{ url_for('blancco_reports') }}">
|
|
<i class="bi bi-shield-check"></i> Blancco Reports
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'enrollment' %}active{% endif %}"
|
|
href="{{ url_for('enrollment') }}">
|
|
<i class="bi bi-pc-display"></i> Enrollment
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'audit_log' %}active{% endif %}"
|
|
href="{{ url_for('audit_log') }}">
|
|
<i class="bi bi-list-check"></i> Audit Log
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<div class="nav-section">GE Aerospace Images</div>
|
|
<ul class="sidebar-nav nav flex-column">
|
|
{% for it in all_image_types if it.startswith('gea-') %}
|
|
<li class="nav-item">
|
|
<a class="nav-link {% if request.endpoint == 'unattend_editor' and image_type is defined and image_type == it %}active{% endif %}"
|
|
href="{{ url_for('unattend_editor', image_type=it) }}">
|
|
<i class="bi bi-windows"></i> {{ all_friendly_names[it] }}
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link nav-sublink {% if request.endpoint == 'image_config' and image_type is defined and image_type == it %}active{% endif %}"
|
|
href="{{ url_for('image_config', image_type=it) }}">
|
|
<i class="bi bi-gear"></i> Configuration
|
|
</a>
|
|
</li>
|
|
{% endfor %}
|
|
</ul>
|
|
|
|
<!-- Footer pinned to bottom -->
|
|
<div class="sidebar-footer mt-auto">
|
|
<button type="button" id="themeToggle" class="theme-toggle">
|
|
<i class="bi bi-moon-stars" id="themeToggleIcon"></i>
|
|
<span id="themeToggleLabel">Dark</span>
|
|
</button>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- Main content -->
|
|
<div class="main-content">
|
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
|
{% if messages %}
|
|
{% for category, message in messages %}
|
|
<div class="alert alert-{{ category }} alert-dismissible fade show" role="alert">
|
|
{{ message }}
|
|
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
|
</div>
|
|
{% endfor %}
|
|
{% endif %}
|
|
{% endwith %}
|
|
|
|
{% block content %}{% endblock %}
|
|
</div>
|
|
|
|
<script>
|
|
// Theme toggle: flips data-theme (and data-bs-theme) and persists to localStorage.
|
|
(function () {
|
|
var KEY = 'pxe-theme';
|
|
var btn = document.getElementById('themeToggle');
|
|
var icon = document.getElementById('themeToggleIcon');
|
|
var label = document.getElementById('themeToggleLabel');
|
|
|
|
function paintToggle(theme) {
|
|
// Label/icon advertise the theme you would switch TO.
|
|
if (theme === 'dark') {
|
|
if (icon) icon.className = 'bi bi-sun';
|
|
if (label) label.textContent = 'Light';
|
|
} else {
|
|
if (icon) icon.className = 'bi bi-moon-stars';
|
|
if (label) label.textContent = 'Dark';
|
|
}
|
|
}
|
|
|
|
function apply(theme) {
|
|
document.documentElement.setAttribute('data-theme', theme);
|
|
document.documentElement.setAttribute('data-bs-theme', theme);
|
|
try { localStorage.setItem(KEY, theme); } catch (e) {}
|
|
paintToggle(theme);
|
|
}
|
|
|
|
paintToggle(document.documentElement.getAttribute('data-theme') || 'light');
|
|
|
|
if (btn) {
|
|
btn.addEventListener('click', function () {
|
|
var current = document.documentElement.getAttribute('data-theme') === 'dark' ? 'dark' : 'light';
|
|
apply(current === 'dark' ? 'light' : 'dark');
|
|
});
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<script src="{{ url_for('static', filename='bootstrap.bundle.min.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='qrcode.min.js') }}"></script>
|
|
<script src="{{ url_for('static', filename='qr-render.js') }}"></script>
|
|
{% block extra_scripts %}{% endblock %}
|
|
</body>
|
|
</html>
|