mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-14 19:41:09 +02:00
refactor(pwa): replace custom service workers with Vite PWA plugin and centralize offline page
Remove hand-written sw.js, offline.html, and manifest.json from todo/skilltree/zitare web apps in favor of the Workbox-based service worker generated by @vite-pwa/sveltekit. This fixes an issue where the custom SW could get stuck serving the offline fallback page even when the server was reachable. Also extracts the duplicated offline page (~80 lines each across 19 apps) into a shared OfflinePage component in @manacore/shared-ui with 3 props (appName, offlineMessage, accentColor), reducing each app's offline route to an 8-line wrapper. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
93a7c90f4f
commit
9bdb997394
33 changed files with 262 additions and 2023 deletions
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Calendar</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Deine Termine werden lokal gespeichert und sind offline verfügbar.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Calendar"
|
||||
offlineMessage="Deine Termine werden lokal gespeichert und sind offline verfügbar."
|
||||
accentColor="#2563eb"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Chat</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Chat benötigt eine Internetverbindung, um Nachrichten zu senden.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-violet-600 hover:bg-violet-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Chat"
|
||||
offlineMessage="Chat benötigt eine Internetverbindung, um Nachrichten zu senden."
|
||||
accentColor="#7c3aed"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Clock</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Die Uhr funktioniert auch offline - Timer und Stoppuhr sind verfügbar.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-cyan-600 hover:bg-cyan-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Clock"
|
||||
offlineMessage="Die Uhr funktioniert auch offline - Timer und Stoppuhr sind verfügbar."
|
||||
accentColor="#0891b2"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Contacts</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Kontakte benötigt eine Internetverbindung für Synchronisierung.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-cyan-600 hover:bg-cyan-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Contacts"
|
||||
offlineMessage="Kontakte benötigt eine Internetverbindung für Synchronisierung."
|
||||
accentColor="#0891b2"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,96 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Context</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg
|
||||
class="w-24 h-24 mx-auto text-slate-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0"
|
||||
/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Einige Funktionen sind offline nicht verfügbar. Bitte überprüfe deine Internetverbindung.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a
|
||||
href="/"
|
||||
class="inline-flex items-center justify-center px-6 py-3 bg-sky-600 hover:bg-sky-700 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button
|
||||
onclick={() => window.location.reload()}
|
||||
class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors"
|
||||
>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Context"
|
||||
offlineMessage="Einige Funktionen sind offline nicht verfügbar. Bitte überprüfe deine Internetverbindung."
|
||||
accentColor="#0284c7"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - ManaCore</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
ManaCore benötigt eine Internetverbindung.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-indigo-600 hover:bg-indigo-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="ManaCore"
|
||||
offlineMessage="ManaCore benötigt eine Internetverbindung."
|
||||
accentColor="#4f46e5"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - ManaDeck</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
ManaDeck benötigt eine Internetverbindung für deine Kartendecks.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-amber-600 hover:bg-amber-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="ManaDeck"
|
||||
offlineMessage="ManaDeck benötigt eine Internetverbindung für deine Kartendecks."
|
||||
accentColor="#d97706"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Matrix</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Matrix benötigt eine Internetverbindung für Nachrichten.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-sky-600 hover:bg-sky-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Matrix"
|
||||
offlineMessage="Matrix benötigt eine Internetverbindung für Nachrichten."
|
||||
accentColor="#0284c7"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,104 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Mukke</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg
|
||||
class="w-24 h-24 mx-auto text-slate-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0"
|
||||
/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Mukke benötigt eine Internetverbindung für Audio.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a
|
||||
href="/"
|
||||
class="inline-flex items-center justify-center px-6 py-3 bg-orange-600 hover:bg-orange-700 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
|
||||
/>
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button
|
||||
onclick={() => window.location.reload()}
|
||||
class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors"
|
||||
>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Mukke"
|
||||
offlineMessage="Mukke benötigt eine Internetverbindung für Audio."
|
||||
accentColor="#ea580c"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - NutriPhi</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
NutriPhi benötigt eine Internetverbindung für Analysen.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-green-600 hover:bg-green-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="NutriPhi"
|
||||
offlineMessage="NutriPhi benötigt eine Internetverbindung für Analysen."
|
||||
accentColor="#16a34a"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Photos</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Photos benötigt eine Internetverbindung für deine Fotos.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Photos"
|
||||
offlineMessage="Photos benötigt eine Internetverbindung für deine Fotos."
|
||||
accentColor="#2563eb"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Picture</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Picture benötigt eine Internetverbindung, um Bilder zu generieren.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-pink-600 hover:bg-pink-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Picture"
|
||||
offlineMessage="Picture benötigt eine Internetverbindung, um Bilder zu generieren."
|
||||
accentColor="#db2777"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Planta</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Planta benötigt eine Internetverbindung für Pflegetipps.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-green-600 hover:bg-green-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Planta"
|
||||
offlineMessage="Planta benötigt eine Internetverbindung für Pflegetipps."
|
||||
accentColor="#16a34a"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Presi</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Presi benötigt eine Internetverbindung für Präsentationen.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-purple-600 hover:bg-purple-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Presi"
|
||||
offlineMessage="Presi benötigt eine Internetverbindung für Präsentationen."
|
||||
accentColor="#9333ea"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Questions</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Questions benötigt eine Internetverbindung für Antworten.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-violet-600 hover:bg-violet-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Questions"
|
||||
offlineMessage="Questions benötigt eine Internetverbindung für Antworten."
|
||||
accentColor="#7c3aed"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
"@manacore/shared-icons": "workspace:*",
|
||||
"@manacore/shared-tailwind": "workspace:*",
|
||||
"@manacore/shared-theme": "workspace:*",
|
||||
"@manacore/shared-ui": "workspace:^",
|
||||
"@manacore/shared-utils": "workspace:*",
|
||||
"idb": "^8.0.0",
|
||||
"svelte-i18n": "^4.0.1",
|
||||
|
|
|
|||
|
|
@ -7,12 +7,8 @@
|
|||
<!-- Favicon -->
|
||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<!-- Theme Color -->
|
||||
<meta name="theme-color" content="#10b981" />
|
||||
<meta name="msapplication-TileColor" content="#10b981" />
|
||||
|
||||
<!-- PWA -->
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - SkillTree</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Deine Skills werden lokal gespeichert und sind offline verfügbar.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-violet-600 hover:bg-violet-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="SkillTree"
|
||||
offlineMessage="Deine Skills werden lokal gespeichert und sind offline verfügbar."
|
||||
accentColor="#7c3aed"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
"name": "SkillTree",
|
||||
"short_name": "SkillTree",
|
||||
"description": "Track your skills like a game. Level up in real life.",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#111827",
|
||||
"theme_color": "#10b981",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon-192.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon-512.png",
|
||||
"sizes": "512x512",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,106 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Storage</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg
|
||||
class="w-24 h-24 mx-auto text-slate-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0"
|
||||
/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Storage benötigt eine Internetverbindung für Cloud-Dateien. Kürzlich besuchte Seiten sind
|
||||
möglicherweise noch verfügbar.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<div class="flex flex-col gap-3 mb-4">
|
||||
<a
|
||||
href="/files"
|
||||
class="inline-flex items-center justify-center px-6 py-3 bg-slate-700 hover:bg-slate-600 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
Meine Dateien (cached)
|
||||
</a>
|
||||
<a
|
||||
href="/favorites"
|
||||
class="inline-flex items-center justify-center px-6 py-3 bg-slate-700 hover:bg-slate-600 text-white font-medium rounded-lg transition-colors"
|
||||
>
|
||||
Favoriten (cached)
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<button
|
||||
onclick={() => window.location.reload()}
|
||||
class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors"
|
||||
>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400" role="status" aria-live="polite">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24" aria-hidden="true">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Storage"
|
||||
offlineMessage="Storage benötigt eine Internetverbindung für Cloud-Dateien. Kürzlich besuchte Seiten sind möglicherweise noch verfügbar."
|
||||
accentColor="#475569"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -8,22 +8,13 @@
|
|||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||
<link rel="icon" type="image/svg+xml" href="/icons/icon.svg" />
|
||||
|
||||
<!-- PWA Manifest -->
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
|
||||
<!-- Theme Color -->
|
||||
<!-- PWA meta tags (manifest injected by @vite-pwa/sveltekit via %sveltekit.head%) -->
|
||||
<meta name="theme-color" content="#8b5cf6" />
|
||||
<meta name="msapplication-TileColor" content="#8b5cf6" />
|
||||
|
||||
<!-- PWA -->
|
||||
<meta name="mobile-web-app-capable" content="yes" />
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||
<meta name="apple-mobile-web-app-title" content="Todo" />
|
||||
<link rel="apple-touch-icon" href="/icons/icon.svg" />
|
||||
|
||||
<!-- Microsoft Tiles -->
|
||||
<meta name="msapplication-config" content="none" />
|
||||
|
||||
<title>Todo</title>
|
||||
%sveltekit.head%
|
||||
<!-- Umami Analytics -->
|
||||
|
|
|
|||
|
|
@ -279,31 +279,6 @@
|
|||
} catch {
|
||||
// localStorage not available
|
||||
}
|
||||
|
||||
// Register Service Worker for PWA
|
||||
if ('serviceWorker' in navigator) {
|
||||
try {
|
||||
const registration = await navigator.serviceWorker.register('/sw.js', {
|
||||
scope: '/',
|
||||
});
|
||||
console.log('Todo PWA: Service Worker registered', registration.scope);
|
||||
|
||||
// Check for updates
|
||||
registration.addEventListener('updatefound', () => {
|
||||
const newWorker = registration.installing;
|
||||
if (newWorker) {
|
||||
newWorker.addEventListener('statechange', () => {
|
||||
if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {
|
||||
// New version available
|
||||
console.log('Todo PWA: New version available');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Todo PWA: Service Worker registration failed', error);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Todo</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Deine Aufgaben werden lokal gespeichert und sind offline verfügbar.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-emerald-600 hover:bg-emerald-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Todo"
|
||||
offlineMessage="Deine Aufgaben werden lokal gespeichert und sind offline verfügbar."
|
||||
accentColor="#059669"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,71 +0,0 @@
|
|||
{
|
||||
"name": "Todo - Aufgabenverwaltung",
|
||||
"short_name": "Todo",
|
||||
"description": "Aufgaben und Projekte verwalten mit Kanban-Board, Subtasks und mehr",
|
||||
"start_url": "/",
|
||||
"scope": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#ffffff",
|
||||
"theme_color": "#8b5cf6",
|
||||
"orientation": "any",
|
||||
"categories": ["productivity", "utilities"],
|
||||
"lang": "de",
|
||||
"dir": "ltr",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "any"
|
||||
},
|
||||
{
|
||||
"src": "/icons/icon.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml",
|
||||
"purpose": "maskable"
|
||||
}
|
||||
],
|
||||
"shortcuts": [
|
||||
{
|
||||
"name": "Neue Aufgabe",
|
||||
"short_name": "Neu",
|
||||
"description": "Neue Aufgabe erstellen",
|
||||
"url": "/?action=new",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Kanban Board",
|
||||
"short_name": "Kanban",
|
||||
"description": "Kanban-Ansicht öffnen",
|
||||
"url": "/kanban",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Einstellungen",
|
||||
"short_name": "Settings",
|
||||
"description": "App-Einstellungen öffnen",
|
||||
"url": "/settings",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/icons/icon.svg",
|
||||
"sizes": "any",
|
||||
"type": "image/svg+xml"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"screenshots": [],
|
||||
"prefer_related_applications": false
|
||||
}
|
||||
|
|
@ -1,228 +0,0 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Offline - Todo</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
background: linear-gradient(135deg, #f5f3ff 0%, #ede9fe 100%);
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
body {
|
||||
background: linear-gradient(135deg, #1e1b2e 0%, #2d2640 100%);
|
||||
color: #f3f4f6;
|
||||
}
|
||||
}
|
||||
|
||||
.container {
|
||||
text-align: center;
|
||||
max-width: 400px;
|
||||
padding: 2rem;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
border-radius: 1.5rem;
|
||||
box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.15);
|
||||
backdrop-filter: blur(20px);
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.container {
|
||||
background: rgba(30, 27, 46, 0.9);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.icon {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
margin: 0 auto 1.5rem;
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
|
||||
border-radius: 1rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.icon svg {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
color: white;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
h1 {
|
||||
color: #f3f4f6;
|
||||
}
|
||||
}
|
||||
|
||||
p {
|
||||
color: #6b7280;
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
p {
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1rem;
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
border-radius: 9999px;
|
||||
color: #ef4444;
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #ef4444;
|
||||
border-radius: 50%;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
||||
button {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.75rem 1.5rem;
|
||||
background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%);
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 9999px;
|
||||
font-size: 0.875rem;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: transform 0.15s, box-shadow 0.15s;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 20px -10px rgba(139, 92, 246, 0.5);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.tips {
|
||||
margin-top: 2rem;
|
||||
padding-top: 1.5rem;
|
||||
border-top: 1px solid rgba(0, 0, 0, 0.1);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.tips {
|
||||
border-top-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.tips h2 {
|
||||
font-size: 0.75rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: #9ca3af;
|
||||
margin-bottom: 0.75rem;
|
||||
}
|
||||
|
||||
.tips ul {
|
||||
list-style: none;
|
||||
font-size: 0.875rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
.tips ul {
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
|
||||
.tips li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.tips li::before {
|
||||
content: '•';
|
||||
color: #8b5cf6;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<div class="icon">
|
||||
<svg fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a4.978 4.978 0 01-1.414-2.83m-1.414 5.658a9 9 0 01-2.167-9.238m7.824 2.167a1 1 0 111.414 1.414m-1.414-1.414L3 3m8.293 8.293l1.414 1.414" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1>Du bist offline</h1>
|
||||
<p>Keine Internetverbindung. Sobald du wieder online bist, kannst du deine Aufgaben verwalten.</p>
|
||||
|
||||
<div class="status">
|
||||
<span class="status-dot"></span>
|
||||
<span>Verbindung wird gesucht...</span>
|
||||
</div>
|
||||
|
||||
<button onclick="location.reload()">
|
||||
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||
d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15" />
|
||||
</svg>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
|
||||
<div class="tips">
|
||||
<h2>Was du tun kannst</h2>
|
||||
<ul>
|
||||
<li>Überprüfe deine WLAN-Verbindung</li>
|
||||
<li>Prüfe deine mobilen Daten</li>
|
||||
<li>Versuche es in einigen Sekunden erneut</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Automatisch neu laden, wenn wieder online
|
||||
window.addEventListener('online', () => {
|
||||
location.reload();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -1,147 +0,0 @@
|
|||
const CACHE_NAME = 'todo-v3';
|
||||
const OFFLINE_URL = '/offline.html';
|
||||
|
||||
// Assets, die immer gecacht werden sollen
|
||||
const STATIC_CACHE_URLS = ['/', '/offline.html', '/icons/icon.svg', '/manifest.json'];
|
||||
|
||||
// Cache-Strategien für verschiedene Ressourcen
|
||||
const CACHE_STRATEGIES = {
|
||||
// Netzwerk zuerst, dann Cache (für HTML/Navigation)
|
||||
networkFirst: [/\/$/, /\.html$/, /^\/kanban/, /^\/settings/, /^\/mana/, /^\/feedback/],
|
||||
// Cache zuerst, dann Netzwerk (für Assets) - nur für gebaute Assets, nicht /src/
|
||||
cacheFirst: [
|
||||
/\/_app\//, // SvelteKit gebaute Assets
|
||||
/\.woff2?$/,
|
||||
/\.ttf$/,
|
||||
/\.otf$/,
|
||||
/\.ico$/,
|
||||
],
|
||||
// Nur Netzwerk (für API-Calls und Dev-Server)
|
||||
networkOnly: [/\/api\//, /^\/src\//, /^\/@/, /^\/node_modules\//],
|
||||
};
|
||||
|
||||
// Service Worker Installation
|
||||
self.addEventListener('install', (event) => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.open(CACHE_NAME)
|
||||
.then((cache) => {
|
||||
console.log('Todo Service Worker: Caching static assets');
|
||||
return cache.addAll(STATIC_CACHE_URLS);
|
||||
})
|
||||
.then(() => self.skipWaiting())
|
||||
);
|
||||
});
|
||||
|
||||
// Service Worker Aktivierung
|
||||
self.addEventListener('activate', (event) => {
|
||||
event.waitUntil(
|
||||
caches
|
||||
.keys()
|
||||
.then((cacheNames) => {
|
||||
return Promise.all(
|
||||
cacheNames
|
||||
.filter((cacheName) => cacheName.startsWith('todo-') && cacheName !== CACHE_NAME)
|
||||
.map((cacheName) => caches.delete(cacheName))
|
||||
);
|
||||
})
|
||||
.then(() => self.clients.claim())
|
||||
);
|
||||
});
|
||||
|
||||
// Fetch-Event Handler
|
||||
self.addEventListener('fetch', (event) => {
|
||||
const { request } = event;
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Ignoriere Chrome Extension Requests
|
||||
if (url.protocol === 'chrome-extension:') {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignoriere Cross-Origin Requests (z.B. Backend API)
|
||||
if (url.origin !== self.location.origin) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Bestimme die Cache-Strategie
|
||||
const strategy = getStrategy(url.pathname);
|
||||
|
||||
if (strategy === 'networkFirst') {
|
||||
event.respondWith(networkFirst(request));
|
||||
} else if (strategy === 'cacheFirst') {
|
||||
event.respondWith(cacheFirst(request));
|
||||
} else if (strategy === 'networkOnly') {
|
||||
event.respondWith(networkOnly(request));
|
||||
} else {
|
||||
// Standard: Network First
|
||||
event.respondWith(networkFirst(request));
|
||||
}
|
||||
});
|
||||
|
||||
// Cache-Strategien Implementierung
|
||||
async function networkFirst(request) {
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(CACHE_NAME);
|
||||
cache.put(request, networkResponse.clone());
|
||||
}
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
// Wenn es eine Navigation ist und wir offline sind, zeige die Offline-Seite
|
||||
if (request.mode === 'navigate') {
|
||||
const offlineResponse = await caches.match(OFFLINE_URL);
|
||||
if (offlineResponse) {
|
||||
return offlineResponse;
|
||||
}
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function cacheFirst(request) {
|
||||
const cachedResponse = await caches.match(request);
|
||||
if (cachedResponse) {
|
||||
return cachedResponse;
|
||||
}
|
||||
|
||||
try {
|
||||
const networkResponse = await fetch(request);
|
||||
if (networkResponse.ok) {
|
||||
const cache = await caches.open(CACHE_NAME);
|
||||
cache.put(request, networkResponse.clone());
|
||||
}
|
||||
return networkResponse;
|
||||
} catch (error) {
|
||||
console.error('Todo SW: Fetch failed:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
async function networkOnly(request) {
|
||||
return fetch(request);
|
||||
}
|
||||
|
||||
// Hilfsfunktion zur Bestimmung der Cache-Strategie
|
||||
function getStrategy(pathname) {
|
||||
for (const [strategy, patterns] of Object.entries(CACHE_STRATEGIES)) {
|
||||
if (patterns.some((pattern) => pattern.test(pathname))) {
|
||||
return strategy;
|
||||
}
|
||||
}
|
||||
return 'networkFirst';
|
||||
}
|
||||
|
||||
// Message Handler für Updates
|
||||
self.addEventListener('message', (event) => {
|
||||
if (event.data && event.data.type === 'SKIP_WAITING') {
|
||||
self.skipWaiting();
|
||||
}
|
||||
});
|
||||
|
|
@ -12,10 +12,30 @@ export default defineConfig({
|
|||
sveltekit(),
|
||||
SvelteKitPWA(
|
||||
createPWAConfig({
|
||||
name: 'Todo - Aufgaben',
|
||||
name: 'Todo - Aufgabenverwaltung',
|
||||
shortName: 'Todo',
|
||||
description: 'Aufgaben und Projekte verwalten',
|
||||
themeColor: '#10b981',
|
||||
description: 'Aufgaben und Projekte verwalten mit Kanban-Board, Subtasks und mehr',
|
||||
themeColor: '#8b5cf6',
|
||||
shortcuts: [
|
||||
{
|
||||
name: 'Neue Aufgabe',
|
||||
short_name: 'Neu',
|
||||
description: 'Neue Aufgabe erstellen',
|
||||
url: '/?action=new',
|
||||
},
|
||||
{
|
||||
name: 'Kanban Board',
|
||||
short_name: 'Kanban',
|
||||
description: 'Kanban-Ansicht öffnen',
|
||||
url: '/kanban',
|
||||
},
|
||||
{
|
||||
name: 'Einstellungen',
|
||||
short_name: 'Settings',
|
||||
description: 'App-Einstellungen öffnen',
|
||||
url: '/settings',
|
||||
},
|
||||
],
|
||||
})
|
||||
),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<meta name="theme-color" content="#8b5cf6" />
|
||||
<meta name="description" content="Zitare - Inspirierende Zitate jeden Tag" />
|
||||
<link rel="manifest" href="%sveltekit.assets%/manifest.json" />
|
||||
<title>Zitare</title>
|
||||
%sveltekit.head%
|
||||
<!-- Umami Analytics -->
|
||||
|
|
|
|||
|
|
@ -1,78 +1,9 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
import { OfflinePage } from '@manacore/shared-ui';
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - Zitare</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg class="w-24 h-24 mx-auto text-slate-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0" />
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
Zitare benötigt eine Internetverbindung für neue Zitate.
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a href="/" class="inline-flex items-center justify-center px-6 py-3 bg-amber-600 hover:bg-amber-700 text-white font-medium rounded-lg transition-colors">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" />
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button onclick={() => window.location.reload()} class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors">
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<OfflinePage
|
||||
appName="Zitare"
|
||||
offlineMessage="Zitare benötigt eine Internetverbindung für neue Zitate."
|
||||
accentColor="#d97706"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
{
|
||||
"name": "Zitare",
|
||||
"short_name": "Zitare",
|
||||
"description": "Inspirierende Zitate jeden Tag",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#1a1a2e",
|
||||
"theme_color": "#8b5cf6",
|
||||
"icons": [
|
||||
{
|
||||
"src": "/favicon.png",
|
||||
"sizes": "192x192",
|
||||
"type": "image/png"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -155,6 +155,7 @@ export type { QuickInputItem, QuickAction, CreatePreview, InputBarSettings } fro
|
|||
|
||||
// Pages
|
||||
export { default as AppsPage } from './pages/AppsPage.svelte';
|
||||
export { default as OfflinePage } from './pages/OfflinePage.svelte';
|
||||
|
||||
// Charts - Statistics Visualization
|
||||
export {
|
||||
|
|
|
|||
119
packages/shared-ui/src/pages/OfflinePage.svelte
Normal file
119
packages/shared-ui/src/pages/OfflinePage.svelte
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
appName: string;
|
||||
offlineMessage: string;
|
||||
accentColor?: string;
|
||||
}
|
||||
|
||||
let { appName, offlineMessage, accentColor = '#3b82f6' }: Props = $props();
|
||||
|
||||
let isOnline = $state(false);
|
||||
|
||||
onMount(() => {
|
||||
isOnline = navigator.onLine;
|
||||
|
||||
const handleOnline = () => {
|
||||
isOnline = true;
|
||||
setTimeout(() => {
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
};
|
||||
|
||||
const handleOffline = () => {
|
||||
isOnline = false;
|
||||
};
|
||||
|
||||
window.addEventListener('online', handleOnline);
|
||||
window.addEventListener('offline', handleOffline);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('online', handleOnline);
|
||||
window.removeEventListener('offline', handleOffline);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Offline - {appName}</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="min-h-screen bg-slate-900 flex items-center justify-center px-4">
|
||||
<div class="text-center max-w-md">
|
||||
<div class="mb-8">
|
||||
<svg
|
||||
class="w-24 h-24 mx-auto text-slate-500"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="1.5"
|
||||
d="M18.364 5.636a9 9 0 010 12.728m0 0l-2.829-2.829m2.829 2.829L21 21M15.536 8.464a5 5 0 010 7.072m0 0l-2.829-2.829m-4.243 2.829a5 5 0 01-.354-7.072L8.95 7.636m1.414 5.657L7.535 16.12m8.485 0a5 5 0 01-7.07 0"
|
||||
/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 3l18 18" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-2xl font-bold text-white mb-4">
|
||||
{isOnline ? 'Verbindung wiederhergestellt!' : 'Du bist offline'}
|
||||
</h1>
|
||||
|
||||
<p class="text-slate-400 mb-8">
|
||||
{#if isOnline}
|
||||
Du wirst gleich weitergeleitet...
|
||||
{:else}
|
||||
{offlineMessage}
|
||||
{/if}
|
||||
</p>
|
||||
|
||||
{#if !isOnline}
|
||||
<div class="space-y-4">
|
||||
<a
|
||||
href="/"
|
||||
class="inline-flex items-center justify-center px-6 py-3 text-white font-medium rounded-lg transition-colors"
|
||||
style="background-color: {accentColor};"
|
||||
onmouseenter={(e) => {
|
||||
e.currentTarget.style.filter = 'brightness(0.85)';
|
||||
}}
|
||||
onmouseleave={(e) => {
|
||||
e.currentTarget.style.filter = '';
|
||||
}}
|
||||
>
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6"
|
||||
/>
|
||||
</svg>
|
||||
Zur Startseite
|
||||
</a>
|
||||
|
||||
<button
|
||||
onclick={() => window.location.reload()}
|
||||
class="block w-full px-6 py-3 text-slate-400 hover:text-white transition-colors"
|
||||
>
|
||||
Erneut versuchen
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="flex items-center justify-center text-green-400">
|
||||
<svg class="w-5 h-5 mr-2 animate-spin" fill="none" viewBox="0 0 24 24">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"
|
||||
></circle>
|
||||
<path
|
||||
class="opacity-75"
|
||||
fill="currentColor"
|
||||
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
|
||||
></path>
|
||||
</svg>
|
||||
Weiterleitung...
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
|
|
@ -4959,6 +4959,9 @@ importers:
|
|||
'@manacore/shared-theme':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../packages/shared-theme
|
||||
'@manacore/shared-ui':
|
||||
specifier: workspace:^
|
||||
version: link:../../../../packages/shared-ui
|
||||
'@manacore/shared-utils':
|
||||
specifier: workspace:*
|
||||
version: link:../../../../packages/shared-utils
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue