2025-06-04 10:03:22 +02:00

218 lines
12 KiB
HTML

<!DOCTYPE html>
<html lang="de" class="scroll-smooth">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="MYP Platform - Mercedes-Benz 3D Druck Management System">
<meta name="robots" content="noindex, nofollow">
<meta name="theme-color" content="#000000">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{% block title %}MYP Platform - Mercedes-Benz{% endblock %}</title>
<!-- Critical CSS inline for instant rendering -->
<style>
/* Critical CSS for above-the-fold content */
*,::after,::before{box-sizing:border-box}
html{line-height:1.5;-webkit-text-size-adjust:100%;font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}
body{margin:0;font-family:inherit;line-height:inherit}
.dark{color-scheme:dark}
.dark body{background-color:#0f172a;color:#e2e8f0}
body{background-color:#fff;color:#1e293b}
/* Glassmorphism navbar - preserved */
.glass-navbar{background:rgba(255,255,255,0.85);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid rgba(255,255,255,0.3);box-shadow:0 2px 4px rgba(0,0,0,0.05)}
.dark .glass-navbar{background:rgba(15,23,42,0.85);border:1px solid rgba(255,255,255,0.1)}
/* Hide content until styles load */
.no-fouc{visibility:hidden;opacity:0}
.fonts-loaded .no-fouc{visibility:visible;opacity:1;transition:opacity 0.2s}
</style>
<!-- Preconnect to speed up font loading -->
<link rel="preconnect" href="{{ url_for('static', filename='fontawesome/webfonts', _external=True) }}" crossorigin>
<!-- Optimized CSS loading -->
<link href="{{ url_for('static', filename='css/tailwind.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/performance-optimized.min.css') }}" rel="stylesheet">
<link href="{{ url_for('static', filename='css/core-utilities.min.css') }}" rel="stylesheet">
<!-- Non-critical CSS -->
<link href="{{ url_for('static', filename='fontawesome/css/all.min.css') }}" rel="stylesheet" media="print" onload="this.media='all'">
<!-- PWA -->
<link rel="manifest" href="{{ url_for('static', filename='manifest.json') }}">
<link rel="icon" type="image/svg+xml" href="{{ url_for('static', filename='favicon.svg') }}">
<!-- Dark Mode Script (inline to prevent flash) -->
<script>
(function(){
const savedMode = localStorage.getItem('myp-dark-mode');
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const isDark = savedMode === 'true' || (savedMode === null && prefersDark);
if (isDark) {
document.documentElement.classList.add('dark');
document.querySelector('meta[name="theme-color"]').content = '#000000';
} else {
document.documentElement.classList.remove('dark');
document.querySelector('meta[name="theme-color"]').content = '#ffffff';
}
})();
</script>
{% block extra_css %}{% endblock %}
</head>
<body class="min-h-screen bg-white dark:bg-slate-900 text-slate-900 dark:text-slate-100 no-fouc">
<!-- Skip to content -->
<a href="#main" class="sr-only focus:not-sr-only">Skip to main content</a>
<!-- Header with glassmorphism navbar -->
<header class="glass-navbar sticky top-0 z-40 w-full">
<nav class="container mx-auto px-4 py-3">
<div class="flex items-center justify-between">
<!-- Logo -->
<a href="{{ url_for('index') }}" class="flex items-center space-x-3">
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 80 80">
<path d="M58.6,4.5C53,1.6,46.7,0,40,0c-6.7,0-13,1.6-18.6,4.5v0C8.7,11.2,0,24.6,0,40c0,15.4,8.7,28.8,21.5,35.5C27,78.3,33.3,80,40,80c6.7,0,12.9-1.7,18.5-4.6C71.3,68.8,80,55.4,80,40C80,24.6,71.3,11.2,58.6,4.5z M4,40c0-13.1,7-24.5,17.5-30.9v0C26.6,6,32.5,4.2,39,4l-4.5,32.7L21.5,46.8v0L8.3,57.1C5.6,52,4,46.2,4,40z M58.6,70.8C53.1,74.1,46.8,76,40,76c-6.8,0-13.2-1.9-18.6-5.2c-4.9-2.9-8.9-6.9-11.9-11.7l11.9-4.9v0L40,46.6l18.6,7.5v0l12,4.9C67.6,63.9,63.4,67.9,58.6,70.8z M58.6,46.8L58.6,46.8l-12.9-10L41.1,4c6.3,0.2,12.3,2,17.4,5.1v0C69,15.4,76,26.9,76,40c0,6.2-1.5,12-4.3,17.1L58.6,46.8z"/>
</svg>
<span class="font-semibold text-xl">MYP Platform</span>
</a>
<!-- Navigation -->
<div class="flex items-center space-x-4">
{% if current_user.is_authenticated %}
<!-- Simplified navigation links -->
<a href="{{ url_for('dashboard') }}" class="px-3 py-2 rounded-md text-sm font-medium hover:bg-slate-100 dark:hover:bg-slate-800">Dashboard</a>
<a href="{{ url_for('printers') }}" class="px-3 py-2 rounded-md text-sm font-medium hover:bg-slate-100 dark:hover:bg-slate-800">Drucker</a>
<a href="{{ url_for('jobs') }}" class="px-3 py-2 rounded-md text-sm font-medium hover:bg-slate-100 dark:hover:bg-slate-800">Aufträge</a>
{% if current_user.is_admin %}
<a href="{{ url_for('admin') }}" class="px-3 py-2 rounded-md text-sm font-medium hover:bg-slate-100 dark:hover:bg-slate-800">Admin</a>
{% endif %}
<!-- User menu -->
<div class="relative">
<button id="user-menu-button" class="flex items-center p-2 rounded-full hover:bg-slate-100 dark:hover:bg-slate-800">
<div class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white text-sm font-medium">
{{ current_user.email[0].upper() if current_user.email else 'U' }}
</div>
</button>
<div id="user-dropdown" class="hidden absolute right-0 mt-2 w-48 bg-white dark:bg-slate-800 rounded-lg shadow-lg">
<a href="{{ url_for('user_profile') }}" class="block px-4 py-2 text-sm hover:bg-slate-100 dark:hover:bg-slate-700">Profil</a>
<a href="{{ url_for('auth.logout') }}" class="block px-4 py-2 text-sm hover:bg-slate-100 dark:hover:bg-slate-700">Abmelden</a>
</div>
</div>
<!-- Dark mode toggle -->
<button id="darkModeToggle" class="p-2 rounded-lg hover:bg-slate-100 dark:hover:bg-slate-800">
<svg class="w-5 h-5 sun-icon" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 2a1 1 0 011 1v1a1 1 0 11-2 0V3a1 1 0 011-1zm4 8a4 4 0 11-8 0 4 4 0 018 0zm-.464 4.95l.707.707a1 1 0 001.414-1.414l-.707-.707a1 1 0 00-1.414 1.414zm2.12-10.607a1 1 0 010 1.414l-.706.707a1 1 0 11-1.414-1.414l.707-.707a1 1 0 011.414 0zM17 11a1 1 0 100-2h-1a1 1 0 100 2h1zm-7 4a1 1 0 011 1v1a1 1 0 11-2 0v-1a1 1 0 011-1zM5.05 6.464A1 1 0 106.465 5.05l-.708-.707a1 1 0 00-1.414 1.414l.707.707zm1.414 8.486l-.707.707a1 1 0 01-1.414-1.414l.707-.707a1 1 0 011.414 1.414zM4 11a1 1 0 100-2H3a1 1 0 000 2h1z" clip-rule="evenodd"></path>
</svg>
<svg class="w-5 h-5 moon-icon hidden" fill="currentColor" viewBox="0 0 20 20">
<path d="M17.293 13.293A8 8 0 016.707 2.707a8.001 8.001 0 1010.586 10.586z"></path>
</svg>
</button>
{% else %}
<a href="{{ url_for('auth.login') }}" class="px-4 py-2 rounded-md text-sm font-medium bg-blue-600 text-white hover:bg-blue-700">Anmelden</a>
{% endif %}
</div>
</div>
</nav>
</header>
<!-- Flash messages container -->
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
<div id="flask-flash-messages"
data-flash-count="{{ messages|length }}"
{% for i, (category, message) in enumerate(messages, 1) %}
data-flash-{{ i }}="{{ category }}|{{ message }}"
{% endfor %}
class="hidden"></div>
{% endif %}
{% endwith %}
<!-- Main content -->
<main id="main" class="container mx-auto px-4 py-8">
{% block content %}{% endblock %}
</main>
<!-- Footer -->
<footer class="mt-auto py-8 text-center text-sm text-slate-600 dark:text-slate-400">
<p>&copy; 2024 Mercedes-Benz AG. Alle Rechte vorbehalten.</p>
</footer>
<!-- Core JavaScript bundle -->
<script src="{{ url_for('static', filename='js/core-bundle.min.js') }}"></script>
<!-- Inline initialization script -->
<script>
// Font loading detection
if ('fonts' in document) {
document.fonts.ready.then(() => {
document.documentElement.classList.add('fonts-loaded');
});
} else {
// Fallback for browsers without font loading API
setTimeout(() => {
document.documentElement.classList.add('fonts-loaded');
}, 100);
}
// Initialize core functionality
document.addEventListener('DOMContentLoaded', function() {
// Dark mode toggle
const darkModeToggle = document.getElementById('darkModeToggle');
if (darkModeToggle) {
darkModeToggle.addEventListener('click', function() {
document.documentElement.classList.toggle('dark');
const isDark = document.documentElement.classList.contains('dark');
localStorage.setItem('myp-dark-mode', isDark);
document.querySelector('meta[name="theme-color"]').content = isDark ? '#000000' : '#ffffff';
// Update icons
document.querySelector('.sun-icon').classList.toggle('hidden', isDark);
document.querySelector('.moon-icon').classList.toggle('hidden', !isDark);
});
}
// Simple dropdown
const userMenuButton = document.getElementById('user-menu-button');
const userDropdown = document.getElementById('user-dropdown');
if (userMenuButton && userDropdown) {
userMenuButton.addEventListener('click', function(e) {
e.stopPropagation();
userDropdown.classList.toggle('hidden');
});
document.addEventListener('click', function() {
userDropdown.classList.add('hidden');
});
}
// Flash messages
const flashContainer = document.getElementById('flask-flash-messages');
if (flashContainer && window.MYP && window.MYP.utils) {
const flashCount = parseInt(flashContainer.getAttribute('data-flash-count')) || 0;
for (let i = 1; i <= flashCount; i++) {
const flashData = flashContainer.getAttribute('data-flash-' + i);
if (flashData) {
const [category, message] = flashData.split('|', 2);
let messageType = category === 'danger' ? 'error' : category;
setTimeout(() => {
window.MYP.utils.notifications[messageType](message, 6000);
}, i * 100);
}
}
flashContainer.remove();
}
});
</script>
{% block scripts %}{% endblock %}
</body>
</html>