feat: add delete insight functionality

Add ability to delete insights from sources with a confirmation dialog.
Uses AlertDialog component for a native React experience instead of
browser confirm().

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Justin Florentine 2025-12-18 18:04:41 -05:00
parent 65166d4d2a
commit 8644f1923c
No known key found for this signature in database
2 changed files with 63 additions and 1 deletions

View file

@ -24,6 +24,16 @@ import {
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'
import {
Select,
SelectContent,
@ -80,6 +90,8 @@ export function SourceDetailContent({
const [isDownloadingFile, setIsDownloadingFile] = useState(false)
const [fileAvailable, setFileAvailable] = useState<boolean | null>(null)
const [selectedInsight, setSelectedInsight] = useState<SourceInsightResponse | null>(null)
const [insightToDelete, setInsightToDelete] = useState<string | null>(null)
const [deletingInsight, setDeletingInsight] = useState(false)
const fetchSource = useCallback(async () => {
try {
@ -152,6 +164,23 @@ export function SourceDetailContent({
}
}
const handleDeleteInsight = async () => {
if (!insightToDelete) return
try {
setDeletingInsight(true)
await insightsApi.delete(insightToDelete)
toast.success('Insight deleted successfully')
setInsightToDelete(null)
await fetchInsights()
} catch (err) {
console.error('Failed to delete insight:', err)
toast.error('Failed to delete insight')
} finally {
setDeletingInsight(false)
}
}
const handleUpdateTitle = async (title: string) => {
if (!source || title === source.title) return
@ -573,10 +602,18 @@ export function SourceDetailContent({
<p className="mt-2 text-sm text-muted-foreground">
{insight.content.slice(0, 180)}{insight.content.length > 180 ? '…' : ''}
</p>
<div className="mt-3 flex justify-end">
<div className="mt-3 flex justify-end gap-2">
<Button size="sm" variant="outline" onClick={() => setSelectedInsight(insight)}>
View Insight
</Button>
<Button
size="sm"
variant="outline"
onClick={() => setInsightToDelete(insight.id)}
className="text-destructive hover:text-destructive"
>
<Trash2 className="h-4 w-4" />
</Button>
</div>
</div>
))}
@ -744,6 +781,27 @@ export function SourceDetailContent({
}}
insight={selectedInsight ?? undefined}
/>
<AlertDialog open={!!insightToDelete} onOpenChange={() => setInsightToDelete(null)}>
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>Delete Insight?</AlertDialogTitle>
<AlertDialogDescription>
This action cannot be undone. This insight will be permanently deleted.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel disabled={deletingInsight}>Cancel</AlertDialogCancel>
<AlertDialogAction
onClick={handleDeleteInsight}
disabled={deletingInsight}
className="bg-destructive text-destructive-foreground hover:bg-destructive/90"
>
{deletingInsight ? 'Deleting...' : 'Delete'}
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
)
}

View file

@ -30,5 +30,9 @@ export const insightsApi = {
data
)
return response.data
},
delete: async (insightId: string) => {
await apiClient.delete(`/insights/${insightId}`)
}
}