diff --git a/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte b/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte index db36577c1..838098a71 100644 --- a/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte +++ b/apps/mana/apps/web/src/lib/components/onboarding/OnboardingWizard.svelte @@ -6,6 +6,7 @@ import ProfileStep from './steps/ProfileStep.svelte'; import AppsStep from './steps/AppsStep.svelte'; import AiTierStep from './steps/AiTierStep.svelte'; + import SyncStep from './steps/SyncStep.svelte'; import CreditsStep from './steps/CreditsStep.svelte'; import CompleteStep from './steps/CompleteStep.svelte'; import { Check } from '@mana/shared-icons'; @@ -31,6 +32,7 @@ { id: 'profile', label: 'Profil', component: ProfileStep }, { id: 'apps', label: 'Apps', component: AppsStep }, { id: 'ai-tier', label: 'KI', component: AiTierStep }, + { id: 'sync', label: 'Sync', component: SyncStep }, { id: 'credits', label: 'Credits', component: CreditsStep }, { id: 'complete', label: 'Fertig', component: CompleteStep }, ]; @@ -159,6 +161,10 @@ {:else if currentStepData.id === 'apps'} + {:else if currentStepData.id === 'ai-tier'} + + {:else if currentStepData.id === 'sync'} + {:else if currentStepData.id === 'credits'} {:else if currentStepData.id === 'complete'} diff --git a/apps/mana/apps/web/src/lib/components/onboarding/steps/SyncStep.svelte b/apps/mana/apps/web/src/lib/components/onboarding/steps/SyncStep.svelte new file mode 100644 index 000000000..54f69ec26 --- /dev/null +++ b/apps/mana/apps/web/src/lib/components/onboarding/steps/SyncStep.svelte @@ -0,0 +1,158 @@ + + +
+
+
+ +
+
Cloud Sync
+

+ Synchronisiere deine Daten verschlüsselt über alle Geräte — oder nutze Mana nur lokal. +

+
+ + +
+
+ +
+
Lokal — immer verfügbar
+
+ Alle deine Daten sind lokal auf deinem Gerät gespeichert. Mana funktioniert vollständig + offline — auch ohne Cloud Sync. +
+
+
+
+ + + {#if syncBilling.active} +
+
+ +
+
Cloud Sync ist aktiv
+
+ Deine Daten werden über alle Geräte synchronisiert. +
+
+
+
+ {:else} +
+
+
+ +
+
+
Cloud Sync aktivieren
+

+ Multi-Device-Sync, automatische Backups, Ende-zu-Ende-Verschlüsselung. +

+
+
+ + +
+ {#each ['monthly', 'quarterly', 'yearly'] as const as iv} + + {/each} +
+ + {#if error} +
+ +

{error}

+
+ {/if} + + + + {#if balance !== null && balance.balance < SYNC_PRICES[selectedInterval].credits} +

+ Nicht genügend Credits ({balance.balance} verfügbar). Du kannst Sync jederzeit später in den + Einstellungen aktivieren. +

+ {/if} +
+ {/if} + +
+ +
+ Sync ist optional — du kannst diesen Schritt überspringen und Mana nur lokal nutzen. Alle + Features funktionieren auch ohne Sync. Du kannst jederzeit in den Einstellungen aktivieren. +
+
+
diff --git a/apps/mana/apps/web/src/routes/(app)/+layout.svelte b/apps/mana/apps/web/src/routes/(app)/+layout.svelte index 454e05ac8..5d4bf43f3 100644 --- a/apps/mana/apps/web/src/routes/(app)/+layout.svelte +++ b/apps/mana/apps/web/src/routes/(app)/+layout.svelte @@ -238,6 +238,72 @@ return first ? first.shortLabel.split(' (')[0] : 'KI'; }); + // ── Sync status dropdown ──────────────────────────────── + let syncStatusItems = $derived.by(() => { + const items: import('@mana/shared-ui').PillDropdownItem[] = []; + + if (syncBilling.active) { + items.push({ + id: 'sync-active', + label: 'Cloud Sync aktiv', + icon: 'cloudCheck', + active: true, + disabled: true, + }); + if (syncBilling.nextChargeAt) { + const date = new Date(syncBilling.nextChargeAt).toLocaleDateString('de-DE', { + day: '2-digit', + month: '2-digit', + year: 'numeric', + }); + items.push({ + id: 'sync-next', + label: `Nächste Abbuchung: ${date}`, + disabled: true, + }); + } + } else if (syncBilling.paused) { + items.push({ + id: 'sync-paused', + label: 'Sync pausiert — Credits aufladen', + icon: 'warning', + onClick: () => goto('/credits?tab=packages'), + }); + } else { + items.push({ + id: 'sync-inactive', + label: 'Sync aktivieren', + icon: 'cloudArrowUp', + onClick: () => goto('/settings/sync'), + }); + items.push({ + id: 'sync-info', + label: 'Nur lokal — ab 30 Credits/Monat', + disabled: true, + }); + } + + items.push({ id: 'sync-divider', label: '', divider: true }); + items.push({ + id: 'sync-settings', + label: 'Sync-Einstellungen', + icon: 'gear', + onClick: () => goto('/settings/sync'), + }); + + return items; + }); + + let currentSyncLabel = $derived( + syncBilling.loading + ? '...' + : syncBilling.active + ? 'Sync' + : syncBilling.paused + ? 'Pausiert' + : 'Lokal' + ); + // ── User / Guest awareness ────────────────────────────── let userEmail = $derived( authStore.isAuthenticated ? authStore.user?.email || $_('nav.menu') : '' @@ -750,6 +816,9 @@ showAiTierSelector={true} {aiTierItems} {currentAiTierLabel} + showSyncStatus={authStore.isAuthenticated} + {syncStatusItems} + {currentSyncLabel} {appItems} {userEmail} settingsHref="/settings" diff --git a/packages/shared-ui/src/navigation/PillNavigation.svelte b/packages/shared-ui/src/navigation/PillNavigation.svelte index 05faac2c6..f7eb2e037 100644 --- a/packages/shared-ui/src/navigation/PillNavigation.svelte +++ b/packages/shared-ui/src/navigation/PillNavigation.svelte @@ -247,6 +247,12 @@ aiTierItems?: PillDropdownItem[]; /** Current AI tier label, e.g. "Browser" or "Server" */ currentAiTierLabel?: string; + /** Show sync status dropdown */ + showSyncStatus?: boolean; + /** Sync status dropdown items */ + syncStatusItems?: PillDropdownItem[]; + /** Current sync status label */ + currentSyncLabel?: string; /** Primary color for active state (CSS custom property or hex) */ primaryColor?: string; /** Elements to prepend before nav items (tab groups, dividers, nav items) */ @@ -342,6 +348,9 @@ showAiTierSelector = false, aiTierItems = [], currentAiTierLabel = 'KI', + showSyncStatus = false, + syncStatusItems = [], + currentSyncLabel = 'Sync', themeMode = 'system', onThemeModeChange, appItems = [], @@ -670,6 +679,16 @@ /> {/if} + + {#if showSyncStatus && syncStatusItems.length > 0} + + {/if} + {#if showThemeToggle && onToggleTheme && !showThemeVariants}