diff --git a/frontend/src/components/format/FormatPage.jsx b/frontend/src/components/format/FormatPage.jsx index 71f368c..cccace5 100644 --- a/frontend/src/components/format/FormatPage.jsx +++ b/frontend/src/components/format/FormatPage.jsx @@ -13,6 +13,7 @@ import MassActionsBar from '@ui/MassActionsBar'; import ImportModal from '@ui/ImportModal'; import {importFormats} from '@api/import'; import DataBar from '@ui/DataBar/DataBar'; +import useSearch from '@hooks/useSearch'; const loadingMessages = [ 'Formatting the formatters...', @@ -62,19 +63,40 @@ function FormatPage() { const [formats, setFormats] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedFormat, setSelectedFormat] = useState(null); - const [sortBy, setSortBy] = useState('name'); - const [filterType, setFilterType] = useState('none'); - const [filterValue, setFilterValue] = useState(''); const [allTags, setAllTags] = useState([]); const [isCloning, setIsCloning] = useState(false); const [isLoading, setIsLoading] = useState(true); const [mergeConflicts, setMergeConflicts] = useState([]); - const [searchQuery, setSearchQuery] = useState(''); const [isImportModalOpen, setIsImportModalOpen] = useState(false); const [willBeSelected, setWillBeSelected] = useState([]); const navigate = useNavigate(); + // Initialize useSearch hook + const { + searchTerms, + currentInput, + setCurrentInput, + addSearchTerm, + removeSearchTerm, + clearSearchTerms, + filterType, + setFilterType, + filterValue, + setFilterValue, + sortBy, + setSortBy, + items: filteredFormats + } = useSearch(formats, { + searchableFields: ['content.name', 'content.tags'], + initialSortBy: 'name', + sortOptions: { + name: (a, b) => a.content.name.localeCompare(b.content.name), + dateModified: (a, b) => + new Date(b.modified_date) - new Date(a.modified_date) + } + }); + const { selectedItems, isSelectionMode, @@ -228,7 +250,6 @@ function FormatPage() { const handleMassDelete = async () => { try { - const filteredFormats = getFilteredAndSortedFormats(); const selectedFormats = Array.from(selectedItems).map( index => filteredFormats[index] ); @@ -246,7 +267,6 @@ function FormatPage() { 'Failed to delete selected formats' ); } finally { - // Always reload data and reset selection state, regardless of success/failure fetchFormats(); toggleSelectionMode(); clearSelection(); @@ -255,7 +275,6 @@ function FormatPage() { const handleMassImport = async arr => { try { - const filteredFormats = getFilteredAndSortedFormats(); const selectedFormats = Array.from(selectedItems).map( index => filteredFormats[index] ); @@ -275,7 +294,7 @@ function FormatPage() { if (e.shiftKey) { handleMouseEnter(index, true); } - handleSelect(formatName, index, e, getFilteredAndSortedFormats()); + handleSelect(formatName, index, e, filteredFormats); }; const handleMouseEnter = (index, isShiftKey) => { @@ -283,7 +302,7 @@ function FormatPage() { const start = Math.min(lastSelectedIndex, index); const end = Math.max(lastSelectedIndex, index); - const potentialSelection = getFilteredAndSortedFormats() + const potentialSelection = filteredFormats .slice(start, end + 1) .map((format, idx) => idx + start); @@ -291,40 +310,6 @@ function FormatPage() { } }; - const getFilteredAndSortedFormats = () => { - let filtered = [...formats]; - - if (searchQuery) { - filtered = filtered.filter( - format => - format.content.name - .toLowerCase() - .includes(searchQuery.toLowerCase()) || - format.content.tags?.some(tag => - tag.toLowerCase().includes(searchQuery.toLowerCase()) - ) - ); - } - - if (filterType === 'tag' && filterValue) { - filtered = filtered.filter(format => - format.content.tags?.includes(filterValue) - ); - } - - return filtered.sort((a, b) => { - switch (sortBy) { - case 'dateModified': - return ( - new Date(b.modified_date) - new Date(a.modified_date) - ); - case 'name': - default: - return a.content.name.localeCompare(b.content.name); - } - }); - }; - if (isLoading) { return ; } @@ -338,7 +323,12 @@ function FormatPage() { return (
- {getFilteredAndSortedFormats().map((format, index) => ( + {filteredFormats.map((format, index) => (
(
@@ -57,19 +58,50 @@ function ProfilePage() { const [formats, setFormats] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedProfile, setSelectedProfile] = useState(null); - const [sortBy, setSortBy] = useState('name'); - const [filterType, setFilterType] = useState('none'); - const [filterValue, setFilterValue] = useState(''); const [allTags, setAllTags] = useState([]); const [isCloning, setIsCloning] = useState(false); const [isLoading, setIsLoading] = useState(true); const [mergeConflicts, setMergeConflicts] = useState([]); - const [searchQuery, setSearchQuery] = useState(''); const [isImportModalOpen, setIsImportModalOpen] = useState(false); const [willBeSelected, setWillBeSelected] = useState([]); const navigate = useNavigate(); + // Initialize useSearch hook with profile-specific configuration + const { + searchTerms, + currentInput, + setCurrentInput, + addSearchTerm, + removeSearchTerm, + clearSearchTerms, + filterType, + setFilterType, + filterValue, + setFilterValue, + sortBy, + setSortBy, + items: filteredProfiles + } = useSearch(profiles, { + searchableFields: ['content.name', 'content.tags'], + initialSortBy: 'name', + sortOptions: { + name: (a, b) => a.content.name.localeCompare(b.content.name), + dateModified: (a, b) => + new Date(b.modified_date) - new Date(a.modified_date), + dateCreated: (a, b) => + new Date(b.created_date) - new Date(a.created_date) + }, + customFilters: { + date: (item, value) => { + if (!value) return true; + const itemDate = new Date(item.modified_date); + const filterDate = new Date(value); + return itemDate.toDateString() === filterDate.toDateString(); + } + } + }); + const { selectedItems, isSelectionMode, @@ -227,7 +259,7 @@ function ProfilePage() { const handleMassDelete = async () => { try { const selectedProfiles = Array.from(selectedItems).map( - index => profiles[index] + index => filteredProfiles[index] ); for (const profile of selectedProfiles) { await Profiles.delete(profile.file_name.replace('.yml', '')); @@ -244,7 +276,7 @@ function ProfilePage() { const handleMassImport = async arr => { try { const selectedProfilesList = Array.from(selectedItems) - .map(index => profiles[index]) + .map(index => filteredProfiles[index]) .filter(profile => profile); if (selectedProfilesList.length === 0) { @@ -269,7 +301,7 @@ function ProfilePage() { if (e.shiftKey) { handleMouseEnter(index, true); } - handleSelect(profileName, index, e, getFilteredAndSortedProfiles()); + handleSelect(profileName, index, e, filteredProfiles); }; const handleMouseEnter = (index, isShiftKey) => { @@ -277,7 +309,7 @@ function ProfilePage() { const start = Math.min(lastSelectedIndex, index); const end = Math.max(lastSelectedIndex, index); - const potentialSelection = getFilteredAndSortedProfiles() + const potentialSelection = filteredProfiles .slice(start, end + 1) .map((profile, idx) => idx + start); @@ -285,54 +317,6 @@ function ProfilePage() { } }; - const getFilteredAndSortedProfiles = () => { - return profiles - .filter(profile => { - if (searchQuery) { - return ( - profile.content.name - .toLowerCase() - .includes(searchQuery.toLowerCase()) || - profile.content.tags?.some(tag => - tag - .toLowerCase() - .includes(searchQuery.toLowerCase()) - ) - ); - } - - if (filterType === 'tag') { - return profile.content.tags?.includes(filterValue); - } - - if (filterType === 'date') { - const profileDate = new Date(profile.modified_date); - const filterDate = new Date(filterValue); - return ( - profileDate.toDateString() === filterDate.toDateString() - ); - } - - return true; - }) - .sort((a, b) => { - switch (sortBy) { - case 'dateModified': - return ( - new Date(b.modified_date) - - new Date(a.modified_date) - ); - case 'dateCreated': - return ( - new Date(b.created_date) - new Date(a.created_date) - ); - case 'name': - default: - return a.content.name.localeCompare(b.content.name); - } - }); - }; - const formatDate = dateString => { return new Date(dateString).toLocaleString(); }; @@ -347,12 +331,15 @@ function ProfilePage() { ); } - const filteredProfiles = getFilteredAndSortedProfiles(); - return (
( ); function RegexPage() { + // Basic state const [patterns, setPatterns] = useState([]); const [isModalOpen, setIsModalOpen] = useState(false); const [selectedPattern, setSelectedPattern] = useState(null); - const [sortBy, setSortBy] = useState('name'); - const [filterType, setFilterType] = useState('none'); - const [filterValue, setFilterValue] = useState(''); const [allTags, setAllTags] = useState([]); const [isCloning, setIsCloning] = useState(false); const [isLoading, setIsLoading] = useState(true); - const [searchQuery, setSearchQuery] = useState(''); const [willBeSelected, setWillBeSelected] = useState([]); - const [originalIndices, setOriginalIndices] = useState(new Map()); + // Initialize useSearch hook + const { + searchTerms, + currentInput, + setCurrentInput, + addSearchTerm, + removeSearchTerm, + clearSearchTerms, + filterType, + setFilterType, + filterValue, + setFilterValue, + sortBy, + setSortBy, + items: filteredPatterns + } = useSearch(patterns, { + searchableFields: ['name', 'tags'], + initialSortBy: 'name', + sortOptions: { + name: (a, b) => a.name.localeCompare(b.name), + dateModified: (a, b) => + new Date(b.modified_date) - new Date(a.modified_date) + } + }); + + // Mass selection functionality const { selectedItems, isSelectionMode, @@ -54,12 +77,10 @@ function RegexPage() { lastSelectedIndex } = useMassSelection(); + // Keyboard shortcuts useKeyboardShortcut('a', toggleSelectionMode, {ctrl: true}); - useEffect(() => { - loadPatterns(); - }, []); - + // Mouse position tracking for shift-select useEffect(() => { const handleKeyDown = e => { if (e.key === 'Shift' && lastSelectedIndex !== null) { @@ -99,6 +120,11 @@ function RegexPage() { }; }, [lastSelectedIndex]); + // Initial load + useEffect(() => { + loadPatterns(); + }, []); + const loadPatterns = async () => { setIsLoading(true); try { @@ -158,7 +184,6 @@ function RegexPage() { const handleMassDelete = async () => { try { - const filteredPatterns = getFilteredAndSortedPatterns(); const selectedPatterns = Array.from(selectedItems).map( index => filteredPatterns[index] ); @@ -173,7 +198,6 @@ function RegexPage() { console.error('Error deleting patterns:', error); Alert.error('Failed to delete selected patterns'); } finally { - // Always reload data and reset selection state, regardless of success/failure loadPatterns(); toggleSelectionMode(); clearSelection(); @@ -184,59 +208,20 @@ function RegexPage() { if (e.shiftKey) { handleMouseEnter(index, true); } - handleSelect(patternName, index, e, getFilteredAndSortedPatterns()); + handleSelect(patternName, index, e, filteredPatterns); }; const handleMouseEnter = (index, isShiftKey) => { if (isShiftKey && lastSelectedIndex !== null) { const start = Math.min(lastSelectedIndex, index); const end = Math.max(lastSelectedIndex, index); - - const potentialSelection = getFilteredAndSortedPatterns() + const potentialSelection = filteredPatterns .slice(start, end + 1) .map((pattern, idx) => idx + start); - setWillBeSelected(potentialSelection); } }; - const getFilteredAndSortedPatterns = () => { - let filtered = patterns.map((pattern, index) => ({ - ...pattern, - originalIndex: index - })); - - if (searchQuery) { - filtered = filtered.filter( - pattern => - pattern.name - .toLowerCase() - .includes(searchQuery.toLowerCase()) || - pattern.tags?.some(tag => - tag.toLowerCase().includes(searchQuery.toLowerCase()) - ) - ); - } - - if (filterType === 'tag' && filterValue) { - filtered = filtered.filter(pattern => - pattern.tags?.includes(filterValue) - ); - } - - return filtered.sort((a, b) => { - switch (sortBy) { - case 'dateModified': - return ( - new Date(b.modified_date) - new Date(a.modified_date) - ); - case 'name': - default: - return a.name.localeCompare(b.name); - } - }); - }; - const formatDate = dateString => { return new Date(dateString).toLocaleString(); }; @@ -248,8 +233,12 @@ function RegexPage() { return (
- {getFilteredAndSortedPatterns().map((pattern, index) => ( + {filteredPatterns.map((pattern, index) => (