From be069aeb0a91e3374afdc5c09b20db4f27f9daac Mon Sep 17 00:00:00 2001 From: Yuriy Shvorob Date: Thu, 16 Jan 2025 16:24:03 +0200 Subject: [PATCH 01/10] ready component for todo list --- src/App.scss | 23 ------------ src/App.tsx | 53 ++++++++++++--------------- src/App.types.ts | 17 +++++++++ src/components/TodoInfo/TodoInfo.scss | 22 +++++++++++ src/components/TodoInfo/TodoInfo.tsx | 28 +++++++++++++- src/components/TodoList/TodoList.tsx | 17 ++++++++- 6 files changed, 106 insertions(+), 54 deletions(-) create mode 100644 src/App.types.ts create mode 100644 src/components/TodoInfo/TodoInfo.scss diff --git a/src/App.scss b/src/App.scss index 9b9ddc979c..ed7adc8ef4 100644 --- a/src/App.scss +++ b/src/App.scss @@ -14,26 +14,3 @@ button { .error { color: red; } - -.TodoInfo { - width: max-content; - padding: 8px; - margin: 12px 0; - border: 1px solid #000; - border-radius: 8px; - background-color: antiquewhite; - - &__title { - margin: 4px 0; - font-size: inherit; - color: #f00; - } - - &--completed &__title { - color: #080; - } -} - -.UserInfo { - font-style: italic; -} diff --git a/src/App.tsx b/src/App.tsx index a9a9bb4c53..f4f63ccff1 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,7 +1,28 @@ import './App.scss'; -// import usersFromServer from './api/users'; -// import todosFromServer from './api/todos'; +import usersFromServer from './api/users'; +import todosFromServer from './api/todos'; +import { TodoWithUser } from './App.types'; +import { TodoList } from './components/TodoList'; + +const todos: TodoWithUser[] = todosFromServer + .map(todo => { + if (!todo) { + return null; + } + + const user = usersFromServer.find(userInfo => userInfo.id === todo.userId); + + if (!user) { + return null; + } + + return { + ...todo, + user, + }; + }) + .filter((todo): todo is TodoWithUser => todo !== null); export const App = () => { return ( @@ -29,33 +50,7 @@ export const App = () => { -
- - - - - -
+ ); }; diff --git a/src/App.types.ts b/src/App.types.ts new file mode 100644 index 0000000000..641492d45e --- /dev/null +++ b/src/App.types.ts @@ -0,0 +1,17 @@ +export type User = { + id: number; + name: string; + username: string; + email: string; +}; + +export type Todo = { + userId: number; + id: number; + title: string; + completed: boolean; +}; + +export interface TodoWithUser extends Todo { + user: User; +} diff --git a/src/components/TodoInfo/TodoInfo.scss b/src/components/TodoInfo/TodoInfo.scss new file mode 100644 index 0000000000..0aa322b7cd --- /dev/null +++ b/src/components/TodoInfo/TodoInfo.scss @@ -0,0 +1,22 @@ +.TodoInfo { + width: max-content; + padding: 8px; + margin: 12px 0; + border: 1px solid #000; + border-radius: 8px; + background-color: antiquewhite; + + &__title { + margin: 4px 0; + font-size: inherit; + color: #f00; + } + + &--completed &__title { + color: #080; + } +} + +.UserInfo { + font-style: italic; +} diff --git a/src/components/TodoInfo/TodoInfo.tsx b/src/components/TodoInfo/TodoInfo.tsx index d164511fa8..9d6dc3f539 100644 --- a/src/components/TodoInfo/TodoInfo.tsx +++ b/src/components/TodoInfo/TodoInfo.tsx @@ -1 +1,27 @@ -export const TodoInfo = () => {}; +import classNames from 'classnames'; +import { TodoWithUser } from '../../App.types'; +import './TodoInfo.scss'; + +interface Props { + todo: TodoWithUser; +} + +export const TodoInfo = ({ todo }: Props) => { + const { title, user, completed } = todo; + const { name, email } = user; + + return ( + + ); +}; diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx index c12fae07c0..f76dc4fbbf 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -1 +1,16 @@ -export const TodoList = () => {}; +import { TodoWithUser } from '../../App.types'; +import { TodoInfo } from '../TodoInfo'; + +type Props = { + todos: TodoWithUser[]; +}; + +export const TodoList = ({ todos }: Props) => { + return ( +
+ {todos.map(todo => { + return ; + })} +
+ ); +}; From 05a8ff89c84226d022400ebbaad1ec3b254ff452 Mon Sep 17 00:00:00 2001 From: Yuriy Shvorob Date: Thu, 16 Jan 2025 16:32:36 +0200 Subject: [PATCH 02/10] fix types --- src/App.tsx | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index f4f63ccff1..0e397591e6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,27 +2,19 @@ import './App.scss'; import usersFromServer from './api/users'; import todosFromServer from './api/todos'; -import { TodoWithUser } from './App.types'; +import { TodoWithUser, User } from './App.types'; import { TodoList } from './components/TodoList'; -const todos: TodoWithUser[] = todosFromServer - .map(todo => { - if (!todo) { - return null; - } +const todos: TodoWithUser[] = todosFromServer.map((todo): TodoWithUser => { + const user = usersFromServer.find( + userInfo => userInfo.id === todo.userId, + ) as User; - const user = usersFromServer.find(userInfo => userInfo.id === todo.userId); - - if (!user) { - return null; - } - - return { - ...todo, - user, - }; - }) - .filter((todo): todo is TodoWithUser => todo !== null); + return { + ...todo, + user, + }; +}); export const App = () => { return ( From 9dd3aba431b7e0eeb1f006b5d88c71b355304b16 Mon Sep 17 00:00:00 2001 From: Yuriy Shvorob Date: Thu, 16 Jan 2025 21:45:11 +0200 Subject: [PATCH 03/10] ready form submit and clear --- src/App.tsx | 74 +++++++++++++++++++++++----- src/components/TodoInfo/TodoInfo.tsx | 2 +- 2 files changed, 63 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 0e397591e6..b74c967f95 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,37 +1,87 @@ import './App.scss'; +import { useState } from 'react'; import usersFromServer from './api/users'; import todosFromServer from './api/todos'; import { TodoWithUser, User } from './App.types'; import { TodoList } from './components/TodoList'; -const todos: TodoWithUser[] = todosFromServer.map((todo): TodoWithUser => { - const user = usersFromServer.find( - userInfo => userInfo.id === todo.userId, - ) as User; +const todosWithUser: TodoWithUser[] = todosFromServer.map( + (todo): TodoWithUser => { + const user = usersFromServer.find( + userInfo => userInfo.id === todo.userId, + ) as User; - return { - ...todo, - user, - }; -}); + return { + ...todo, + user, + }; + }, +); export const App = () => { + const [todos, setTodos] = useState([...todosWithUser]); + const [title, setTitle] = useState(''); + const [userName, setUserName] = useState('0'); + return (

Add todo form

-
+ { + event.preventDefault(); + if (title && userName) { + const newTodo: TodoWithUser = { + userId: usersFromServer.find(user => user.name === userName) + ?.id as number, + id: Math.max(...todos.map(todo => todo.id)) + 1, + title, + completed: false, + user: usersFromServer.find( + user => user.id === Number(userName), + ) as User, + }; + + setTodos([...todos, newTodo]); + setTitle(''); + setUserName('0'); + } + }} + >
- + { + setTitle(event.target.value); + }} + /> Please enter a title
- { + setUserName(event.target.value); + }} + > + {usersFromServer.map(user => { + return ( + + ); + })} Please choose a user diff --git a/src/components/TodoInfo/TodoInfo.tsx b/src/components/TodoInfo/TodoInfo.tsx index 9d6dc3f539..f90ce565c0 100644 --- a/src/components/TodoInfo/TodoInfo.tsx +++ b/src/components/TodoInfo/TodoInfo.tsx @@ -12,7 +12,7 @@ export const TodoInfo = ({ todo }: Props) => { return (
Date: Fri, 17 Jan 2025 12:15:30 +0200 Subject: [PATCH 04/10] required task is read --- src/App.tsx | 69 ++++++++++++++++++---------- src/components/TodoInfo/TodoInfo.tsx | 6 +-- src/components/UserInfo/UserInfo.tsx | 16 ++++++- 3 files changed, 61 insertions(+), 30 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index b74c967f95..bc13e1c947 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import './App.scss'; -import { useState } from 'react'; +import React, { useState } from 'react'; import usersFromServer from './api/users'; import todosFromServer from './api/todos'; @@ -8,9 +8,7 @@ import { TodoList } from './components/TodoList'; const todosWithUser: TodoWithUser[] = todosFromServer.map( (todo): TodoWithUser => { - const user = usersFromServer.find( - userInfo => userInfo.id === todo.userId, - ) as User; + const user = usersFromServer.find(({ id }) => id === todo.userId) as User; return { ...todo, @@ -18,11 +16,46 @@ const todosWithUser: TodoWithUser[] = todosFromServer.map( }; }, ); +const defaultOptionIndex = '0'; export const App = () => { const [todos, setTodos] = useState([...todosWithUser]); const [title, setTitle] = useState(''); - const [userName, setUserName] = useState('0'); + const [userName, setUserName] = useState(defaultOptionIndex); + const [hasTitleError, setHasTitleError] = useState(false); + const [hasUserError, setHasUserError] = useState(false); + + const clearForm = () => { + setTitle(''); + setUserName(defaultOptionIndex); + }; + + const handleSubmit = (event: React.FormEvent): null => { + event.preventDefault(); + if (!title) { + setHasTitleError(true); + } + + if (userName === defaultOptionIndex) { + setHasUserError(true); + } + + if (title && userName !== defaultOptionIndex) { + const newTodo: TodoWithUser = { + userId: usersFromServer.find(user => user.name === userName) + ?.id as number, + id: Math.max(...todos.map(todo => todo.id)) + 1, + title, + completed: false, + user: usersFromServer.find( + user => user.id === Number(userName), + ) as User, + }; + + setTodos([...todos, newTodo]); + clearForm(); + } + }; return (
@@ -32,23 +65,7 @@ export const App = () => { action="/api/todos" method="POST" onSubmit={event => { - event.preventDefault(); - if (title && userName) { - const newTodo: TodoWithUser = { - userId: usersFromServer.find(user => user.name === userName) - ?.id as number, - id: Math.max(...todos.map(todo => todo.id)) + 1, - title, - completed: false, - user: usersFromServer.find( - user => user.id === Number(userName), - ) as User, - }; - - setTodos([...todos, newTodo]); - setTitle(''); - setUserName('0'); - } + handleSubmit(event); }} >
@@ -59,9 +76,10 @@ export const App = () => { placeholder="Please enter a title" onChange={event => { setTitle(event.target.value); + setHasTitleError(false); }} /> - Please enter a title + {hasTitleError && Please enter a title}
@@ -70,9 +88,10 @@ export const App = () => { value={userName} onChange={event => { setUserName(event.target.value); + setHasUserError(false); }} > - {usersFromServer.map(user => { @@ -84,7 +103,7 @@ export const App = () => { })} - Please choose a user + {hasUserError && Please choose a user}