From 1135d423c931c0f32bce56ec2216188520248b3b Mon Sep 17 00:00:00 2001 From: avishek0769 Date: Thu, 11 Dec 2025 19:53:06 +0530 Subject: [PATCH] Show error when user selects an empty folder or continues without selecting a folder in the onboarding steps --- backend/app/routes/folders.py | 26 ++++++++++ backend/app/schemas/folders.py | 11 ++++- frontend/src/api/api-functions/folders.ts | 12 +++++ frontend/src/api/apiEndpoints.ts | 1 + .../OnboardingSteps/FolderSetupStep.tsx | 49 +++++++++++++++++-- 5 files changed, 95 insertions(+), 4 deletions(-) diff --git a/backend/app/routes/folders.py b/backend/app/routes/folders.py index a66cca27c..593547b8d 100644 --- a/backend/app/routes/folders.py +++ b/backend/app/routes/folders.py @@ -16,6 +16,9 @@ AddFolderRequest, AddFolderResponse, AddFolderData, + CheckFolderRequest, + CheckFolderEmptyResponse, + CheckFolderEmptyData, ErrorResponse, UpdateAITaggingRequest, UpdateAITaggingResponse, @@ -476,3 +479,26 @@ def get_all_folders(): message=f"Unable to retrieve folders: {str(e)}", ).model_dump(), ) + +@router.post("/check-empty") +def check_folder_empty(request: CheckFolderRequest): + if not os.path.exists(request.folder_path): + raise HTTPException(status_code=404, detail="Folder not found") + + if not os.path.isdir(request.folder_path): + raise HTTPException(status_code=400, detail="Path is not a directory") + + try: + # Efficiently check if directory yields any entries (files or subdirs) + with os.scandir(request.folder_path) as it: + is_empty = not any(it) + + return CheckFolderEmptyResponse( + success=True, + data=CheckFolderEmptyData(is_empty=is_empty), + message="Folder emptiness checked successfully" + ) + except PermissionError: + raise HTTPException(status_code=403, detail="Permission denied accessing folder") + except Exception as e: + raise HTTPException(status_code=500, detail=str(e)) \ No newline at end of file diff --git a/backend/app/schemas/folders.py b/backend/app/schemas/folders.py index 63045241b..42b9f3859 100644 --- a/backend/app/schemas/folders.py +++ b/backend/app/schemas/folders.py @@ -21,6 +21,8 @@ class SyncFolderRequest(BaseModel): folder_path: str # Path of the folder to sync folder_id: str # UUID of the folder to sync +class CheckFolderRequest(BaseModel): + folder_path: str # Response Data Models (for the 'data' field) class FolderDetails(BaseModel): @@ -60,6 +62,8 @@ class SyncFolderData(BaseModel): folder_id: str folder_path: str +class CheckFolderEmptyData(BaseModel): + is_empty: bool # Response Models class GetAllFoldersResponse(BaseModel): @@ -82,7 +86,6 @@ class UpdateAITaggingResponse(BaseModel): error: Optional[str] = None data: Optional[UpdateAITaggingData] = None - class DeleteFoldersResponse(BaseModel): success: bool message: Optional[str] = None @@ -101,3 +104,9 @@ class ErrorResponse(BaseModel): success: bool = False message: Optional[str] = None error: Optional[str] = None + +class CheckFolderEmptyResponse(BaseModel): + success: bool + message: Optional[str] = None + error: Optional[str] = None + data: Optional[CheckFolderEmptyData] = None \ No newline at end of file diff --git a/frontend/src/api/api-functions/folders.ts b/frontend/src/api/api-functions/folders.ts index d4fc619bc..e17528fe4 100644 --- a/frontend/src/api/api-functions/folders.ts +++ b/frontend/src/api/api-functions/folders.ts @@ -23,6 +23,10 @@ export interface SyncFolderRequest { folder_id: string; } +export interface CheckFolderEmptyRequest { + folder_path: string; +} + // API Functions export const getAllFolders = async (): Promise => { const response = await apiClient.get( @@ -41,6 +45,14 @@ export const addFolder = async ( return response.data; }; +export const checkFolderEmpty = async (request: CheckFolderEmptyRequest): Promise => { + const response = await apiClient.post( + foldersEndpoints.checkEmpty, + request, + ); + return response.data; +}; + export const enableAITagging = async ( request: UpdateAITaggingRequest, ): Promise => { diff --git a/frontend/src/api/apiEndpoints.ts b/frontend/src/api/apiEndpoints.ts index 69a7e570d..d106329f3 100644 --- a/frontend/src/api/apiEndpoints.ts +++ b/frontend/src/api/apiEndpoints.ts @@ -15,6 +15,7 @@ export const faceClustersEndpoints = { export const foldersEndpoints = { getAllFolders: '/folders/all-folders', addFolder: '/folders/add-folder', + checkEmpty: '/folders/check-empty', enableAITagging: '/folders/enable-ai-tagging', disableAITagging: '/folders/disable-ai-tagging', deleteFolders: '/folders/delete-folders', diff --git a/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx b/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx index e76c1079a..6e1252323 100644 --- a/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx +++ b/frontend/src/components/OnboardingSteps/FolderSetupStep.tsx @@ -15,6 +15,8 @@ import { markCompleted, previousStep } from '@/features/onboardingSlice'; import { AppFeatures } from '@/components/OnboardingSteps/AppFeatures'; import { useFolder } from '@/hooks/useFolder'; import { useEffect, useState } from 'react'; +import { Alert, AlertTitle } from '../ui/alert'; +import { checkFolderEmpty } from '@/api/api-functions/folders'; interface FolderSetupStepProps { stepIndex: number; @@ -27,8 +29,17 @@ export function FolderSetupStep({ }: FolderSetupStepProps) { const dispatch = useDispatch(); - // Local state for folders - const [folder, setFolder] = useState(''); + const [folder, setFolder] = useState(''); // Local state for folders + const [error, setError] = useState(''); + + useEffect(() => { + if (error) { + const timer = setTimeout(() => { + setError(''); + }, 4000); + return () => clearTimeout(timer); + } + }, [error]); useEffect(() => { if (localStorage.getItem('folderChosen') === 'true') { @@ -43,7 +54,27 @@ export function FolderSetupStep({ const handleSelectFolders = async () => { const selectedFolder = await pickSingleFolder(); if (selectedFolder) { - setFolder(selectedFolder); + try { + const data = await checkFolderEmpty({ folder_path: selectedFolder }); + console.log(data); + + if (!data.success) { + setError('Error checking folder contents. Please try again.'); + return; + } + + if (data.data && data.data.is_empty) { + setError('Selected folder is empty. Please select a non-empty folder.'); + return; + } + + setFolder(selectedFolder); + setError(''); + } + catch (err) { + console.error(err); + setError('Unable to verify folder. Please try again.'); + } } }; @@ -52,6 +83,10 @@ export function FolderSetupStep({ }; const handleNext = () => { + if (!folder || folder.trim() === '') { + setError('Please select a folder to proceed'); + return + } localStorage.setItem('folderChosen', 'true'); addFolderMutate(folder); dispatch(markCompleted(stepIndex)); @@ -69,6 +104,14 @@ export function FolderSetupStep({ return ( <> + {error ? ( +
+ + {error} + +
+ ) : null} +