Files
Projektarbeit-MYP/backend/app/templates/base.html
Till Tomczak 62e131c02f """
feat: Update frontend and backend configurations for development environment

- Downgrade PyP100 version in requirements.txt for compatibility.
- Add new frontend routes for index, login, dashboard, printers, jobs, and profile pages.
- Modify docker-compose files for development setup, including environment variables and service names.
- Update Caddyfile for local development with Raspberry Pi backend.
- Adjust health check route to use updated backend URL.
- Enhance setup-backend-url.sh for development environment configuration.
"""
2025-05-24 18:58:17 +02:00

504 lines
21 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="de" class="h-full">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}MYP - Mercedes 3D Printing Platform{% endblock %}</title>
<!-- Tailwind CSS CDN -->
<script src="https://cdn.tailwindcss.com"></script>
<!-- Custom CSS für Mercedes Farben -->
<style>
:root {
--mercedes-silver: #C0C0C0;
--mercedes-dark-gray: #2D2D2D;
--mercedes-light-gray: #F5F5F5;
--mercedes-blue: #0066CC;
--mercedes-green: #00B04F;
--mercedes-red: #E60012;
--mercedes-yellow: #FFD700;
--mercedes-black: #000000;
--mercedes-white: #FFFFFF;
}
/* Mercedes Color Classes */
.bg-mercedes-silver { background-color: var(--mercedes-silver); }
.bg-mercedes-dark-gray { background-color: var(--mercedes-dark-gray); }
.bg-mercedes-light-gray { background-color: var(--mercedes-light-gray); }
.bg-mercedes-blue { background-color: var(--mercedes-blue); }
.bg-mercedes-green { background-color: var(--mercedes-green); }
.bg-mercedes-red { background-color: var(--mercedes-red); }
.bg-mercedes-yellow { background-color: var(--mercedes-yellow); }
.bg-mercedes-black { background-color: var(--mercedes-black); }
.bg-mercedes-white { background-color: var(--mercedes-white); }
.text-mercedes-silver { color: var(--mercedes-silver); }
.text-mercedes-dark-gray { color: var(--mercedes-dark-gray); }
.text-mercedes-light-gray { color: var(--mercedes-light-gray); }
.text-mercedes-blue { color: var(--mercedes-blue); }
.text-mercedes-green { color: var(--mercedes-green); }
.text-mercedes-red { color: var(--mercedes-red); }
.text-mercedes-yellow { color: var(--mercedes-yellow); }
.text-mercedes-black { color: var(--mercedes-black); }
.text-mercedes-white { color: var(--mercedes-white); }
.border-mercedes-silver { border-color: var(--mercedes-silver); }
.border-mercedes-dark-gray { border-color: var(--mercedes-dark-gray); }
.border-mercedes-light-gray { border-color: var(--mercedes-light-gray); }
.border-mercedes-blue { border-color: var(--mercedes-blue); }
.border-mercedes-green { border-color: var(--mercedes-green); }
.border-mercedes-red { border-color: var(--mercedes-red); }
.border-mercedes-yellow { border-color: var(--mercedes-yellow); }
.border-mercedes-black { border-color: var(--mercedes-black); }
.border-mercedes-white { border-color: var(--mercedes-white); }
/* Mercedes Gradient */
.mercedes-gradient {
background: linear-gradient(135deg, var(--mercedes-black) 0%, var(--mercedes-dark-gray) 100%);
}
/* Mercedes Shadow */
.mercedes-shadow {
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
}
.mercedes-shadow-lg {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
/* Mercedes Button */
.mercedes-button {
transition: all 0.2s ease-in-out;
border-radius: 0.5rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.mercedes-button:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
.mercedes-button:active {
transform: translateY(0);
}
/* Mercedes Card */
.mercedes-card {
background: var(--mercedes-white);
border: 1px solid var(--mercedes-light-gray);
border-radius: 0.75rem;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
transition: all 0.2s ease-in-out;
}
.mercedes-card:hover {
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
transform: translateY(-2px);
}
/* Mercedes Input */
.mercedes-input {
border: 2px solid var(--mercedes-light-gray);
border-radius: 0.5rem;
padding: 0.75rem 1rem;
transition: all 0.2s ease-in-out;
background: var(--mercedes-white);
}
.mercedes-input:focus {
outline: none;
border-color: var(--mercedes-blue);
box-shadow: 0 0 0 3px rgba(0, 102, 204, 0.1);
}
/* Mercedes Table */
.mercedes-table {
background: var(--mercedes-white);
border-radius: 0.75rem;
overflow: hidden;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.mercedes-table th {
background: var(--mercedes-dark-gray);
color: var(--mercedes-white);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 1rem;
}
.mercedes-table td {
padding: 0.75rem 1rem;
border-bottom: 1px solid var(--mercedes-light-gray);
}
.mercedes-table tr:hover {
background: var(--mercedes-light-gray);
}
/* Mercedes Modal */
.mercedes-modal {
background: rgba(0, 0, 0, 0.5);
backdrop-filter: blur(4px);
}
.mercedes-modal-content {
background: var(--mercedes-white);
border-radius: 1rem;
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1);
max-height: 90vh;
overflow-y: auto;
}
/* Mercedes Progress Bar */
.mercedes-progress {
background: var(--mercedes-light-gray);
border-radius: 9999px;
overflow: hidden;
}
.mercedes-progress-bar {
background: linear-gradient(90deg, var(--mercedes-blue), var(--mercedes-green));
height: 100%;
transition: width 0.3s ease-in-out;
}
/* Mercedes Status Badges */
.status-online { background: var(--mercedes-green); color: white; }
.status-offline { background: var(--mercedes-red); color: white; }
.status-busy { background: var(--mercedes-yellow); color: var(--mercedes-black); }
.status-maintenance { background: var(--mercedes-silver); color: var(--mercedes-black); }
.status-pending { background: var(--mercedes-yellow); color: var(--mercedes-black); }
.status-printing { background: var(--mercedes-blue); color: white; }
.status-completed { background: var(--mercedes-green); color: white; }
.status-failed { background: var(--mercedes-red); color: white; }
.status-cancelled { background: var(--mercedes-silver); color: var(--mercedes-black); }
/* Mercedes Navigation */
.mercedes-nav-item {
position: relative;
transition: all 0.2s ease-in-out;
}
.mercedes-nav-item:hover::after {
content: '';
position: absolute;
bottom: -2px;
left: 0;
right: 0;
height: 2px;
background: var(--mercedes-silver);
}
/* Mercedes Animations */
@keyframes mercedes-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.5; }
}
.mercedes-pulse {
animation: mercedes-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
@keyframes mercedes-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.mercedes-spin {
animation: mercedes-spin 1s linear infinite;
}
/* Mercedes Responsive */
@media (max-width: 768px) {
.mercedes-card {
margin: 0.5rem;
}
.mercedes-table {
font-size: 0.875rem;
}
.mercedes-button {
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
}
/* Mercedes Dark Mode Support */
@media (prefers-color-scheme: dark) {
.mercedes-card {
background: var(--mercedes-dark-gray);
border-color: var(--mercedes-silver);
color: var(--mercedes-white);
}
.mercedes-input {
background: var(--mercedes-dark-gray);
color: var(--mercedes-white);
border-color: var(--mercedes-silver);
}
}
</style>
{% block head %}{% endblock %}
</head>
<body class="h-full bg-gradient-to-br from-mercedes-light-gray to-white font-sans">
<!-- Navigation -->
<nav class="mercedes-gradient mercedes-shadow sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<!-- Logo und Marke -->
<div class="flex items-center space-x-4">
<div class="flex-shrink-0">
<svg class="h-10 w-10 text-mercedes-silver" 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.5
C27,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,40
c0-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.8
C53.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.9
C67.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,40
c0,6.2-1.5,12-4.3,17.1L58.6,46.8z"/>
</svg>
</div>
<div class="text-white">
<h1 class="text-xl font-bold tracking-wide">MYP</h1>
<p class="text-xs text-mercedes-silver">3D Printing Platform</p>
</div>
</div>
<!-- Navigation Links -->
<div class="hidden md:block">
<div class="ml-10 flex items-baseline space-x-8">
<a href="/dashboard" class="mercedes-nav-item text-white hover:text-mercedes-silver px-3 py-2 text-sm font-medium transition-colors duration-200">
Dashboard
</a>
<a href="/printers" class="mercedes-nav-item text-white hover:text-mercedes-silver px-3 py-2 text-sm font-medium transition-colors duration-200">
Drucker
</a>
<a href="/jobs" class="mercedes-nav-item text-white hover:text-mercedes-silver px-3 py-2 text-sm font-medium transition-colors duration-200">
Jobs
</a>
<a href="/stats" class="mercedes-nav-item text-white hover:text-mercedes-silver px-3 py-2 text-sm font-medium transition-colors duration-200">
Statistiken
</a>
{% if current_user.is_authenticated and current_user.is_admin %}
<a href="/admin" class="mercedes-nav-item text-mercedes-yellow hover:text-white px-3 py-2 text-sm font-medium transition-colors duration-200">
Admin
</a>
{% endif %}
</div>
</div>
<!-- User Menu -->
<div class="flex items-center space-x-4">
{% if current_user.is_authenticated %}
<div class="text-white text-sm">
<span class="text-mercedes-silver">Willkommen,</span>
<span class="font-medium">{{ current_user.email }}</span>
</div>
<button onclick="logout()" class="bg-mercedes-red hover:bg-red-700 text-white px-4 py-2 rounded-lg text-sm font-medium mercedes-button transition-all duration-200">
Abmelden
</button>
{% else %}
<a href="/login" class="bg-mercedes-blue hover:bg-blue-700 text-white px-4 py-2 rounded-lg text-sm font-medium mercedes-button transition-all duration-200">
Anmelden
</a>
{% endif %}
</div>
<!-- Mobile menu button -->
<div class="md:hidden">
<button type="button" class="text-white hover:text-mercedes-silver focus:outline-none focus:text-mercedes-silver" onclick="toggleMobileMenu()">
<svg class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
</div>
</div>
<!-- Mobile menu -->
<div id="mobile-menu" class="md:hidden hidden">
<div class="px-2 pt-2 pb-3 space-y-1 sm:px-3 bg-mercedes-gray">
<a href="/dashboard" class="text-white hover:text-mercedes-silver block px-3 py-2 text-base font-medium">Dashboard</a>
<a href="/printers" class="text-white hover:text-mercedes-silver block px-3 py-2 text-base font-medium">Drucker</a>
<a href="/jobs" class="text-white hover:text-mercedes-silver block px-3 py-2 text-base font-medium">Jobs</a>
<a href="/stats" class="text-white hover:text-mercedes-silver block px-3 py-2 text-base font-medium">Statistiken</a>
{% if current_user.is_authenticated and current_user.is_admin %}
<a href="/admin" class="text-mercedes-yellow hover:text-white block px-3 py-2 text-base font-medium">Admin</a>
{% endif %}
</div>
</div>
</nav>
<!-- Flash Messages -->
<div id="flash-messages" class="fixed top-20 right-4 z-40 space-y-2">
<!-- Flash messages will be inserted here by JavaScript -->
</div>
<!-- Main Content -->
<main class="min-h-screen">
{% block content %}{% endblock %}
</main>
<!-- Footer -->
<footer class="mercedes-gradient text-white py-8 mt-16">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-col md:flex-row justify-between items-center">
<div class="flex items-center space-x-4 mb-4 md:mb-0">
<svg class="h-8 w-8 text-mercedes-silver" 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.5
C27,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,40
c0-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.8
C53.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.9
C67.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,40
c0,6.2-1.5,12-4.3,17.1L58.6,46.8z"/>
</svg>
<div>
<p class="text-sm font-medium">MYP - 3D Printing Platform</p>
<p class="text-xs text-mercedes-silver">Powered by Mercedes Excellence</p>
</div>
</div>
<div class="text-center md:text-right">
<p class="text-sm text-mercedes-silver">© 2024 MYP Platform. Alle Rechte vorbehalten.</p>
<p class="text-xs text-mercedes-silver mt-1">Version 1.0</p>
</div>
</div>
</div>
</footer>
<!-- JavaScript -->
<script>
// Mobile menu toggle
function toggleMobileMenu() {
const menu = document.getElementById('mobile-menu');
menu.classList.toggle('hidden');
}
// Logout function
async function logout() {
try {
const response = await fetch('/auth/logout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
}
});
if (response.ok) {
window.location.href = '/login';
} else {
showFlashMessage('Fehler beim Abmelden', 'error');
}
} catch (error) {
showFlashMessage('Netzwerkfehler beim Abmelden', 'error');
}
}
// Flash message system
function showFlashMessage(message, type = 'info') {
const container = document.getElementById('flash-messages');
const messageDiv = document.createElement('div');
let bgColor = 'bg-mercedes-blue';
let textColor = 'text-white';
switch(type) {
case 'success':
bgColor = 'bg-mercedes-green';
break;
case 'error':
bgColor = 'bg-mercedes-red';
break;
case 'warning':
bgColor = 'bg-mercedes-yellow';
textColor = 'text-mercedes-black';
break;
}
messageDiv.className = `${bgColor} ${textColor} px-6 py-3 rounded-lg shadow-lg mercedes-shadow transform transition-all duration-300 translate-x-full`;
messageDiv.innerHTML = `
<div class="flex items-center justify-between">
<span class="font-medium">${message}</span>
<button onclick="this.parentElement.parentElement.remove()" class="ml-4 text-lg font-bold hover:opacity-75">×</button>
</div>
`;
container.appendChild(messageDiv);
// Animate in
setTimeout(() => {
messageDiv.classList.remove('translate-x-full');
}, 100);
// Auto remove after 5 seconds
setTimeout(() => {
messageDiv.classList.add('translate-x-full');
setTimeout(() => {
if (messageDiv.parentElement) {
messageDiv.remove();
}
}, 300);
}, 5000);
}
// API helper function
async function apiCall(url, options = {}) {
try {
const response = await fetch(url, {
headers: {
'Content-Type': 'application/json',
...options.headers
},
...options
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'API-Fehler');
}
return data;
} catch (error) {
showFlashMessage(error.message, 'error');
throw error;
}
}
// Format date helper
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleString('de-DE', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
// Format duration helper
function formatDuration(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
if (hours > 0) {
return `${hours}h ${minutes}m ${secs}s`;
} else if (minutes > 0) {
return `${minutes}m ${secs}s`;
} else {
return `${secs}s`;
}
}
</script>
{% block scripts %}{% endblock %}
</body>
</html>