diff --git a/README.md b/README.md index f6e09d1094..8191238f1c 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,4 @@ Implement the ability to add TODOs to the `TodoList` implemented in the **Static - Implement a solution following the [React task guideline](https://github.com/mate-academy/react_task-guideline#react-tasks-guideline). - Use the [React TypeScript cheat sheet](https://mate-academy.github.io/fe-program/js/extra/react-typescript). - Open one more terminal and run tests with `npm test` to ensure your solution is correct. -- Replace `` with your Github username in the [DEMO LINK](https://.github.io/react_add-todo-form/) and add it to the PR description. +- Replace `` with your Github username in the [DEMO LINK](https://kovachhh.github.io/react_add-todo-form/) and add it to the PR description. diff --git a/src/App.tsx b/src/App.tsx index 9646bf5c6f..f74d687594 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,61 +1,39 @@ +import { useState } from 'react'; + import './App.scss'; +import { TodoList } from './components/TodoList'; + +import usersFromServer from './api/users'; +import todosFromServer from './api/todos'; +import { TodoForm } from './components/TodoForm/TodoForm'; +import { Todo } from './types/Todo'; +import { getNewTodoId } from './helpers/GetNewTodoId'; +import { getUserById } from './helpers/GetUserById'; -// import usersFromServer from './api/users'; -// import todosFromServer from './api/todos'; +export const initialTodos: Todo[] = todosFromServer.map((todo) => ({ + ...todo, + user: getUserById(todo.userId), +})); export const App = () => { + const [users] = useState(usersFromServer); + const [todos, setTodos] = useState(initialTodos); + + const todoAdd = (newTodo: Todo) => setTodos( + currentTodos => [...currentTodos, newTodo], + ); + return (

Add todo form

-
-
- - Please enter a title -
- -
- - - Please choose a user -
- - -
- -
- - - - - -
+ + +
); }; diff --git a/src/components/TodoForm/TodoForm.tsx b/src/components/TodoForm/TodoForm.tsx new file mode 100644 index 0000000000..3fcaa92b25 --- /dev/null +++ b/src/components/TodoForm/TodoForm.tsx @@ -0,0 +1,111 @@ +import { useState } from 'react'; + +import { Todo } from '../../types/Todo'; +import { User } from '../../types/User'; +import { getUserById } from '../../helpers/GetUserById'; + +type Props = { + users: User[], + todoAdd: (newTodo: Todo) => void, + newTodoId: number, +}; + +export const TodoForm: React.FC = ({ users, todoAdd, newTodoId }) => { + const [title, setTitle] = useState(''); + const [titleError, setTitleError] = useState(false); + + const [userId, setUserId] = useState(0); + const [userIdError, setUserIdError] = useState(false); + + const titleRegex = /^[a-zA-Zа-яА-Я0-9\s]*$/; + + const resetForm = () => { + setTitle(''); + setUserId(0); + }; + + const handleTitleChange = ( + { target: { value } }: React.ChangeEvent, + ) => { + if (titleRegex.test(value)) { + setTitle(value); + } + + setTitleError(false); + }; + + const handleUserChange = (event: React.ChangeEvent) => { + setUserId(+(event.target.value)); + setUserIdError(false); + }; + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + + if (!title.trim()) { + setTitleError(true); + } + + if (userId <= 0) { + setUserIdError(true); + } + + if (!title.trim() || userId <= 0) { + return; + } + + todoAdd({ + id: newTodoId, + title, + completed: false, + userId, + user: getUserById(userId), + }); + + resetForm(); + }; + + return ( +
+
+ + {titleError && ( + Please enter a title + )} +
+ +
+ + + {userIdError && ( + Please choose a user + )} +
+ + +
+ ); +}; diff --git a/src/components/TodoInfo/TodoInfo.tsx b/src/components/TodoInfo/TodoInfo.tsx index d164511fa8..043e45e711 100644 --- a/src/components/TodoInfo/TodoInfo.tsx +++ b/src/components/TodoInfo/TodoInfo.tsx @@ -1 +1,29 @@ -export const TodoInfo = () => {}; +import cn from 'classnames'; +import { Todo } from '../../types/Todo'; +import { UserInfo } from '../UserInfo'; + +type Props = { + todo: Todo +}; + +export const TodoInfo: React.FC = ({ todo }) => { + const { + id, + title, + completed, + user, + } = todo; + + return ( +
+

{title}

+ + {user && } +
+ ); +}; diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx index c12fae07c0..3c4f084c4f 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -1 +1,19 @@ -export const TodoList = () => {}; +import { Todo } from '../../types/Todo'; +import { TodoInfo } from '../TodoInfo'; + +type Props = { + todos: Todo[] +}; + +export const TodoList: React.FC = ({ todos }) => { + return ( +
+ {todos.map(todo => ( + + ))} +
+ ); +}; diff --git a/src/components/UserInfo/UserInfo.tsx b/src/components/UserInfo/UserInfo.tsx index f7bf0410ec..60dfe6ef53 100644 --- a/src/components/UserInfo/UserInfo.tsx +++ b/src/components/UserInfo/UserInfo.tsx @@ -1 +1,15 @@ -export const UserInfo = () => {}; +import { User } from '../../types/User'; + +type Props = { + user: User, +}; + +export const UserInfo: React.FC = ({ user }) => { + const { name, email } = user; + + return ( + + {name} + + ); +}; diff --git a/src/helpers/GetNewTodoId.tsx b/src/helpers/GetNewTodoId.tsx new file mode 100644 index 0000000000..dd2565f25d --- /dev/null +++ b/src/helpers/GetNewTodoId.tsx @@ -0,0 +1,7 @@ +import { Todo } from '../types/Todo'; + +export const getNewTodoId = (array: Todo[]) => { + const maxId = Math.max(...array.map(item => item.id)); + + return maxId + 1; +}; diff --git a/src/helpers/GetUserById.tsx b/src/helpers/GetUserById.tsx new file mode 100644 index 0000000000..a75fc814dd --- /dev/null +++ b/src/helpers/GetUserById.tsx @@ -0,0 +1,8 @@ +import users from '../api/users'; +import { User } from '../types/User'; + +export const getUserById = (userId: number): User | null => { + const result = users.find(user => user.id === userId); + + return result || null; +}; diff --git a/src/types/Todo.tsx b/src/types/Todo.tsx new file mode 100644 index 0000000000..2a6daccb9a --- /dev/null +++ b/src/types/Todo.tsx @@ -0,0 +1,9 @@ +import { User } from './User'; + +export type Todo = { + id: number, + title: string, + completed: boolean, + userId: number, + user: User | null; +}; diff --git a/src/types/User.tsx b/src/types/User.tsx new file mode 100644 index 0000000000..773696700d --- /dev/null +++ b/src/types/User.tsx @@ -0,0 +1,6 @@ +export type User = { + id: number, + name: string, + username: string, + email: string, +};