diff --git a/apps/ui/src/components/views/ideation-view/components/prompt-list.tsx b/apps/ui/src/components/views/ideation-view/components/prompt-list.tsx index a402b8d1..8833bb30 100644 --- a/apps/ui/src/components/views/ideation-view/components/prompt-list.tsx +++ b/apps/ui/src/components/views/ideation-view/components/prompt-list.tsx @@ -11,7 +11,6 @@ import { useIdeationStore } from '@/store/ideation-store'; import { useAppStore } from '@/store/app-store'; import { useGenerateIdeationSuggestions } from '@/hooks/mutations'; import { toast } from 'sonner'; -import { useNavigate } from '@tanstack/react-router'; import type { IdeaCategory, IdeationPrompt } from '@automaker/types'; interface PromptListProps { @@ -24,10 +23,8 @@ export function PromptList({ category, onBack }: PromptListProps) { const generationJobs = useIdeationStore((s) => s.generationJobs); const setMode = useIdeationStore((s) => s.setMode); const addGenerationJob = useIdeationStore((s) => s.addGenerationJob); - const updateJobStatus = useIdeationStore((s) => s.updateJobStatus); const [loadingPromptId, setLoadingPromptId] = useState(null); const [startedPrompts, setStartedPrompts] = useState>(new Set()); - const navigate = useNavigate(); // React Query mutation const generateMutation = useGenerateIdeationSuggestions(currentProject?.path ?? ''); @@ -72,27 +69,13 @@ export function PromptList({ category, onBack }: PromptListProps) { toast.info(`Generating ideas for "${prompt.title}"...`); setMode('dashboard'); + // Start mutation - onSuccess/onError are handled at the hook level to ensure + // they fire even after this component unmounts (which happens due to setMode above) generateMutation.mutate( - { promptId: prompt.id, category }, + { promptId: prompt.id, category, jobId, promptTitle: prompt.title }, { - onSuccess: (data) => { - updateJobStatus(jobId, 'ready', data.suggestions); - toast.success(`Generated ${data.suggestions.length} ideas for "${prompt.title}"`, { - duration: 10000, - action: { - label: 'View Ideas', - onClick: () => { - setMode('dashboard'); - navigate({ to: '/ideation' }); - }, - }, - }); - setLoadingPromptId(null); - }, - onError: (error) => { - console.error('Failed to generate suggestions:', error); - updateJobStatus(jobId, 'error', undefined, error.message); - toast.error(error.message); + // Optional: reset local loading state if component is still mounted + onSettled: () => { setLoadingPromptId(null); }, } diff --git a/apps/ui/src/hooks/mutations/use-ideation-mutations.ts b/apps/ui/src/hooks/mutations/use-ideation-mutations.ts index 61841d9e..2c81b3ee 100644 --- a/apps/ui/src/hooks/mutations/use-ideation-mutations.ts +++ b/apps/ui/src/hooks/mutations/use-ideation-mutations.ts @@ -8,7 +8,8 @@ import { useMutation, useQueryClient } from '@tanstack/react-query'; import { getElectronAPI } from '@/lib/electron'; import { queryKeys } from '@/lib/query-keys'; import { toast } from 'sonner'; -import type { IdeaCategory, IdeaSuggestion } from '@automaker/types'; +import type { IdeaCategory, AnalysisSuggestion } from '@automaker/types'; +import { useIdeationStore } from '@/store/ideation-store'; /** * Input for generating ideation suggestions @@ -16,15 +17,23 @@ import type { IdeaCategory, IdeaSuggestion } from '@automaker/types'; interface GenerateSuggestionsInput { promptId: string; category: IdeaCategory; + /** Job ID for tracking generation progress - used to update job status on completion */ + jobId: string; + /** Prompt title for toast notifications */ + promptTitle: string; } /** * Result from generating suggestions */ interface GenerateSuggestionsResult { - suggestions: IdeaSuggestion[]; + suggestions: AnalysisSuggestion[]; promptId: string; category: IdeaCategory; + /** Job ID passed through for onSuccess handler */ + jobId: string; + /** Prompt title passed through for toast notifications */ + promptTitle: string; } /** @@ -52,7 +61,7 @@ export function useGenerateIdeationSuggestions(projectPath: string) { return useMutation({ mutationFn: async (input: GenerateSuggestionsInput): Promise => { - const { promptId, category } = input; + const { promptId, category, jobId, promptTitle } = input; const api = getElectronAPI(); if (!api.ideation?.generateSuggestions) { @@ -69,14 +78,33 @@ export function useGenerateIdeationSuggestions(projectPath: string) { suggestions: result.suggestions ?? [], promptId, category, + jobId, + promptTitle, }; }, - onSuccess: () => { + onSuccess: (data) => { + // Update job status in Zustand store - this runs even if the component unmounts + // Using getState() to access store directly without hooks (safe in callbacks) + const updateJobStatus = useIdeationStore.getState().updateJobStatus; + updateJobStatus(data.jobId, 'ready', data.suggestions); + + // Show success toast + toast.success(`Generated ${data.suggestions.length} ideas for "${data.promptTitle}"`, { + duration: 10000, + }); + // Invalidate ideation ideas cache queryClient.invalidateQueries({ queryKey: queryKeys.ideation.ideas(projectPath), }); }, - // Toast notifications are handled by the component since it has access to prompt title + onError: (error, variables) => { + // Update job status to error - this runs even if the component unmounts + const updateJobStatus = useIdeationStore.getState().updateJobStatus; + updateJobStatus(variables.jobId, 'error', undefined, error.message); + + // Show error toast + toast.error(`Failed to generate ideas: ${error.message}`); + }, }); }