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. """
504 lines
21 KiB
HTML
504 lines
21 KiB
HTML
<!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> |