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:
parent
65166d4d2a
commit
8644f1923c
2 changed files with 63 additions and 1 deletions
|
|
@ -24,6 +24,16 @@ import {
|
||||||
DropdownMenuSeparator,
|
DropdownMenuSeparator,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from '@/components/ui/dropdown-menu'
|
} from '@/components/ui/dropdown-menu'
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from '@/components/ui/alert-dialog'
|
||||||
import {
|
import {
|
||||||
Select,
|
Select,
|
||||||
SelectContent,
|
SelectContent,
|
||||||
|
|
@ -80,6 +90,8 @@ export function SourceDetailContent({
|
||||||
const [isDownloadingFile, setIsDownloadingFile] = useState(false)
|
const [isDownloadingFile, setIsDownloadingFile] = useState(false)
|
||||||
const [fileAvailable, setFileAvailable] = useState<boolean | null>(null)
|
const [fileAvailable, setFileAvailable] = useState<boolean | null>(null)
|
||||||
const [selectedInsight, setSelectedInsight] = useState<SourceInsightResponse | 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 () => {
|
const fetchSource = useCallback(async () => {
|
||||||
try {
|
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) => {
|
const handleUpdateTitle = async (title: string) => {
|
||||||
if (!source || title === source.title) return
|
if (!source || title === source.title) return
|
||||||
|
|
||||||
|
|
@ -573,10 +602,18 @@ export function SourceDetailContent({
|
||||||
<p className="mt-2 text-sm text-muted-foreground">
|
<p className="mt-2 text-sm text-muted-foreground">
|
||||||
{insight.content.slice(0, 180)}{insight.content.length > 180 ? '…' : ''}
|
{insight.content.slice(0, 180)}{insight.content.length > 180 ? '…' : ''}
|
||||||
</p>
|
</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)}>
|
<Button size="sm" variant="outline" onClick={() => setSelectedInsight(insight)}>
|
||||||
View Insight
|
View Insight
|
||||||
</Button>
|
</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>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
@ -744,6 +781,27 @@ export function SourceDetailContent({
|
||||||
}}
|
}}
|
||||||
insight={selectedInsight ?? undefined}
|
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>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -30,5 +30,9 @@ export const insightsApi = {
|
||||||
data
|
data
|
||||||
)
|
)
|
||||||
return response.data
|
return response.data
|
||||||
|
},
|
||||||
|
|
||||||
|
delete: async (insightId: string) => {
|
||||||
|
await apiClient.delete(`/insights/${insightId}`)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
Reference in a new issue