Add LearnMoreButton component for displaying additional information
This commit is contained in:
parent
d3b36238d4
commit
1b7751c5d1
3 changed files with 234 additions and 129 deletions
|
@ -390,26 +390,25 @@ const MyChores = () => {
|
|||
/>
|
||||
)}
|
||||
{activeTextField != 'task' && (
|
||||
<Button
|
||||
<IconButton
|
||||
variant='outlined'
|
||||
size='sm'
|
||||
color='neutral'
|
||||
sx={{
|
||||
height: 24,
|
||||
borderRadius: 24,
|
||||
minWidth: 100,
|
||||
// minWidth: 100,
|
||||
}}
|
||||
startDecorator={<EditCalendar />}
|
||||
onClick={() => {
|
||||
setActiveTextField('task')
|
||||
setTaskInputFocus(taskInputFocus + 1)
|
||||
}}
|
||||
>
|
||||
Task
|
||||
</Button>
|
||||
<EditCalendar />
|
||||
</IconButton>
|
||||
)}
|
||||
{activeTextField != 'search' && (
|
||||
<Button
|
||||
<IconButton
|
||||
variant='outlined'
|
||||
color='neutral'
|
||||
size='sm'
|
||||
|
@ -417,7 +416,6 @@ const MyChores = () => {
|
|||
height: 24,
|
||||
borderRadius: 24,
|
||||
}}
|
||||
startDecorator={<Search />}
|
||||
onClick={() => {
|
||||
setActiveTextField('search')
|
||||
setSearchInputFocus(searchInputFocus + 1)
|
||||
|
@ -429,13 +427,37 @@ const MyChores = () => {
|
|||
searchInputRef.current.value?.length
|
||||
}}
|
||||
>
|
||||
Search
|
||||
</Button>
|
||||
<Search />
|
||||
</IconButton>
|
||||
)}
|
||||
<Divider orientation='vertical' />
|
||||
|
||||
<IconButtonWithMenu
|
||||
title='Group by'
|
||||
icon={<Sort />}
|
||||
options={[
|
||||
{ name: 'Due Date', value: 'due_date' },
|
||||
{ name: 'Priority', value: 'priority' },
|
||||
{ name: 'Labels', value: 'labels' },
|
||||
]}
|
||||
selectedItem={selectedChoreSection}
|
||||
onItemSelect={selected => {
|
||||
const section = ChoresGrouper(selected.value, chores)
|
||||
setChoreSections(section)
|
||||
setSelectedChoreSection(selected.value)
|
||||
setFilteredChores(chores)
|
||||
setSelectedFilter('All')
|
||||
}}
|
||||
mouseClickHandler={handleMenuOutsideClick}
|
||||
/>
|
||||
</Box>
|
||||
{activeTextField === 'search' && (
|
||||
<div className='flex gap-4'>
|
||||
<div className='grid flex-1 grid-cols-3 gap-4'>
|
||||
<IconButtonWithMenu
|
||||
label={' Priority'}
|
||||
key={'icon-menu-labels-filter'}
|
||||
icon={<PriorityHigh />}
|
||||
title='Filter by Priority'
|
||||
options={Priorities}
|
||||
selectedItem={selectedFilter}
|
||||
onItemSelect={selected => {
|
||||
|
@ -444,26 +466,12 @@ const MyChores = () => {
|
|||
mouseClickHandler={handleMenuOutsideClick}
|
||||
isActive={selectedFilter.startsWith('Priority: ')}
|
||||
/>
|
||||
|
||||
<IconButtonWithMenu
|
||||
key={'icon-menu-labels-filter'}
|
||||
label={' Labels'}
|
||||
icon={<Style />}
|
||||
// TODO : this need simplification we want to display both user labels and chore labels
|
||||
// that why we are merging them here.
|
||||
// we also filter out the labels that user created as those will be part of user labels
|
||||
title='Filter by Label'
|
||||
options={[
|
||||
...userLabels,
|
||||
...chores
|
||||
.map(c => c.labelsV2)
|
||||
.flat()
|
||||
.filter(l => l.created_by !== userProfile.id)
|
||||
.map(l => {
|
||||
// if user created it don't show it:
|
||||
return {
|
||||
...l,
|
||||
name: l.name + ' (Shared Label)',
|
||||
}
|
||||
}),
|
||||
]}
|
||||
options={userLabels}
|
||||
selectedItem={selectedFilter}
|
||||
onItemSelect={selected => {
|
||||
handleLabelFiltering({ label: selected })
|
||||
|
@ -472,9 +480,11 @@ const MyChores = () => {
|
|||
mouseClickHandler={handleMenuOutsideClick}
|
||||
useChips
|
||||
/>
|
||||
<IconButton
|
||||
|
||||
<Button
|
||||
onClick={handleFilterMenuOpen}
|
||||
variant='outlined'
|
||||
startDecorator={<FilterAlt />}
|
||||
color={
|
||||
selectedFilter &&
|
||||
FILTERS[selectedFilter] &&
|
||||
|
@ -488,8 +498,9 @@ const MyChores = () => {
|
|||
borderRadius: 24,
|
||||
}}
|
||||
>
|
||||
<FilterAlt />
|
||||
</IconButton>
|
||||
{' Other'}
|
||||
</Button>
|
||||
|
||||
<List
|
||||
orientation='horizontal'
|
||||
wrap
|
||||
|
@ -519,7 +530,9 @@ const MyChores = () => {
|
|||
>
|
||||
{filter}
|
||||
<Chip
|
||||
color={selectedFilter === filter ? 'primary' : 'neutral'}
|
||||
color={
|
||||
selectedFilter === filter ? 'primary' : 'neutral'
|
||||
}
|
||||
>
|
||||
{FILTERS[filter].length === 2
|
||||
? FILTERS[filter](chores, userProfile.id).length
|
||||
|
@ -527,6 +540,7 @@ const MyChores = () => {
|
|||
</Chip>
|
||||
</MenuItem>
|
||||
))}
|
||||
|
||||
{selectedFilter.startsWith('Label: ') ||
|
||||
(selectedFilter.startsWith('Priority: ') && (
|
||||
<MenuItem
|
||||
|
@ -541,26 +555,9 @@ const MyChores = () => {
|
|||
))}
|
||||
</Menu>
|
||||
</List>
|
||||
<Divider orientation='vertical' />
|
||||
<IconButtonWithMenu
|
||||
title='Group by'
|
||||
icon={<Sort />}
|
||||
options={[
|
||||
{ name: 'Due Date', value: 'due_date' },
|
||||
{ name: 'Priority', value: 'priority' },
|
||||
{ name: 'Labels', value: 'labels' },
|
||||
]}
|
||||
selectedItem={selectedChoreSection}
|
||||
onItemSelect={selected => {
|
||||
const section = ChoresGrouper(selected.value, chores)
|
||||
setChoreSections(section)
|
||||
setSelectedChoreSection(selected.value)
|
||||
setFilteredChores(chores)
|
||||
setSelectedFilter('All')
|
||||
}}
|
||||
mouseClickHandler={handleMenuOutsideClick}
|
||||
/>
|
||||
</Box>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{selectedFilter !== 'All' && (
|
||||
<Chip
|
||||
level='title-md'
|
||||
|
|
|
@ -20,6 +20,7 @@ import { useNavigate } from 'react-router-dom'
|
|||
import { CSSTransition } from 'react-transition-group'
|
||||
import { UserContext } from '../../contexts/UserContext'
|
||||
import { CreateChore } from '../../utils/Fetcher'
|
||||
import LearnMoreButton from './LearnMore'
|
||||
const VALID_DAYS = {
|
||||
monday: 'Monday',
|
||||
mon: 'Monday',
|
||||
|
@ -203,7 +204,7 @@ const TaskInput = ({ autoFocus, onChoreUpdate }) => {
|
|||
{
|
||||
frequencyType: 'days_of_the_week',
|
||||
regex: /every ([\w, ]+(?:day)?(?:, [\w, ]+(?:day)?)*)$/i,
|
||||
name: 'Every {days} of the week',
|
||||
name: 'Every {days}',
|
||||
},
|
||||
{
|
||||
frequencyType: 'day_of_the_month',
|
||||
|
@ -314,7 +315,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate }) => {
|
|||
const handleTextChange = e => {
|
||||
if (!e.target.value) {
|
||||
setTaskText('')
|
||||
setOpenModal(false)
|
||||
setDueDate(null)
|
||||
setFrequency(null)
|
||||
setFrequencyHumanReadable(null)
|
||||
|
@ -327,6 +327,13 @@ const TaskInput = ({ autoFocus, onChoreUpdate }) => {
|
|||
if (priority.result) setPriority(priority.result)
|
||||
cleanedSentence = priority.cleanedSentence
|
||||
|
||||
const repeat = parseRepeatV2(cleanedSentence)
|
||||
if (repeat.result) {
|
||||
setFrequency(repeat.result)
|
||||
setFrequencyHumanReadable(repeat.name)
|
||||
cleanedSentence = repeat.cleanedSentence
|
||||
}
|
||||
|
||||
const parsedDueDate = chrono.parse(cleanedSentence, new Date(), {
|
||||
forwardDate: true,
|
||||
})
|
||||
|
@ -337,13 +344,6 @@ const TaskInput = ({ autoFocus, onChoreUpdate }) => {
|
|||
cleanedSentence = cleanedSentence.replace(parsedDueDate[0].text, '')
|
||||
}
|
||||
|
||||
const repeat = parseRepeatV2(cleanedSentence)
|
||||
if (repeat.result) {
|
||||
setFrequency(repeat.result)
|
||||
setFrequencyHumanReadable(repeat.name)
|
||||
cleanedSentence = repeat.cleanedSentence
|
||||
}
|
||||
|
||||
if (priority.result || parsedDueDate[0]?.index > -1 || repeat.result) {
|
||||
setOpenModal(true)
|
||||
}
|
||||
|
@ -425,7 +425,7 @@ const TaskInput = ({ autoFocus, onChoreUpdate }) => {
|
|||
</Button> */}
|
||||
<Typography level='h4'>Create new task</Typography>
|
||||
<Chip startDecorator='🚧' variant='soft' color='warning' size='sm'>
|
||||
Experimental
|
||||
Experimental Feature
|
||||
</Chip>
|
||||
<Box>
|
||||
<Typography level='body-sm'>Task in a sentence:</Typography>
|
||||
|
@ -438,13 +438,54 @@ const TaskInput = ({ autoFocus, onChoreUpdate }) => {
|
|||
placeholder='Type your full text here...'
|
||||
sx={{ width: '100%', fontSize: '16px' }}
|
||||
/>
|
||||
<LearnMoreButton
|
||||
content={
|
||||
<>
|
||||
<Typography level='body-sm' sx={{ mb: 1 }}>
|
||||
This feature lets you create a task simply by typing a
|
||||
sentence. It attempt parses the sentence to identify the
|
||||
task's due date, priority, and frequency.
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
level='body-sm'
|
||||
sx={{ fontWeight: 'bold', mt: 2 }}
|
||||
>
|
||||
Examples:
|
||||
</Typography>
|
||||
|
||||
<Typography
|
||||
level='body-sm'
|
||||
component='ul'
|
||||
sx={{ pl: 2, mt: 1, listStyle: 'disc' }}
|
||||
>
|
||||
<li>
|
||||
<strong>Priority:</strong>For highest priority any of the
|
||||
following keyword <em>P1</em>, <em>Urgent</em>,{' '}
|
||||
<em>Important</em>, or <em>ASAP</em>. For lower
|
||||
priorities, use <em>P2</em>, <em>P3</em>, or <em>P4</em>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Due date:</strong> Specify dates with phrases like{' '}
|
||||
<em>tomorrow</em>, <em>next week</em>, <em>Monday</em>, or{' '}
|
||||
<em>August 1st at 12pm</em>.
|
||||
</li>
|
||||
<li>
|
||||
<strong>Frequency:</strong> Set recurring tasks with terms
|
||||
like <em>daily</em>, <em>weekly</em>, <em>monthly</em>,{' '}
|
||||
<em>yearly</em>, or patterns such as{' '}
|
||||
<em>every Tuesday and Thursday</em>.
|
||||
</li>
|
||||
</Typography>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<Typography level='body-sm'>Title:</Typography>
|
||||
<Input
|
||||
value={taskTitle}
|
||||
onChange={e => setTaskTitle(e.target.value)}
|
||||
placeholder='Type your full text here...'
|
||||
sx={{ width: '100%', fontSize: '16px' }}
|
||||
/>
|
||||
</Box>
|
||||
|
|
67
src/views/components/LearnMore.jsx
Normal file
67
src/views/components/LearnMore.jsx
Normal file
|
@ -0,0 +1,67 @@
|
|||
import { Info } from '@mui/icons-material'
|
||||
import { Box, Button, Sheet } from '@mui/joy'
|
||||
import React, { useRef, useState } from 'react'
|
||||
|
||||
const LearnMoreButton = ({ content }) => {
|
||||
const [open, setOpen] = useState(false)
|
||||
const anchorRef = useRef(null)
|
||||
|
||||
const handleToggle = () => {
|
||||
setOpen(prev => !prev)
|
||||
}
|
||||
|
||||
const handleClickOutside = event => {
|
||||
if (anchorRef.current && !anchorRef.current.contains(event.target)) {
|
||||
setOpen(false)
|
||||
}
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (open) {
|
||||
document.addEventListener('mousedown', handleClickOutside)
|
||||
} else {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
return () => {
|
||||
document.removeEventListener('mousedown', handleClickOutside)
|
||||
}
|
||||
}, [open])
|
||||
|
||||
return (
|
||||
<Box sx={{ position: 'relative', display: 'inline-block' }}>
|
||||
<Button
|
||||
ref={anchorRef}
|
||||
variant='plain'
|
||||
startDecorator={<Info />}
|
||||
size='sm'
|
||||
color='primary'
|
||||
onClick={handleToggle}
|
||||
>
|
||||
Learn More
|
||||
</Button>
|
||||
{open && (
|
||||
<Sheet
|
||||
variant='outlined'
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
top: '100%',
|
||||
left: 0,
|
||||
mt: 1,
|
||||
zIndex: 1000,
|
||||
p: 2,
|
||||
borderRadius: 'sm',
|
||||
boxShadow: 'md',
|
||||
backgroundColor: 'background.surface',
|
||||
minWidth: 240,
|
||||
maxHeight: 260,
|
||||
overflowY: 'auto',
|
||||
}}
|
||||
>
|
||||
{content}
|
||||
</Sheet>
|
||||
)}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default LearnMoreButton
|
Loading…
Add table
Reference in a new issue