mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-30 06:12:03 +00:00
Merge pull request #506 from AutoMaker-Org/feature/v0.12.0rc-1768509904121-pjft
feat: add discard all functionality to ideation view
This commit is contained in:
@@ -4,10 +4,11 @@
|
||||
*/
|
||||
|
||||
import { useState, useMemo, useEffect, useCallback } from 'react';
|
||||
import { Loader2, AlertCircle, Plus, X, Sparkles, Lightbulb } from 'lucide-react';
|
||||
import { Loader2, AlertCircle, Plus, X, Sparkles, Lightbulb, Trash2 } from 'lucide-react';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { ConfirmDialog } from '@/components/ui/confirm-dialog';
|
||||
import { useIdeationStore, type GenerationJob } from '@/store/ideation-store';
|
||||
import { useAppStore } from '@/store/app-store';
|
||||
import { getElectronAPI } from '@/lib/electron';
|
||||
@@ -15,9 +16,13 @@ import { toast } from 'sonner';
|
||||
import { cn } from '@/lib/utils';
|
||||
import type { AnalysisSuggestion } from '@automaker/types';
|
||||
|
||||
// Helper for consistent pluralization of "idea/ideas"
|
||||
const pluralizeIdea = (count: number) => `idea${count !== 1 ? 's' : ''}`;
|
||||
|
||||
interface IdeationDashboardProps {
|
||||
onGenerateIdeas: () => void;
|
||||
onAcceptAllReady?: (isReady: boolean, count: number, handler: () => Promise<void>) => void;
|
||||
onDiscardAllReady?: (isReady: boolean, count: number, handler: () => void) => void;
|
||||
}
|
||||
|
||||
function SuggestionCard({
|
||||
@@ -169,13 +174,18 @@ function TagFilter({
|
||||
);
|
||||
}
|
||||
|
||||
export function IdeationDashboard({ onGenerateIdeas, onAcceptAllReady }: IdeationDashboardProps) {
|
||||
export function IdeationDashboard({
|
||||
onGenerateIdeas,
|
||||
onAcceptAllReady,
|
||||
onDiscardAllReady,
|
||||
}: IdeationDashboardProps) {
|
||||
const currentProject = useAppStore((s) => s.currentProject);
|
||||
const generationJobs = useIdeationStore((s) => s.generationJobs);
|
||||
const removeSuggestionFromJob = useIdeationStore((s) => s.removeSuggestionFromJob);
|
||||
const [addingId, setAddingId] = useState<string | null>(null);
|
||||
const [isAcceptingAll, setIsAcceptingAll] = useState(false);
|
||||
const [selectedTags, setSelectedTags] = useState<Set<string>>(new Set());
|
||||
const [showDiscardConfirm, setShowDiscardConfirm] = useState(false);
|
||||
|
||||
// Get jobs for current project only (memoized to prevent unnecessary re-renders)
|
||||
const projectJobs = useMemo(
|
||||
@@ -304,23 +314,40 @@ export function IdeationDashboard({ onGenerateIdeas, onAcceptAllReady }: Ideatio
|
||||
setIsAcceptingAll(false);
|
||||
|
||||
if (successCount > 0 && failCount === 0) {
|
||||
toast.success(`Added ${successCount} idea${successCount > 1 ? 's' : ''} to board`);
|
||||
toast.success(`Added ${successCount} ${pluralizeIdea(successCount)} to board`);
|
||||
} else if (successCount > 0 && failCount > 0) {
|
||||
toast.warning(
|
||||
`Added ${successCount} idea${successCount > 1 ? 's' : ''}, ${failCount} failed`
|
||||
);
|
||||
toast.warning(`Added ${successCount} ${pluralizeIdea(successCount)}, ${failCount} failed`);
|
||||
} else {
|
||||
toast.error('Failed to add ideas to board');
|
||||
}
|
||||
}, [currentProject?.path, filteredSuggestions, removeSuggestionFromJob]);
|
||||
|
||||
// Show discard confirmation dialog
|
||||
const handleDiscardAll = useCallback(() => {
|
||||
setShowDiscardConfirm(true);
|
||||
}, []);
|
||||
|
||||
// Actually discard all filtered suggestions
|
||||
const confirmDiscardAll = useCallback(() => {
|
||||
const count = filteredSuggestions.length;
|
||||
for (const { suggestion, job } of filteredSuggestions) {
|
||||
removeSuggestionFromJob(job.id, suggestion.id);
|
||||
}
|
||||
toast.info(`Discarded ${count} ${pluralizeIdea(count)}`);
|
||||
}, [filteredSuggestions, removeSuggestionFromJob]);
|
||||
|
||||
// Common readiness state for bulk operations
|
||||
const bulkActionsReady = filteredSuggestions.length > 0 && !isAcceptingAll && !addingId;
|
||||
|
||||
// Notify parent about accept all readiness
|
||||
useEffect(() => {
|
||||
if (onAcceptAllReady) {
|
||||
const isReady = filteredSuggestions.length > 0 && !isAcceptingAll && !addingId;
|
||||
onAcceptAllReady(isReady, filteredSuggestions.length, handleAcceptAll);
|
||||
}
|
||||
}, [filteredSuggestions.length, isAcceptingAll, addingId, handleAcceptAll, onAcceptAllReady]);
|
||||
onAcceptAllReady?.(bulkActionsReady, filteredSuggestions.length, handleAcceptAll);
|
||||
}, [bulkActionsReady, filteredSuggestions.length, handleAcceptAll, onAcceptAllReady]);
|
||||
|
||||
// Notify parent about discard all readiness
|
||||
useEffect(() => {
|
||||
onDiscardAllReady?.(bulkActionsReady, filteredSuggestions.length, handleDiscardAll);
|
||||
}, [bulkActionsReady, filteredSuggestions.length, handleDiscardAll, onDiscardAllReady]);
|
||||
|
||||
const isEmpty = allSuggestions.length === 0 && activeJobs.length === 0;
|
||||
|
||||
@@ -331,10 +358,10 @@ export function IdeationDashboard({ onGenerateIdeas, onAcceptAllReady }: Ideatio
|
||||
{(generatingCount > 0 || allSuggestions.length > 0) && (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
{generatingCount > 0
|
||||
? `Generating ${generatingCount} idea${generatingCount > 1 ? 's' : ''}...`
|
||||
? `Generating ${generatingCount} ${pluralizeIdea(generatingCount)}...`
|
||||
: selectedTags.size > 0
|
||||
? `Showing ${filteredSuggestions.length} of ${allSuggestions.length} ideas`
|
||||
: `${allSuggestions.length} idea${allSuggestions.length > 1 ? 's' : ''} ready for review`}
|
||||
? `Showing ${filteredSuggestions.length} of ${allSuggestions.length} ${pluralizeIdea(allSuggestions.length)}`
|
||||
: `${allSuggestions.length} ${pluralizeIdea(allSuggestions.length)} ready for review`}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -419,6 +446,19 @@ export function IdeationDashboard({ onGenerateIdeas, onAcceptAllReady }: Ideatio
|
||||
</Card>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Discard All Confirmation Dialog */}
|
||||
<ConfirmDialog
|
||||
open={showDiscardConfirm}
|
||||
onOpenChange={setShowDiscardConfirm}
|
||||
onConfirm={confirmDiscardAll}
|
||||
title="Discard All Ideas"
|
||||
description={`Are you sure you want to discard ${filteredSuggestions.length} ${pluralizeIdea(filteredSuggestions.length)}? This cannot be undone.`}
|
||||
icon={Trash2}
|
||||
iconClassName="text-destructive"
|
||||
confirmText="Discard"
|
||||
confirmVariant="destructive"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { PromptList } from './components/prompt-list';
|
||||
import { IdeationDashboard } from './components/ideation-dashboard';
|
||||
import { useGuidedPrompts } from '@/hooks/use-guided-prompts';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ArrowLeft, ChevronRight, Lightbulb, CheckCheck, Loader2 } from 'lucide-react';
|
||||
import { ArrowLeft, ChevronRight, Lightbulb, CheckCheck, Loader2, Trash2 } from 'lucide-react';
|
||||
import type { IdeaCategory } from '@automaker/types';
|
||||
import type { IdeationMode } from '@/store/ideation-store';
|
||||
|
||||
@@ -71,6 +71,9 @@ function IdeationHeader({
|
||||
acceptAllCount,
|
||||
onAcceptAll,
|
||||
isAcceptingAll,
|
||||
discardAllReady,
|
||||
discardAllCount,
|
||||
onDiscardAll,
|
||||
}: {
|
||||
currentMode: IdeationMode;
|
||||
selectedCategory: IdeaCategory | null;
|
||||
@@ -81,6 +84,9 @@ function IdeationHeader({
|
||||
acceptAllCount: number;
|
||||
onAcceptAll: () => void;
|
||||
isAcceptingAll: boolean;
|
||||
discardAllReady: boolean;
|
||||
discardAllCount: number;
|
||||
onDiscardAll: () => void;
|
||||
}) {
|
||||
const { getCategoryById } = useGuidedPrompts();
|
||||
const showBackButton = currentMode === 'prompts';
|
||||
@@ -128,6 +134,17 @@ function IdeationHeader({
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2 items-center">
|
||||
{currentMode === 'dashboard' && discardAllReady && (
|
||||
<Button
|
||||
onClick={onDiscardAll}
|
||||
variant="outline"
|
||||
className="gap-2 text-destructive hover:text-destructive"
|
||||
disabled={isAcceptingAll}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
Discard All ({discardAllCount})
|
||||
</Button>
|
||||
)}
|
||||
{currentMode === 'dashboard' && acceptAllReady && (
|
||||
<Button
|
||||
onClick={onAcceptAll}
|
||||
@@ -162,6 +179,11 @@ export function IdeationView() {
|
||||
const [acceptAllHandler, setAcceptAllHandler] = useState<(() => Promise<void>) | null>(null);
|
||||
const [isAcceptingAll, setIsAcceptingAll] = useState(false);
|
||||
|
||||
// Discard all state
|
||||
const [discardAllReady, setDiscardAllReady] = useState(false);
|
||||
const [discardAllCount, setDiscardAllCount] = useState(0);
|
||||
const [discardAllHandler, setDiscardAllHandler] = useState<(() => void) | null>(null);
|
||||
|
||||
const handleAcceptAllReady = useCallback(
|
||||
(isReady: boolean, count: number, handler: () => Promise<void>) => {
|
||||
setAcceptAllReady(isReady);
|
||||
@@ -182,6 +204,21 @@ export function IdeationView() {
|
||||
}
|
||||
}, [acceptAllHandler]);
|
||||
|
||||
const handleDiscardAllReady = useCallback(
|
||||
(isReady: boolean, count: number, handler: () => void) => {
|
||||
setDiscardAllReady(isReady);
|
||||
setDiscardAllCount(count);
|
||||
setDiscardAllHandler(() => handler);
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
const handleDiscardAll = useCallback(() => {
|
||||
if (discardAllHandler) {
|
||||
discardAllHandler();
|
||||
}
|
||||
}, [discardAllHandler]);
|
||||
|
||||
const handleNavigate = useCallback(
|
||||
(mode: IdeationMode, category?: IdeaCategory | null) => {
|
||||
setMode(mode);
|
||||
@@ -245,6 +282,9 @@ export function IdeationView() {
|
||||
acceptAllCount={acceptAllCount}
|
||||
onAcceptAll={handleAcceptAll}
|
||||
isAcceptingAll={isAcceptingAll}
|
||||
discardAllReady={discardAllReady}
|
||||
discardAllCount={discardAllCount}
|
||||
onDiscardAll={handleDiscardAll}
|
||||
/>
|
||||
|
||||
{/* Dashboard - main view */}
|
||||
@@ -252,6 +292,7 @@ export function IdeationView() {
|
||||
<IdeationDashboard
|
||||
onGenerateIdeas={handleGenerateIdeas}
|
||||
onAcceptAllReady={handleAcceptAllReady}
|
||||
onDiscardAllReady={handleDiscardAllReady}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user