diff --git a/src/components/common/filter/ActiveFilterChips.jsx b/src/components/common/filter/ActiveFilterChips.jsx index b9f9049..7978629 100644 --- a/src/components/common/filter/ActiveFilterChips.jsx +++ b/src/components/common/filter/ActiveFilterChips.jsx @@ -2,21 +2,21 @@ import { Add, Close } from '@mui/icons-material' import { Box, Button, Chip, ChipDelete, Typography } from '@mui/joy' const ActiveFilterChips = ({ - chips = [], - onOpen, - onClearAll, - onAdd, - showAddChip = false, - resultCount, - totalCount, - maxVisible = 2, chipSize = 'md', + chipSx, + chips = [], clearButtonSize = 'sm', clearButtonSx, containerSx, - chipSx, + maxVisible = 2, + onAdd, + onClearAll, + onOpen, overflowChipSx, + resultCount, resultSx, + showAddChip = false, + totalCount, }) => { if (!chips.length) { return null @@ -39,7 +39,7 @@ const ActiveFilterChips = ({ ...containerSx, }} > - {visible.map(({ key, label, onClear, color = 'primary' }) => ( + {visible.map(({ color = 'primary', key, label, onClear }) => ( onClear?.()} + onDelete={event => { + event.stopPropagation() + onClear?.() + }} aria-label={`Remove ${label} filter`} sx={{ '--Chip-deleteSize': chipSize === 'sm' ? '1.1rem' : '1.4rem', diff --git a/src/utils/Chores.jsx b/src/utils/Chores.jsx index bd225ab..5697755 100644 --- a/src/utils/Chores.jsx +++ b/src/utils/Chores.jsx @@ -89,6 +89,8 @@ const buildActualDateGroups = chores => { export const ChoresGrouper = (groupBy, chores, filter) => { if (filter) { chores = chores.filter(chore => filter(chore)) + } else { + chores = [...chores] } // sort by priority then due date: diff --git a/src/views/ChoreEdit/ChoreEdit.jsx b/src/views/ChoreEdit/ChoreEdit.jsx index 142a93c..e7adf3a 100644 --- a/src/views/ChoreEdit/ChoreEdit.jsx +++ b/src/views/ChoreEdit/ChoreEdit.jsx @@ -37,6 +37,7 @@ import { import moment from 'moment' import { useEffect, useState } from 'react' import { useNavigate, useParams, useSearchParams } from 'react-router-dom' + import DurationInput from '../../components/common/DurationInput' import KeyboardShortcutHint from '../../components/common/KeyboardShortcutHint' import NotificationTemplate from '../../components/NotificationTemplate.jsx' @@ -60,10 +61,10 @@ import { } from '../../utils/Fetcher' import { isPlusAccount, resolvePhotoURL } from '../../utils/Helpers' import { getImageSrc, removeCachedImage } from '../../utils/ImageCache' -import { generateUUID } from '../../utils/UUID' import Priorities from '../../utils/Priorities.jsx' import { getIconComponent } from '../../utils/ProjectIcons' import { getSafeBottomPadding } from '../../utils/SafeAreaUtils.js' +import { generateUUID } from '../../utils/UUID' import { useProjectFilter } from '../Chores/hooks/useProjectFilter.js' import LoadingComponent from '../components/Loading.jsx' import RichTextEditor from '../components/RichTextEditor.jsx' @@ -84,6 +85,7 @@ const ASSIGN_STRATEGIES = [ 'round_robin', 'no_assignee', ] +const DEFAULT_ASSIGN_STRATEGY = ASSIGN_STRATEGIES[3] // keep_last_assigned const REPEAT_ON_TYPE = ['interval', 'days_of_the_week', 'day_of_the_month'] const NO_DUE_DATE_REQUIRED_TYPE = ['no_repeat', 'once'] @@ -103,7 +105,7 @@ const ChoreEdit = () => { const [anyone, setAnyone] = useState(false) const [assignableTo, setAssignableTo] = useState([]) const [performers, setPerformers] = useState([]) - const [assignStrategy, setAssignStrategy] = useState(ASSIGN_STRATEGIES[2]) + const [assignStrategy, setAssignStrategy] = useState(DEFAULT_ASSIGN_STRATEGY) const [dueDate, setDueDate] = useState(null) const [dueDateOnly, setDueDateOnly] = useState(null) const [dueTime, setDueTime] = useState(null) @@ -151,7 +153,7 @@ const ChoreEdit = () => { const { data: userLabelsRaw, isLoading: isUserLabelsLoading } = useLabels() const { data: projects = [], isLoading: isProjectsLoading } = useProjects() - const { selectedProject, projectsWithDefault, setSelectedProjectWithCache } = + const { projectsWithDefault, selectedProject, setSelectedProjectWithCache } = useProjectFilter(projects) const [projectId, setProjectId] = useState( @@ -170,7 +172,7 @@ const ChoreEdit = () => { } = useChore(choreId) const { data: membersData, isLoading: isMemberDataLoading } = useCircleMembers() - const { showSuccess, showError } = useNotification() + const { showError, showSuccess } = useNotification() const [userLabels, setUserLabels] = useState([]) @@ -183,20 +185,26 @@ const ChoreEdit = () => { const Navigate = useNavigate() const assignees = anyone ? performers : assignableTo + const hasSpecificAssignees = !anyone && assignableTo.length > 0 + const canPickStrategy = hasSpecificAssignees && assignableTo.length > 1 + const assignStrategyValue = !hasSpecificAssignees + ? 'no_assignee' + : canPickStrategy + ? assignStrategy + : DEFAULT_ASSIGN_STRATEGY + const assignedToValue = + !hasSpecificAssignees || assignStrategyValue === 'no_assignee' + ? null + : assignableTo.some(a => a.userId === assignedTo) + ? assignedTo + : assignableTo[0].userId + const HandleValidateChore = () => { const errors = {} if (name.trim() === '') { errors.name = 'Name is required' } - if (assignStrategy !== 'no_assignee') { - if (assignees.length === 0) { - errors.assignees = 'At least 1 assignees is required' - } - if (assignedTo === null || assignedTo < 0) { - errors.assignedTo = 'Assigned to is required' - } - } if (frequencyType === 'interval' && !frequency > 0) { errors.frequency = `Invalid frequency, the ${frequencyMetadata.unit} should be > 0` } @@ -366,8 +374,8 @@ const ChoreEdit = () => { frequencyType: frequencyType, frequency: Number(frequency), frequencyMetadata: frequencyMetadata, - assignedTo: assignStrategy === 'no_assignee' ? null : assignedTo, - assignStrategy: assignStrategy, + assignedTo: assignedToValue, + assignStrategy: assignStrategyValue, isRolling: isRolling, isActive: isActive, notification: isNotificable, @@ -463,6 +471,20 @@ const ChoreEdit = () => { } } }, []) + useEffect(() => { + if (choreId || !userProfile?.id) return + + const defaultAnyoneSetting = localStorage.getItem('defaultAnyoneSetting') + const defaultAssigneeSetting = localStorage.getItem( + 'defaultAssigneeSetting', + ) + + if (defaultAnyoneSetting === null && defaultAssigneeSetting === null) { + setAnyone(false) + setAssignableTo([{ userId: userProfile.id }]) + setAssignedTo(userProfile.id) + } + }, [choreId, userProfile?.id]) useEffect(() => { const anyoneSetting = localStorage.getItem('defaultAnyoneSetting') const anyoneDirty = anyoneSetting !== JSON.stringify(anyone) @@ -549,7 +571,7 @@ const ChoreEdit = () => { setAssignStrategy( data.res.assignStrategy ? data.res.assignStrategy - : ASSIGN_STRATEGIES[2], + : DEFAULT_ASSIGN_STRATEGY, ) setIsRolling(data.res.isRolling) setIsActive(data.res.isActive) @@ -632,21 +654,6 @@ const ChoreEdit = () => { } }, [frequencyType]) - useEffect(() => { - if (anyone || assignableTo.length === 0) { - setAssignStrategy('no_assignee') - setAssignedTo(null) - } else if (assignStrategy === 'no_assignee') { - // user explicitly picked no_assignee while having assignees, keep it - // but there is nobody currently assigned - if (assignedTo !== null) { - setAssignedTo(null) - } - } else if (!assignableTo.some(a => a.userId === assignedTo)) { - setAssignedTo(assignableTo[0].userId) - } - }, [assignStrategy, assignedTo, assignableTo, anyone]) - // useEffect(() => { // if (performers.length > 0 && assignees.length === 0 && userProfile) { // setAssignees([ @@ -1260,12 +1267,13 @@ const ChoreEdit = () => { )} - {!anyone && assignableTo.length > 1 && ( + {canPickStrategy && ( <> Currently Assigned To @@ -1279,7 +1287,7 @@ const ChoreEdit = () => { : 'Select an assignee for this task' } disabled={assignees.length === 0} - value={assignedTo > -1 ? assignedTo : null} + value={assignedToValue} onChange={(_, selectedUserId) => setAssignedTo(selectedUserId)} > {performers @@ -1309,7 +1317,7 @@ const ChoreEdit = () => { {ASSIGN_STRATEGIES.map((item, idx) => ( setAssignStrategy(item)} overlay disableIcon diff --git a/src/views/Chores/MyChores.jsx b/src/views/Chores/MyChores.jsx index 2e133a1..d453449 100644 --- a/src/views/Chores/MyChores.jsx +++ b/src/views/Chores/MyChores.jsx @@ -4,9 +4,9 @@ import { CalendarMonth, CloudOff, EditCalendar, - SearchOff, ExpandCircleDown, PriorityHigh, + SearchOff, Style, } from '@mui/icons-material' import { @@ -20,44 +20,42 @@ import { IconButton, Typography, } from '@mui/joy' -import Fuse from 'fuse.js' -import { useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useNavigate, useSearchParams } from 'react-router-dom' -import { useChores } from '../../queries/ChoreQueries' -import { useNotification } from '../../service/NotificationProvider' -import Priorities from '../../utils/Priorities' -import LoadingComponent from '../components/Loading' -import { useLabels } from '../Labels/LabelQueries' -import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' -import IconButtonWithMenu from './IconButtonWithMenu' - import { useMediaQuery } from '@mui/material' import { useQueryClient } from '@tanstack/react-query' +import { useEffect, useMemo, useRef, useState } from 'react' +import { useNavigate, useSearchParams } from 'react-router-dom' + import EmptyState from '../../components/common/EmptyState' import KeyboardShortcutHint from '../../components/common/KeyboardShortcutHint' -import { useFilter } from '../../hooks/useFilter' import { useImpersonateUser } from '../../contexts/ImpersonateUserContext.jsx' +import { useFilter } from '../../hooks/useFilter' +import { useChores } from '../../queries/ChoreQueries' import { useCircleMembers, useUserProfile } from '../../queries/UserQueries' +import { useNotification } from '../../service/NotificationProvider' import { ChoreFilters, ChoresGrouper, ChoreSorter, filterByProject, } from '../../utils/Chores' +import Priorities from '../../utils/Priorities' import { getSafeBottom } from '../../utils/SafeAreaUtils.js' import TaskInput from '../components/AddTaskModal' import CalendarDual from '../components/CalendarDual' import CalendarMonthly from '../components/CalendarMonthly.jsx' import FeedbackPrompt from '../components/FeedbackPrompt.jsx' +import LoadingComponent from '../components/Loading' +import { useLabels } from '../Labels/LabelQueries' import AdvancedFilterBuilder from '../Modals/Inputs/AdvancedFilterBuilder' +import ConfirmationModal from '../Modals/Inputs/ConfirmationModal' import { useProjects } from '../Projects/ProjectQueries.js' import ChoreListView from './ChoreListView.jsx' +import ChoreModals from './components/ChoreModals' import ChoreToolbar from './components/ChoreToolbarPrototype' import { conditionsToSelections, selectionsToConditions, } from './components/FilterBuilderContent' -import ChoreModals from './components/ChoreModals' import MultiSelectToolbar from './components/MultiSelectToolbar' import MyChoreHeader from './components/MyChoreHeader' import { useChoreActions } from './hooks/useChoreActions' @@ -79,7 +77,7 @@ const MyChores = () => { const { data: userProfile, isLoading: isUserProfileLoading } = useUserProfile() const isLargeScreen = useMediaQuery(theme => theme.breakpoints.up('md')) - const { showSuccess, showError, showWarning, showUndo } = useNotification() + const { showError, showSuccess, showUndo, showWarning } = useNotification() const queryClient = useQueryClient() const { impersonatedUser } = useImpersonateUser() const Navigate = useNavigate() @@ -88,20 +86,19 @@ const MyChores = () => { const { data: projects = [], isLoading: projectsLoading } = useProjects() const { data: choresData, - isLoading: choresLoading, - isError: choresError, error: choresErrorDetails, + isError: choresError, + isLoading: choresLoading, refetch: refetchChores, } = useChores(false) const { data: membersData, - isLoading: membersLoading, isError: membersError, + isLoading: membersLoading, } = useCircleMembers() const [chores, setChores] = useState([]) const [filteredChores, setFilteredChores] = useState([]) - const [choreSections, setChoreSections] = useState([]) const [addTaskModalOpen, setAddTaskModalOpen] = useState(false) // 'voice' | 'scan' | null — set by the quick-capture widget deep links const [addTaskInitialMode, setAddTaskInitialMode] = useState(null) @@ -118,6 +115,9 @@ const MyChores = () => { return {} } }) + const openSectionsInitializedRef = useRef( + localStorage.getItem('openChoreSections') !== null, + ) const [anchorEl, setAnchorEl] = useState(null) const [viewMode, setViewMode] = useState( localStorage.getItem('choreCardViewMode') || 'default', @@ -126,15 +126,15 @@ const MyChores = () => { const menuRef = useRef(null) const [confirmModelConfig, setConfirmModelConfig] = useState({}) - const { selectedProject, projectsWithDefault, setSelectedProjectWithCache } = + const { projectsWithDefault, selectedProject, setSelectedProjectWithCache } = useProjectFilter(projects, !projectsLoading) const { - searchTerm, - selectedChoreFilter, + nonProjectFilteredChores, projectFilteredChores, searchFilteredChores, - nonProjectFilteredChores, + searchTerm, + selectedChoreFilter, setSearchTerm, setSelectedChoreFilterWithCache, } = useChoreFilters({ @@ -145,37 +145,37 @@ const MyChores = () => { }) const { - isMultiSelectMode, - selectedChores, - toggleMultiSelectMode, - toggleChoreSelection, - enterMultiSelectWithChore, - selectAllVisibleChores, clearSelection, + enterMultiSelectWithChore, getSelectedChoresData, + isMultiSelectMode, + selectAllVisibleChores, + selectedChores, + toggleChoreSelection, + toggleMultiSelectMode, } = useMultiSelect() - const { activeModal, modalChore, modalData, openModal, closeModal } = + const { activeModal, closeModal, modalChore, modalData, openModal } = useChoreModals() const { - savedFilters, activeFilter, activeFilterId, + applyCustomFilter, + applyTempFilter, + clearActiveFilter, + clearTempFilter, + createFilterFromCurrentState, + deleteFilter, + filteredChores: customFilteredChores, + hasFilterApplied, + hasProjectConditions, + pinFilter, + saveFilter, + savedFilters, tempFilter, tempFilterMeta, - filteredChores: customFilteredChores, - applyCustomFilter, - clearActiveFilter, - applyTempFilter, - clearTempFilter, - saveFilter, updateFilter, - deleteFilter, - pinFilter, - createFilterFromCurrentState, - hasProjectConditions, - hasFilterApplied, } = useCustomFilters( nonProjectFilteredChores, membersData?.res, @@ -276,10 +276,10 @@ const MyChores = () => { ) const { - filteredData: quickFilteredChores, - setFilter: setQuickFilter, clearAll: clearQuickFilters, + filteredData: quickFilteredChores, hasActiveFilters: hasQuickFilters, + setFilter: setQuickFilter, } = useFilter(projectFilteredChores, quickFilterDefs) const processedChores = useMemo(() => { @@ -301,7 +301,7 @@ const MyChores = () => { return sortedChores }, [choresData?.res, impersonatedUser]) - const processedSections = useMemo(() => { + const choreSections = useMemo(() => { if (!chores.length || !userProfile?.id) { return [] } @@ -385,26 +385,16 @@ const MyChores = () => { impersonatedUser?.userId, ]) - // Auto-update sections when processedSections changes useEffect(() => { - // Always update choreSections to match processedSections, even if empty - setChoreSections(processedSections) + if (openSectionsInitializedRef.current || choreSections.length === 0) return - // Auto-open sections if needed - only check localStorage once - if (processedSections.length > 0) { - const storedSections = localStorage.getItem('openChoreSections') - if (storedSections === null) { - const openSections = processedSections.reduce( - (acc, _section, index) => { - acc[index] = true - return acc - }, - {}, - ) - setOpenChoreSections(openSections) - } - } - }, [processedSections]) + openSectionsInitializedRef.current = true + const openSections = choreSections.reduce((acc, _section, index) => { + acc[index] = true + return acc + }, {}) + setOpenChoreSections(openSections) + }, [choreSections]) useEffect(() => { document.addEventListener('mousedown', handleMenuOutsideClick) @@ -567,17 +557,17 @@ const MyChores = () => { }, [tempFilterMeta?.id, searchParams]) const { - handleChoreAction, - handleChangeDueDate, - handleCompleteWithPastDate, handleAssigneeChange, - handleCompleteWithNote, - handleNudge, - handleBulkComplete, handleBulkArchive, + handleBulkComplete, handleBulkDelete, - handleBulkSkip, handleBulkMoveToProject, + handleBulkSkip, + handleChangeDueDate, + handleChoreAction, + handleCompleteWithNote, + handleCompleteWithPastDate, + handleNudge, } = useChoreActions({ chores, filteredChores, @@ -603,24 +593,14 @@ const MyChores = () => { return customFilteredChores } + if (searchTerm?.length > 0) { + return searchFilteredChores + } + const baseChores = hasQuickFilters ? quickFilteredChores : projectFilteredChores - if (searchTerm?.length > 0) { - const searchableChores = baseChores.map(c => ({ - ...c, - raw_label: c.labelsV2?.map(l => l.name).join(' '), - })) - const fuse = new Fuse(searchableChores, { - keys: ['name', 'raw_label'], - includeScore: true, - isCaseSensitive: false, - findAllMatches: true, - }) - return fuse.search(searchTerm).map(result => result.item) - } - return baseChores }, [ activeFilterId, @@ -629,6 +609,7 @@ const MyChores = () => { hasQuickFilters, quickFilteredChores, projectFilteredChores, + searchFilteredChores, searchTerm, ]) @@ -751,29 +732,10 @@ const MyChores = () => { ) } - const searchOptions = useMemo( - () => ({ - keys: ['name', 'raw_label'], - includeScore: true, - isCaseSensitive: false, - findAllMatches: true, - }), - [], - ) - - const processedChoresForSearch = useMemo( - () => - chores.map(c => ({ - ...c, - raw_label: c.labelsV2?.map(l => l.name).join(' '), - })), - [chores], - ) - - const fuse = useMemo( - () => new Fuse(processedChoresForSearch, searchOptions), - [processedChoresForSearch, searchOptions], - ) + const clearTempFilterAndUrl = () => { + clearTempFilter() + updateFilterUrl(null, null) + } const handleSearchChange = e => { clearActiveFilter() @@ -790,22 +752,6 @@ const MyChores = () => { const term = search.toLowerCase() setSearchTerm(term) - - // Use project-filtered chores as base for search - const baseChores = selectedProject ? projectFilteredChores : chores - const searchableChores = baseChores.map(c => ({ - ...c, - raw_label: c.labelsV2?.map(l => l.name).join(' '), - })) - - const fuse = new Fuse(searchableChores, { - keys: ['name', 'raw_label'], - includeScore: true, - isCaseSensitive: false, - findAllMatches: true, - }) - - setFilteredChores(fuse.search(term).map(result => result.item)) // Clear selected calendar date when search changes setSelectedCalendarDate(null) } @@ -895,19 +841,15 @@ const MyChores = () => { // ) // } - const getChoresForDate = useCallback( - date => { - const filteredChoresData = getFilteredChores - return filteredChoresData.filter(chore => { - if (!chore.nextDueDate) return false - const choreDate = new Date(chore.nextDueDate).toLocaleDateString() - const selectedDate = date.toLocaleDateString() - return choreDate === selectedDate - }) - }, - [getFilteredChores], - ) + const selectedDateChores = useMemo(() => { + if (!selectedCalendarDate) return [] + const selectedDate = selectedCalendarDate.toLocaleDateString() + return getFilteredChores.filter(chore => { + if (!chore.nextDueDate) return false + return new Date(chore.nextDueDate).toLocaleDateString() === selectedDate + }) + }, [getFilteredChores, selectedCalendarDate]) // "Narrowed" means the user actively cut the list down (search, quick // filters, a saved filter). Picking a project is not narrowing: an empty @@ -929,7 +871,6 @@ const MyChores = () => { const appendChore = (prev, newChore) => { let newChores = [...prev, newChore] - if (impersonatedUser) { newChores = newChores.filter( chore => chore.assignedTo === impersonatedUser.userId, @@ -1010,7 +951,7 @@ const MyChores = () => { tempFilter={tempFilter} tempFilterMeta={tempFilterMeta} applyTempFilter={applyTempFilter} - clearTempFilter={clearTempFilter} + clearTempFilter={clearTempFilterAndUrl} saveFilter={saveFilter} updateFilter={updateFilter} onFilterSaved={name => @@ -1363,7 +1304,7 @@ const MyChores = () => { overflowY: 'auto', }} > - {getChoresForDate(selectedCalendarDate).length === 0 ? ( + {selectedDateChores.length === 0 ? ( } @@ -1377,7 +1318,7 @@ const MyChores = () => { /> ) : ( { }, }} > - + {openChoreSections[index] && ( + + )} ) @@ -1574,7 +1517,7 @@ const MyChores = () => { allChores={chores} performers={membersData?.res || []} applyTempFilter={applyTempFilter} - clearTempFilter={clearTempFilter} + clearTempFilter={clearTempFilterAndUrl} tempFilter={tempFilter} /> diff --git a/src/views/Chores/hooks/useChoreFilters.js b/src/views/Chores/hooks/useChoreFilters.js index e30bb32..a8903a6 100644 --- a/src/views/Chores/hooks/useChoreFilters.js +++ b/src/views/Chores/hooks/useChoreFilters.js @@ -22,23 +22,36 @@ export const useChoreFilters = ({ return filterByProject(chores, selectedProject.id) }, [chores, selectedProject]) + const hasSearchTerm = searchTerm.length > 0 + + const searchIndex = useMemo(() => { + if (!hasSearchTerm) return null + + const searchableChores = chores.map(chore => ({ + ...chore, + raw_label: chore.labelsV2?.map(label => label.name).join(' '), + })) + return new Fuse(searchableChores, { + keys: ['name', 'raw_label'], + includeScore: true, + isCaseSensitive: false, + findAllMatches: true, + }) + }, [chores, hasSearchTerm]) + + const projectChoreIds = useMemo( + () => new Set(projectFilteredChores.map(chore => chore.id)), + [projectFilteredChores], + ) + const searchFilteredChores = useMemo(() => { let baseChores = projectFilteredChores - if (searchTerm?.length > 0) { - const searchableChores = baseChores.map(c => ({ - ...c, - raw_label: c.labelsV2?.map(l => l.name).join(' '), - })) - - const fuse = new Fuse(searchableChores, { - keys: ['name', 'raw_label'], - includeScore: true, - isCaseSensitive: false, - findAllMatches: true, - }) - - return fuse.search(searchTerm.toLowerCase()).map(result => result.item) + if (searchIndex) { + return searchIndex + .search(searchTerm.toLowerCase()) + .map(result => result.item) + .filter(chore => projectChoreIds.has(chore.id)) } if (impersonatedUser) { @@ -55,6 +68,8 @@ export const useChoreFilters = ({ }, [ searchTerm, projectFilteredChores, + searchIndex, + projectChoreIds, impersonatedUser, userProfile?.id, selectedChoreFilter, @@ -64,20 +79,10 @@ export const useChoreFilters = ({ const nonProjectFilteredChores = useMemo(() => { let baseChores = chores - if (searchTerm?.length > 0) { - const searchableChores = baseChores.map(c => ({ - ...c, - raw_label: c.labelsV2?.map(l => l.name).join(' '), - })) - - const fuse = new Fuse(searchableChores, { - keys: ['name', 'raw_label'], - includeScore: true, - isCaseSensitive: false, - findAllMatches: true, - }) - - return fuse.search(searchTerm.toLowerCase()).map(result => result.item) + if (searchIndex) { + return searchIndex + .search(searchTerm.toLowerCase()) + .map(result => result.item) } if (impersonatedUser) { @@ -94,6 +99,7 @@ export const useChoreFilters = ({ }, [ searchTerm, chores, + searchIndex, impersonatedUser, userProfile?.id, selectedChoreFilter, diff --git a/src/views/Chores/hooks/useCustomFilters.js b/src/views/Chores/hooks/useCustomFilters.js index 5e70af6..b3ca52a 100644 --- a/src/views/Chores/hooks/useCustomFilters.js +++ b/src/views/Chores/hooks/useCustomFilters.js @@ -1,11 +1,7 @@ import { useCallback, useMemo, useState } from 'react' + import { useUserProfile } from '../../../queries/UserQueries' -import { - applyFilter, - getFilterCount, - getFilterOverdueCount, - validateFilter, -} from '../../../utils/FilterEngine' +import { applyFilter, validateFilter } from '../../../utils/FilterEngine' import { useCreateFilter, useDeleteFilter, @@ -43,12 +39,17 @@ export const useCustomFilters = (chores, membersData, labels, projects) => { return filtersData.map(filter => { const validation = validateFilter(filter, context) - const count = validation.isValid - ? getFilterCount(chores, filter, context) - : 0 - const overdueCount = validation.isValid - ? getFilterOverdueCount(chores, filter, context) - : 0 + const matchingChores = validation.isValid + ? applyFilter(chores, filter, context) + : [] + const count = matchingChores.length + const now = new Date() + const overdueCount = matchingChores.reduce((total, chore) => { + if (!chore.nextDueDate || new Date(chore.nextDueDate) >= now) { + return total + } + return total + 1 + }, 0) const result = { ...filter,