🔥 remove: demo mode from todo, contacts, clock, questions, chat apps

- Enforce login redirect for unauthenticated users
- Remove demo banner, AuthGateModal, and GuestWelcomeModal
- Remove guest mode state variables and CSS styles
- Simplify showLogout to always show when user is logged in

Affected apps: todo-web, contacts-web, clock-web, questions-web, chat-web
This commit is contained in:
Till-JS 2026-01-30 17:21:22 +01:00
parent 68219a01df
commit f07387d12c
5 changed files with 76 additions and 603 deletions

View file

@ -24,11 +24,6 @@
import { getPillAppItems } from '@manacore/shared-branding';
import { getLanguageDropdownItems, getCurrentLanguageLabel } from '@manacore/shared-i18n';
import { setLocale, supportedLocales } from '$lib/i18n';
import {
AuthGateModal,
GuestWelcomeModal,
shouldShowGuestWelcome,
} from '@manacore/shared-auth-ui';
import type { LayoutData } from './$types';
// App switcher items
@ -40,17 +35,6 @@
let isSidebarMode = $state(false);
let isCollapsed = $state(false);
// Guest mode state
let showAuthGateModal = $state(false);
let authGateAction = $state<'save' | 'sync' | 'ai' | 'feature'>('ai');
// Guest welcome modal state
let showGuestWelcome = $state(false);
// Check if in guest mode
let isGuestMode = $derived(!authStore.isAuthenticated);
let sessionConversationCount = $derived(sessionConversationsStore.count);
// Use theme store's isDark directly
let isDark = $derived(theme.isDark);
@ -170,8 +154,15 @@
goto('/login');
}
// Initialize on mount - supports both authenticated and guest mode
// Initialize on mount - enforce login
onMount(async () => {
// Initialize auth and redirect if not authenticated
await authStore.initialize();
if (!authStore.isAuthenticated) {
goto('/login');
return;
}
// Initialize theme
theme.initialize();
@ -189,27 +180,18 @@
collapsedStore.set(true);
}
await authStore.initialize();
// Load user settings
await userSettings.load();
// Show guest welcome modal for unauthenticated users
if (!authStore.isAuthenticated && shouldShowGuestWelcome('chat')) {
showGuestWelcome = true;
// Check for session conversations to migrate
if (conversationsStore.hasSessionConversations) {
await conversationsStore.migrateSessionConversations();
}
// Load user settings if authenticated
if (authStore.isAuthenticated) {
await userSettings.load();
// Check for session conversations to migrate
if (conversationsStore.hasSessionConversations) {
await conversationsStore.migrateSessionConversations();
}
// Redirect to start page if on /chat and a custom start page is set
const currentPath = window.location.pathname;
if (currentPath === '/chat' && userSettings.startPage && userSettings.startPage !== '/chat') {
goto(userSettings.startPage, { replaceState: true });
}
// Redirect to start page if on /chat and a custom start page is set
const currentPath = window.location.pathname;
if (currentPath === '/chat' && userSettings.startPage && userSettings.startPage !== '/chat') {
goto(userSettings.startPage, { replaceState: true });
}
isChecking = false;
@ -229,22 +211,8 @@
</div>
</div>
{:else}
<!-- Guest Mode Banner -->
{#if isGuestMode}
<div class="guest-banner">
<span>
Du bist im Gast-Modus.
{#if sessionConversationCount > 0}
{sessionConversationCount}
{sessionConversationCount === 1 ? 'Unterhaltung' : 'Unterhaltungen'} in dieser Session.
{/if}
</span>
<button onclick={() => goto('/login')}>Anmelden</button>
</div>
{/if}
<!-- Navigation Layout -->
<div class="layout-container" class:has-guest-banner={isGuestMode}>
<div class="layout-container">
<!-- Floating/Sidebar Pill Navigation -->
<PillNavigation
items={navItems}
@ -296,91 +264,15 @@
{/if}
</main>
</div>
<!-- Auth Gate Modal -->
<AuthGateModal
visible={showAuthGateModal}
onClose={() => (showAuthGateModal = false)}
onLogin={() => {
showAuthGateModal = false;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('auth-return-url', window.location.pathname);
}
goto('/login');
}}
onRegister={() => {
showAuthGateModal = false;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('auth-return-url', window.location.pathname);
}
goto('/register');
}}
action={authGateAction}
migrationCount={sessionConversationCount}
locale={currentLocale === 'en' ? 'en' : 'de'}
/>
<!-- Guest Welcome Modal -->
<GuestWelcomeModal
appId="chat"
visible={showGuestWelcome}
onClose={() => (showGuestWelcome = false)}
onLogin={() => {
showGuestWelcome = false;
goto('/login');
}}
onRegister={() => {
showGuestWelcome = false;
goto('/register');
}}
helpHref="/help"
locale={currentLocale === 'en' ? 'en' : 'de'}
/>
{/if}
<style>
.guest-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 60;
background-color: #3b82f6;
color: white;
padding: 0.5rem 1rem;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
font-size: 0.875rem;
}
.guest-banner button {
background-color: white;
color: #3b82f6;
padding: 0.25rem 0.75rem;
border-radius: 0.375rem;
font-weight: 500;
font-size: 0.875rem;
border: none;
cursor: pointer;
transition: background-color 0.15s;
}
.guest-banner button:hover {
background-color: #f0f9ff;
}
.layout-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.layout-container.has-guest-banner {
padding-top: 40px;
}
.main-content {
flex: 1;
transition: all 300ms ease;

View file

@ -33,8 +33,6 @@
import { setLocale, supportedLocales } from '$lib/i18n';
import { alarmsApi } from '$lib/api/alarms';
import { timersApi } from '$lib/api/timers';
import AuthGateModal from '$lib/components/AuthGateModal.svelte';
import { GuestWelcomeModal, shouldShowGuestWelcome } from '@manacore/shared-auth-ui';
// App switcher items
const appItems = getPillAppItems('clock');
@ -119,20 +117,6 @@
let isSidebarMode = $state(false);
let isCollapsed = $state(false);
// Guest mode state
let showAuthGateModal = $state(false);
let authGateAction = $state<'save' | 'sync' | 'feature'>('save');
// Guest welcome modal state
let showGuestWelcome = $state(false);
// Check if in guest mode
let isGuestMode = $derived(!authStore.isAuthenticated);
let sessionItemCount = $derived(sessionAlarmsStore.count + sessionTimersStore.count);
// Language for GuestWelcomeModal
let currentLocale = $derived($locale || 'de');
// Use theme store's isDark directly
let isDark = $derived(theme.isDark);
@ -195,7 +179,7 @@
{ href: '/feedback', label: 'Feedback', icon: 'chat' },
];
// Navigation items filtered by visibility settings (with fallback for guest mode)
// Navigation items filtered by visibility settings
const navItems = $derived(
filterHiddenNavItems('clock', baseNavItems, userSettings.nav?.hiddenNavItems || {})
);
@ -259,6 +243,13 @@
}
onMount(async () => {
// Initialize auth and redirect if not authenticated
await authStore.initialize();
if (!authStore.isAuthenticated) {
goto('/login');
return;
}
// Initialize sidebar mode from localStorage
const savedSidebar = localStorage.getItem('clock-nav-sidebar');
if (savedSidebar === 'true') {
@ -273,49 +264,28 @@
collapsedStore.set(true);
}
// Show guest welcome modal for unauthenticated users
if (!authStore.isAuthenticated && shouldShowGuestWelcome('clock')) {
showGuestWelcome = true;
// Load user settings
await userSettings.load();
// Check for session data to migrate
if (alarmsStore.hasSessionAlarms) {
await alarmsStore.migrateSessionAlarms();
}
if (timersStore.hasSessionTimers) {
await timersStore.migrateSessionTimers();
}
// Load user settings if authenticated
if (authStore.isAuthenticated) {
await userSettings.load();
// Check for session data to migrate
if (alarmsStore.hasSessionAlarms) {
await alarmsStore.migrateSessionAlarms();
}
if (timersStore.hasSessionTimers) {
await timersStore.migrateSessionTimers();
}
// Redirect to start page if on root and a custom start page is set
const currentPath = window.location.pathname;
if (currentPath === '/' && userSettings.startPage && userSettings.startPage !== '/') {
goto(userSettings.startPage, { replaceState: true });
}
// Redirect to start page if on root and a custom start page is set
const currentPath = window.location.pathname;
if (currentPath === '/' && userSettings.startPage && userSettings.startPage !== '/') {
goto(userSettings.startPage, { replaceState: true });
}
});
</script>
<svelte:window onkeydown={handleKeydown} />
<!-- Guest Mode Banner -->
{#if isGuestMode}
<div class="guest-banner">
<span>
Du bist im Gast-Modus.
{#if sessionItemCount > 0}
{sessionItemCount}
{sessionItemCount === 1 ? 'Element' : 'Elemente'} in dieser Session.
{/if}
</span>
<button onclick={() => goto('/login')}>Anmelden</button>
</div>
{/if}
<div class="layout-container" class:has-guest-banner={isGuestMode}>
<div class="layout-container">
<PillNavigation
items={navItems}
currentPath={$page.url.pathname}
@ -337,7 +307,7 @@
showLanguageSwitcher={true}
{languageItems}
{currentLanguageLabel}
showLogout={authStore.isAuthenticated}
showLogout={true}
onLogout={handleLogout}
loginHref="/login"
primaryColor="#f59e0b"
@ -371,76 +341,15 @@
emptyText="Keine Ergebnisse"
searchingText="Suche..."
/>
<!-- Auth Gate Modal -->
<AuthGateModal
open={showAuthGateModal}
action={authGateAction}
itemCount={sessionItemCount}
onClose={() => (showAuthGateModal = false)}
/>
<!-- Guest Welcome Modal -->
<GuestWelcomeModal
appId="clock"
visible={showGuestWelcome}
onClose={() => (showGuestWelcome = false)}
onLogin={() => {
showGuestWelcome = false;
goto('/login');
}}
onRegister={() => {
showGuestWelcome = false;
goto('/register');
}}
helpHref="/help"
locale={currentLocale === 'en' ? 'en' : 'de'}
/>
</div>
<style>
.guest-banner {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 60;
background-color: #f59e0b;
color: white;
padding: 0.5rem 1rem;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
font-size: 0.875rem;
}
.guest-banner button {
background-color: white;
color: #f59e0b;
padding: 0.25rem 0.75rem;
border-radius: 0.375rem;
font-weight: 500;
font-size: 0.875rem;
border: none;
cursor: pointer;
transition: background-color 0.15s;
}
.guest-banner button:hover {
background-color: #fef3c7;
}
.layout-container {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.layout-container.has-guest-banner {
padding-top: 40px;
}
.main-content {
transition: all 300ms ease;
position: relative;

View file

@ -46,12 +46,6 @@
formatParsedContactPreview,
} from '$lib/utils/contact-parser';
import ContactsToolbar from '$lib/components/ContactsToolbar.svelte';
import {
AuthGateModal,
GuestWelcomeModal,
shouldShowGuestWelcome,
} from '@manacore/shared-auth-ui';
import { browser } from '$app/environment';
// Tags state for Quick-Create
let availableTags = $state<{ id: string; name: string }[]>([]);
@ -216,31 +210,6 @@
goto('/login');
}
// Auth gate modal state
let showAuthGateModal = $state(false);
let authGateAction = $state<'save' | 'sync' | 'feature'>('save');
// Guest welcome modal state
let showGuestWelcome = $state(false);
// Show auth gate modal (can be called from child components)
function showAuthGate(action: 'save' | 'sync' | 'feature' = 'save') {
authGateAction = action;
showAuthGateModal = true;
}
// Listen for show-auth-gate events from child components
$effect(() => {
if (browser) {
const handler = (e: Event) => {
const customEvent = e as CustomEvent<{ action?: 'save' | 'sync' | 'feature' }>;
showAuthGate(customEvent.detail?.action || 'save');
};
window.addEventListener('show-auth-gate', handler);
return () => window.removeEventListener('show-auth-gate', handler);
}
});
async function handleCloseContactModal() {
// Refresh contacts list in case something was changed
await contactsStore.loadContacts();
@ -301,6 +270,13 @@
}
onMount(async () => {
// Initialize auth and redirect if not authenticated
await authStore.initialize();
if (!authStore.isAuthenticated) {
goto('/login');
return;
}
// Initialize split-panel from URL/localStorage
splitPanel.initialize();
@ -309,13 +285,7 @@
viewModeStore.initialize();
contactsFilterStore.initialize();
// Show guest welcome modal for unauthenticated users
if (!authStore.isAuthenticated && shouldShowGuestWelcome('contacts')) {
showGuestWelcome = true;
}
// Only fetch user data if authenticated
if (authStore.isAuthenticated) {
// Fetch user data
// Load user settings and tags
await userSettings.load();
@ -357,41 +327,6 @@
<SplitPaneContainer>
<!-- Navigation Layout -->
<div class="layout-container">
<!-- Demo Mode Banner -->
{#if !authStore.isAuthenticated}
<div
class="guest-banner bg-primary/10 border-primary/20 fixed top-0 right-0 left-0 z-50 flex items-center justify-between border-b px-4 py-2"
>
<div class="flex items-center gap-2 text-sm">
<svg class="text-primary h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
<span class="text-foreground">
<strong>Demo-Modus</strong>
<span class="text-muted-foreground hidden sm:inline">
- Beispiel-Kontakte zum Ausprobieren
</span>
</span>
</div>
<button
onclick={() => showAuthGate('save')}
class="bg-primary text-primary-foreground hover:bg-primary/90 rounded-md px-3 py-1 text-sm font-medium transition-colors"
>
Anmelden
</button>
</div>
{/if}
<!-- UI Elements (hidden in immersive mode) -->
{#if !contactsSettings.immersiveModeEnabled}
<!-- Floating/Sidebar Pill Navigation (at bottom) -->
@ -416,7 +351,7 @@
showLanguageSwitcher={true}
{languageItems}
{currentLanguageLabel}
showLogout={authStore.isAuthenticated}
showLogout={true}
onLogout={handleLogout}
loginHref="/login"
primaryColor="#3b82f6"
@ -482,58 +417,7 @@
</div>
</SplitPaneContainer>
<!-- Auth Gate Modal -->
<AuthGateModal
visible={showAuthGateModal}
onClose={() => (showAuthGateModal = false)}
onLogin={() => {
showAuthGateModal = false;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('auth-return-url', window.location.pathname);
}
goto('/login');
}}
onRegister={() => {
showAuthGateModal = false;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('auth-return-url', window.location.pathname);
}
goto('/register');
}}
action={authGateAction}
locale={currentLocale === 'en' ? 'en' : 'de'}
infoText="Im Demo-Modus werden Beispielkontakte angezeigt. Melde dich an, um eigene Kontakte zu erstellen."
/>
<!-- Guest Welcome Modal -->
<GuestWelcomeModal
appId="contacts"
visible={showGuestWelcome}
onClose={() => (showGuestWelcome = false)}
onLogin={() => {
showGuestWelcome = false;
goto('/login');
}}
onRegister={() => {
showGuestWelcome = false;
goto('/register');
}}
helpHref="/help"
locale={currentLocale === 'en' ? 'en' : 'de'}
/>
<style>
/* Guest banner styling */
.guest-banner {
height: 40px;
min-height: 40px;
}
/* Offset content when guest banner is visible */
.layout-container:has(.guest-banner) .main-content {
padding-top: 40px;
}
.layout-container {
display: flex;
flex-direction: column;

View file

@ -15,7 +15,6 @@
CreatePreview,
} from '@manacore/shared-ui';
import { getPillAppItems } from '@manacore/shared-branding';
import { GuestWelcomeModal, shouldShowGuestWelcome } from '@manacore/shared-auth-ui';
let { children } = $props();
@ -41,22 +40,19 @@
// User email for nav
let userEmail = $derived(authStore.user?.email || 'Menu');
// Guest welcome modal state
let showGuestWelcome = $state(false);
onMount(async () => {
// Set API token if authenticated
if (authStore.isAuthenticated) {
const token = await authStore.getValidToken();
apiClient.setAccessToken(token);
} else {
// Show guest welcome modal for unauthenticated users
if (shouldShowGuestWelcome('questions')) {
showGuestWelcome = true;
}
// Initialize auth and redirect if not authenticated
await authStore.initialize();
if (!authStore.isAuthenticated) {
goto('/login');
return;
}
// Load initial data (works in both guest and authenticated mode)
// Set API token
const token = await authStore.getValidToken();
apiClient.setAccessToken(token);
// Load initial data
await collectionsStore.load();
await questionsStore.load();
@ -146,12 +142,6 @@
async function handleCreate(query: string): Promise<void> {
if (!query.trim()) return;
// Demo mode: show login prompt
if (!authStore.isAuthenticated) {
showGuestWelcome = true;
return;
}
const question = await questionsStore.create({
title: query,
collectionId: collectionsStore.selectedId || undefined,
@ -202,54 +192,11 @@
{ href: '/collections', label: 'Collections', icon: 'folder' },
{ href: '/settings', label: 'Settings', icon: 'settings' },
]);
// Guest features for welcome modal
const guestFeatures = [
'Browse sample research questions',
'Explore the app interface',
'See how AI research works',
];
</script>
<svelte:window onresize={updateMobileState} />
<div class="layout-container">
<!-- Demo Mode Banner -->
{#if !authStore.isAuthenticated}
<div
class="guest-banner fixed left-0 right-0 top-0 z-50 flex items-center justify-between border-b border-primary/20 bg-primary/10 px-4 py-2"
>
<div class="flex items-center gap-2 text-sm">
<svg class="h-4 w-4 text-primary" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
<span class="text-foreground">
<strong>Demo Mode</strong>
<span class="hidden text-muted-foreground sm:inline">
- Sample questions to explore
</span>
</span>
</div>
<button
onclick={() => goto('/login')}
class="rounded-md bg-primary px-3 py-1 text-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90"
>
Sign In
</button>
</div>
{/if}
<!-- Navigation -->
<PillNavigation
items={navItems}
@ -264,7 +211,7 @@
onCollapsedChange={handleCollapsedChange}
desktopPosition="bottom"
showThemeToggle={true}
showLogout={authStore.isAuthenticated}
showLogout={true}
onLogout={handleSignOut}
loginHref="/login"
primaryColor="#8b5cf6"
@ -289,35 +236,13 @@
/>
<!-- Main Content -->
<main
class="main-content bg-background"
class:sidebar-mode={isSidebarMode && !isCollapsed}
class:has-banner={!authStore.isAuthenticated}
>
<main class="main-content bg-background" class:sidebar-mode={isSidebarMode && !isCollapsed}>
<div class="content-wrapper">
{@render children()}
</div>
</main>
</div>
<!-- Guest Welcome Modal -->
<GuestWelcomeModal
appId="questions"
visible={showGuestWelcome}
onClose={() => (showGuestWelcome = false)}
onLogin={() => {
showGuestWelcome = false;
goto('/login');
}}
onRegister={() => {
showGuestWelcome = false;
goto('/register');
}}
helpHref="/help"
locale="en"
features={guestFeatures}
/>
<style>
.layout-container {
display: flex;
@ -326,12 +251,6 @@
overflow: hidden;
}
/* Guest banner styling */
.guest-banner {
height: 40px;
min-height: 40px;
}
.main-content {
flex: 1;
display: flex;
@ -341,10 +260,6 @@
transition: all 300ms ease;
}
.main-content.has-banner {
padding-top: 40px;
}
.main-content.sidebar-mode {
padding-left: 180px;
padding-bottom: 0;
@ -373,10 +288,6 @@
padding-bottom: calc(150px + env(safe-area-inset-bottom));
}
.main-content.has-banner {
padding-top: 40px;
}
.content-wrapper {
padding: 0.75rem;
}

View file

@ -39,12 +39,6 @@
import { getPillAppItems } from '@manacore/shared-branding';
import { getTasks } from '$lib/api/tasks';
import { parseTaskInput, resolveTaskIds, formatParsedTaskPreview } from '$lib/utils/task-parser';
import {
AuthGateModal,
GuestWelcomeModal,
shouldShowGuestWelcome,
} from '@manacore/shared-auth-ui';
import { browser } from '$app/environment';
// App switcher items
const appItems = getPillAppItems('todo');
@ -110,11 +104,6 @@
projectId: resolved.projectId,
labelIds: resolved.labelIds,
});
// Show auth gate if authentication required (demo mode)
if (result && 'error' in result && result.error === 'auth_required') {
showAuthGate('save');
}
}
let isSidebarMode = $state(false);
@ -279,55 +268,28 @@
goto('/login');
}
// Auth gate modal state
let showAuthGateModal = $state(false);
let authGateAction = $state<'save' | 'sync' | 'feature'>('save');
// Guest welcome modal state
let showGuestWelcome = $state(false);
// Show auth gate modal (can be called from child components)
function showAuthGate(action: 'save' | 'sync' | 'feature' = 'save') {
authGateAction = action;
showAuthGateModal = true;
}
// Listen for show-auth-gate events from child components
$effect(() => {
if (browser) {
const handler = (e: Event) => {
const customEvent = e as CustomEvent<{ action?: 'save' | 'sync' | 'feature' }>;
showAuthGate(customEvent.detail?.action || 'save');
};
window.addEventListener('show-auth-gate', handler);
return () => window.removeEventListener('show-auth-gate', handler);
}
});
onMount(async () => {
// Initialize auth and redirect if not authenticated
await authStore.initialize();
if (!authStore.isAuthenticated) {
goto('/login');
return;
}
// Initialize split-panel from URL/localStorage
splitPanel.initialize();
// Initialize todo settings
todoSettings.initialize();
// Show guest welcome modal for unauthenticated users
if (!authStore.isAuthenticated && shouldShowGuestWelcome('todo')) {
showGuestWelcome = true;
}
// Load projects (works in both guest and authenticated mode)
// Load projects, labels, and user settings
await projectsStore.fetchProjects();
await Promise.all([labelsStore.fetchLabels(), userSettings.load()]);
// Only fetch labels and user settings if authenticated
if (authStore.isAuthenticated) {
await Promise.all([labelsStore.fetchLabels(), userSettings.load()]);
// Redirect to start page if on root and a custom start page is set
const currentPath = window.location.pathname;
if (currentPath === '/' && userSettings.startPage && userSettings.startPage !== '/') {
goto(userSettings.startPage, { replaceState: true });
}
// Redirect to start page if on root and a custom start page is set
const currentPath = window.location.pathname;
if (currentPath === '/' && userSettings.startPage && userSettings.startPage !== '/') {
goto(userSettings.startPage, { replaceState: true });
}
// Initialize sidebar mode from localStorage (with error handling for private browsing)
@ -394,41 +356,6 @@
<SplitPaneContainer>
<div class="layout-container">
<!-- Demo Mode Banner -->
{#if !authStore.isAuthenticated}
<div
class="guest-banner bg-primary/10 border-primary/20 fixed top-0 right-0 left-0 z-50 flex items-center justify-between border-b px-4 py-2"
>
<div class="flex items-center gap-2 text-sm">
<svg class="text-primary h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
<span class="text-foreground">
<strong>Demo-Modus</strong>
<span class="text-muted-foreground hidden sm:inline">
- Beispiel-Aufgaben zum Ausprobieren
</span>
</span>
</div>
<button
onclick={() => showAuthGate('save')}
class="bg-primary text-primary-foreground hover:bg-primary/90 rounded-md px-3 py-1 text-sm font-medium transition-colors"
>
Anmelden
</button>
</div>
{/if}
<!-- UI Elements (hidden in immersive mode) -->
{#if !todoSettings.immersiveModeEnabled}
<PillNavigation
@ -452,7 +379,7 @@
showLanguageSwitcher={true}
{languageItems}
{currentLanguageLabel}
showLogout={authStore.isAuthenticated}
showLogout={true}
onLogout={handleLogout}
loginHref="/login"
primaryColor="#8b5cf6"
@ -514,57 +441,7 @@
</div>
</SplitPaneContainer>
<!-- Auth Gate Modal -->
<AuthGateModal
visible={showAuthGateModal}
onClose={() => (showAuthGateModal = false)}
onLogin={() => {
showAuthGateModal = false;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('auth-return-url', window.location.pathname);
}
goto('/login');
}}
onRegister={() => {
showAuthGateModal = false;
if (typeof sessionStorage !== 'undefined') {
sessionStorage.setItem('auth-return-url', window.location.pathname);
}
goto('/register');
}}
action={authGateAction}
locale={currentLocale === 'en' ? 'en' : 'de'}
infoText="Du kannst die Demo-Aufgaben ansehen, aber um eigene Aufgaben zu erstellen benötigst du ein Konto."
/>
<!-- Guest Welcome Modal -->
<GuestWelcomeModal
appId="todo"
visible={showGuestWelcome}
onClose={() => (showGuestWelcome = false)}
onLogin={() => {
showGuestWelcome = false;
goto('/login');
}}
onRegister={() => {
showGuestWelcome = false;
goto('/register');
}}
helpHref="/help"
locale={currentLocale === 'en' ? 'en' : 'de'}
/>
<style>
/* Guest banner styling */
.guest-banner {
height: 40px;
min-height: 40px;
}
/* Offset content when guest banner is visible */
.layout-container:has(.guest-banner) .main-content.floating-mode {
padding-top: calc(70px + 40px);
}
.layout-container {
display: flex;
flex-direction: column;