fix: update assignee filter logic and improve display options in MyChores and ChoreToolbarPrototype

This commit is contained in:
Mo Tarbin
2026-08-16 02:07:44 -04:00
parent 79d92ca8c9
commit 325d5e6df7
2 changed files with 70 additions and 21 deletions

View File

@@ -74,6 +74,16 @@ import NotificationAccessSnackbar from './NotificationAccessSnackbar'
import Sidepanel from './Sidepanel'
import { INSIGHT_FILTER_DEFS } from './SmartInsightsCard'
// Mirrors the assignee options in the toolbar, phrased to drop into a
// sentence ("none of them are assigned to you").
const ASSIGNEE_FILTER_LABELS = {
assigned_to_me: 'assigned to you',
available_for_me: 'available for you to pick up',
assigned_to_others: 'assigned to someone else',
assigned_to_me_tasks: 'assigned to you',
created_by_me: 'created by you',
}
const MyChores = () => {
const { data: userProfile, isLoading: isUserProfileLoading } =
useUserProfile()
@@ -884,20 +894,51 @@ const MyChores = () => {
})
}, [getFilteredChores, selectedCalendarDate])
// The assignee filter ("Mine", "Available to me", ...) is applied inside
// ChoresGrouper, not in projectFilteredChores, so it can hide every task
// while the unfiltered list still looks full. It narrows like any other.
const assigneeFilterLabel = ASSIGNEE_FILTER_LABELS[selectedChoreFilter]
const hasAssigneeFilter = Boolean(
selectedChoreFilter && selectedChoreFilter !== 'anyone',
)
// "Narrowed" means the user actively cut the list down (search, quick
// filters, a saved filter). Picking a project is not narrowing: an empty
// project is an empty place, not a filtered-away result.
// filters, a saved filter, the assignee filter). Picking a project is not
// narrowing: an empty project is an empty place, not a filtered-away result.
const isNarrowed = Boolean(
searchTerm?.length > 0 || hasQuickFilters || activeFilterId,
searchTerm?.length > 0 ||
hasQuickFilters ||
activeFilterId ||
hasAssigneeFilter,
)
const isCustomProjectSelected = Boolean(
selectedProject && selectedProject.id !== 'default',
)
// Worth its own wording: the assignee filter is the one narrowing that is
// easy to forget you left on, so name it rather than saying "filters".
const isAssigneeOnlyNarrowing = Boolean(
assigneeFilterLabel &&
!searchTerm?.length &&
!hasQuickFilters &&
!activeFilterId,
)
// What the list actually renders. Sections are the source of truth outside
// of search, since they are the only place the assignee filter is applied.
const visibleChoreCount = useMemo(
() =>
choreSections.reduce(
(total, section) => total + (section.content?.length || 0),
0,
),
[choreSections],
)
const clearNarrowing = () => {
clearQuickFilters()
setSearchTerm('')
clearActiveFilter()
setSelectedChoreFilterWithCache('anyone')
updateFilterUrl(null, null)
}
@@ -1106,10 +1147,13 @@ const MyChores = () => {
{/* Empty state. Three different situations, three different messages:
nothing created yet, nothing left after narrowing, or an empty
project. Only the middle one is about filters. */}
{(isNarrowed
project. Only the middle one is about filters.
The trigger is what the list actually renders, not the pre-filter
count, so a view emptied purely by the assignee filter still
explains itself instead of showing a blank page. */}
{(searchTerm?.length > 0
? getFilteredChores.length === 0
: projectFilteredChores.length === 0) &&
: visibleChoreCount === 0) &&
// only if not in calendar view:
viewMode !== 'calendar' &&
(chores.length === 0 ? (
@@ -1129,7 +1173,10 @@ const MyChores = () => {
onClick: () => Navigate('/chores/create'),
}}
/>
) : isNarrowed ? (
) : isNarrowed &&
(searchTerm?.length > 0 ||
activeFilterId ||
projectFilteredChores.length > 0) ? (
<EmptyState
variant='no-results'
fullHeight
@@ -1138,11 +1185,17 @@ const MyChores = () => {
description={
searchTerm?.length > 0
? `Nothing matches "${searchTerm}". Try a different search, or clear what is narrowing the list.`
: 'You have tasks, but none of them fit the filters that are currently on.'
: isAssigneeOnlyNarrowing
? `There are tasks here, but none of them are ${assigneeFilterLabel}. Switch back to everyone to see the rest.`
: 'You have tasks, but none of them fit the filters that are currently on.'
}
primaryAction={{
label:
searchTerm?.length > 0 ? 'Clear search' : 'Clear filters',
searchTerm?.length > 0
? 'Clear search'
: isAssigneeOnlyNarrowing
? "Show everyone's tasks"
: 'Clear filters',
onClick: clearNarrowing,
}}
/>

View File

@@ -23,13 +23,13 @@ import {
Check,
CheckBox,
CheckBoxOutlineBlank,
DisplaySettings,
FilterList,
Save,
Sort,
Tune,
ViewAgenda,
ViewComfy,
ViewModule,
} from '@mui/icons-material'
import {
Badge,
@@ -594,23 +594,19 @@ const ChoreToolbar = ({
/>
)}
{/* Display button — View + Group combined */}
{/* Display button — View + Group combined.
Icon stays fixed: mirroring viewMode made this read as a toggle
showing the current view rather than a button that opens a sheet. */}
<IconButton
variant='outlined'
color='neutral'
size='sm'
sx={{ height: 32, width: 32, borderRadius: '50%' }}
onClick={() => setDisplaySheetOpen(true)}
aria-label='View and group options'
title='View & Group'
aria-label='Display options'
title='Display'
>
{viewMode === 'calendar' ? (
<CalendarMonth />
) : viewMode === 'compact' ? (
<ViewModule />
) : (
<ViewAgenda />
)}
<DisplaySettings />
</IconButton>
{/* Multiselect */}
@@ -884,7 +880,7 @@ const ChoreToolbar = ({
onClose={() => setDisplaySheetOpen(false)}
title={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<ViewAgenda sx={{ fontSize: 20 }} />
<DisplaySettings sx={{ fontSize: 20 }} />
Display
</Box>
}