diff --git a/frontend/src/components/common/ConfirmDialog.tsx b/frontend/src/components/common/ConfirmDialog.tsx new file mode 100644 index 00000000..2d44e451 --- /dev/null +++ b/frontend/src/components/common/ConfirmDialog.tsx @@ -0,0 +1,65 @@ +import { Modal } from './Modal' +import { cn } from '@/lib/utils' + +interface ConfirmDialogProps { + isOpen: boolean + onClose: () => void + onConfirm: () => void + title: string + message: string + confirmLabel?: string + confirmVariant?: 'destructive' | 'default' + isLoading?: boolean +} + +export function ConfirmDialog({ + isOpen, + onClose, + onConfirm, + title, + message, + confirmLabel = 'Delete', + confirmVariant = 'destructive', + isLoading = false, +}: ConfirmDialogProps) { + return ( + + + + + } + > +

{message}

+
+ ) +} + +export default ConfirmDialog diff --git a/frontend/src/pages/TreeLibraryPage.tsx b/frontend/src/pages/TreeLibraryPage.tsx index 33b393c5..65dbbb75 100644 --- a/frontend/src/pages/TreeLibraryPage.tsx +++ b/frontend/src/pages/TreeLibraryPage.tsx @@ -1,17 +1,18 @@ import { useEffect, useState, useCallback } from 'react' import { useNavigate, Link } from 'react-router-dom' -import { Plus, Pencil, Globe, Lock, X } from 'lucide-react' +import { Plus, Pencil, Globe, Lock, X, Trash2 } from 'lucide-react' import { treesApi, categoriesApi, foldersApi } from '@/api' import type { TreeListItem, CategoryListItem, FolderListItem } from '@/types' import { TagBadges } from '@/components/common/TagBadges' import { FolderSidebar } from '@/components/library/FolderSidebar' import { FolderEditModal } from '@/components/library/FolderEditModal' import { AddToFolderMenu } from '@/components/library/AddToFolderMenu' +import { ConfirmDialog } from '@/components/common/ConfirmDialog' import { cn } from '@/lib/utils' import { usePermissions } from '@/hooks/usePermissions' export function TreeLibraryPage() { - const { canCreateTrees, canEditTree } = usePermissions() + const { canCreateTrees, canEditTree, canDeleteTree } = usePermissions() const navigate = useNavigate() const [trees, setTrees] = useState([]) const [categories, setCategories] = useState([]) @@ -28,6 +29,11 @@ export function TreeLibraryPage() { const [editingFolder, setEditingFolder] = useState(null) const [newFolderParentId, setNewFolderParentId] = useState(null) + // Delete confirmation state + const [treeToDelete, setTreeToDelete] = useState(null) + const [showDeleteConfirm, setShowDeleteConfirm] = useState(false) + const [isDeleting, setIsDeleting] = useState(false) + const loadFolders = useCallback(async () => { try { const foldersData = await foldersApi.list() @@ -122,6 +128,22 @@ export function TreeLibraryPage() { setFolderModalOpen(true) } + const handleDeleteTree = async () => { + if (!treeToDelete) return + setIsDeleting(true) + try { + await treesApi.delete(treeToDelete.id) + setTrees(trees.filter((t) => t.id !== treeToDelete.id)) + } catch (err) { + console.error('Failed to delete tree:', err) + setError('Failed to delete tree') + } finally { + setIsDeleting(false) + setShowDeleteConfirm(false) + setTreeToDelete(null) + } + } + const hasActiveFilters = selectedCategoryId || selectedTags.length > 0 || searchQuery || selectedFolderId @@ -322,6 +344,22 @@ export function TreeLibraryPage() { )} + {canDeleteTree({ author_id: tree.author_id }) && ( + + )}