diff --git a/apps/chat/apps/web/src/lib/components/Toast.svelte b/apps/chat/apps/web/src/lib/components/Toast.svelte
new file mode 100644
index 000000000..af43a277e
--- /dev/null
+++ b/apps/chat/apps/web/src/lib/components/Toast.svelte
@@ -0,0 +1,63 @@
+
+
+{#if toasts.length > 0}
+
+ {#each toasts as toast (toast.id)}
+ {@const Icon = icons[toast.type]}
+
+
+
{toast.message}
+
+
+ {/each}
+
+{/if}
+
+
diff --git a/apps/chat/apps/web/src/lib/stores/conversations.svelte.ts b/apps/chat/apps/web/src/lib/stores/conversations.svelte.ts
index 1fb0cf9a4..912f8676b 100644
--- a/apps/chat/apps/web/src/lib/stores/conversations.svelte.ts
+++ b/apps/chat/apps/web/src/lib/stores/conversations.svelte.ts
@@ -3,6 +3,7 @@
*/
import { conversationService } from '$lib/services/conversation';
+import { toastStore } from './toast.svelte';
import type { Conversation } from '@chat/types';
// State
@@ -36,7 +37,9 @@ export const conversationsStore = {
try {
conversations = await conversationService.getConversations(spaceId);
} catch (e) {
- error = e instanceof Error ? e.message : 'Failed to load conversations';
+ const message = e instanceof Error ? e.message : 'Konversationen konnten nicht geladen werden';
+ error = message;
+ toastStore.error(message);
conversations = [];
} finally {
isLoading = false;
@@ -53,7 +56,9 @@ export const conversationsStore = {
try {
archivedConversations = await conversationService.getArchivedConversations();
} catch (e) {
- error = e instanceof Error ? e.message : 'Failed to load archived conversations';
+ const message = e instanceof Error ? e.message : 'Archivierte Konversationen konnten nicht geladen werden';
+ error = message;
+ toastStore.error(message);
archivedConversations = [];
} finally {
isLoading = false;
diff --git a/apps/chat/apps/web/src/lib/stores/toast.svelte.ts b/apps/chat/apps/web/src/lib/stores/toast.svelte.ts
new file mode 100644
index 000000000..af686ceab
--- /dev/null
+++ b/apps/chat/apps/web/src/lib/stores/toast.svelte.ts
@@ -0,0 +1,100 @@
+/**
+ * Toast Store - Centralized notification system using Svelte 5 runes
+ */
+
+export type ToastType = 'success' | 'error' | 'warning' | 'info';
+
+export interface Toast {
+ id: string;
+ type: ToastType;
+ message: string;
+ duration: number;
+}
+
+// State
+let toasts = $state([]);
+
+// Auto-incrementing ID
+let nextId = 0;
+
+function generateId(): string {
+ return `toast-${++nextId}-${Date.now()}`;
+}
+
+export const toastStore = {
+ // Getter for reading toasts
+ get toasts() {
+ return toasts;
+ },
+
+ /**
+ * Show a toast notification
+ */
+ show(message: string, type: ToastType = 'info', duration: number = 4000) {
+ const id = generateId();
+ const toast: Toast = { id, type, message, duration };
+
+ toasts = [...toasts, toast];
+
+ // Auto-remove after duration
+ if (duration > 0) {
+ setTimeout(() => {
+ this.dismiss(id);
+ }, duration);
+ }
+
+ return id;
+ },
+
+ /**
+ * Show success toast
+ */
+ success(message: string, duration?: number) {
+ return this.show(message, 'success', duration);
+ },
+
+ /**
+ * Show error toast
+ */
+ error(message: string, duration: number = 6000) {
+ return this.show(message, 'error', duration);
+ },
+
+ /**
+ * Show warning toast
+ */
+ warning(message: string, duration?: number) {
+ return this.show(message, 'warning', duration);
+ },
+
+ /**
+ * Show info toast
+ */
+ info(message: string, duration?: number) {
+ return this.show(message, 'info', duration);
+ },
+
+ /**
+ * Dismiss a specific toast
+ */
+ dismiss(id: string) {
+ toasts = toasts.filter((t) => t.id !== id);
+ },
+
+ /**
+ * Dismiss all toasts
+ */
+ dismissAll() {
+ toasts = [];
+ },
+};
+
+/**
+ * Helper function for API error handling
+ * Use this in services/stores to show user-friendly error messages
+ */
+export function handleApiError(error: unknown, fallbackMessage: string = 'Ein Fehler ist aufgetreten'): string {
+ const message = error instanceof Error ? error.message : fallbackMessage;
+ toastStore.error(message);
+ return message;
+}
diff --git a/apps/chat/apps/web/src/routes/+layout.svelte b/apps/chat/apps/web/src/routes/+layout.svelte
index 6123a0073..cb7ce45ce 100644
--- a/apps/chat/apps/web/src/routes/+layout.svelte
+++ b/apps/chat/apps/web/src/routes/+layout.svelte
@@ -2,6 +2,7 @@
import '../app.css';
import { onMount } from 'svelte';
import { theme } from '$lib/stores/theme';
+ import Toast from '$lib/components/Toast.svelte';
let { children } = $props();
@@ -14,3 +15,6 @@
{@render children()}
+
+
+
diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml
index f3adfed32..606309043 100644
--- a/docker-compose.dev.yml
+++ b/docker-compose.dev.yml
@@ -68,6 +68,7 @@ services:
CORS_ORIGINS: ${CORS_ORIGINS:-http://localhost:3000,http://localhost:5173,http://localhost:8081}
CREDITS_SIGNUP_BONUS: ${CREDITS_SIGNUP_BONUS:-150}
CREDITS_DAILY_FREE: ${CREDITS_DAILY_FREE:-5}
+ GOOGLE_GENAI_API_KEY: ${GOOGLE_GENAI_API_KEY}
depends_on:
postgres:
condition: service_healthy