From 5a8dc6c1668cfc0d12d8f56ef5939a4c05976582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hartmut=20N=C3=B6renberg?= Date: Fri, 3 Apr 2026 11:25:42 +0200 Subject: [PATCH] fix: resolve 3 UX bugs from gitlooper ticket sweep (#45 #47 #48) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - #47: Remove misleading asterisk from Budget (EUR) label in project wizard — budget is optional per canGoNext() logic - #48: Parse Zod validation JSON in wizard submit error handler so users see "Responsible person is required" instead of raw JSON array - #45: Expose isEntriesError from timeline query context; TimelineView now renders an explicit error message instead of a silent empty canvas when the getEntriesView query fails Co-Authored-By: claude-flow --- .../src/components/projects/ProjectWizard.tsx | 20 +++++++++++++++++-- .../components/timeline/TimelineContext.tsx | 7 ++++++- .../src/components/timeline/TimelineView.tsx | 7 ++++++- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/projects/ProjectWizard.tsx b/apps/web/src/components/projects/ProjectWizard.tsx index c2bbe1e..81fcd70 100644 --- a/apps/web/src/components/projects/ProjectWizard.tsx +++ b/apps/web/src/components/projects/ProjectWizard.tsx @@ -423,7 +423,7 @@ function Step2({ state, onChange }: Step2Props) {
- + 0) { + errorMessage = (parsed as { message?: string }[]) + .map((e) => e.message) + .filter(Boolean) + .join("; "); + } else { + errorMessage = err.message; + } + } catch { + errorMessage = err.message; + } + } + setSubmitError(errorMessage); } finally { setIsSubmitting(false); } diff --git a/apps/web/src/components/timeline/TimelineContext.tsx b/apps/web/src/components/timeline/TimelineContext.tsx index f5563da..70bc80d 100644 --- a/apps/web/src/components/timeline/TimelineContext.tsx +++ b/apps/web/src/components/timeline/TimelineContext.tsx @@ -203,6 +203,7 @@ export interface TimelineContextValue { // ─ Loading isLoading: boolean; isInitialLoading: boolean; + isEntriesError: boolean; totalAllocCount: number; activeFilterCount: number; @@ -328,6 +329,7 @@ export function TimelineProvider({ ) as { data: TimelineEntriesView | undefined; isLoading: boolean; + isError: boolean; refetch: () => Promise; }; @@ -344,11 +346,12 @@ export function TimelineProvider({ ) as { data: TimelineEntriesView | undefined; isLoading: boolean; + isError: boolean; refetch: () => Promise; }; const entriesViewQuery = isSelfServiceTimeline ? selfEntriesViewQuery : staffEntriesViewQuery; - const { data: entriesView, isLoading, refetch: refetchEntriesView } = entriesViewQuery; + const { data: entriesView, isLoading, isError: isEntriesError, refetch: refetchEntriesView } = entriesViewQuery; const assignments = entriesView?.assignments ?? []; const demands = entriesView?.demands ?? []; @@ -774,6 +777,7 @@ export function TimelineProvider({ blinkOverbookedDays, isLoading, isInitialLoading, + isEntriesError, totalAllocCount, activeFilterCount, }), @@ -800,6 +804,7 @@ export function TimelineProvider({ blinkOverbookedDays, isLoading, isInitialLoading, + isEntriesError, totalAllocCount, activeFilterCount, ], diff --git a/apps/web/src/components/timeline/TimelineView.tsx b/apps/web/src/components/timeline/TimelineView.tsx index fea127b..7c496e3 100644 --- a/apps/web/src/components/timeline/TimelineView.tsx +++ b/apps/web/src/components/timeline/TimelineView.tsx @@ -332,6 +332,7 @@ function TimelineViewContent({ today, isLoading, isInitialLoading, + isEntriesError, totalAllocCount, } = ctx; @@ -708,7 +709,11 @@ function TimelineViewContent({ onScroll={handleContainerScroll} className="app-surface relative z-0 flex-1 overflow-auto" > - {isInitialLoading ? ( + {isEntriesError ? ( +
+ Failed to load timeline data. Please try refreshing the page. +
+ ) : isInitialLoading ? (
Loading timeline...