mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-01 08:13:37 +00:00
Merge pull request #358 from AutoMaker-Org/ideation-fix
refactor: update ideation dashboard and prompt list to use project-sp…
This commit is contained in:
@@ -168,15 +168,39 @@ function TagFilter({
|
||||
|
||||
export function IdeationDashboard({ onGenerateIdeas }: IdeationDashboardProps) {
|
||||
const currentProject = useAppStore((s) => s.currentProject);
|
||||
const { generationJobs, 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());
|
||||
|
||||
// Separate generating/error jobs from ready jobs with suggestions
|
||||
const activeJobs = generationJobs.filter(
|
||||
(j) => j.status === 'generating' || j.status === 'error'
|
||||
// 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]
|
||||
);
|
||||
const readyJobs = generationJobs.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(
|
||||
@@ -203,8 +227,6 @@ export function IdeationDashboard({ onGenerateIdeas }: IdeationDashboardProps) {
|
||||
return allSuggestions.filter(({ job }) => selectedTags.has(job.prompt.title));
|
||||
}, [allSuggestions, selectedTags]);
|
||||
|
||||
const generatingCount = generationJobs.filter((j) => j.status === 'generating').length;
|
||||
|
||||
const handleToggleTag = (tag: string) => {
|
||||
setSelectedTags((prev) => {
|
||||
const next = new Set(prev);
|
||||
@@ -316,6 +338,16 @@ export function IdeationDashboard({ onGenerateIdeas }: IdeationDashboardProps) {
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Generate More Ideas Button - shown when there are items */}
|
||||
{!isEmpty && (
|
||||
<div className="pt-2">
|
||||
<Button onClick={onGenerateIdeas} variant="outline" className="w-full gap-2">
|
||||
<Lightbulb className="w-4 h-4" />
|
||||
Generate More Ideas
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Empty State */}
|
||||
{isEmpty && (
|
||||
<Card>
|
||||
|
||||
@@ -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, generationJobs } = 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,9 +35,19 @@ export function PromptList({ category, onBack }: PromptListProps) {
|
||||
|
||||
const prompts = getPromptsByCategory(category);
|
||||
|
||||
// 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 = new Set(
|
||||
generationJobs.filter((j) => j.status === 'generating').map((j) => j.prompt.id)
|
||||
const generatingPromptIds = useMemo(
|
||||
() => new Set(projectJobs.filter((j) => j.status === 'generating').map((j) => j.prompt.id)),
|
||||
[projectJobs]
|
||||
);
|
||||
|
||||
const handleSelectPrompt = async (prompt: IdeationPrompt) => {
|
||||
@@ -48,7 +61,7 @@ export function PromptList({ category, onBack }: PromptListProps) {
|
||||
setLoadingPromptId(prompt.id);
|
||||
|
||||
// Add a job and navigate to dashboard
|
||||
const jobId = addGenerationJob(prompt);
|
||||
const jobId = addGenerationJob(currentProject.path, prompt);
|
||||
setStartedPrompts((prev) => new Set(prev).add(prompt.id));
|
||||
|
||||
// Show toast and navigate to dashboard
|
||||
|
||||
@@ -21,6 +21,7 @@ export type GenerationJobStatus = 'generating' | 'ready' | 'error';
|
||||
|
||||
export interface GenerationJob {
|
||||
id: string;
|
||||
projectPath: string;
|
||||
prompt: IdeationPrompt;
|
||||
status: GenerationJobStatus;
|
||||
suggestions: AnalysisSuggestion[];
|
||||
@@ -76,7 +77,7 @@ interface IdeationActions {
|
||||
getSelectedIdea: () => Idea | null;
|
||||
|
||||
// Generation Jobs
|
||||
addGenerationJob: (prompt: IdeationPrompt) => string;
|
||||
addGenerationJob: (projectPath: string, prompt: IdeationPrompt) => string;
|
||||
updateJobStatus: (
|
||||
jobId: string,
|
||||
status: GenerationJobStatus,
|
||||
@@ -172,10 +173,11 @@ export const useIdeationStore = create<IdeationState & IdeationActions>()(
|
||||
},
|
||||
|
||||
// Generation Jobs
|
||||
addGenerationJob: (prompt) => {
|
||||
const jobId = `job-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
addGenerationJob: (projectPath, prompt) => {
|
||||
const jobId = `job-${Date.now()}-${Math.random().toString(36).substring(2, 11)}`;
|
||||
const job: GenerationJob = {
|
||||
id: jobId,
|
||||
projectPath,
|
||||
prompt,
|
||||
status: 'generating',
|
||||
suggestions: [],
|
||||
@@ -311,7 +313,7 @@ export const useIdeationStore = create<IdeationState & IdeationActions>()(
|
||||
}),
|
||||
{
|
||||
name: 'automaker-ideation-store',
|
||||
version: 3,
|
||||
version: 4,
|
||||
partialize: (state) => ({
|
||||
// Only persist these fields
|
||||
ideas: state.ideas,
|
||||
@@ -319,6 +321,18 @@ export const useIdeationStore = create<IdeationState & IdeationActions>()(
|
||||
analysisResult: state.analysisResult,
|
||||
filterStatus: state.filterStatus,
|
||||
}),
|
||||
migrate: (persistedState: unknown, version: number) => {
|
||||
const state = persistedState as Record<string, unknown>;
|
||||
if (version < 4) {
|
||||
// Remove legacy jobs that don't have projectPath (from before project-scoping was added)
|
||||
const jobs = (state.generationJobs as GenerationJob[]) || [];
|
||||
return {
|
||||
...state,
|
||||
generationJobs: jobs.filter((job) => job.projectPath !== undefined),
|
||||
};
|
||||
}
|
||||
return state;
|
||||
},
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user