Skip to content

Commit

Permalink
feat: add time range for add task (#17)
Browse files Browse the repository at this point in the history
  • Loading branch information
ZL-Asica authored Jan 15, 2025
1 parent 2cc489e commit 979fc8f
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 118 deletions.
118 changes: 0 additions & 118 deletions src/components/Home/Calendar/AddTaskButton.tsx

This file was deleted.

32 changes: 32 additions & 0 deletions src/components/Home/Calendar/AddTaskButton/CustomInputField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { TextField } from '@mui/material'

interface CustomInputFieldProps {
label: string
type: 'date' | 'time' | 'text'
value: string
onChange: (value: string) => void
required?: boolean
}

const CustomInputField = ({
label,
type,
value,
onChange,
required = false,
}: CustomInputFieldProps) => (
<TextField
label={label}
type={type}
value={value}
onChange={event_ => onChange(event_.target.value)}
required={required}
fullWidth
margin="normal"
slotProps={{
inputLabel: { shrink: true },
}}
/>
)

export default CustomInputField
159 changes: 159 additions & 0 deletions src/components/Home/Calendar/AddTaskButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
import type { Dayjs } from 'dayjs'
import { SmallLoadingCircle } from '@/components/common'
import { useScheduleStore } from '@/stores'
import {
Button,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
FormControl,
InputLabel,
MenuItem,
Select,
} from '@mui/material'
import { generateUniqueId, useToggle } from '@zl-asica/react'
import { useCallback, useEffect, useState } from 'react'
import CustomInputField from './CustomInputField'

interface AddTaskButtonProps {
selectedDate: Dayjs | null
}

const AddTaskButton = ({ selectedDate }: AddTaskButtonProps) => {
const addTask = useScheduleStore(state => state.addTask)

const [open, toggleOpen] = useToggle()
const [title, setTitle] = useState('')
const [description, setDescription] = useState('')
const [category, setCategory] = useState<TaskCategory>('work')
const [priority, setPriority] = useState<TaskPriority>('medium')
const [date, setDate] = useState(
selectedDate?.format('YYYY-MM-DD') ?? new Date().toISOString().split('T')[0],
)
const [startTime, setStartTime] = useState('')
const [endTime, setEndTime] = useState('')
const [loading, setLoading] = useState(false)

useEffect(() => {
setDate(selectedDate?.format('YYYY-MM-DD') ?? new Date().toISOString().split('T')[0])
}, [selectedDate])

const isTimeRangeValid = !startTime || !endTime || startTime < endTime

const resetStates = useCallback(() => {
setTitle('')
setDescription('')
setCategory('work')
setPriority('medium')
setDate(selectedDate?.format('YYYY-MM-DD') ?? new Date().toISOString().split('T')[0])
setStartTime('')
setEndTime('')
}, [selectedDate])

const handleAddTask = useCallback(async () => {
setLoading(true)
const randomId = await generateUniqueId([title, description, category, priority])
const newTask = {
taskId: randomId,
title,
description,
category,
priority,
date,
timeRange: startTime && endTime ? { start: startTime, end: endTime } : undefined,
status: 'pending' as const,
}

try {
await addTask(newTask)
toggleOpen()
resetStates()
}
catch (error_) {
console.error('Error while adding task:', error_)
}
finally {
setLoading(false)
}
}, [title, description, category, priority, date, startTime, endTime, addTask, resetStates, toggleOpen])

return (
<>
<Button
onClick={toggleOpen}
variant="contained"
sx={{
p: '0.5rem 1.5rem',
display: 'flex',
alignItems: 'center',
mb: '1rem',
}}
>
Add Task
</Button>

<Dialog
open={open}
onClose={() => {
toggleOpen()
resetStates()
}}
>
<DialogTitle>Add Task</DialogTitle>
<DialogContent>
<CustomInputField label="Date" type="date" value={date} onChange={setDate} required />
<CustomInputField label="Start Time" type="time" value={startTime} onChange={setStartTime} />
<CustomInputField label="End Time" type="time" value={endTime} onChange={setEndTime} />
<CustomInputField label="Title" type="text" value={title} onChange={setTitle} required />
<CustomInputField label="Description" type="text" value={description} onChange={setDescription} />

<FormControl fullWidth margin="normal">
<InputLabel>Category *</InputLabel>
<Select
value={category}
onChange={event_ => setCategory(event_.target.value as TaskCategory)}
>
<MenuItem value="work">Work</MenuItem>
<MenuItem value="personal">Personal</MenuItem>
<MenuItem value="health">Health</MenuItem>
<MenuItem value="learning">Learning</MenuItem>
<MenuItem value="other">Other</MenuItem>
<MenuItem value="school">School</MenuItem>
</Select>
</FormControl>
<FormControl fullWidth margin="normal">
<InputLabel>Priority *</InputLabel>
<Select
value={priority}
onChange={event_ => setPriority(event_.target.value as TaskPriority)}
>
<MenuItem value="low">Low</MenuItem>
<MenuItem value="medium">Medium</MenuItem>
<MenuItem value="high">High</MenuItem>
</Select>
</FormControl>
</DialogContent>
<DialogActions>
<Button
onClick={() => {
toggleOpen()
resetStates()
}}
disabled={loading}
>
Cancel
</Button>
<Button
onClick={handleAddTask}
disabled={!title.trim() || !date || !category || !priority || !isTimeRangeValid || loading}
>
{loading ? <SmallLoadingCircle text="Adding..." /> : 'Add Task'}
</Button>
</DialogActions>
</Dialog>
</>
)
}

export default AddTaskButton
6 changes: 6 additions & 0 deletions src/types/user.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,18 @@ type TaskCategory = 'work' | 'personal' | 'health' | 'learning' | 'other' | 'sch

type TaskPriority = 'low' | 'medium' | 'high'

interface TaskTimeRange {
start: string
end: string
}

interface Task {
taskId: string
title: string
description: string
category: TaskCategory
date: string
timeRange?: TaskTimeRange
priority: TaskPriority
status: 'pending' | 'completed'
}
Expand Down

0 comments on commit 979fc8f

Please sign in to comment.