From 10f777f3cc2be0c8420edc1837037088676906be Mon Sep 17 00:00:00 2001 From: tanyar09 Date: Thu, 11 Dec 2025 11:42:23 -0500 Subject: [PATCH] feat: Add pagination and setup area toggle in Identify component for improved navigation This commit introduces pagination controls in the Identify component, allowing users to navigate through faces more efficiently with "Previous" and "Next" buttons. Additionally, a setup area toggle is added to collapse or expand the filters section, enhancing the user interface. The state management for the current page is updated to persist across sessions, improving overall user experience. Documentation has been updated to reflect these changes. --- frontend/src/pages/Identify.tsx | 100 +++++++++++++++++++++++++++++--- 1 file changed, 92 insertions(+), 8 deletions(-) diff --git a/frontend/src/pages/Identify.tsx b/frontend/src/pages/Identify.tsx index d4615e5..6c8e478 100644 --- a/frontend/src/pages/Identify.tsx +++ b/frontend/src/pages/Identify.tsx @@ -20,6 +20,7 @@ export default function Identify() { const [searchParams, setSearchParams] = useSearchParams() const [faces, setFaces] = useState([]) const [, setTotal] = useState(0) + const [page, setPage] = useState(1) const [pageSize, setPageSize] = useState(50) const [minQuality, setMinQuality] = useState(0.0) const [sortBy, setSortBy] = useState('quality') @@ -63,6 +64,7 @@ export default function Identify() { const [busy, setBusy] = useState(false) const [imageLoading, setImageLoading] = useState(false) const [filtersCollapsed, setFiltersCollapsed] = useState(true) + const [setupAreaCollapsed, setSetupAreaCollapsed] = useState(false) const [loadingFaces, setLoadingFaces] = useState(false) const [availableTags, setAvailableTags] = useState([]) const [selectedTags, setSelectedTags] = useState([]) @@ -134,8 +136,13 @@ export default function Identify() { return Boolean(firstName.trim() && lastName.trim()) }, [personId, firstName, lastName, currentFace]) - const loadFaces = async (clearState: boolean = false, ignorePhotoIds: boolean = false) => { + const loadFaces = async ( + clearState: boolean = false, + ignorePhotoIds: boolean = false, + targetPage?: number + ) => { setLoadingFaces(true) + const pageToLoad = targetPage ?? page try { // Clear saved state if explicitly requested (Refresh button) @@ -144,7 +151,7 @@ export default function Identify() { } const res = await facesApi.getUnidentified({ - page: 1, + page: pageToLoad, page_size: pageSize, min_quality: minQuality, date_taken_from: dateFrom || undefined, @@ -167,6 +174,7 @@ export default function Identify() { setFaces(filtered) setTotal(filtered.length) + setPage(pageToLoad) setCurrentIdx(0) // Clear form data when refreshing if (clearState) { @@ -408,6 +416,9 @@ export default function Identify() { if (state.selectedSimilar && typeof state.selectedSimilar === 'object') { setSelectedSimilar(state.selectedSimilar) } + if (state.page !== undefined) { + setPage(state.page) + } // Mark that we restored state, so we don't reload initialLoadRef.current = true // Mark restoration as complete after state is restored @@ -436,12 +447,13 @@ export default function Identify() { similar, faceFormData, selectedSimilar, + page, } sessionStorage.setItem(STATE_KEY, JSON.stringify(state)) } catch (error) { console.error('Error saving state to sessionStorage:', error) } - }, [faces, currentIdx, similar, faceFormData, selectedSimilar, stateRestored]) + }, [faces, currentIdx, similar, faceFormData, selectedSimilar, page, stateRestored]) // Save state on unmount (when navigating away) - use refs to capture latest values const facesRef = useRef(faces) @@ -449,6 +461,7 @@ export default function Identify() { const similarRef = useRef(similar) const faceFormDataRef = useRef(faceFormData) const selectedSimilarRef = useRef(selectedSimilar) + const pageRef = useRef(page) // Update refs whenever state changes useEffect(() => { @@ -457,7 +470,8 @@ export default function Identify() { similarRef.current = similar faceFormDataRef.current = faceFormData selectedSimilarRef.current = selectedSimilar - }, [faces, currentIdx, similar, faceFormData, selectedSimilar]) + pageRef.current = page + }, [faces, currentIdx, similar, faceFormData, selectedSimilar, page]) // Save state on unmount (when navigating away) useEffect(() => { @@ -469,6 +483,7 @@ export default function Identify() { similar: similarRef.current, faceFormData: faceFormDataRef.current, selectedSimilar: selectedSimilarRef.current, + page: pageRef.current, } sessionStorage.setItem(STATE_KEY, JSON.stringify(state)) } catch (error) { @@ -503,7 +518,8 @@ export default function Identify() { // Reload faces when includeExcludedFaces changes useEffect(() => { if (settingsLoaded && !photoIds) { - loadFaces(false) + setPage(1) + loadFaces(false, false, 1) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [includeExcludedFaces]) @@ -558,7 +574,8 @@ export default function Identify() { // But only if restoration is complete (prevents reload during initial restoration) useEffect(() => { if (initialLoadRef.current && restorationCompleteRef.current) { - loadFaces() + setPage(1) + loadFaces(false, false, 1) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [uniqueFacesOnly]) @@ -567,7 +584,8 @@ export default function Identify() { // But only if restoration is complete (prevents reload during initial restoration) useEffect(() => { if (initialLoadRef.current && restorationCompleteRef.current) { - loadFaces() + setPage(1) + loadFaces(false, false, 1) } // eslint-disable-next-line react-hooks/exhaustive-deps }, [pageSize]) @@ -733,6 +751,19 @@ export default function Identify() { return `Face ${currentIdx + 1} of ${faces.length}` }, [currentFace, currentIdx, faces.length]) + const handleNextPage = () => { + if (loadingFaces) return + const nextPage = page + 1 + setPage(nextPage) + loadFaces(false, false, nextPage) + } + const handlePrevPage = () => { + if (loadingFaces || page <= 1) return + const prevPage = Math.max(1, page - 1) + setPage(prevPage) + loadFaces(false, false, prevPage) + } + // Toggle excluded status for current face const toggleBlockFace = useCallback(async () => { if (!currentFace) return @@ -957,6 +988,7 @@ export default function Identify() { {/* Left: Controls and current face */}
{/* Unique Faces Checkbox and Batch Size - Outside Filters */} + {!setupAreaCollapsed && (
@@ -983,8 +1015,29 @@ export default function Identify() {
+
+
Page {page}
+
+ + +
+
+ )} + {!setupAreaCollapsed && (
setFiltersCollapsed(!filtersCollapsed)}>

Filters

@@ -1108,7 +1161,10 @@ export default function Identify() {
)} + {/* Collapse button at bottom of filters section */} +
+ +
+ )} + + {/* Show expand button when collapsed */} + {setupAreaCollapsed && ( +
+ +
+ )}