diff --git a/backend/app/routes/images.py b/backend/app/routes/images.py index 1a7db0b6..48fc9e3b 100644 --- a/backend/app/routes/images.py +++ b/backend/app/routes/images.py @@ -250,8 +250,34 @@ def delete_multiple_images(payload: dict): }, }, ) + path = os.path.normpath(path) + parts = path.split(os.sep) + parts.insert(parts.index("images") + 1, "PictoPy.thumbnails") + thumb_nail_image_path = os.sep.join(parts) + + + if os.path.exists(path) : + try : + os.remove(path) + except PermissionError: + print(f"Permission denied for file '{thumb_nail_image_path}'.") + except Exception as e: + print(f"An error occurred: {e}") + + else: + print(f"File '{path}' does not exist.") + + if os.path.exists(thumb_nail_image_path) : + try : + os.remove(thumb_nail_image_path) + except PermissionError: + print(f"Permission denied for file '{thumb_nail_image_path}'.") + except Exception as e: + print(f"An error occurred: {e}") + else : + print(f"File '{thumb_nail_image_path}' does not exist.") + - os.remove(path) delete_image_db(path) deleted_paths.append(path) @@ -289,6 +315,7 @@ def get_all_image_objects(): image_path = get_path_from_id(image_id) classes = get_objects_db(image_path) data[image_path] = classes if classes else "None" + print(image_path) return JSONResponse( status_code=200, diff --git a/frontend/api/api-functions/images.ts b/frontend/api/api-functions/images.ts index b3d70ee4..783dc465 100644 --- a/frontend/api/api-functions/images.ts +++ b/frontend/api/api-functions/images.ts @@ -35,6 +35,7 @@ const parseAndSortImageData = (data: APIResponse['data']): Image[] => { extractThumbnailPath(data.folder_path, src), ); return { + imagePath:src, title: src.substring(src.lastIndexOf('\\') + 1), thumbnailUrl, url, diff --git a/frontend/src/components/AITagging/AIgallery.tsx b/frontend/src/components/AITagging/AIgallery.tsx index 23df0315..e2a89f8e 100644 --- a/frontend/src/components/AITagging/AIgallery.tsx +++ b/frontend/src/components/AITagging/AIgallery.tsx @@ -10,6 +10,14 @@ import { getAllImageObjects, generateThumbnails, } from '../../../api/api-functions/images'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger, +} from '@radix-ui/react-dropdown-menu'; +import { Button } from '../ui/button'; export default function AIGallery({ title, @@ -37,24 +45,28 @@ export default function AIGallery({ const [selectedMediaIndex, setSelectedMediaIndex] = useState(0); const [isVisibleSelectedImage, setIsVisibleSelectedImage] = useState(true); - const itemsPerPage: number = 20; const itemsPerRow: number = 3; - + const noOfPages: number[] = Array.from( + { length: 41 }, + (_, index) => index + 10, + ); const filteredMediaItems = useMemo(() => { return filterTag - ? mediaItems.filter((mediaItem: any) => - mediaItem.tags.includes(filterTag), - ) - : mediaItems; - }, [filterTag, mediaItems, loading]); + ? mediaItems.filter((mediaItem: any) => + mediaItem.tags.includes(filterTag), + ) + : mediaItems; +}, [filterTag, mediaItems, loading]); +const [pageNo,setpageNo] = useState(20); + const currentItems = useMemo(() => { - const indexOfLastItem = currentPage * itemsPerPage; - const indexOfFirstItem = indexOfLastItem - itemsPerPage; + const indexOfLastItem = currentPage * pageNo; + const indexOfFirstItem = indexOfLastItem - pageNo; return filteredMediaItems.slice(indexOfFirstItem, indexOfLastItem); - }, [filteredMediaItems, currentPage, itemsPerPage, mediaItems]); + }, [filteredMediaItems, currentPage, pageNo, mediaItems]); - const totalPages = Math.ceil(filteredMediaItems.length / itemsPerPage); + const totalPages = Math.ceil(filteredMediaItems.length / pageNo); const openMediaViewer = useCallback((index: number) => { setSelectedMediaIndex(index); @@ -107,11 +119,45 @@ export default function AIGallery({ openMediaViewer={openMediaViewer} type={type} /> - +
+ {/* Pagination Controls - Centered */} + + + {/* Dropdown Menu - Right-Aligned */} +
+ + + + + + setpageNo(Number(value))} + > + {noOfPages.map((itemsPerPage) => ( + + {itemsPerPage} + + ))} + + + +
+
)} {showMediaViewer && ( @@ -120,7 +166,7 @@ export default function AIGallery({ onClose={closeMediaViewer} allMedia={filteredMediaItems.map((item: any) => item.url)} currentPage={currentPage} - itemsPerPage={itemsPerPage} + itemsPerPage={pageNo} type={type} /> )} diff --git a/frontend/src/components/AITagging/FilterControls.tsx b/frontend/src/components/AITagging/FilterControls.tsx index e409c02e..4aed312e 100644 --- a/frontend/src/components/AITagging/FilterControls.tsx +++ b/frontend/src/components/AITagging/FilterControls.tsx @@ -82,6 +82,8 @@ export default function FilterControls({ ); diff --git a/frontend/src/components/FolderPicker/DeleteSelectedImagePage.tsx b/frontend/src/components/FolderPicker/DeleteSelectedImagePage.tsx index 926f7dd8..54b7ed54 100644 --- a/frontend/src/components/FolderPicker/DeleteSelectedImagePage.tsx +++ b/frontend/src/components/FolderPicker/DeleteSelectedImagePage.tsx @@ -1,6 +1,5 @@ import React, { useState } from 'react'; import { Button } from '@/components/ui/button'; -import { convertFileSrc } from '@tauri-apps/api/core'; import { usePictoQuery, usePictoMutation, @@ -10,15 +9,27 @@ import { delMultipleImages, fetchAllImages, } from '../../../api/api-functions/images'; -import { extractThumbnailPath } from '@/hooks/useImages'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger, +} from '@radix-ui/react-dropdown-menu'; +import { Filter } from 'lucide-react'; +import { MediaItem } from '@/types/Media'; interface DeleteSelectedImageProps { setIsVisibleSelectedImage: (value: boolean) => void; onError: (title: string, err: any) => void; + uniqueTags: string[]; + mediaItems: MediaItem[]; } const DeleteSelectedImagePage: React.FC = ({ setIsVisibleSelectedImage, onError, + uniqueTags, + mediaItems }) => { const [selectedImages, setSelectedImages] = useState([]); @@ -27,29 +38,18 @@ const DeleteSelectedImagePage: React.FC = ({ queryKey: ['all-images'], }); - console.log('All Images Data : ', response); - + const { mutate: deleteMultipleImages, isPending: isAddingImages } = - usePictoMutation({ - mutationFn: delMultipleImages, - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ['all-images'] }); - }, - autoInvalidateTags: ['ai-tagging-images', 'ai'], - }); - + usePictoMutation({ + mutationFn: delMultipleImages, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['all-images'] }); + }, + autoInvalidateTags: ['ai-tagging-images', 'ai'], + }); + // Extract the array of image paths const allImages: string[] = response?.image_files || []; - - const imagesWithThumbnails = allImages.map((imagePath) => { - return { - imagePath, - url: convertFileSrc(imagePath), - thumbnailUrl: convertFileSrc( - extractThumbnailPath(response.folder_path, imagePath), - ), - }; - }); const toggleImageSelection = (imagePath: string) => { setSelectedImages((prev) => prev.includes(imagePath) @@ -73,13 +73,33 @@ const DeleteSelectedImagePage: React.FC = ({ } }; - const handleSelectAllImages = () => { - if (selectedImages.length === allImages.length) { - setSelectedImages([]); + + const [filterTag, setFilterTag] = useState(uniqueTags[0]); + + const handleFilterTag = (value: string) => { + setSelectedImages([]); + setFilterTag(value); + + if(value.length === 0) { + setSelectedImages(allImages); return; } - setSelectedImages(allImages); + + const selectedImagesPaths: string[] = []; + + mediaItems.forEach((ele) => { + if (ele.tags?.includes(value)) { + selectedImagesPaths.push(ele.imagePath); + } + }); + + console.log("Selected Images Path = ", selectedImagesPaths); + setSelectedImages(selectedImagesPaths); }; + + + + const getImageName = (path: string) => { return path.split('\\').pop() || path; @@ -97,10 +117,52 @@ const DeleteSelectedImagePage: React.FC = ({

Select Images

- +
+ + + + + + + handleFilterTag(value)} + > + + All tags + + {uniqueTags.map((tag) => ( + + {tag} + + ))} + + + +
+ {/* */}
- {imagesWithThumbnails.map(({ imagePath, thumbnailUrl }, index) => { + {mediaItems.map(({ imagePath, thumbnailUrl }, index) => { return (
= ({ ); })}
-
- +
+