Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: #7 Todo 리스트 구현 #13

Merged
merged 5 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions src/apis/todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { AuthInstance } from './instance'
import { TodoType } from '../components/TodoList/types'

export const createTodoRequest = async (todo: string): Promise<TodoType> => {
const { data } = await AuthInstance.post('/todos', { todo })
return data
}

export const getTodosRequest = async (): Promise<TodoType[]> => {
const { data } = await AuthInstance.get('/todos')
return data
}

export const updateTodoRequest = async (
id: number,
todo: string,
isCompleted: boolean
): Promise<TodoType> => {
const { data } = await AuthInstance.put(`/todos/${id}`, { todo, isCompleted })
return data
}

export const deleteTodoRequest = async (id: number) => {
return AuthInstance.delete(`/todos/${id}`)
}
81 changes: 81 additions & 0 deletions src/components/TodoItem/TodoItem.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useState } from 'react'
import TodoItemProps from './types'

function TodoItem({ id, todo, isCompleted, updateTodo, deleteTodo }: TodoItemProps) {
const [todoCheck, setTodoCheck] = useState(isCompleted)
const [todoModify, setTodoModify] = useState('')
const [isEditMode, setIsEditMode] = useState(false)

// TODO의 체크박스가 변경 되었을 때
salmontaker marked this conversation as resolved.
Show resolved Hide resolved
const onTodoCheckChanged = () => {
updateTodo(id, todo, !todoCheck)
setTodoCheck(!todoCheck)
}

// TODO input창의 값이 변경 되었을 때 (수정모드)
const onTodoModifyChanged = (e: React.ChangeEvent<HTMLInputElement>) => {
const modifiedValue = e.target.value
setTodoModify(modifiedValue)
}

// 수정 버튼을 눌렀을 때
const onEditButtonClicked = () => {
setTodoModify(todo)
setIsEditMode(true)
}

// 취소 버튼을 눌렀을 때
const onCancelButtonClicked = () => {
setIsEditMode(false)
}

// 제출 버튼을 눌렀을 때
const onSubmitButtonClicked = () => {
updateTodo(id, todoModify, isCompleted)
setIsEditMode(false)
}

// 삭제 버튼을 눌렀을 때
const onDeleteButtonClicked = () => {
deleteTodo(id)
}

return (
<li>
<label>
<input type="checkbox" checked={todoCheck} onChange={onTodoCheckChanged} />
{isEditMode ? (
<input
data-testid="modify-input"
type="text"
onChange={onTodoModifyChanged}
value={todoModify}
/>
) : (
<span>{todo}</span>
)}
</label>
{isEditMode ? (
<>
<button data-testid="submit-button" onClick={onSubmitButtonClicked}>
제출
</button>
<button data-testid="cancel-button" onClick={onCancelButtonClicked}>
취소
</button>
</>
) : (
<>
<button data-testid="modify-button" onClick={onEditButtonClicked}>
수정
</button>
<button data-testid="delete-button" onClick={onDeleteButtonClicked}>
삭제
</button>
</>
)}
</li>
)
salmontaker marked this conversation as resolved.
Show resolved Hide resolved
}

export default TodoItem
8 changes: 8 additions & 0 deletions src/components/TodoItem/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { TodoType } from '../TodoList/types'

interface TodoItemProps extends Omit<TodoType, 'userId'> {
salmontaker marked this conversation as resolved.
Show resolved Hide resolved
updateTodo: (id: number, todo: string, isCompleted: boolean) => void
deleteTodo: (id: number) => void
}

export default TodoItemProps
83 changes: 83 additions & 0 deletions src/components/TodoList/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { useState, useEffect } from 'react'

import TodoItem from '../TodoItem/TodoItem'
import { TodoType } from './types'
import {
createTodoRequest,
getTodosRequest,
updateTodoRequest,
deleteTodoRequest,
} from '../../apis/todo'
import { AxiosError } from 'axios'

function TodoList() {
const [todos, setTodos] = useState<TodoType[]>([])
const [newTodo, setNewTodo] = useState('')

const createTodo = (todo: string) => {
createTodoRequest(todo)
.then((createdTodo) => {
setTodos((prevTodos) => [...prevTodos, createdTodo])
setNewTodo('')
})
.catch((e: AxiosError) => alert(e))
}

const getTodo = () => {
getTodosRequest()
.then((todos) => setTodos(todos))
.catch((e: AxiosError) => alert(e.message))
}

const updateTodo = (id: number, todo: string, isCompleted: boolean) => {
updateTodoRequest(id, todo, isCompleted)
.then((updatedTodo) =>
setTodos((prevTodos) =>
prevTodos.map((prevTodo) => (prevTodo.id === id ? updatedTodo : prevTodo))
)
)
.catch((e: AxiosError) => alert(e.message))
}

const deleteTodo = (id: number) => {
deleteTodoRequest(id)
.then(() => setTodos((prevTodos) => prevTodos.filter((prevTodo) => prevTodo.id !== id)))
.catch((e: AxiosError) => alert(e.message))
}

useEffect(() => {
getTodo()
}, [])

return (
<div>
<div>
<label htmlFor="addTodo">
<input
id="addTodo"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
data-testid="new-todo-input"
/>
</label>
<button onClick={() => createTodo(newTodo)} type="button" data-testid="new-todo-add-button">
추가
</button>
</div>
<ul>
{todos.map((todo) => (
<TodoItem
key={todo.id}
id={todo.id}
todo={todo.todo}
isCompleted={todo.isCompleted}
updateTodo={updateTodo}
deleteTodo={deleteTodo}
/>
))}
</ul>
</div>
)
}
salmontaker marked this conversation as resolved.
Show resolved Hide resolved

export default TodoList
6 changes: 6 additions & 0 deletions src/components/TodoList/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface TodoType {
id: number
todo: string
isCompleted: boolean
userId: number
}
2 changes: 2 additions & 0 deletions src/pages/Todo.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { useNavigate } from 'react-router-dom'
import TodoList from '../components/TodoList/TodoList'

function Todo() {
const navigate = useNavigate()

return (
<div>
투두리스트 페이지입니다.
<TodoList />
<button
onClick={() => {
localStorage.removeItem('access_token')
Expand Down