mirror of
https://github.com/ershisan99/it-incubator-todolist-ts-17-live-2024-08-17.git
synced 2026-02-04 21:02:13 +00:00
chore: add prettier and run it on all files
This commit is contained in:
@@ -5,34 +5,63 @@ import { Delete } from '@mui/icons-material'
|
||||
import { TaskStatuses, TaskType } from '../../../../api/todolists-api'
|
||||
|
||||
type TaskPropsType = {
|
||||
task: TaskType
|
||||
todolistId: string
|
||||
changeTaskStatus: (id: string, status: TaskStatuses, todolistId: string) => void
|
||||
changeTaskTitle: (taskId: string, newTitle: string, todolistId: string) => void
|
||||
removeTask: (taskId: string, todolistId: string) => void
|
||||
task: TaskType
|
||||
todolistId: string
|
||||
changeTaskStatus: (
|
||||
id: string,
|
||||
status: TaskStatuses,
|
||||
todolistId: string
|
||||
) => void
|
||||
changeTaskTitle: (
|
||||
taskId: string,
|
||||
newTitle: string,
|
||||
todolistId: string
|
||||
) => void
|
||||
removeTask: (taskId: string, todolistId: string) => void
|
||||
}
|
||||
export const Task = React.memo((props: TaskPropsType) => {
|
||||
const onClickHandler = useCallback(() => props.removeTask(props.task.id, props.todolistId), [props.task.id, props.todolistId]);
|
||||
const onClickHandler = useCallback(
|
||||
() => props.removeTask(props.task.id, props.todolistId),
|
||||
[props.task.id, props.todolistId]
|
||||
)
|
||||
|
||||
const onChangeHandler = useCallback((e: ChangeEvent<HTMLInputElement>) => {
|
||||
let newIsDoneValue = e.currentTarget.checked
|
||||
props.changeTaskStatus(props.task.id, newIsDoneValue ? TaskStatuses.Completed : TaskStatuses.New, props.todolistId)
|
||||
}, [props.task.id, props.todolistId]);
|
||||
const onChangeHandler = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
let newIsDoneValue = e.currentTarget.checked
|
||||
props.changeTaskStatus(
|
||||
props.task.id,
|
||||
newIsDoneValue ? TaskStatuses.Completed : TaskStatuses.New,
|
||||
props.todolistId
|
||||
)
|
||||
},
|
||||
[props.task.id, props.todolistId]
|
||||
)
|
||||
|
||||
const onTitleChangeHandler = useCallback((newValue: string) => {
|
||||
props.changeTaskTitle(props.task.id, newValue, props.todolistId)
|
||||
}, [props.task.id, props.todolistId]);
|
||||
const onTitleChangeHandler = useCallback(
|
||||
(newValue: string) => {
|
||||
props.changeTaskTitle(props.task.id, newValue, props.todolistId)
|
||||
},
|
||||
[props.task.id, props.todolistId]
|
||||
)
|
||||
|
||||
return <div key={props.task.id} className={props.task.status === TaskStatuses.Completed ? 'is-done' : ''}>
|
||||
<Checkbox
|
||||
checked={props.task.status === TaskStatuses.Completed}
|
||||
color="primary"
|
||||
onChange={onChangeHandler}
|
||||
/>
|
||||
return (
|
||||
<div
|
||||
key={props.task.id}
|
||||
className={props.task.status === TaskStatuses.Completed ? 'is-done' : ''}
|
||||
>
|
||||
<Checkbox
|
||||
checked={props.task.status === TaskStatuses.Completed}
|
||||
color='primary'
|
||||
onChange={onChangeHandler}
|
||||
/>
|
||||
|
||||
<EditableSpan value={props.task.title} onChange={onTitleChangeHandler}/>
|
||||
<IconButton onClick={onClickHandler}>
|
||||
<Delete/>
|
||||
</IconButton>
|
||||
</div>
|
||||
<EditableSpan
|
||||
value={props.task.title}
|
||||
onChange={onTitleChangeHandler}
|
||||
/>
|
||||
<IconButton onClick={onClickHandler}>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
@@ -5,92 +5,139 @@ import { Task } from './Task/Task'
|
||||
import { TaskStatuses, TaskType } from '../../../api/todolists-api'
|
||||
import { FilterValuesType, TodolistDomainType } from '../todolists-reducer'
|
||||
import { fetchTasksTC } from '../tasks-reducer'
|
||||
import { useAppDispatch } from '../../../hooks/useAppDispatch';
|
||||
import { useAppDispatch } from '../../../hooks/useAppDispatch'
|
||||
import { Button, IconButton } from '@mui/material'
|
||||
import { Delete } from '@mui/icons-material'
|
||||
|
||||
type PropsType = {
|
||||
todolist: TodolistDomainType
|
||||
tasks: Array<TaskType>
|
||||
changeFilter: (value: FilterValuesType, todolistId: string) => void
|
||||
addTask: (title: string, todolistId: string) => void
|
||||
changeTaskStatus: (id: string, status: TaskStatuses, todolistId: string) => void
|
||||
changeTaskTitle: (taskId: string, newTitle: string, todolistId: string) => void
|
||||
removeTask: (taskId: string, todolistId: string) => void
|
||||
removeTodolist: (id: string) => void
|
||||
changeTodolistTitle: (id: string, newTitle: string) => void
|
||||
demo?: boolean
|
||||
todolist: TodolistDomainType
|
||||
tasks: Array<TaskType>
|
||||
changeFilter: (value: FilterValuesType, todolistId: string) => void
|
||||
addTask: (title: string, todolistId: string) => void
|
||||
changeTaskStatus: (
|
||||
id: string,
|
||||
status: TaskStatuses,
|
||||
todolistId: string
|
||||
) => void
|
||||
changeTaskTitle: (
|
||||
taskId: string,
|
||||
newTitle: string,
|
||||
todolistId: string
|
||||
) => void
|
||||
removeTask: (taskId: string, todolistId: string) => void
|
||||
removeTodolist: (id: string) => void
|
||||
changeTodolistTitle: (id: string, newTitle: string) => void
|
||||
demo?: boolean
|
||||
}
|
||||
|
||||
export const Todolist = React.memo(function ({demo = false, ...props}: PropsType) {
|
||||
export const Todolist = React.memo(function ({
|
||||
demo = false,
|
||||
...props
|
||||
}: PropsType) {
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
useEffect(() => {
|
||||
if (demo) {
|
||||
return
|
||||
}
|
||||
const thunk = fetchTasksTC(props.todolist.id)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const addTask = useCallback((title: string) => {
|
||||
props.addTask(title, props.todolist.id)
|
||||
}, [props.addTask, props.todolist.id])
|
||||
|
||||
const removeTodolist = () => {
|
||||
props.removeTodolist(props.todolist.id)
|
||||
useEffect(() => {
|
||||
if (demo) {
|
||||
return
|
||||
}
|
||||
const changeTodolistTitle = useCallback((title: string) => {
|
||||
props.changeTodolistTitle(props.todolist.id, title)
|
||||
}, [props.todolist.id, props.changeTodolistTitle])
|
||||
const thunk = fetchTasksTC(props.todolist.id)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const onAllClickHandler = useCallback(() => props.changeFilter('all', props.todolist.id), [props.todolist.id, props.changeFilter])
|
||||
const onActiveClickHandler = useCallback(() => props.changeFilter('active', props.todolist.id), [props.todolist.id, props.changeFilter])
|
||||
const onCompletedClickHandler = useCallback(() => props.changeFilter('completed', props.todolist.id), [props.todolist.id, props.changeFilter])
|
||||
const addTask = useCallback(
|
||||
(title: string) => {
|
||||
props.addTask(title, props.todolist.id)
|
||||
},
|
||||
[props.addTask, props.todolist.id]
|
||||
)
|
||||
|
||||
const removeTodolist = () => {
|
||||
props.removeTodolist(props.todolist.id)
|
||||
}
|
||||
const changeTodolistTitle = useCallback(
|
||||
(title: string) => {
|
||||
props.changeTodolistTitle(props.todolist.id, title)
|
||||
},
|
||||
[props.todolist.id, props.changeTodolistTitle]
|
||||
)
|
||||
|
||||
let tasksForTodolist = props.tasks
|
||||
const onAllClickHandler = useCallback(
|
||||
() => props.changeFilter('all', props.todolist.id),
|
||||
[props.todolist.id, props.changeFilter]
|
||||
)
|
||||
const onActiveClickHandler = useCallback(
|
||||
() => props.changeFilter('active', props.todolist.id),
|
||||
[props.todolist.id, props.changeFilter]
|
||||
)
|
||||
const onCompletedClickHandler = useCallback(
|
||||
() => props.changeFilter('completed', props.todolist.id),
|
||||
[props.todolist.id, props.changeFilter]
|
||||
)
|
||||
|
||||
if (props.todolist.filter === 'active') {
|
||||
tasksForTodolist = props.tasks.filter(t => t.status === TaskStatuses.New)
|
||||
}
|
||||
if (props.todolist.filter === 'completed') {
|
||||
tasksForTodolist = props.tasks.filter(t => t.status === TaskStatuses.Completed)
|
||||
}
|
||||
let tasksForTodolist = props.tasks
|
||||
|
||||
return <div>
|
||||
<h3><EditableSpan value={props.todolist.title} onChange={changeTodolistTitle}/>
|
||||
<IconButton onClick={removeTodolist} disabled={props.todolist.entityStatus === 'loading'}>
|
||||
<Delete/>
|
||||
</IconButton>
|
||||
</h3>
|
||||
<AddItemForm addItem={addTask} disabled={props.todolist.entityStatus === 'loading'}/>
|
||||
<div>
|
||||
{
|
||||
tasksForTodolist.map(t => <Task key={t.id} task={t} todolistId={props.todolist.id}
|
||||
removeTask={props.removeTask}
|
||||
changeTaskTitle={props.changeTaskTitle}
|
||||
changeTaskStatus={props.changeTaskStatus}
|
||||
/>)
|
||||
}
|
||||
</div>
|
||||
<div style={{paddingTop: '10px'}}>
|
||||
<Button variant={props.todolist.filter === 'all' ? 'outlined' : 'text'}
|
||||
onClick={onAllClickHandler}
|
||||
color={'inherit'}
|
||||
>All
|
||||
</Button>
|
||||
<Button variant={props.todolist.filter === 'active' ? 'outlined' : 'text'}
|
||||
onClick={onActiveClickHandler}
|
||||
color={'primary'}>Active
|
||||
</Button>
|
||||
<Button variant={props.todolist.filter === 'completed' ? 'outlined' : 'text'}
|
||||
onClick={onCompletedClickHandler}
|
||||
color={'secondary'}>Completed
|
||||
</Button>
|
||||
</div>
|
||||
if (props.todolist.filter === 'active') {
|
||||
tasksForTodolist = props.tasks.filter((t) => t.status === TaskStatuses.New)
|
||||
}
|
||||
if (props.todolist.filter === 'completed') {
|
||||
tasksForTodolist = props.tasks.filter(
|
||||
(t) => t.status === TaskStatuses.Completed
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h3>
|
||||
<EditableSpan
|
||||
value={props.todolist.title}
|
||||
onChange={changeTodolistTitle}
|
||||
/>
|
||||
<IconButton
|
||||
onClick={removeTodolist}
|
||||
disabled={props.todolist.entityStatus === 'loading'}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</h3>
|
||||
<AddItemForm
|
||||
addItem={addTask}
|
||||
disabled={props.todolist.entityStatus === 'loading'}
|
||||
/>
|
||||
<div>
|
||||
{tasksForTodolist.map((t) => (
|
||||
<Task
|
||||
key={t.id}
|
||||
task={t}
|
||||
todolistId={props.todolist.id}
|
||||
removeTask={props.removeTask}
|
||||
changeTaskTitle={props.changeTaskTitle}
|
||||
changeTaskStatus={props.changeTaskStatus}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<div style={{ paddingTop: '10px' }}>
|
||||
<Button
|
||||
variant={props.todolist.filter === 'all' ? 'outlined' : 'text'}
|
||||
onClick={onAllClickHandler}
|
||||
color={'inherit'}
|
||||
>
|
||||
All
|
||||
</Button>
|
||||
<Button
|
||||
variant={props.todolist.filter === 'active' ? 'outlined' : 'text'}
|
||||
onClick={onActiveClickHandler}
|
||||
color={'primary'}
|
||||
>
|
||||
Active
|
||||
</Button>
|
||||
<Button
|
||||
variant={props.todolist.filter === 'completed' ? 'outlined' : 'text'}
|
||||
onClick={onCompletedClickHandler}
|
||||
color={'secondary'}
|
||||
>
|
||||
Completed
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -2,112 +2,148 @@ import React, { useCallback, useEffect } from 'react'
|
||||
import { useSelector } from 'react-redux'
|
||||
import { AppRootStateType } from '../../app/store'
|
||||
import {
|
||||
addTodolistTC,
|
||||
changeTodolistFilterAC,
|
||||
changeTodolistTitleTC,
|
||||
fetchTodolistsTC,
|
||||
FilterValuesType,
|
||||
removeTodolistTC,
|
||||
TodolistDomainType
|
||||
addTodolistTC,
|
||||
changeTodolistFilterAC,
|
||||
changeTodolistTitleTC,
|
||||
fetchTodolistsTC,
|
||||
FilterValuesType,
|
||||
removeTodolistTC,
|
||||
TodolistDomainType,
|
||||
} from './todolists-reducer'
|
||||
import { addTaskTC, removeTaskTC, TasksStateType, updateTaskTC } from './tasks-reducer'
|
||||
import {
|
||||
addTaskTC,
|
||||
removeTaskTC,
|
||||
TasksStateType,
|
||||
updateTaskTC,
|
||||
} from './tasks-reducer'
|
||||
import { TaskStatuses } from '../../api/todolists-api'
|
||||
import { Grid, Paper } from '@mui/material'
|
||||
import { AddItemForm } from '../../components/AddItemForm/AddItemForm'
|
||||
import { Todolist } from './Todolist/Todolist'
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { useAppDispatch } from '../../hooks/useAppDispatch';
|
||||
import { useAppDispatch } from '../../hooks/useAppDispatch'
|
||||
|
||||
type PropsType = {
|
||||
demo?: boolean
|
||||
demo?: boolean
|
||||
}
|
||||
|
||||
export const TodolistsList: React.FC<PropsType> = ({demo = false}) => {
|
||||
const todolists = useSelector<AppRootStateType, Array<TodolistDomainType>>(state => state.todolists)
|
||||
const tasks = useSelector<AppRootStateType, TasksStateType>(state => state.tasks)
|
||||
const isLoggedIn = useSelector<AppRootStateType, boolean>(state => state.auth.isLoggedIn)
|
||||
export const TodolistsList: React.FC<PropsType> = ({ demo = false }) => {
|
||||
const todolists = useSelector<AppRootStateType, Array<TodolistDomainType>>(
|
||||
(state) => state.todolists
|
||||
)
|
||||
const tasks = useSelector<AppRootStateType, TasksStateType>(
|
||||
(state) => state.tasks
|
||||
)
|
||||
const isLoggedIn = useSelector<AppRootStateType, boolean>(
|
||||
(state) => state.auth.isLoggedIn
|
||||
)
|
||||
|
||||
const dispatch = useAppDispatch()
|
||||
const dispatch = useAppDispatch()
|
||||
|
||||
useEffect(() => {
|
||||
if (demo || !isLoggedIn) {
|
||||
return;
|
||||
}
|
||||
const thunk = fetchTodolistsTC()
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const removeTask = useCallback(function (id: string, todolistId: string) {
|
||||
const thunk = removeTaskTC(id, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const addTask = useCallback(function (title: string, todolistId: string) {
|
||||
const thunk = addTaskTC(title, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeStatus = useCallback(function (id: string, status: TaskStatuses, todolistId: string) {
|
||||
const thunk = updateTaskTC(id, {status}, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeTaskTitle = useCallback(function (id: string, newTitle: string, todolistId: string) {
|
||||
const thunk = updateTaskTC(id, {title: newTitle}, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeFilter = useCallback(function (value: FilterValuesType, todolistId: string) {
|
||||
const action = changeTodolistFilterAC(todolistId, value)
|
||||
dispatch(action)
|
||||
}, [])
|
||||
|
||||
const removeTodolist = useCallback(function (id: string) {
|
||||
const thunk = removeTodolistTC(id)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeTodolistTitle = useCallback(function (id: string, title: string) {
|
||||
const thunk = changeTodolistTitleTC(id, title)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const addTodolist = useCallback((title: string) => {
|
||||
const thunk = addTodolistTC(title)
|
||||
dispatch(thunk)
|
||||
}, [dispatch])
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return <Navigate to={"/login"} />
|
||||
useEffect(() => {
|
||||
if (demo || !isLoggedIn) {
|
||||
return
|
||||
}
|
||||
const thunk = fetchTodolistsTC()
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
return <>
|
||||
<Grid container style={{padding: '20px'}}>
|
||||
<AddItemForm addItem={addTodolist}/>
|
||||
</Grid>
|
||||
<Grid container spacing={3}>
|
||||
{
|
||||
todolists.map(tl => {
|
||||
let allTodolistTasks = tasks[tl.id]
|
||||
const removeTask = useCallback(function (id: string, todolistId: string) {
|
||||
const thunk = removeTaskTC(id, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
return <Grid item key={tl.id}>
|
||||
<Paper style={{padding: '10px'}}>
|
||||
<Todolist
|
||||
todolist={tl}
|
||||
tasks={allTodolistTasks}
|
||||
removeTask={removeTask}
|
||||
changeFilter={changeFilter}
|
||||
addTask={addTask}
|
||||
changeTaskStatus={changeStatus}
|
||||
removeTodolist={removeTodolist}
|
||||
changeTaskTitle={changeTaskTitle}
|
||||
changeTodolistTitle={changeTodolistTitle}
|
||||
demo={demo}
|
||||
/>
|
||||
</Paper>
|
||||
</Grid>
|
||||
})
|
||||
}
|
||||
</Grid>
|
||||
const addTask = useCallback(function (title: string, todolistId: string) {
|
||||
const thunk = addTaskTC(title, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeStatus = useCallback(function (
|
||||
id: string,
|
||||
status: TaskStatuses,
|
||||
todolistId: string
|
||||
) {
|
||||
const thunk = updateTaskTC(id, { status }, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeTaskTitle = useCallback(function (
|
||||
id: string,
|
||||
newTitle: string,
|
||||
todolistId: string
|
||||
) {
|
||||
const thunk = updateTaskTC(id, { title: newTitle }, todolistId)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeFilter = useCallback(function (
|
||||
value: FilterValuesType,
|
||||
todolistId: string
|
||||
) {
|
||||
const action = changeTodolistFilterAC(todolistId, value)
|
||||
dispatch(action)
|
||||
}, [])
|
||||
|
||||
const removeTodolist = useCallback(function (id: string) {
|
||||
const thunk = removeTodolistTC(id)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const changeTodolistTitle = useCallback(function (id: string, title: string) {
|
||||
const thunk = changeTodolistTitleTC(id, title)
|
||||
dispatch(thunk)
|
||||
}, [])
|
||||
|
||||
const addTodolist = useCallback(
|
||||
(title: string) => {
|
||||
const thunk = addTodolistTC(title)
|
||||
dispatch(thunk)
|
||||
},
|
||||
[dispatch]
|
||||
)
|
||||
|
||||
if (!isLoggedIn) {
|
||||
return <Navigate to={'/login'} />
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<Grid
|
||||
container
|
||||
style={{ padding: '20px' }}
|
||||
>
|
||||
<AddItemForm addItem={addTodolist} />
|
||||
</Grid>
|
||||
<Grid
|
||||
container
|
||||
spacing={3}
|
||||
>
|
||||
{todolists.map((tl) => {
|
||||
let allTodolistTasks = tasks[tl.id]
|
||||
|
||||
return (
|
||||
<Grid
|
||||
item
|
||||
key={tl.id}
|
||||
>
|
||||
<Paper style={{ padding: '10px' }}>
|
||||
<Todolist
|
||||
todolist={tl}
|
||||
tasks={allTodolistTasks}
|
||||
removeTask={removeTask}
|
||||
changeFilter={changeFilter}
|
||||
addTask={addTask}
|
||||
changeTaskStatus={changeStatus}
|
||||
removeTodolist={removeTodolist}
|
||||
changeTaskTitle={changeTaskTitle}
|
||||
changeTodolistTitle={changeTodolistTitle}
|
||||
demo={demo}
|
||||
/>
|
||||
</Paper>
|
||||
</Grid>
|
||||
)
|
||||
})}
|
||||
</Grid>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,133 +1,205 @@
|
||||
import { addTaskAC, removeTaskAC, setTasksAC, tasksReducer, TasksStateType, updateTaskAC } from './tasks-reducer'
|
||||
import {
|
||||
addTaskAC,
|
||||
removeTaskAC,
|
||||
setTasksAC,
|
||||
tasksReducer,
|
||||
TasksStateType,
|
||||
updateTaskAC,
|
||||
} from './tasks-reducer'
|
||||
|
||||
import {addTodolistAC, removeTodolistAC, setTodolistsAC} from './todolists-reducer'
|
||||
import {TaskPriorities, TaskStatuses} from '../../api/todolists-api'
|
||||
import {
|
||||
addTodolistAC,
|
||||
removeTodolistAC,
|
||||
setTodolistsAC,
|
||||
} from './todolists-reducer'
|
||||
import { TaskPriorities, TaskStatuses } from '../../api/todolists-api'
|
||||
|
||||
let startState: TasksStateType = {};
|
||||
let startState: TasksStateType = {}
|
||||
beforeEach(() => {
|
||||
startState = {
|
||||
"todolistId1": [
|
||||
{ id: "1", title: "CSS", status: TaskStatuses.New, todoListId: "todolistId1", description: '',
|
||||
startDate: '', deadline: '', addedDate: '', order: 0, priority: TaskPriorities.Low },
|
||||
{ id: "2", title: "JS", status: TaskStatuses.Completed, todoListId: "todolistId1", description: '',
|
||||
startDate: '', deadline: '', addedDate: '', order: 0, priority: TaskPriorities.Low },
|
||||
{ id: "3", title: "React", status: TaskStatuses.New, todoListId: "todolistId1", description: '',
|
||||
startDate: '', deadline: '', addedDate: '', order: 0, priority: TaskPriorities.Low }
|
||||
],
|
||||
"todolistId2": [
|
||||
{ id: "1", title: "bread", status: TaskStatuses.New, todoListId: "todolistId2", description: '',
|
||||
startDate: '', deadline: '', addedDate: '', order: 0, priority: TaskPriorities.Low },
|
||||
{ id: "2", title: "milk", status: TaskStatuses.Completed, todoListId: "todolistId2", description: '',
|
||||
startDate: '', deadline: '', addedDate: '', order: 0, priority: TaskPriorities.Low },
|
||||
{ id: "3", title: "tea", status: TaskStatuses.New, todoListId: "todolistId2", description: '',
|
||||
startDate: '', deadline: '', addedDate: '', order: 0, priority: TaskPriorities.Low }
|
||||
]
|
||||
};
|
||||
});
|
||||
startState = {
|
||||
todolistId1: [
|
||||
{
|
||||
id: '1',
|
||||
title: 'CSS',
|
||||
status: TaskStatuses.New,
|
||||
todoListId: 'todolistId1',
|
||||
description: '',
|
||||
startDate: '',
|
||||
deadline: '',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
priority: TaskPriorities.Low,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'JS',
|
||||
status: TaskStatuses.Completed,
|
||||
todoListId: 'todolistId1',
|
||||
description: '',
|
||||
startDate: '',
|
||||
deadline: '',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
priority: TaskPriorities.Low,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'React',
|
||||
status: TaskStatuses.New,
|
||||
todoListId: 'todolistId1',
|
||||
description: '',
|
||||
startDate: '',
|
||||
deadline: '',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
priority: TaskPriorities.Low,
|
||||
},
|
||||
],
|
||||
todolistId2: [
|
||||
{
|
||||
id: '1',
|
||||
title: 'bread',
|
||||
status: TaskStatuses.New,
|
||||
todoListId: 'todolistId2',
|
||||
description: '',
|
||||
startDate: '',
|
||||
deadline: '',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
priority: TaskPriorities.Low,
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'milk',
|
||||
status: TaskStatuses.Completed,
|
||||
todoListId: 'todolistId2',
|
||||
description: '',
|
||||
startDate: '',
|
||||
deadline: '',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
priority: TaskPriorities.Low,
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'tea',
|
||||
status: TaskStatuses.New,
|
||||
todoListId: 'todolistId2',
|
||||
description: '',
|
||||
startDate: '',
|
||||
deadline: '',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
priority: TaskPriorities.Low,
|
||||
},
|
||||
],
|
||||
}
|
||||
})
|
||||
|
||||
test('correct task should be deleted from correct array', () => {
|
||||
const action = removeTaskAC("2", "todolistId2");
|
||||
const action = removeTaskAC('2', 'todolistId2')
|
||||
|
||||
const endState = tasksReducer(startState, action)
|
||||
const endState = tasksReducer(startState, action)
|
||||
|
||||
expect(endState["todolistId1"].length).toBe(3);
|
||||
expect(endState["todolistId2"].length).toBe(2);
|
||||
expect(endState["todolistId2"].every(t => t.id != "2")).toBeTruthy();
|
||||
});
|
||||
expect(endState['todolistId1'].length).toBe(3)
|
||||
expect(endState['todolistId2'].length).toBe(2)
|
||||
expect(endState['todolistId2'].every((t) => t.id != '2')).toBeTruthy()
|
||||
})
|
||||
test('correct task should be added to correct array', () => {
|
||||
//const action = addTaskAC("juce", "todolistId2");
|
||||
const action = addTaskAC({
|
||||
todoListId: "todolistId2",
|
||||
title: "juce",
|
||||
status: TaskStatuses.New,
|
||||
addedDate: "",
|
||||
deadline: "",
|
||||
description: "",
|
||||
order: 0,
|
||||
priority: 0,
|
||||
startDate: "",
|
||||
id: "id exists"
|
||||
});
|
||||
//const action = addTaskAC("juce", "todolistId2");
|
||||
const action = addTaskAC({
|
||||
todoListId: 'todolistId2',
|
||||
title: 'juce',
|
||||
status: TaskStatuses.New,
|
||||
addedDate: '',
|
||||
deadline: '',
|
||||
description: '',
|
||||
order: 0,
|
||||
priority: 0,
|
||||
startDate: '',
|
||||
id: 'id exists',
|
||||
})
|
||||
|
||||
const endState = tasksReducer(startState, action)
|
||||
const endState = tasksReducer(startState, action)
|
||||
|
||||
expect(endState["todolistId1"].length).toBe(3);
|
||||
expect(endState["todolistId2"].length).toBe(4);
|
||||
expect(endState["todolistId2"][0].id).toBeDefined();
|
||||
expect(endState["todolistId2"][0].title).toBe("juce");
|
||||
expect(endState["todolistId2"][0].status).toBe(TaskStatuses.New);
|
||||
});
|
||||
expect(endState['todolistId1'].length).toBe(3)
|
||||
expect(endState['todolistId2'].length).toBe(4)
|
||||
expect(endState['todolistId2'][0].id).toBeDefined()
|
||||
expect(endState['todolistId2'][0].title).toBe('juce')
|
||||
expect(endState['todolistId2'][0].status).toBe(TaskStatuses.New)
|
||||
})
|
||||
test('status of specified task should be changed', () => {
|
||||
const action = updateTaskAC("2", {status: TaskStatuses.New}, "todolistId2");
|
||||
const action = updateTaskAC('2', { status: TaskStatuses.New }, 'todolistId2')
|
||||
|
||||
const endState = tasksReducer(startState, action)
|
||||
const endState = tasksReducer(startState, action)
|
||||
|
||||
expect(endState["todolistId1"][1].status).toBe(TaskStatuses.Completed);
|
||||
expect(endState["todolistId2"][1].status).toBe(TaskStatuses.New);
|
||||
});
|
||||
expect(endState['todolistId1'][1].status).toBe(TaskStatuses.Completed)
|
||||
expect(endState['todolistId2'][1].status).toBe(TaskStatuses.New)
|
||||
})
|
||||
test('title of specified task should be changed', () => {
|
||||
const action = updateTaskAC("2", {title: "yogurt"}, "todolistId2");
|
||||
const action = updateTaskAC('2', { title: 'yogurt' }, 'todolistId2')
|
||||
|
||||
const endState = tasksReducer(startState, action)
|
||||
const endState = tasksReducer(startState, action)
|
||||
|
||||
expect(endState["todolistId1"][1].title).toBe("JS");
|
||||
expect(endState["todolistId2"][1].title).toBe("yogurt");
|
||||
expect(endState["todolistId2"][0].title).toBe("bread");
|
||||
});
|
||||
expect(endState['todolistId1'][1].title).toBe('JS')
|
||||
expect(endState['todolistId2'][1].title).toBe('yogurt')
|
||||
expect(endState['todolistId2'][0].title).toBe('bread')
|
||||
})
|
||||
test('new array should be added when new todolist is added', () => {
|
||||
const action = addTodolistAC({
|
||||
id: "blabla",
|
||||
title: "new todolist",
|
||||
order: 0,
|
||||
addedDate: ''
|
||||
});
|
||||
const action = addTodolistAC({
|
||||
id: 'blabla',
|
||||
title: 'new todolist',
|
||||
order: 0,
|
||||
addedDate: '',
|
||||
})
|
||||
|
||||
const endState = tasksReducer(startState, action)
|
||||
const endState = tasksReducer(startState, action)
|
||||
|
||||
const keys = Object.keys(endState)
|
||||
const newKey = keys.find((k) => k != 'todolistId1' && k != 'todolistId2')
|
||||
if (!newKey) {
|
||||
throw Error('new key should be added')
|
||||
}
|
||||
|
||||
const keys = Object.keys(endState);
|
||||
const newKey = keys.find(k => k != "todolistId1" && k != "todolistId2");
|
||||
if (!newKey) {
|
||||
throw Error("new key should be added")
|
||||
}
|
||||
|
||||
expect(keys.length).toBe(3);
|
||||
expect(endState[newKey]).toEqual([]);
|
||||
});
|
||||
expect(keys.length).toBe(3)
|
||||
expect(endState[newKey]).toEqual([])
|
||||
})
|
||||
test('propertry with todolistId should be deleted', () => {
|
||||
const action = removeTodolistAC("todolistId2");
|
||||
const action = removeTodolistAC('todolistId2')
|
||||
|
||||
const endState = tasksReducer(startState, action)
|
||||
const endState = tasksReducer(startState, action)
|
||||
|
||||
const keys = Object.keys(endState);
|
||||
const keys = Object.keys(endState)
|
||||
|
||||
expect(keys.length).toBe(1);
|
||||
expect(endState["todolistId2"]).not.toBeDefined();
|
||||
});
|
||||
expect(keys.length).toBe(1)
|
||||
expect(endState['todolistId2']).not.toBeDefined()
|
||||
})
|
||||
|
||||
test('empty arrays should be added when we set todolists', () => {
|
||||
const action = setTodolistsAC([
|
||||
{id: "1", title: "title 1", order: 0, addedDate: ""},
|
||||
{id: "2", title: "title 2", order: 0, addedDate: ""}
|
||||
])
|
||||
const action = setTodolistsAC([
|
||||
{ id: '1', title: 'title 1', order: 0, addedDate: '' },
|
||||
{ id: '2', title: 'title 2', order: 0, addedDate: '' },
|
||||
])
|
||||
|
||||
const endState = tasksReducer({}, action)
|
||||
const endState = tasksReducer({}, action)
|
||||
|
||||
const keys = Object.keys(endState)
|
||||
const keys = Object.keys(endState)
|
||||
|
||||
expect(keys.length).toBe(2)
|
||||
expect(endState['1']).toBeDefined()
|
||||
expect(endState['2']).toBeDefined()
|
||||
expect(keys.length).toBe(2)
|
||||
expect(endState['1']).toBeDefined()
|
||||
expect(endState['2']).toBeDefined()
|
||||
})
|
||||
test('tasks should be added for todolist', () => {
|
||||
const action = setTasksAC(startState["todolistId1"], "todolistId1");
|
||||
const action = setTasksAC(startState['todolistId1'], 'todolistId1')
|
||||
|
||||
const endState = tasksReducer({
|
||||
"todolistId2": [],
|
||||
"todolistId1": []
|
||||
}, action)
|
||||
const endState = tasksReducer(
|
||||
{
|
||||
todolistId2: [],
|
||||
todolistId1: [],
|
||||
},
|
||||
action
|
||||
)
|
||||
|
||||
expect(endState["todolistId1"].length).toBe(3)
|
||||
expect(endState["todolistId2"].length).toBe(0)
|
||||
expect(endState['todolistId1'].length).toBe(3)
|
||||
expect(endState['todolistId2'].length).toBe(0)
|
||||
})
|
||||
|
||||
|
||||
@@ -1,140 +1,192 @@
|
||||
import {AddTodolistActionType, RemoveTodolistActionType, SetTodolistsActionType} from './todolists-reducer'
|
||||
import {TaskPriorities, TaskStatuses, TaskType, todolistsAPI, UpdateTaskModelType} from '../../api/todolists-api'
|
||||
import {Dispatch} from 'redux'
|
||||
import {AppRootStateType} from '../../app/store'
|
||||
import {setAppErrorAC, SetAppErrorActionType, setAppStatusAC, SetAppStatusActionType} from '../../app/app-reducer'
|
||||
import {handleServerAppError, handleServerNetworkError} from '../../utils/error-utils'
|
||||
import {
|
||||
AddTodolistActionType,
|
||||
RemoveTodolistActionType,
|
||||
SetTodolistsActionType,
|
||||
} from './todolists-reducer'
|
||||
import {
|
||||
TaskPriorities,
|
||||
TaskStatuses,
|
||||
TaskType,
|
||||
todolistsAPI,
|
||||
UpdateTaskModelType,
|
||||
} from '../../api/todolists-api'
|
||||
import { Dispatch } from 'redux'
|
||||
import { AppRootStateType } from '../../app/store'
|
||||
import {
|
||||
setAppErrorAC,
|
||||
SetAppErrorActionType,
|
||||
setAppStatusAC,
|
||||
SetAppStatusActionType,
|
||||
} from '../../app/app-reducer'
|
||||
import {
|
||||
handleServerAppError,
|
||||
handleServerNetworkError,
|
||||
} from '../../utils/error-utils'
|
||||
|
||||
const initialState: TasksStateType = {}
|
||||
|
||||
export const tasksReducer = (state: TasksStateType = initialState, action: ActionsType): TasksStateType => {
|
||||
switch (action.type) {
|
||||
case 'REMOVE-TASK':
|
||||
return {...state, [action.todolistId]: state[action.todolistId].filter(t => t.id != action.taskId)}
|
||||
case 'ADD-TASK':
|
||||
return {...state, [action.task.todoListId]: [action.task, ...state[action.task.todoListId]]}
|
||||
case 'UPDATE-TASK':
|
||||
return {
|
||||
...state,
|
||||
[action.todolistId]: state[action.todolistId]
|
||||
.map(t => t.id === action.taskId ? {...t, ...action.model} : t)
|
||||
}
|
||||
case 'ADD-TODOLIST':
|
||||
return {...state, [action.todolist.id]: []}
|
||||
case 'REMOVE-TODOLIST':
|
||||
const copyState = {...state}
|
||||
delete copyState[action.id]
|
||||
return copyState
|
||||
case 'SET-TODOLISTS': {
|
||||
const copyState = {...state}
|
||||
action.todolists.forEach(tl => {
|
||||
copyState[tl.id] = []
|
||||
})
|
||||
return copyState
|
||||
}
|
||||
case 'SET-TASKS':
|
||||
return {...state, [action.todolistId]: action.tasks}
|
||||
default:
|
||||
return state
|
||||
export const tasksReducer = (
|
||||
state: TasksStateType = initialState,
|
||||
action: ActionsType
|
||||
): TasksStateType => {
|
||||
switch (action.type) {
|
||||
case 'REMOVE-TASK':
|
||||
return {
|
||||
...state,
|
||||
[action.todolistId]: state[action.todolistId].filter(
|
||||
(t) => t.id != action.taskId
|
||||
),
|
||||
}
|
||||
case 'ADD-TASK':
|
||||
return {
|
||||
...state,
|
||||
[action.task.todoListId]: [
|
||||
action.task,
|
||||
...state[action.task.todoListId],
|
||||
],
|
||||
}
|
||||
case 'UPDATE-TASK':
|
||||
return {
|
||||
...state,
|
||||
[action.todolistId]: state[action.todolistId].map((t) =>
|
||||
t.id === action.taskId ? { ...t, ...action.model } : t
|
||||
),
|
||||
}
|
||||
case 'ADD-TODOLIST':
|
||||
return { ...state, [action.todolist.id]: [] }
|
||||
case 'REMOVE-TODOLIST':
|
||||
const copyState = { ...state }
|
||||
delete copyState[action.id]
|
||||
return copyState
|
||||
case 'SET-TODOLISTS': {
|
||||
const copyState = { ...state }
|
||||
action.todolists.forEach((tl) => {
|
||||
copyState[tl.id] = []
|
||||
})
|
||||
return copyState
|
||||
}
|
||||
case 'SET-TASKS':
|
||||
return { ...state, [action.todolistId]: action.tasks }
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
// actions
|
||||
export const removeTaskAC = (taskId: string, todolistId: string) =>
|
||||
({type: 'REMOVE-TASK', taskId, todolistId} as const)
|
||||
({ type: 'REMOVE-TASK', taskId, todolistId }) as const
|
||||
export const addTaskAC = (task: TaskType) =>
|
||||
({type: 'ADD-TASK', task} as const)
|
||||
export const updateTaskAC = (taskId: string, model: UpdateDomainTaskModelType, todolistId: string) =>
|
||||
({type: 'UPDATE-TASK', model, todolistId, taskId} as const)
|
||||
({ type: 'ADD-TASK', task }) as const
|
||||
export const updateTaskAC = (
|
||||
taskId: string,
|
||||
model: UpdateDomainTaskModelType,
|
||||
todolistId: string
|
||||
) => ({ type: 'UPDATE-TASK', model, todolistId, taskId }) as const
|
||||
export const setTasksAC = (tasks: Array<TaskType>, todolistId: string) =>
|
||||
({type: 'SET-TASKS', tasks, todolistId} as const)
|
||||
({ type: 'SET-TASKS', tasks, todolistId }) as const
|
||||
|
||||
// thunks
|
||||
export const fetchTasksTC = (todolistId: string) => (dispatch: Dispatch<ActionsType | SetAppStatusActionType>) => {
|
||||
export const fetchTasksTC =
|
||||
(todolistId: string) =>
|
||||
(dispatch: Dispatch<ActionsType | SetAppStatusActionType>) => {
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
todolistsAPI.getTasks(todolistId)
|
||||
.then((res) => {
|
||||
const tasks = res.data.items
|
||||
dispatch(setTasksAC(tasks, todolistId))
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
}
|
||||
export const removeTaskTC = (taskId: string, todolistId: string) => (dispatch: Dispatch<ActionsType>) => {
|
||||
todolistsAPI.deleteTask(todolistId, taskId)
|
||||
.then(res => {
|
||||
const action = removeTaskAC(taskId, todolistId)
|
||||
dispatch(action)
|
||||
})
|
||||
}
|
||||
export const addTaskTC = (title: string, todolistId: string) => (dispatch: Dispatch<ActionsType | SetAppErrorActionType | SetAppStatusActionType>) => {
|
||||
todolistsAPI.getTasks(todolistId).then((res) => {
|
||||
const tasks = res.data.items
|
||||
dispatch(setTasksAC(tasks, todolistId))
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
}
|
||||
export const removeTaskTC =
|
||||
(taskId: string, todolistId: string) => (dispatch: Dispatch<ActionsType>) => {
|
||||
todolistsAPI.deleteTask(todolistId, taskId).then((res) => {
|
||||
const action = removeTaskAC(taskId, todolistId)
|
||||
dispatch(action)
|
||||
})
|
||||
}
|
||||
export const addTaskTC =
|
||||
(title: string, todolistId: string) =>
|
||||
(
|
||||
dispatch: Dispatch<
|
||||
ActionsType | SetAppErrorActionType | SetAppStatusActionType
|
||||
>
|
||||
) => {
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
todolistsAPI.createTask(todolistId, title)
|
||||
.then(res => {
|
||||
if (res.data.resultCode === 0) {
|
||||
const task = res.data.data.item
|
||||
const action = addTaskAC(task)
|
||||
dispatch(action)
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
} else {
|
||||
handleServerAppError(res.data, dispatch);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
handleServerNetworkError(error, dispatch)
|
||||
})
|
||||
}
|
||||
export const updateTaskTC = (taskId: string, domainModel: UpdateDomainTaskModelType, todolistId: string) =>
|
||||
(dispatch: ThunkDispatch, getState: () => AppRootStateType) => {
|
||||
const state = getState()
|
||||
const task = state.tasks[todolistId].find(t => t.id === taskId)
|
||||
if (!task) {
|
||||
//throw new Error("task not found in the state");
|
||||
console.warn('task not found in the state')
|
||||
return
|
||||
todolistsAPI
|
||||
.createTask(todolistId, title)
|
||||
.then((res) => {
|
||||
if (res.data.resultCode === 0) {
|
||||
const task = res.data.data.item
|
||||
const action = addTaskAC(task)
|
||||
dispatch(action)
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
} else {
|
||||
handleServerAppError(res.data, dispatch)
|
||||
}
|
||||
|
||||
const apiModel: UpdateTaskModelType = {
|
||||
deadline: task.deadline,
|
||||
description: task.description,
|
||||
priority: task.priority,
|
||||
startDate: task.startDate,
|
||||
title: task.title,
|
||||
status: task.status,
|
||||
...domainModel
|
||||
}
|
||||
|
||||
todolistsAPI.updateTask(todolistId, taskId, apiModel)
|
||||
.then(res => {
|
||||
if (res.data.resultCode === 0) {
|
||||
const action = updateTaskAC(taskId, domainModel, todolistId)
|
||||
dispatch(action)
|
||||
} else {
|
||||
handleServerAppError(res.data, dispatch);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
handleServerNetworkError(error, dispatch);
|
||||
})
|
||||
})
|
||||
.catch((error) => {
|
||||
handleServerNetworkError(error, dispatch)
|
||||
})
|
||||
}
|
||||
export const updateTaskTC =
|
||||
(
|
||||
taskId: string,
|
||||
domainModel: UpdateDomainTaskModelType,
|
||||
todolistId: string
|
||||
) =>
|
||||
(dispatch: ThunkDispatch, getState: () => AppRootStateType) => {
|
||||
const state = getState()
|
||||
const task = state.tasks[todolistId].find((t) => t.id === taskId)
|
||||
if (!task) {
|
||||
//throw new Error("task not found in the state");
|
||||
console.warn('task not found in the state')
|
||||
return
|
||||
}
|
||||
|
||||
const apiModel: UpdateTaskModelType = {
|
||||
deadline: task.deadline,
|
||||
description: task.description,
|
||||
priority: task.priority,
|
||||
startDate: task.startDate,
|
||||
title: task.title,
|
||||
status: task.status,
|
||||
...domainModel,
|
||||
}
|
||||
|
||||
todolistsAPI
|
||||
.updateTask(todolistId, taskId, apiModel)
|
||||
.then((res) => {
|
||||
if (res.data.resultCode === 0) {
|
||||
const action = updateTaskAC(taskId, domainModel, todolistId)
|
||||
dispatch(action)
|
||||
} else {
|
||||
handleServerAppError(res.data, dispatch)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
handleServerNetworkError(error, dispatch)
|
||||
})
|
||||
}
|
||||
|
||||
// types
|
||||
export type UpdateDomainTaskModelType = {
|
||||
title?: string
|
||||
description?: string
|
||||
status?: TaskStatuses
|
||||
priority?: TaskPriorities
|
||||
startDate?: string
|
||||
deadline?: string
|
||||
title?: string
|
||||
description?: string
|
||||
status?: TaskStatuses
|
||||
priority?: TaskPriorities
|
||||
startDate?: string
|
||||
deadline?: string
|
||||
}
|
||||
export type TasksStateType = {
|
||||
[key: string]: Array<TaskType>
|
||||
[key: string]: Array<TaskType>
|
||||
}
|
||||
type ActionsType =
|
||||
| ReturnType<typeof removeTaskAC>
|
||||
| ReturnType<typeof addTaskAC>
|
||||
| ReturnType<typeof updateTaskAC>
|
||||
| AddTodolistActionType
|
||||
| RemoveTodolistActionType
|
||||
| SetTodolistsActionType
|
||||
| ReturnType<typeof setTasksAC>
|
||||
type ThunkDispatch = Dispatch<ActionsType | SetAppStatusActionType | SetAppErrorActionType>
|
||||
| ReturnType<typeof removeTaskAC>
|
||||
| ReturnType<typeof addTaskAC>
|
||||
| ReturnType<typeof updateTaskAC>
|
||||
| AddTodolistActionType
|
||||
| RemoveTodolistActionType
|
||||
| SetTodolistsActionType
|
||||
| ReturnType<typeof setTasksAC>
|
||||
type ThunkDispatch = Dispatch<
|
||||
ActionsType | SetAppStatusActionType | SetAppErrorActionType
|
||||
>
|
||||
|
||||
@@ -1,89 +1,102 @@
|
||||
import {
|
||||
addTodolistAC, changeTodolistEntityStatusAC,
|
||||
changeTodolistFilterAC,
|
||||
changeTodolistTitleAC, FilterValuesType,
|
||||
removeTodolistAC, setTodolistsAC, TodolistDomainType,
|
||||
todolistsReducer
|
||||
addTodolistAC,
|
||||
changeTodolistEntityStatusAC,
|
||||
changeTodolistFilterAC,
|
||||
changeTodolistTitleAC,
|
||||
FilterValuesType,
|
||||
removeTodolistAC,
|
||||
setTodolistsAC,
|
||||
TodolistDomainType,
|
||||
todolistsReducer,
|
||||
} from './todolists-reducer'
|
||||
import {v1} from 'uuid'
|
||||
import {TodolistType} from '../../api/todolists-api'
|
||||
import {RequestStatusType} from '../../app/app-reducer'
|
||||
import { v1 } from 'uuid'
|
||||
import { TodolistType } from '../../api/todolists-api'
|
||||
import { RequestStatusType } from '../../app/app-reducer'
|
||||
|
||||
let todolistId1: string
|
||||
let todolistId2: string
|
||||
let startState: Array<TodolistDomainType> = []
|
||||
|
||||
beforeEach(() => {
|
||||
todolistId1 = v1()
|
||||
todolistId2 = v1()
|
||||
startState = [
|
||||
{id: todolistId1, title: 'What to learn', filter: 'all', entityStatus: 'idle', addedDate: '', order: 0},
|
||||
{id: todolistId2, title: 'What to buy', filter: 'all', entityStatus: 'idle', addedDate: '', order: 0}
|
||||
]
|
||||
todolistId1 = v1()
|
||||
todolistId2 = v1()
|
||||
startState = [
|
||||
{
|
||||
id: todolistId1,
|
||||
title: 'What to learn',
|
||||
filter: 'all',
|
||||
entityStatus: 'idle',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
},
|
||||
{
|
||||
id: todolistId2,
|
||||
title: 'What to buy',
|
||||
filter: 'all',
|
||||
entityStatus: 'idle',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
test('correct todolist should be removed', () => {
|
||||
const endState = todolistsReducer(startState, removeTodolistAC(todolistId1))
|
||||
const endState = todolistsReducer(startState, removeTodolistAC(todolistId1))
|
||||
|
||||
expect(endState.length).toBe(1)
|
||||
expect(endState[0].id).toBe(todolistId2)
|
||||
expect(endState.length).toBe(1)
|
||||
expect(endState[0].id).toBe(todolistId2)
|
||||
})
|
||||
|
||||
test('correct todolist should be added', () => {
|
||||
let todolist: TodolistType = {
|
||||
title: 'New Todolist',
|
||||
id: 'any id',
|
||||
addedDate: '',
|
||||
order: 0
|
||||
}
|
||||
let todolist: TodolistType = {
|
||||
title: 'New Todolist',
|
||||
id: 'any id',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
}
|
||||
|
||||
const endState = todolistsReducer(startState, addTodolistAC(todolist))
|
||||
|
||||
const endState = todolistsReducer(startState, addTodolistAC(todolist))
|
||||
|
||||
expect(endState.length).toBe(3)
|
||||
expect(endState[0].title).toBe(todolist.title)
|
||||
expect(endState[0].filter).toBe('all')
|
||||
expect(endState.length).toBe(3)
|
||||
expect(endState[0].title).toBe(todolist.title)
|
||||
expect(endState[0].filter).toBe('all')
|
||||
})
|
||||
|
||||
test('correct todolist should change its name', () => {
|
||||
let newTodolistTitle = 'New Todolist'
|
||||
let newTodolistTitle = 'New Todolist'
|
||||
|
||||
const action = changeTodolistTitleAC(todolistId2, newTodolistTitle)
|
||||
const action = changeTodolistTitleAC(todolistId2, newTodolistTitle)
|
||||
|
||||
const endState = todolistsReducer(startState, action)
|
||||
const endState = todolistsReducer(startState, action)
|
||||
|
||||
expect(endState[0].title).toBe('What to learn')
|
||||
expect(endState[1].title).toBe(newTodolistTitle)
|
||||
expect(endState[0].title).toBe('What to learn')
|
||||
expect(endState[1].title).toBe(newTodolistTitle)
|
||||
})
|
||||
|
||||
test('correct filter of todolist should be changed', () => {
|
||||
let newFilter: FilterValuesType = 'completed'
|
||||
let newFilter: FilterValuesType = 'completed'
|
||||
|
||||
const action = changeTodolistFilterAC(todolistId2, newFilter)
|
||||
const action = changeTodolistFilterAC(todolistId2, newFilter)
|
||||
|
||||
const endState = todolistsReducer(startState, action)
|
||||
const endState = todolistsReducer(startState, action)
|
||||
|
||||
expect(endState[0].filter).toBe('all')
|
||||
expect(endState[1].filter).toBe(newFilter)
|
||||
expect(endState[0].filter).toBe('all')
|
||||
expect(endState[1].filter).toBe(newFilter)
|
||||
})
|
||||
test('todolists should be added', () => {
|
||||
const action = setTodolistsAC(startState)
|
||||
|
||||
const action = setTodolistsAC(startState)
|
||||
const endState = todolistsReducer([], action)
|
||||
|
||||
const endState = todolistsReducer([], action)
|
||||
|
||||
expect(endState.length).toBe(2)
|
||||
expect(endState.length).toBe(2)
|
||||
})
|
||||
test('correct entity status of todolist should be changed', () => {
|
||||
let newStatus: RequestStatusType = 'loading'
|
||||
let newStatus: RequestStatusType = 'loading'
|
||||
|
||||
const action = changeTodolistEntityStatusAC(todolistId2, newStatus)
|
||||
const action = changeTodolistEntityStatusAC(todolistId2, newStatus)
|
||||
|
||||
const endState = todolistsReducer(startState, action)
|
||||
const endState = todolistsReducer(startState, action)
|
||||
|
||||
expect(endState[0].entityStatus).toBe('idle')
|
||||
expect(endState[1].entityStatus).toBe(newStatus)
|
||||
expect(endState[0].entityStatus).toBe('idle')
|
||||
expect(endState[1].entityStatus).toBe(newStatus)
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,109 +1,142 @@
|
||||
import {todolistsAPI, TodolistType} from '../../api/todolists-api'
|
||||
import {Dispatch} from 'redux'
|
||||
import {RequestStatusType, SetAppErrorActionType, setAppStatusAC, SetAppStatusActionType} from '../../app/app-reducer'
|
||||
import {handleServerNetworkError} from '../../utils/error-utils'
|
||||
import { AppThunk } from '../../app/store';
|
||||
import { todolistsAPI, TodolistType } from '../../api/todolists-api'
|
||||
import { Dispatch } from 'redux'
|
||||
import {
|
||||
RequestStatusType,
|
||||
SetAppErrorActionType,
|
||||
setAppStatusAC,
|
||||
SetAppStatusActionType,
|
||||
} from '../../app/app-reducer'
|
||||
import { handleServerNetworkError } from '../../utils/error-utils'
|
||||
import { AppThunk } from '../../app/store'
|
||||
|
||||
const initialState: Array<TodolistDomainType> = []
|
||||
|
||||
export const todolistsReducer = (state: Array<TodolistDomainType> = initialState, action: ActionsType): Array<TodolistDomainType> => {
|
||||
switch (action.type) {
|
||||
case 'REMOVE-TODOLIST':
|
||||
return state.filter(tl => tl.id != action.id)
|
||||
case 'ADD-TODOLIST':
|
||||
return [{...action.todolist, filter: 'all', entityStatus: 'idle'}, ...state]
|
||||
export const todolistsReducer = (
|
||||
state: Array<TodolistDomainType> = initialState,
|
||||
action: ActionsType
|
||||
): Array<TodolistDomainType> => {
|
||||
switch (action.type) {
|
||||
case 'REMOVE-TODOLIST':
|
||||
return state.filter((tl) => tl.id != action.id)
|
||||
case 'ADD-TODOLIST':
|
||||
return [
|
||||
{ ...action.todolist, filter: 'all', entityStatus: 'idle' },
|
||||
...state,
|
||||
]
|
||||
|
||||
case 'CHANGE-TODOLIST-TITLE':
|
||||
return state.map(tl => tl.id === action.id ? {...tl, title: action.title} : tl)
|
||||
case 'CHANGE-TODOLIST-FILTER':
|
||||
return state.map(tl => tl.id === action.id ? {...tl, filter: action.filter} : tl)
|
||||
case 'CHANGE-TODOLIST-ENTITY-STATUS':
|
||||
return state.map(tl => tl.id === action.id ? {...tl, entityStatus: action.status} : tl)
|
||||
case 'SET-TODOLISTS':
|
||||
return action.todolists.map(tl => ({...tl, filter: 'all', entityStatus: 'idle'}))
|
||||
default:
|
||||
return state
|
||||
}
|
||||
case 'CHANGE-TODOLIST-TITLE':
|
||||
return state.map((tl) =>
|
||||
tl.id === action.id ? { ...tl, title: action.title } : tl
|
||||
)
|
||||
case 'CHANGE-TODOLIST-FILTER':
|
||||
return state.map((tl) =>
|
||||
tl.id === action.id ? { ...tl, filter: action.filter } : tl
|
||||
)
|
||||
case 'CHANGE-TODOLIST-ENTITY-STATUS':
|
||||
return state.map((tl) =>
|
||||
tl.id === action.id ? { ...tl, entityStatus: action.status } : tl
|
||||
)
|
||||
case 'SET-TODOLISTS':
|
||||
return action.todolists.map((tl) => ({
|
||||
...tl,
|
||||
filter: 'all',
|
||||
entityStatus: 'idle',
|
||||
}))
|
||||
default:
|
||||
return state
|
||||
}
|
||||
}
|
||||
|
||||
// actions
|
||||
export const removeTodolistAC = (id: string) => ({type: 'REMOVE-TODOLIST', id} as const)
|
||||
export const addTodolistAC = (todolist: TodolistType) => ({type: 'ADD-TODOLIST', todolist} as const)
|
||||
export const changeTodolistTitleAC = (id: string, title: string) => ({
|
||||
export const removeTodolistAC = (id: string) =>
|
||||
({ type: 'REMOVE-TODOLIST', id }) as const
|
||||
export const addTodolistAC = (todolist: TodolistType) =>
|
||||
({ type: 'ADD-TODOLIST', todolist }) as const
|
||||
export const changeTodolistTitleAC = (id: string, title: string) =>
|
||||
({
|
||||
type: 'CHANGE-TODOLIST-TITLE',
|
||||
id,
|
||||
title
|
||||
} as const)
|
||||
export const changeTodolistFilterAC = (id: string, filter: FilterValuesType) => ({
|
||||
title,
|
||||
}) as const
|
||||
export const changeTodolistFilterAC = (id: string, filter: FilterValuesType) =>
|
||||
({
|
||||
type: 'CHANGE-TODOLIST-FILTER',
|
||||
id,
|
||||
filter
|
||||
} as const)
|
||||
export const changeTodolistEntityStatusAC = (id: string, status: RequestStatusType) => ({
|
||||
type: 'CHANGE-TODOLIST-ENTITY-STATUS', id, status } as const)
|
||||
export const setTodolistsAC = (todolists: Array<TodolistType>) => ({type: 'SET-TODOLISTS', todolists} as const)
|
||||
filter,
|
||||
}) as const
|
||||
export const changeTodolistEntityStatusAC = (
|
||||
id: string,
|
||||
status: RequestStatusType
|
||||
) =>
|
||||
({
|
||||
type: 'CHANGE-TODOLIST-ENTITY-STATUS',
|
||||
id,
|
||||
status,
|
||||
}) as const
|
||||
export const setTodolistsAC = (todolists: Array<TodolistType>) =>
|
||||
({ type: 'SET-TODOLISTS', todolists }) as const
|
||||
|
||||
// thunks
|
||||
export const fetchTodolistsTC = (): AppThunk => {
|
||||
return (dispatch) => {
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
todolistsAPI.getTodolists()
|
||||
.then((res) => {
|
||||
dispatch(setTodolistsAC(res.data))
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
.catch(error => {
|
||||
handleServerNetworkError(error, dispatch);
|
||||
})
|
||||
}
|
||||
return (dispatch) => {
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
todolistsAPI
|
||||
.getTodolists()
|
||||
.then((res) => {
|
||||
dispatch(setTodolistsAC(res.data))
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
.catch((error) => {
|
||||
handleServerNetworkError(error, dispatch)
|
||||
})
|
||||
}
|
||||
}
|
||||
export const removeTodolistTC = (todolistId: string) => {
|
||||
return (dispatch: ThunkDispatch) => {
|
||||
//изменим глобальный статус приложения, чтобы вверху полоса побежала
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
//изменим статус конкретного тудулиста, чтобы он мог задизеблить что надо
|
||||
dispatch(changeTodolistEntityStatusAC(todolistId, 'loading'))
|
||||
todolistsAPI.deleteTodolist(todolistId)
|
||||
.then((res) => {
|
||||
dispatch(removeTodolistAC(todolistId))
|
||||
//скажем глобально приложению, что асинхронная операция завершена
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
}
|
||||
return (dispatch: ThunkDispatch) => {
|
||||
//изменим глобальный статус приложения, чтобы вверху полоса побежала
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
//изменим статус конкретного тудулиста, чтобы он мог задизеблить что надо
|
||||
dispatch(changeTodolistEntityStatusAC(todolistId, 'loading'))
|
||||
todolistsAPI.deleteTodolist(todolistId).then((res) => {
|
||||
dispatch(removeTodolistAC(todolistId))
|
||||
//скажем глобально приложению, что асинхронная операция завершена
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
}
|
||||
}
|
||||
export const addTodolistTC = (title: string) => {
|
||||
return (dispatch: ThunkDispatch) => {
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
todolistsAPI.createTodolist(title)
|
||||
.then((res) => {
|
||||
dispatch(addTodolistAC(res.data.data.item))
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
}
|
||||
return (dispatch: ThunkDispatch) => {
|
||||
dispatch(setAppStatusAC('loading'))
|
||||
todolistsAPI.createTodolist(title).then((res) => {
|
||||
dispatch(addTodolistAC(res.data.data.item))
|
||||
dispatch(setAppStatusAC('succeeded'))
|
||||
})
|
||||
}
|
||||
}
|
||||
export const changeTodolistTitleTC = (id: string, title: string) => {
|
||||
return (dispatch: Dispatch<ActionsType>) => {
|
||||
todolistsAPI.updateTodolist(id, title)
|
||||
.then((res) => {
|
||||
dispatch(changeTodolistTitleAC(id, title))
|
||||
})
|
||||
}
|
||||
return (dispatch: Dispatch<ActionsType>) => {
|
||||
todolistsAPI.updateTodolist(id, title).then((res) => {
|
||||
dispatch(changeTodolistTitleAC(id, title))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// types
|
||||
export type AddTodolistActionType = ReturnType<typeof addTodolistAC>;
|
||||
export type RemoveTodolistActionType = ReturnType<typeof removeTodolistAC>;
|
||||
export type SetTodolistsActionType = ReturnType<typeof setTodolistsAC>;
|
||||
export type AddTodolistActionType = ReturnType<typeof addTodolistAC>
|
||||
export type RemoveTodolistActionType = ReturnType<typeof removeTodolistAC>
|
||||
export type SetTodolistsActionType = ReturnType<typeof setTodolistsAC>
|
||||
type ActionsType =
|
||||
| RemoveTodolistActionType
|
||||
| AddTodolistActionType
|
||||
| ReturnType<typeof changeTodolistTitleAC>
|
||||
| ReturnType<typeof changeTodolistFilterAC>
|
||||
| SetTodolistsActionType
|
||||
| ReturnType<typeof changeTodolistEntityStatusAC>
|
||||
export type FilterValuesType = 'all' | 'active' | 'completed';
|
||||
| RemoveTodolistActionType
|
||||
| AddTodolistActionType
|
||||
| ReturnType<typeof changeTodolistTitleAC>
|
||||
| ReturnType<typeof changeTodolistFilterAC>
|
||||
| SetTodolistsActionType
|
||||
| ReturnType<typeof changeTodolistEntityStatusAC>
|
||||
export type FilterValuesType = 'all' | 'active' | 'completed'
|
||||
export type TodolistDomainType = TodolistType & {
|
||||
filter: FilterValuesType
|
||||
entityStatus: RequestStatusType
|
||||
filter: FilterValuesType
|
||||
entityStatus: RequestStatusType
|
||||
}
|
||||
type ThunkDispatch = Dispatch<ActionsType | SetAppStatusActionType | SetAppErrorActionType>
|
||||
type ThunkDispatch = Dispatch<
|
||||
ActionsType | SetAppStatusActionType | SetAppErrorActionType
|
||||
>
|
||||
|
||||
@@ -1,27 +1,31 @@
|
||||
import {addTodolistAC, TodolistDomainType, todolistsReducer} from './todolists-reducer'
|
||||
import {tasksReducer, TasksStateType} from './tasks-reducer'
|
||||
import {TodolistType} from '../../api/todolists-api'
|
||||
import {
|
||||
addTodolistAC,
|
||||
TodolistDomainType,
|
||||
todolistsReducer,
|
||||
} from './todolists-reducer'
|
||||
import { tasksReducer, TasksStateType } from './tasks-reducer'
|
||||
import { TodolistType } from '../../api/todolists-api'
|
||||
|
||||
test('ids should be equals', () => {
|
||||
const startTasksState: TasksStateType = {};
|
||||
const startTodolistsState: Array<TodolistDomainType> = [];
|
||||
const startTasksState: TasksStateType = {}
|
||||
const startTodolistsState: Array<TodolistDomainType> = []
|
||||
|
||||
let todolist: TodolistType = {
|
||||
title: 'new todolist',
|
||||
id: 'any id',
|
||||
addedDate: '',
|
||||
order: 0
|
||||
}
|
||||
let todolist: TodolistType = {
|
||||
title: 'new todolist',
|
||||
id: 'any id',
|
||||
addedDate: '',
|
||||
order: 0,
|
||||
}
|
||||
|
||||
const action = addTodolistAC(todolist);
|
||||
const action = addTodolistAC(todolist)
|
||||
|
||||
const endTasksState = tasksReducer(startTasksState, action)
|
||||
const endTodolistsState = todolistsReducer(startTodolistsState, action)
|
||||
const endTasksState = tasksReducer(startTasksState, action)
|
||||
const endTodolistsState = todolistsReducer(startTodolistsState, action)
|
||||
|
||||
const keys = Object.keys(endTasksState);
|
||||
const idFromTasks = keys[0];
|
||||
const idFromTodolists = endTodolistsState[0].id;
|
||||
const keys = Object.keys(endTasksState)
|
||||
const idFromTasks = keys[0]
|
||||
const idFromTodolists = endTodolistsState[0].id
|
||||
|
||||
expect(idFromTasks).toBe(action.todolist.id);
|
||||
expect(idFromTodolists).toBe(action.todolist.id);
|
||||
});
|
||||
expect(idFromTasks).toBe(action.todolist.id)
|
||||
expect(idFromTodolists).toBe(action.todolist.id)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user