Remove gray panel in add task modal and update label interaction (#187)

* Remove the gray panel in add task modal

* migration clicking label to the new advance filtered.

* Fix: Clicking x on filter chip remove the filter condition
This commit is contained in:
Mohamad Tarbin
2026-08-02 20:53:41 -04:00
committed by GitHub
parent d641710a41
commit 66d742d091
5 changed files with 149 additions and 69 deletions

View File

@@ -1,10 +1,12 @@
import { Close } from '@mui/icons-material' import { Add, Close } from '@mui/icons-material'
import { Box, Button, Chip, Typography } from '@mui/joy' import { Box, Button, Chip, ChipDelete, Typography } from '@mui/joy'
const ActiveFilterChips = ({ const ActiveFilterChips = ({
chips = [], chips = [],
onOpen, onOpen,
onClearAll, onClearAll,
onAdd,
showAddChip = false,
resultCount, resultCount,
totalCount, totalCount,
maxVisible = 2, maxVisible = 2,
@@ -44,13 +46,21 @@ const ActiveFilterChips = ({
variant='soft' variant='soft'
color={color} color={color}
endDecorator={ endDecorator={
<Close // ChipDelete rather than a bare icon: Joy's chip end decorator is
sx={{ cursor: 'pointer', fontSize: chipSize === 'sm' ? 12 : 16 }} // `pointer-events: none`, so anything else here is swallowed by the
onClick={e => { // chip's own click surface and can never clear the condition.
e.stopPropagation() <ChipDelete
onClear?.() variant='plain'
color={color}
onDelete={() => onClear?.()}
aria-label={`Remove ${label} filter`}
sx={{
'--Chip-deleteSize': chipSize === 'sm' ? '1.1rem' : '1.4rem',
'--Icon-fontSize': chipSize === 'sm' ? '12px' : '16px',
}} }}
/> >
<Close />
</ChipDelete>
} }
onClick={onOpen} onClick={onOpen}
sx={{ sx={{
@@ -74,7 +84,10 @@ const ActiveFilterChips = ({
</Chip> </Chip>
))} ))}
{overflow > 0 && ( {/* Everything already visible → spend that slot on a "+" for appending
another condition instead. With overflow, the count chip opens the
same sheet anyway. */}
{overflow > 0 ? (
<Chip <Chip
size={chipSize} size={chipSize}
variant='soft' variant='soft'
@@ -90,6 +103,30 @@ const ActiveFilterChips = ({
> >
+{overflow} more +{overflow} more
</Chip> </Chip>
) : (
showAddChip &&
(onAdd || onOpen) && (
<Chip
size={chipSize}
variant='outlined'
color='neutral'
onClick={onAdd || onOpen}
aria-label='Add filter condition'
title='Add filter condition'
sx={{
cursor: 'pointer',
flexShrink: 0,
px: 0.75,
transition: 'all 0.15s ease',
'&:hover': { opacity: 0.85 },
...overflowChipSx,
}}
>
<Add
sx={{ fontSize: chipSize === 'sm' ? 12 : 16, display: 'block' }}
/>
</Chip>
)
)} )}
{resultCount != null && totalCount != null && ( {resultCount != null && totalCount != null && (

View File

@@ -52,6 +52,10 @@ import AdvancedFilterBuilder from '../Modals/Inputs/AdvancedFilterBuilder'
import { useProjects } from '../Projects/ProjectQueries.js' import { useProjects } from '../Projects/ProjectQueries.js'
import ChoreListView from './ChoreListView.jsx' import ChoreListView from './ChoreListView.jsx'
import ChoreToolbar from './components/ChoreToolbarPrototype' import ChoreToolbar from './components/ChoreToolbarPrototype'
import {
conditionsToSelections,
selectionsToConditions,
} from './components/FilterBuilderContent'
import ChoreModals from './components/ChoreModals' import ChoreModals from './components/ChoreModals'
import MultiSelectToolbar from './components/MultiSelectToolbar' import MultiSelectToolbar from './components/MultiSelectToolbar'
import MyChoreHeader from './components/MyChoreHeader' import MyChoreHeader from './components/MyChoreHeader'
@@ -683,14 +687,46 @@ const MyChores = () => {
setAnchorEl(null) setAnchorEl(null)
} }
// Clicking a label / priority chip on a task card feeds the advanced filter
// (as a temp filter) rather than the legacy quick filters. Clicking the same
// chip again removes that value, so chips toggle.
const handleLabelFiltering = chipClicked => { const handleLabelFiltering = chipClicked => {
clearActiveFilter() const type = chipClicked.label ? 'label' : 'priority'
if (chipClicked.label) { const value = chipClicked.label
setQuickFilter('label', [chipClicked.label.id]) ? chipClicked.label.id
} else if (chipClicked.priority) { : chipClicked.priority
setQuickFilter('priority', [chipClicked.priority]) if (value === undefined || value === null) return
const selections = conditionsToSelections(tempFilter?.conditions)
const currentValues = selections[type].values || []
const isActive = currentValues.some(v => String(v) === String(value))
selections[type] = {
operator: selections[type].operator || 'is',
values: isActive
? currentValues.filter(v => String(v) !== String(value))
: [...currentValues, value],
} }
const conditions = selectionsToConditions(selections)
clearQuickFilters()
setSelectedCalendarDate(null) setSelectedCalendarDate(null)
if (conditions.length === 0) {
clearTempFilter()
clearActiveFilter()
return
}
const chipName = chipClicked.label
? chipClicked.label.name
: `P${chipClicked.priority}`
applyTempFilter(
{ conditions, operator: 'AND' },
// Keep whatever the temp filter was already labelled as (e.g. an
// in-progress saved-filter edit); only name it when starting fresh.
tempFilterMeta ?? { name: chipName },
)
} }
// Helper to update URL with filter parameters // Helper to update URL with filter parameters

View File

@@ -317,27 +317,6 @@ const ChoreToolbar = ({
return `${prefix}${typeLabel} (${rawValues.length})` return `${prefix}${typeLabel} (${rawValues.length})`
} }
const clearConditionAtIndex = index => {
const nextConditions = (tempFilter?.conditions || []).filter(
(_condition, conditionIndex) => conditionIndex !== index,
)
if (nextConditions.length === 0) {
setLocalSelections(defaultSelections())
clearTempFilter?.()
return
}
const nextFilter = {
...tempFilter,
operator: tempFilter?.operator || 'AND',
conditions: nextConditions,
}
setLocalSelections(conditionsToSelections(nextConditions))
applyTempFilter?.(nextFilter)
}
const activeSavedFilter = savedFilterActive const activeSavedFilter = savedFilterActive
? savedFilters.find(f => f.id === activeFilterId) ? savedFilters.find(f => f.id === activeFilterId)
: null : null
@@ -346,17 +325,61 @@ const ChoreToolbar = ({
? activeSavedFilter?.conditions || [] ? activeSavedFilter?.conditions || []
: tempFilter?.conditions || [] : tempFilter?.conditions || []
const savedFilterEditMeta = activeSavedFilter
? {
name: activeSavedFilter.name,
description: activeSavedFilter.description,
sourceFilterId: activeSavedFilter.id,
sourceFilterName: activeSavedFilter.name,
sourceFilterDescription: activeSavedFilter.description,
sourceFilterColor: activeSavedFilter.color,
isEditingSavedFilter: true,
}
: null
const clearConditionAtIndex = index => {
const nextConditions = activeChipConditions.filter(
(_condition, conditionIndex) => conditionIndex !== index,
)
if (nextConditions.length === 0) {
setLocalSelections(defaultSelections())
clearTempFilter?.()
// A saved filter still owns the view until it's toggled back off.
if (savedFilterActive) onSavedFilterClick?.(activeFilterId)
return
}
setLocalSelections(conditionsToSelections(nextConditions))
// Dropping a condition from a saved filter detaches it into a temp filter
// that remembers its source, rather than editing the saved filter itself.
if (savedFilterActive) {
applyTempFilter?.(
{
conditions: nextConditions,
operator: activeSavedFilter?.operator || 'AND',
},
savedFilterEditMeta,
)
return
}
applyTempFilter?.(
{
...tempFilter,
operator: tempFilter?.operator || 'AND',
conditions: nextConditions,
},
tempFilterMeta,
)
}
activeChipConditions.forEach((condition, index) => { activeChipConditions.forEach((condition, index) => {
inlineChips.push({ inlineChips.push({
key: `${savedFilterActive ? '__saved' : '__temp'}_${index}`, key: `${savedFilterActive ? '__saved' : '__temp'}_${index}`,
label: getConditionChipLabel(condition), label: getConditionChipLabel(condition),
onClear: () => { onClear: () => clearConditionAtIndex(index),
if (savedFilterActive) {
onSavedFilterClick?.(activeFilterId)
return
}
clearConditionAtIndex(index)
},
}) })
}) })
@@ -638,6 +661,8 @@ const ChoreToolbar = ({
<ActiveFilterChips <ActiveFilterChips
chips={inlineChips} chips={inlineChips}
onOpen={openFilterSheet} onOpen={openFilterSheet}
showAddChip
onAdd={openFilterSheet}
onClearAll={() => { onClearAll={() => {
setLocalSelections(defaultSelections()) setLocalSelections(defaultSelections())
onClearAllFilters?.() onClearAllFilters?.()

View File

@@ -138,15 +138,7 @@ const ScanPanel = ({
const isProcessing = phase === 'processing' const isProcessing = phase === 'processing'
return ( return (
<Box <Box>
sx={{
borderRadius: 'md',
border: '1px solid',
borderColor: 'primary.outlinedBorder',
overflow: 'hidden',
bgcolor: 'background.level1',
}}
>
{/* ── Capture phase ── */} {/* ── Capture phase ── */}
{phase === 'capture' && ( {phase === 'capture' && (
<> <>
@@ -160,6 +152,8 @@ const ScanPanel = ({
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
bgcolor: 'neutral.900', bgcolor: 'neutral.900',
// The wrapper used to clip this; it owns its own corners now
borderRadius: 'md',
overflow: 'hidden', overflow: 'hidden',
}} }}
> >
@@ -211,7 +205,6 @@ const ScanPanel = ({
{(isNativeScanner || cameraAvailable) && ( {(isNativeScanner || cameraAvailable) && (
<Box <Box
sx={{ sx={{
px: 1.5,
py: 1, py: 1,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
@@ -240,7 +233,7 @@ const ScanPanel = ({
flexDirection: 'column', flexDirection: 'column',
alignItems: 'center', alignItems: 'center',
gap: 1.5, gap: 1.5,
p: 2.5, py: 2.5,
}} }}
> >
{capturedImage && ( {capturedImage && (
@@ -297,7 +290,7 @@ const ScanPanel = ({
{/* ── Error phase ── */} {/* ── Error phase ── */}
{phase === 'error' && ( {phase === 'error' && (
<Box sx={{ p: 2 }}> <Box sx={{ py: 2 }}>
{capturedImage && ( {capturedImage && (
<img <img
src={capturedImage} src={capturedImage}

View File

@@ -470,20 +470,11 @@ const VoicePanel = ({
: 'Hold to speak · quick tap for hands-free' : 'Hold to speak · quick tap for hands-free'
return ( return (
<Box <Box>
sx={{
borderRadius: 'md',
border: '1px solid',
borderColor: 'primary.outlinedBorder',
overflow: 'hidden',
bgcolor: 'background.level1',
}}
>
{/* ── Header ── */} {/* ── Header ── */}
<Box <Box
sx={{ sx={{
px: 1.5, pt: 0.5,
pt: 1.25,
display: 'flex', display: 'flex',
alignItems: 'center', alignItems: 'center',
gap: 1, gap: 1,
@@ -506,7 +497,7 @@ const VoicePanel = ({
{/* ── Permission denied ── */} {/* ── Permission denied ── */}
{phase === 'denied' && ( {phase === 'denied' && (
<Box sx={{ p: 2 }}> <Box sx={{ py: 2 }}>
<Box <Box
sx={{ display: 'flex', alignItems: 'flex-start', gap: 1, mb: 1.5 }} sx={{ display: 'flex', alignItems: 'flex-start', gap: 1, mb: 1.5 }}
> >
@@ -532,7 +523,6 @@ const VoicePanel = ({
<Box <Box
ref={segmentsScrollRef} ref={segmentsScrollRef}
sx={{ sx={{
px: 1.5,
pt: 1.25, pt: 1.25,
display: 'flex', display: 'flex',
flexDirection: 'column', flexDirection: 'column',
@@ -556,14 +546,13 @@ const VoicePanel = ({
{/* ── Live transcript ── */} {/* ── Live transcript ── */}
{isListening && ( {isListening && (
<Box sx={{ px: 1.5, pt: 1.25 }}> <Box sx={{ pt: 1.25 }}>
<Box <Box
sx={{ sx={{
minHeight: 44, minHeight: 44,
borderRadius: 'md', borderRadius: 'md',
border: '1px dashed', border: '1px dashed',
borderColor: 'neutral.outlinedBorder', borderColor: 'neutral.outlinedBorder',
bgcolor: 'background.surface',
px: 1.25, px: 1.25,
py: 1, py: 1,
}} }}