import { useCallback, useEffect, useMemo, useState } from 'react'; import { TAG_FILTER_UNTAGGED, resolveDocumentRowKey, isDocumentRowKey, } from './appLayoutUtils'; const useDocumentsSearch = ({ api, assetManager, token, selectedFolder, navigate, locationPathname, isDocumentsRoute, selectionHelpers, searchIncludeDescendants, documentsSortField, documentsSortDirection, notifyApiError, setLoading, setSearchIncludeDescendants, }) => { const [searchQuery, setSearchQuery] = useState(''); const [activeTagFilters, setActiveTagFilters] = useState([]); const [activeCorrespondentFilters, setActiveCorrespondentFilters] = useState([]); const [searchResults, setSearchResults] = useState(null); const [searchLoading, setSearchLoading] = useState(false); const toggleTagFilter = useCallback((tagId) => { if (!tagId) return; setActiveTagFilters((previous) => { if (tagId === TAG_FILTER_UNTAGGED) { return previous.includes(TAG_FILTER_UNTAGGED) ? [] : [TAG_FILTER_UNTAGGED]; } const sanitized = previous.filter((id) => id !== TAG_FILTER_UNTAGGED); if (sanitized.includes(tagId)) { return sanitized.filter((id) => id !== tagId); } return sanitized.concat([tagId]); }); }, []); const toggleCorrespondentFilter = useCallback((correspondentId) => { setActiveCorrespondentFilters((previous) => { if (!correspondentId) { return []; } return previous.includes(correspondentId) ? [] : [correspondentId]; }); }, []); const isFilterActive = useMemo( () => searchQuery.trim().length > 0 || activeTagFilters.length > 0 || activeCorrespondentFilters.length > 0, [searchQuery, activeTagFilters, activeCorrespondentFilters], ); const clearFilters = useCallback(() => { setSearchQuery(''); setActiveTagFilters([]); setActiveCorrespondentFilters([]); setSearchLoading(false); setSearchIncludeDescendants(true); }, [ setSearchIncludeDescendants, ]); const handleSearchChange = useCallback((value) => { setSearchQuery(value); }, []); const handleSearchSubmit = useCallback(() => { if (!navigate) return; const targetFolder = selectedFolder && selectedFolder !== 'root' ? selectedFolder : 'root'; const targetPath = targetFolder === 'root' ? '/documents' : `/documents/folder/${targetFolder}`; if (!isDocumentsRoute || locationPathname !== targetPath) { navigate(targetPath, { replace: false }); } }, [navigate, selectedFolder, isDocumentsRoute, locationPathname]); useEffect(() => { if (!token) return undefined; if (!isFilterActive) { setSearchResults(null); setSearchLoading(false); return undefined; } let cancelled = false; let started = false; setSearchLoading(true); const debounce = setTimeout(async () => { started = true; setLoading(true); try { const params = {}; const trimmedQuery = searchQuery.trim(); if (trimmedQuery.length) { params.query = trimmedQuery; } if (activeTagFilters.length) { const onlyUntagged = activeTagFilters.length === 1 && activeTagFilters[0] === TAG_FILTER_UNTAGGED; if (onlyUntagged) { params.tags = 'none'; } else { const tagIds = activeTagFilters.filter((id) => id !== TAG_FILTER_UNTAGGED); if (tagIds.length) { params.tags = tagIds.join(','); } } } if (activeCorrespondentFilters.length) { params.correspondents = activeCorrespondentFilters.join(','); } const folderIdentifier = selectedFolder === 'root' ? null : selectedFolder; if (folderIdentifier) { params.folder_id = folderIdentifier; } if (!searchIncludeDescendants) { params.include_descendants = false; } if (documentsSortField) { params.sort = documentsSortField; } if (documentsSortDirection) { params.dir = documentsSortDirection; } const { data } = await api.get('/documents', { params }); if (cancelled) return; const results = assetManager.hydrateDocuments(data || []); setSearchResults(results); if (!results.length) { setSearchLoading(false); selectionHelpers.setSelectedEntries([]); selectionHelpers.setFocusedDocumentId(null); selectionHelpers.selectionOrderRef.current = []; selectionHelpers.setSelectionOrder([]); selectionHelpers.selectionAnchorRef.current = null; return; } const resultKeys = results .map((doc) => resolveDocumentRowKey(doc.id)) .filter(Boolean); let targetKey = null; let nextSelectionKeys = []; selectionHelpers.setSelectedEntries((previous) => { const previousDocKeys = previous.filter(isDocumentRowKey); const filtered = previousDocKeys.filter((key) => resultKeys.includes(key)); if (filtered.length) { targetKey = filtered[filtered.length - 1]; nextSelectionKeys = filtered; return filtered; } targetKey = null; nextSelectionKeys = []; return []; }); selectionHelpers.selectionOrderRef.current = nextSelectionKeys; selectionHelpers.setSelectionOrder(nextSelectionKeys); selectionHelpers.setFocusedDocumentId((previous) => { if (previous && resultKeys.includes(resolveDocumentRowKey(previous))) { return previous; } return null; }); selectionHelpers.selectionAnchorRef.current = targetKey; } catch (error) { if (cancelled) return; notifyApiError(error, 'Search failed. Please try again.'); setSearchResults(null); } finally { if (!cancelled && started) { setLoading(false); setSearchLoading(false); } } }, 300); return () => { cancelled = true; clearTimeout(debounce); if (started) { setLoading(false); setSearchLoading(false); } }; }, [ api, token, isFilterActive, searchQuery, activeTagFilters, activeCorrespondentFilters, searchIncludeDescendants, documentsSortField, documentsSortDirection, selectedFolder, notifyApiError, assetManager, selectionHelpers, setLoading, ]); return { searchQuery, setSearchQuery, searchResults, setSearchResults, searchLoading, setSearchLoading, activeTagFilters, setActiveTagFilters, activeCorrespondentFilters, setActiveCorrespondentFilters, toggleTagFilter, toggleCorrespondentFilter, isFilterActive, clearFilters, handleSearchChange, handleSearchSubmit, }; }; export default useDocumentsSearch;