refactor: optimize ideation components and store for project-specific job handling

- Updated IdeationDashboard and PromptList components to utilize memoization for improved performance when retrieving generation jobs specific to the current project.
- Removed the getJobsForProject function from the ideation store, streamlining job management by directly filtering jobs in the components.
- Enhanced the addGenerationJob function to ensure consistent job ID generation format.
- Implemented migration logic in the ideation store to clean up legacy jobs without project paths, improving data integrity.
This commit is contained in:
webdevcody
2026-01-04 16:22:25 -05:00
parent 4ac1edf314
commit e4d86aa654
9 changed files with 1802 additions and 22 deletions

View File

@@ -168,16 +168,39 @@ function TagFilter({
export function IdeationDashboard({ onGenerateIdeas }: IdeationDashboardProps) {
const currentProject = useAppStore((s) => s.currentProject);
const { getJobsForProject, removeSuggestionFromJob } = useIdeationStore();
const generationJobs = useIdeationStore((s) => s.generationJobs);
const removeSuggestionFromJob = useIdeationStore((s) => s.removeSuggestionFromJob);
const [addingId, setAddingId] = useState<string | null>(null);
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
// Get jobs for current project only
const projectJobs = currentProject?.path ? getJobsForProject(currentProject.path) : [];
// Get jobs for current project only (memoized to prevent unnecessary re-renders)
const projectJobs = useMemo(
() =>
currentProject?.path
? generationJobs.filter((job) => job.projectPath === currentProject.path)
: [],
[generationJobs, currentProject?.path]
);
// Separate generating/error jobs from ready jobs with suggestions
const activeJobs = projectJobs.filter((j) => j.status === 'generating' || j.status === 'error');
const readyJobs = projectJobs.filter((j) => j.status === 'ready' && j.suggestions.length > 0);
// Separate jobs by status and compute counts in a single pass
const { activeJobs, readyJobs, generatingCount } = useMemo(() => {
const active: GenerationJob[] = [];
const ready: GenerationJob[] = [];
let generating = 0;
for (const job of projectJobs) {
if (job.status === 'generating') {
active.push(job);
generating++;
} else if (job.status === 'error') {
active.push(job);
} else if (job.status === 'ready' && job.suggestions.length > 0) {
ready.push(job);
}
}
return { activeJobs: active, readyJobs: ready, generatingCount: generating };
}, [projectJobs]);
// Flatten all suggestions with their parent job
const allSuggestions = useMemo(
@@ -204,8 +227,6 @@ export function IdeationDashboard({ onGenerateIdeas }: IdeationDashboardProps) {
return allSuggestions.filter(({ job }) => selectedTags.has(job.prompt.title));
}, [allSuggestions, selectedTags]);
const generatingCount = projectJobs.filter((j) => j.status === 'generating').length;
const handleToggleTag = (tag: string) => {
setSelectedTags((prev) => {
const next = new Set(prev);

View File

@@ -2,7 +2,7 @@
* PromptList - List of prompts for a specific category
*/
import { useState } from 'react';
import { useState, useMemo } from 'react';
import { ArrowLeft, Lightbulb, Loader2, CheckCircle2 } from 'lucide-react';
import { Card, CardContent } from '@/components/ui/card';
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
@@ -20,7 +20,10 @@ interface PromptListProps {
export function PromptList({ category, onBack }: PromptListProps) {
const currentProject = useAppStore((s) => s.currentProject);
const { setMode, addGenerationJob, updateJobStatus, getJobsForProject } = useIdeationStore();
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<string | null>(null);
const [startedPrompts, setStartedPrompts] = useState<Set<string>>(new Set());
const navigate = useNavigate();
@@ -32,10 +35,19 @@ export function PromptList({ category, onBack }: PromptListProps) {
const prompts = getPromptsByCategory(category);
// Get jobs for current project only and check which prompts are already generating
const projectJobs = currentProject?.path ? getJobsForProject(currentProject.path) : [];
const generatingPromptIds = new Set(
projectJobs.filter((j) => j.status === 'generating').map((j) => j.prompt.id)
// Get jobs for current project only (memoized to prevent unnecessary re-renders)
const projectJobs = useMemo(
() =>
currentProject?.path
? generationJobs.filter((job) => job.projectPath === currentProject.path)
: [],
[generationJobs, currentProject?.path]
);
// Check which prompts are already generating
const generatingPromptIds = useMemo(
() => new Set(projectJobs.filter((j) => j.status === 'generating').map((j) => j.prompt.id)),
[projectJobs]
);
const handleSelectPrompt = async (prompt: IdeationPrompt) => {