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 { Box, Button, Chip, Typography } from '@mui/joy'
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,
@@ -44,13 +46,21 @@ const ActiveFilterChips = ({
variant='soft'
color={color}
endDecorator={
<Close
sx={{ cursor: 'pointer', fontSize: chipSize === 'sm' ? 12 : 16 }}
onClick={e => {
e.stopPropagation()
onClear?.()
// ChipDelete rather than a bare icon: Joy's chip end decorator is
// `pointer-events: none`, so anything else here is swallowed by the
// chip's own click surface and can never clear the condition.
<ChipDelete
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}
sx={{
@@ -74,7 +84,10 @@ const ActiveFilterChips = ({
</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
size={chipSize}
variant='soft'
@@ -90,6 +103,30 @@ const ActiveFilterChips = ({
>
+{overflow} more
</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 && (

View File

@@ -52,6 +52,10 @@ import AdvancedFilterBuilder from '../Modals/Inputs/AdvancedFilterBuilder'
import { useProjects } from '../Projects/ProjectQueries.js'
import ChoreListView from './ChoreListView.jsx'
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'
@@ -683,14 +687,46 @@ const MyChores = () => {
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 => {
clearActiveFilter()
if (chipClicked.label) {
setQuickFilter('label', [chipClicked.label.id])
} else if (chipClicked.priority) {
setQuickFilter('priority', [chipClicked.priority])
const type = chipClicked.label ? 'label' : 'priority'
const value = chipClicked.label
? chipClicked.label.id
: 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)
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

View File

@@ -317,27 +317,6 @@ const ChoreToolbar = ({
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
? savedFilters.find(f => f.id === activeFilterId)
: null
@@ -346,17 +325,61 @@ const ChoreToolbar = ({
? activeSavedFilter?.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) => {
inlineChips.push({
key: `${savedFilterActive ? '__saved' : '__temp'}_${index}`,
label: getConditionChipLabel(condition),
onClear: () => {
if (savedFilterActive) {
onSavedFilterClick?.(activeFilterId)
return
}
clearConditionAtIndex(index)
},
onClear: () => clearConditionAtIndex(index),
})
})
@@ -638,6 +661,8 @@ const ChoreToolbar = ({
<ActiveFilterChips
chips={inlineChips}
onOpen={openFilterSheet}
showAddChip
onAdd={openFilterSheet}
onClearAll={() => {
setLocalSelections(defaultSelections())
onClearAllFilters?.()

View File

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

View File

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