From 6f8585e9bb450ac1766ebe16a358b10e4c42e3b2 Mon Sep 17 00:00:00 2001 From: Till-JS <101404291+Till-JS@users.noreply.github.com> Date: Sun, 7 Dec 2025 16:10:51 +0100 Subject: [PATCH] fix(manacore): improve API response handling and auth flow MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix calendar/todo API services to handle wrapped response format ({ events: [...] }, { tasks: [...] }, etc.) - Add null-safety guards in dashboard widgets for data arrays - Simplify default dashboard to 3 widgets: Clock, Tasks, Calendar - Fix auth layout initialization to prevent redirect race conditions - Update auth store import path in dashboard page 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../apps/web/src/lib/api/services/calendar.ts | 36 ++++++++++++++-- .../apps/web/src/lib/api/services/todo.ts | 32 ++++++++++++-- .../widgets/CalendarEventsWidget.svelte | 10 ++--- .../dashboard/widgets/TasksTodayWidget.svelte | 10 ++--- .../web/src/lib/config/default-dashboard.ts | 39 ++++------------- .../apps/web/src/routes/(app)/+layout.svelte | 42 +++++++------------ .../src/routes/(app)/dashboard/+page.svelte | 5 ++- .../apps/web/src/routes/(auth)/+layout.svelte | 16 +++++-- 8 files changed, 110 insertions(+), 80 deletions(-) diff --git a/apps/manacore/apps/web/src/lib/api/services/calendar.ts b/apps/manacore/apps/web/src/lib/api/services/calendar.ts index 825bbf197..cefced7d4 100644 --- a/apps/manacore/apps/web/src/lib/api/services/calendar.ts +++ b/apps/manacore/apps/web/src/lib/api/services/calendar.ts @@ -59,7 +59,15 @@ export const calendarService = { const startDate = new Date().toISOString().split('T')[0]; const endDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; - return client.get(`/events?startDate=${startDate}&endDate=${endDate}`); + const result = await client.get<{ events: CalendarEvent[] }>( + `/events?startDate=${startDate}&endDate=${endDate}` + ); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.events || [], error: null }; }, /** @@ -67,14 +75,28 @@ export const calendarService = { */ async getTodayEvents(): Promise> { const today = new Date().toISOString().split('T')[0]; - return client.get(`/events?startDate=${today}&endDate=${today}`); + const result = await client.get<{ events: CalendarEvent[] }>( + `/events?startDate=${today}&endDate=${today}` + ); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.events || [], error: null }; }, /** * Get all calendars */ async getCalendars(): Promise> { - return client.get('/calendars'); + const result = await client.get<{ calendars: Calendar[] }>('/calendars'); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.calendars || [], error: null }; }, /** @@ -87,8 +109,14 @@ export const calendarService = { const startDate = new Date().toISOString().split('T')[0]; const endDate = new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; - return client.get( + const result = await client.get<{ events: CalendarEvent[] }>( `/events?calendarIds=${calendarId}&startDate=${startDate}&endDate=${endDate}` ); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.events || [], error: null }; }, }; diff --git a/apps/manacore/apps/web/src/lib/api/services/todo.ts b/apps/manacore/apps/web/src/lib/api/services/todo.ts index d5d6690f0..8ab3f6bc6 100644 --- a/apps/manacore/apps/web/src/lib/api/services/todo.ts +++ b/apps/manacore/apps/web/src/lib/api/services/todo.ts @@ -49,28 +49,52 @@ export const todoService = { * Get today's tasks */ async getTodayTasks(): Promise> { - return client.get('/tasks/today'); + const result = await client.get<{ tasks: Task[] }>('/tasks/today'); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.tasks || [], error: null }; }, /** * Get upcoming tasks for the next N days */ async getUpcomingTasks(days: number = 7): Promise> { - return client.get(`/tasks/upcoming?days=${days}`); + const result = await client.get<{ tasks: Task[] }>(`/tasks/upcoming?days=${days}`); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.tasks || [], error: null }; }, /** * Get inbox tasks (unassigned to project) */ async getInboxTasks(): Promise> { - return client.get('/tasks/inbox'); + const result = await client.get<{ tasks: Task[] }>('/tasks/inbox'); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.tasks || [], error: null }; }, /** * Get all projects */ async getProjects(): Promise> { - return client.get('/projects'); + const result = await client.get<{ projects: Project[] }>('/projects'); + + if (result.error || !result.data) { + return { data: null, error: result.error }; + } + + return { data: result.data.projects || [], error: null }; }, /** diff --git a/apps/manacore/apps/web/src/lib/components/dashboard/widgets/CalendarEventsWidget.svelte b/apps/manacore/apps/web/src/lib/components/dashboard/widgets/CalendarEventsWidget.svelte index 66e2f6676..eabca8a58 100644 --- a/apps/manacore/apps/web/src/lib/components/dashboard/widgets/CalendarEventsWidget.svelte +++ b/apps/manacore/apps/web/src/lib/components/dashboard/widgets/CalendarEventsWidget.svelte @@ -71,8 +71,8 @@ return `${dateStr}, ${timeStr}`; } - const displayedEvents = $derived(data.slice(0, MAX_DISPLAY)); - const remainingCount = $derived(Math.max(0, data.length - MAX_DISPLAY)); + const displayedEvents = $derived((data || []).slice(0, MAX_DISPLAY)); + const remainingCount = $derived(Math.max(0, (data || []).length - MAX_DISPLAY));
@@ -81,9 +81,9 @@ 🗓️ {$_('dashboard.widgets.calendar.title')} - {#if data.length > 0} + {#if (data || []).length > 0} - {data.length} + {(data || []).length} {/if}
@@ -92,7 +92,7 @@ {:else if state === 'error'} - {:else if data.length === 0} + {:else if (data || []).length === 0}
📅

diff --git a/apps/manacore/apps/web/src/lib/components/dashboard/widgets/TasksTodayWidget.svelte b/apps/manacore/apps/web/src/lib/components/dashboard/widgets/TasksTodayWidget.svelte index 8bc531a80..c056d0d65 100644 --- a/apps/manacore/apps/web/src/lib/components/dashboard/widgets/TasksTodayWidget.svelte +++ b/apps/manacore/apps/web/src/lib/components/dashboard/widgets/TasksTodayWidget.svelte @@ -57,8 +57,8 @@ } } - const displayedTasks = $derived(data.slice(0, MAX_DISPLAY)); - const remainingCount = $derived(Math.max(0, data.length - MAX_DISPLAY)); + const displayedTasks = $derived((data || []).slice(0, MAX_DISPLAY)); + const remainingCount = $derived(Math.max(0, (data || []).length - MAX_DISPLAY));

@@ -67,9 +67,9 @@ {$_('dashboard.widgets.tasks_today.title')} - {#if data.length > 0} + {#if (data || []).length > 0} - {data.length} + {(data || []).length} {/if}
@@ -78,7 +78,7 @@ {:else if state === 'error'} - {:else if data.length === 0} + {:else if (data || []).length === 0}
🎉

diff --git a/apps/manacore/apps/web/src/lib/config/default-dashboard.ts b/apps/manacore/apps/web/src/lib/config/default-dashboard.ts index daa6da39a..ed9a4cd7f 100644 --- a/apps/manacore/apps/web/src/lib/config/default-dashboard.ts +++ b/apps/manacore/apps/web/src/lib/config/default-dashboard.ts @@ -7,15 +7,15 @@ import type { DashboardConfig } from '$lib/types/dashboard'; /** - * Default dashboard configuration with 6 widgets in a 2-column layout + * Default dashboard configuration with 3 widgets: Clock, Tasks, Calendar */ export const DEFAULT_DASHBOARD_CONFIG: DashboardConfig = { widgets: [ - // Row 0: Credits and Tasks Today + // Row 0: Clock and Tasks Today { - id: 'credits-1', - type: 'credits', - title: 'dashboard.widgets.credits.title', + id: 'clock-timers-1', + type: 'clock-timers', + title: 'dashboard.widgets.clock.title', size: 'medium', position: { x: 0, y: 0 }, visible: true, @@ -28,40 +28,15 @@ export const DEFAULT_DASHBOARD_CONFIG: DashboardConfig = { position: { x: 6, y: 0 }, visible: true, }, - // Row 1: Calendar and Quick Actions + // Row 1: Calendar (full width) { id: 'calendar-events-1', type: 'calendar-events', title: 'dashboard.widgets.calendar.title', - size: 'medium', + size: 'large', position: { x: 0, y: 1 }, visible: true, }, - { - id: 'quick-actions-1', - type: 'quick-actions', - title: 'dashboard.widgets.quick_actions.title', - size: 'medium', - position: { x: 6, y: 1 }, - visible: true, - }, - // Row 2: Chat and Contacts - { - id: 'chat-recent-1', - type: 'chat-recent', - title: 'dashboard.widgets.chat.title', - size: 'medium', - position: { x: 0, y: 2 }, - visible: true, - }, - { - id: 'contacts-favorites-1', - type: 'contacts-favorites', - title: 'dashboard.widgets.contacts.title', - size: 'medium', - position: { x: 6, y: 2 }, - visible: true, - }, ], gridColumns: 12, lastModified: new Date().toISOString(), diff --git a/apps/manacore/apps/web/src/routes/(app)/+layout.svelte b/apps/manacore/apps/web/src/routes/(app)/+layout.svelte index f4413aa2a..0aa148f23 100644 --- a/apps/manacore/apps/web/src/routes/(app)/+layout.svelte +++ b/apps/manacore/apps/web/src/routes/(app)/+layout.svelte @@ -123,23 +123,22 @@ goto('/login'); } - $effect(() => { - // Redirect to login if not authenticated (after initialization) - // Use a small delay to ensure state has propagated after navigation - if (authStore.initialized && !authStore.loading && !authStore.isAuthenticated) { - // Small delay to handle navigation timing - setTimeout(() => { - if (!authStore.isAuthenticated) { - goto('/login'); - } - }, 100); - } - }); + // Track initialization state + let isInitializing = $state(true); onMount(async () => { // Initialize auth store first await authStore.initialize(); + // Only after initialization is complete, check auth status + isInitializing = false; + + // Redirect to login if not authenticated + if (!authStore.isAuthenticated) { + goto('/login'); + return; + } + // Initialize sidebar mode from localStorage const savedSidebar = localStorage.getItem('manacore-nav-sidebar'); if (savedSidebar === 'true') { @@ -155,19 +154,10 @@ } // Load user settings from server (don't await - let it load in background) - if (authStore.isAuthenticated) { - userSettings.load().then(() => { - // Redirect to start page if on /dashboard and a custom start page is set - const currentPath = window.location.pathname; - if ( - currentPath === '/dashboard' && - userSettings.startPage && - userSettings.startPage !== '/dashboard' - ) { - goto(userSettings.startPage, { replaceState: true }); - } - }); - } + // Silently catch errors since settings endpoint may not exist yet + userSettings.load().catch(() => { + // Settings API not available - use defaults + }); loading = false; }); @@ -175,7 +165,7 @@ -{#if loading || authStore.loading} +{#if isInitializing || loading || authStore.loading}

import { onMount } from 'svelte'; + import { _ } from 'svelte-i18n'; import { Card, PageHeader } from '@manacore/shared-ui'; import { creditsService } from '$lib/api/credits'; import type { CreditBalance, CreditTransaction } from '$lib/api/credits'; - import { authStore } from '$lib/stores/authStore.svelte'; + import { authStore } from '$lib/stores/auth.svelte'; + import { dashboardStore } from '$lib/stores/dashboard.svelte'; + import DashboardGrid from '$lib/components/dashboard/DashboardGrid.svelte'; onMount(() => { dashboardStore.initialize(); diff --git a/apps/manacore/apps/web/src/routes/(auth)/+layout.svelte b/apps/manacore/apps/web/src/routes/(auth)/+layout.svelte index b20b848ee..344d5dab4 100644 --- a/apps/manacore/apps/web/src/routes/(auth)/+layout.svelte +++ b/apps/manacore/apps/web/src/routes/(auth)/+layout.svelte @@ -1,14 +1,24 @@