diff --git a/frontend/src/features/folderSlice.ts b/frontend/src/features/folderSlice.ts index 2efbe42f6..6d57b0c9d 100644 --- a/frontend/src/features/folderSlice.ts +++ b/frontend/src/features/folderSlice.ts @@ -5,12 +5,14 @@ import { FolderTaggingInfo } from '@/types/FolderStatus'; interface FolderState { folders: FolderDetails[]; taggingStatus: Record; + folderStatusTimestamps: Record; lastUpdatedAt?: number; } const initialState: FolderState = { folders: [], taggingStatus: {}, + folderStatusTimestamps: {}, }; const folderSlice = createSlice({ @@ -74,16 +76,29 @@ const folderSlice = createSlice({ // Set tagging status for folders setTaggingStatus(state, action: PayloadAction) { const map: Record = {}; + const now = Date.now(); + for (const info of action.payload) { map[info.folder_id] = info; + + const existingStatus = state.taggingStatus[info.folder_id]; + if ( + !existingStatus || + existingStatus.total_images !== info.total_images || + existingStatus.tagged_images !== info.tagged_images + ) { + state.folderStatusTimestamps[info.folder_id] = now; + } } + state.taggingStatus = map; - state.lastUpdatedAt = Date.now(); + state.lastUpdatedAt = now; }, // Clear tagging status clearTaggingStatus(state) { state.taggingStatus = {}; + state.folderStatusTimestamps = {}; state.lastUpdatedAt = undefined; }, }, diff --git a/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx b/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx index db4b029fa..bd11d0184 100644 --- a/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx +++ b/frontend/src/pages/SettingsPage/components/FolderManagementCard.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Folder, Trash2, Check } from 'lucide-react'; +import { Folder, Trash2, Check, Loader2, AlertCircle } from 'lucide-react'; import { Switch } from '@/components/ui/switch'; import { Button } from '@/components/ui/button'; @@ -28,6 +28,27 @@ const FolderManagementCard: React.FC = () => { const taggingStatus = useSelector( (state: RootState) => state.folders.taggingStatus, ); + const folderStatusTimestamps = useSelector( + (state: RootState) => state.folders.folderStatusTimestamps, + ); + + const isStatusLoading = (folderId: string, folderHasAITagging: boolean) => { + if (!folderHasAITagging) return false; + + const status = taggingStatus[folderId]; + if (!status) return true; + + const timestamp = folderStatusTimestamps[folderId]; + if (!timestamp) return true; + + const timeSinceUpdate = Date.now() - timestamp; + + if (status.total_images === 0 && timeSinceUpdate < 3000) { + return true; + } + + return false; + }; return ( { > {folders.length > 0 ? (
- {folders.map((folder: FolderDetails, index: number) => ( -
-
-
-
- - - {folder.folder_path} - -
-
+ {folders.map((folder: FolderDetails, index: number) => { + const status = taggingStatus[folder.folder_id]; + const loading = isStatusLoading( + folder.folder_id, + folder.AI_Tagging, + ); + const hasImages = status && status.total_images > 0; + const isEmpty = status && status.total_images === 0 && !loading; + const isComplete = status && status.tagging_percentage >= 100; -
-
- - AI Tagging - - toggleAITagging(folder)} - disabled={ - enableAITaggingPending || disableAITaggingPending - } - /> + return ( +
+
+
+
+ + + {folder.folder_path} + +
- -
-
+
+
+ + AI Tagging + + toggleAITagging(folder)} + disabled={ + enableAITaggingPending || disableAITaggingPending + } + /> +
- {folder.AI_Tagging && ( -
-
- AI Tagging Progress - = 100 - ? 'flex items-center gap-1 text-green-500' - : 'text-muted-foreground' - } +
- = 100 - ? 'bg-green-500' - : 'bg-blue-500' - } - />
- )} -
- ))} + + {folder.AI_Tagging && ( +
+ {loading ? ( +
+ + Loading status... +
+ ) : isEmpty ? ( +
+ + No images found in this folder +
+ ) : hasImages ? ( + <> +
+ AI Tagging Progress + + {isComplete && } + {Math.round(status.tagging_percentage)}% + +
+ + + ) : null} +
+ )} +
+ ); + })}
) : (
diff --git a/frontend/src/types/FolderStatus.ts b/frontend/src/types/FolderStatus.ts index 19b80f0eb..0f6a2d0ea 100644 --- a/frontend/src/types/FolderStatus.ts +++ b/frontend/src/types/FolderStatus.ts @@ -1,7 +1,9 @@ export interface FolderTaggingInfo { folder_id: string; folder_path: string; - tagging_percentage: number; // 0 - 100 + total_images: number; + tagged_images: number; + tagging_percentage: number; } export interface FolderTaggingStatusResponse { diff --git a/sync-microservice/app/database/folders.py b/sync-microservice/app/database/folders.py index 0cc6b3ade..89a5fe40c 100644 --- a/sync-microservice/app/database/folders.py +++ b/sync-microservice/app/database/folders.py @@ -16,6 +16,8 @@ class FolderTaggingInfo(NamedTuple): folder_id: FolderId folder_path: FolderPath + total_images: int + tagged_images: int tagging_percentage: float @@ -101,7 +103,6 @@ def db_get_tagging_progress() -> List[FolderTaggingInfo]: folder_info_list = [] for folder_id, folder_path, total_images, tagged_images in results: - # Calculate percentage, handle division by zero if total_images > 0: tagging_percentage = (tagged_images / total_images) * 100 else: @@ -111,6 +112,8 @@ def db_get_tagging_progress() -> List[FolderTaggingInfo]: FolderTaggingInfo( folder_id=folder_id, folder_path=folder_path, + total_images=total_images, + tagged_images=tagged_images, tagging_percentage=round(tagging_percentage, 2), ) ) diff --git a/sync-microservice/app/routes/folders.py b/sync-microservice/app/routes/folders.py index 4f7f8c847..b91d1f91c 100644 --- a/sync-microservice/app/routes/folders.py +++ b/sync-microservice/app/routes/folders.py @@ -33,6 +33,8 @@ def get_folders_tagging_status(): FolderTaggingInfo( folder_id=folder.folder_id, folder_path=folder.folder_path, + total_images=folder.total_images, + tagged_images=folder.tagged_images, tagging_percentage=folder.tagging_percentage, ) for folder in tagging_progress diff --git a/sync-microservice/app/schemas/folders.py b/sync-microservice/app/schemas/folders.py index 403a201c8..705238896 100644 --- a/sync-microservice/app/schemas/folders.py +++ b/sync-microservice/app/schemas/folders.py @@ -7,6 +7,8 @@ class FolderTaggingInfo(BaseModel): folder_id: str = Field(..., description="Unique identifier for the folder") folder_path: str = Field(..., description="Path to the folder") + total_images: int = Field(..., ge=0, description="Total number of images in folder") + tagged_images: int = Field(..., ge=0, description="Number of tagged images") tagging_percentage: float = Field( ..., ge=0,