373 lines
13 KiB
HTML
373 lines
13 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Offline - Webshop System</title>
|
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
|
<link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
|
|
<style>
|
|
body {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
.offline-container {
|
|
background: white;
|
|
border-radius: 1rem;
|
|
padding: 3rem;
|
|
text-align: center;
|
|
box-shadow: 0 1rem 3rem rgba(0,0,0,0.1);
|
|
max-width: 500px;
|
|
width: 90%;
|
|
}
|
|
.offline-icon {
|
|
font-size: 4rem;
|
|
color: #6c757d;
|
|
margin-bottom: 1rem;
|
|
}
|
|
.btn-retry {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
border: none;
|
|
color: white;
|
|
padding: 0.75rem 2rem;
|
|
border-radius: 0.5rem;
|
|
font-weight: 600;
|
|
transition: transform 0.2s;
|
|
}
|
|
.btn-retry:hover {
|
|
transform: translateY(-2px);
|
|
color: white;
|
|
}
|
|
.offline-features {
|
|
margin-top: 2rem;
|
|
text-align: left;
|
|
}
|
|
.feature-item {
|
|
display: flex;
|
|
align-items: center;
|
|
margin-bottom: 1rem;
|
|
padding: 0.5rem;
|
|
border-radius: 0.5rem;
|
|
background: #f8f9fa;
|
|
}
|
|
.feature-icon {
|
|
margin-right: 1rem;
|
|
color: #28a745;
|
|
font-size: 1.2rem;
|
|
}
|
|
.cached-content {
|
|
margin-top: 2rem;
|
|
padding: 1rem;
|
|
background: #e9ecef;
|
|
border-radius: 0.5rem;
|
|
}
|
|
.cached-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 0.5rem 0;
|
|
border-bottom: 1px solid #dee2e6;
|
|
}
|
|
.cached-item:last-child {
|
|
border-bottom: none;
|
|
}
|
|
.btn-cached {
|
|
background: #6c757d;
|
|
border: none;
|
|
color: white;
|
|
padding: 0.25rem 0.75rem;
|
|
border-radius: 0.25rem;
|
|
font-size: 0.875rem;
|
|
}
|
|
.btn-cached:hover {
|
|
background: #5a6268;
|
|
color: white;
|
|
}
|
|
.connection-status {
|
|
position: fixed;
|
|
top: 1rem;
|
|
right: 1rem;
|
|
padding: 0.5rem 1rem;
|
|
border-radius: 0.5rem;
|
|
color: white;
|
|
font-weight: 600;
|
|
z-index: 1000;
|
|
transition: opacity 0.3s;
|
|
}
|
|
.status-online {
|
|
background: #28a745;
|
|
}
|
|
.status-offline {
|
|
background: #dc3545;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Connection Status -->
|
|
<div id="connectionStatus" class="connection-status status-offline">
|
|
<i class="fas fa-wifi-slash"></i> Offline
|
|
</div>
|
|
|
|
<div class="offline-container">
|
|
<div class="offline-icon">
|
|
<i class="fas fa-wifi-slash"></i>
|
|
</div>
|
|
|
|
<h1 class="h3 mb-3">Keine Internetverbindung</h1>
|
|
<p class="text-muted mb-4">
|
|
Es scheint, als hätten Sie keine Internetverbindung.
|
|
Einige Funktionen sind möglicherweise nicht verfügbar.
|
|
</p>
|
|
|
|
<button id="retryBtn" class="btn btn-retry mb-4">
|
|
<i class="fas fa-redo me-2"></i>Verbindung testen
|
|
</button>
|
|
|
|
<div class="offline-features">
|
|
<h5 class="mb-3">
|
|
<i class="fas fa-check-circle text-success me-2"></i>
|
|
Verfügbare Offline-Funktionen:
|
|
</h5>
|
|
|
|
<div class="feature-item">
|
|
<i class="fas fa-shopping-cart feature-icon"></i>
|
|
<div>
|
|
<strong>Warenkorb verwalten</strong><br>
|
|
<small class="text-muted">Produkte hinzufügen und entfernen</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="feature-item">
|
|
<i class="fas fa-eye feature-icon"></i>
|
|
<div>
|
|
<strong>Gecachte Produkte anzeigen</strong><br>
|
|
<small class="text-muted">Zuletzt besuchte Produkte</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="feature-item">
|
|
<i class="fas fa-user feature-icon"></i>
|
|
<div>
|
|
<strong>Profil verwalten</strong><br>
|
|
<small class="text-muted">Persönliche Daten bearbeiten</small>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="feature-item">
|
|
<i class="fas fa-heart feature-icon"></i>
|
|
<div>
|
|
<strong>Wunschliste</strong><br>
|
|
<small class="text-muted">Gespeicherte Produkte anzeigen</small>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="cached-content">
|
|
<h6 class="mb-3">
|
|
<i class="fas fa-download me-2"></i>
|
|
Gecachte Inhalte:
|
|
</h6>
|
|
|
|
<div id="cachedItems">
|
|
<!-- Wird dynamisch gefüllt -->
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mt-4">
|
|
<small class="text-muted">
|
|
<i class="fas fa-info-circle me-1"></i>
|
|
Diese Seite wird automatisch aktualisiert, sobald eine Internetverbindung verfügbar ist.
|
|
</small>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
|
|
<script>
|
|
// Connection Status Management
|
|
function updateConnectionStatus() {
|
|
const statusElement = document.getElementById('connectionStatus');
|
|
const retryBtn = document.getElementById('retryBtn');
|
|
|
|
if (navigator.onLine) {
|
|
statusElement.className = 'connection-status status-online';
|
|
statusElement.innerHTML = '<i class="fas fa-wifi"></i> Online';
|
|
statusElement.style.opacity = '0.8';
|
|
|
|
// Automatische Weiterleitung nach 3 Sekunden
|
|
setTimeout(() => {
|
|
window.location.href = '/';
|
|
}, 3000);
|
|
|
|
retryBtn.innerHTML = '<i class="fas fa-check me-2"></i>Verbunden!';
|
|
retryBtn.disabled = true;
|
|
} else {
|
|
statusElement.className = 'connection-status status-offline';
|
|
statusElement.innerHTML = '<i class="fas fa-wifi-slash"></i> Offline';
|
|
statusElement.style.opacity = '1';
|
|
|
|
retryBtn.innerHTML = '<i class="fas fa-redo me-2"></i>Verbindung testen';
|
|
retryBtn.disabled = false;
|
|
}
|
|
}
|
|
|
|
// Retry Button Handler
|
|
document.getElementById('retryBtn').addEventListener('click', function() {
|
|
this.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Teste...';
|
|
this.disabled = true;
|
|
|
|
// Teste Internetverbindung
|
|
fetch('/api/stats', { method: 'HEAD' })
|
|
.then(response => {
|
|
if (response.ok) {
|
|
updateConnectionStatus();
|
|
window.location.reload();
|
|
} else {
|
|
throw new Error('Server nicht erreichbar');
|
|
}
|
|
})
|
|
.catch(error => {
|
|
console.log('Verbindungstest fehlgeschlagen:', error);
|
|
this.innerHTML = '<i class="fas fa-redo me-2"></i>Verbindung testen';
|
|
this.disabled = false;
|
|
});
|
|
});
|
|
|
|
// Cached Items laden
|
|
async function loadCachedItems() {
|
|
const cachedItemsContainer = document.getElementById('cachedItems');
|
|
|
|
try {
|
|
// Prüfe Cache für verschiedene Inhalte
|
|
const cacheNames = ['webshop-static-v1', 'webshop-dynamic-v1'];
|
|
const cachedItems = [];
|
|
|
|
for (const cacheName of cacheNames) {
|
|
const cache = await caches.open(cacheName);
|
|
const requests = await cache.keys();
|
|
|
|
for (const request of requests) {
|
|
const url = new URL(request.url);
|
|
|
|
// Nur relevante Inhalte anzeigen
|
|
if (url.pathname.startsWith('/product/') ||
|
|
url.pathname.startsWith('/category/') ||
|
|
url.pathname === '/products') {
|
|
|
|
const response = await cache.match(request);
|
|
const contentType = response.headers.get('content-type');
|
|
|
|
if (contentType && contentType.includes('text/html')) {
|
|
cachedItems.push({
|
|
url: url.pathname,
|
|
title: getPageTitle(url.pathname),
|
|
type: getContentType(url.pathname)
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Cached Items anzeigen
|
|
if (cachedItems.length > 0) {
|
|
cachedItems.forEach(item => {
|
|
const itemElement = document.createElement('div');
|
|
itemElement.className = 'cached-item';
|
|
itemElement.innerHTML = `
|
|
<div>
|
|
<strong>${item.title}</strong><br>
|
|
<small class="text-muted">${item.type}</small>
|
|
</div>
|
|
<button class="btn btn-cached" onclick="openCachedPage('${item.url}')">
|
|
<i class="fas fa-external-link-alt"></i>
|
|
</button>
|
|
`;
|
|
cachedItemsContainer.appendChild(itemElement);
|
|
});
|
|
} else {
|
|
cachedItemsContainer.innerHTML = `
|
|
<div class="text-muted">
|
|
<i class="fas fa-info-circle me-1"></i>
|
|
Keine gecachten Inhalte verfügbar
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('Fehler beim Laden der gecachten Inhalte:', error);
|
|
cachedItemsContainer.innerHTML = `
|
|
<div class="text-muted">
|
|
<i class="fas fa-exclamation-triangle me-1"></i>
|
|
Fehler beim Laden der gecachten Inhalte
|
|
</div>
|
|
`;
|
|
}
|
|
}
|
|
|
|
// Hilfsfunktionen
|
|
function getPageTitle(pathname) {
|
|
const titles = {
|
|
'/products': 'Produktkatalog',
|
|
'/cart': 'Warenkorb',
|
|
'/account': 'Mein Konto'
|
|
};
|
|
|
|
if (pathname.startsWith('/product/')) {
|
|
return 'Produktdetails';
|
|
}
|
|
|
|
if (pathname.startsWith('/category/')) {
|
|
return 'Kategorie';
|
|
}
|
|
|
|
return titles[pathname] || 'Seite';
|
|
}
|
|
|
|
function getContentType(pathname) {
|
|
if (pathname.startsWith('/product/')) {
|
|
return 'Produktseite';
|
|
}
|
|
|
|
if (pathname.startsWith('/category/')) {
|
|
return 'Kategorieseite';
|
|
}
|
|
|
|
return 'Seite';
|
|
}
|
|
|
|
function openCachedPage(url) {
|
|
window.location.href = url;
|
|
}
|
|
|
|
// Event Listeners
|
|
window.addEventListener('online', updateConnectionStatus);
|
|
window.addEventListener('offline', updateConnectionStatus);
|
|
|
|
// Initialisierung
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
updateConnectionStatus();
|
|
loadCachedItems();
|
|
|
|
// Service Worker registrieren
|
|
if ('serviceWorker' in navigator) {
|
|
navigator.serviceWorker.register('/sw.js')
|
|
.then(registration => {
|
|
console.log('Service Worker registriert:', registration);
|
|
})
|
|
.catch(error => {
|
|
console.error('Service Worker Registrierung fehlgeschlagen:', error);
|
|
});
|
|
}
|
|
});
|
|
|
|
// Periodische Verbindungsprüfung
|
|
setInterval(() => {
|
|
if (navigator.onLine) {
|
|
updateConnectionStatus();
|
|
}
|
|
}, 5000);
|
|
</script>
|
|
</body>
|
|
</html> |