From 3940fdfdb9a6cab4eb690abbce93bcfd67e4afe2 Mon Sep 17 00:00:00 2001 From: Alex Shmyhelskyi Date: Thu, 9 Jan 2025 10:18:30 +0200 Subject: [PATCH 1/5] Message --- src/App.scss | 9 ++ src/App.tsx | 123 +++++++++++++++++++-------- src/components/TodoInfo/TodoInfo.tsx | 37 +++++++- src/components/TodoList/TodoList.tsx | 34 +++++++- src/components/UserInfo/UserInfo.tsx | 21 ++++- src/serveses/todos.ts | 10 +++ src/serveses/user.ts | 10 +++ 7 files changed, 206 insertions(+), 38 deletions(-) create mode 100644 src/serveses/todos.ts create mode 100644 src/serveses/user.ts diff --git a/src/App.scss b/src/App.scss index 9b9ddc979c..803ddbb506 100644 --- a/src/App.scss +++ b/src/App.scss @@ -12,6 +12,8 @@ button { } .error { + margin: 0; + padding: 0; color: red; } @@ -37,3 +39,10 @@ button { .UserInfo { font-style: italic; } +.field { + padding-bottom: 10px; + + &__title { + margin-right: 4px; + } +} diff --git a/src/App.tsx b/src/App.tsx index a9a9bb4c53..1cb9bbf8d6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,27 +1,106 @@ +import { useState } from 'react'; import './App.scss'; -// import usersFromServer from './api/users'; -// import todosFromServer from './api/todos'; +import usersFromServer from './api/users'; +import todosFromServer from './api/todos'; +import { TodoList } from './components/TodoList'; +import { getHigherId } from './serveses/todos'; + +type User = { + id: number; + name: string; + username: string; + email: string; +}; export const App = () => { + const [title, setTitle] = useState(''); + const [hasTitleError, setHasTitleError] = useState(''); + + const [selectedUserId, setSelectedUserId] = useState(0); + const [hasUserError, setHasUserError] = useState(''); + + const [todos, setTodos] = useState(todosFromServer); + + const handleTitle = (event: React.ChangeEvent) => { + setTitle(event.target.value); + setHasTitleError(''); + }; + + const handlUser = (event: React.ChangeEvent) => { + setSelectedUserId(+event.target.value); + setHasUserError(''); + }; + + const addTodo = (event: React.FormEvent) => { + event.preventDefault(); + + if (!title) { + setHasTitleError('Write a title'); + } + + if (!selectedUserId) { + setHasUserError('Choose some User'); + } + + if (!title || !selectedUserId) { + return; + } + + setTodos(currentTodos => { + const todo = { + id: getHigherId(todos), + title, + completed: false, + userId: +selectedUserId, + }; + + return [...currentTodos, todo]; + }); + + setTitle(''); + setSelectedUserId(0); + }; + return (

Add todo form

- -
+
- - Please enter a title + + + {hasTitleError && {hasTitleError}}
- + {usersFromServer.map((user: User) => { + return ( + + ); + })} - Please choose a user + {hasUserError && {hasUserError}}
-
- - - - - -
+
); }; diff --git a/src/components/TodoInfo/TodoInfo.tsx b/src/components/TodoInfo/TodoInfo.tsx index d164511fa8..7728494fe8 100644 --- a/src/components/TodoInfo/TodoInfo.tsx +++ b/src/components/TodoInfo/TodoInfo.tsx @@ -1 +1,36 @@ -export const TodoInfo = () => {}; +import classNames from 'classnames'; +import React from 'react'; +import { UserInfo } from '../UserInfo'; + +type Todo = { + id: number; + title: string; + completed: boolean; + userId: number; +}; + +type User = { + id: number; + name: string; + username: string; + email: string; +}; + +type Props = { + todo: Todo; + user: User; +}; + +export const TodoInfo: React.FC = ({ todo, user }) => { + return ( +
+

{todo.title}

+ +
+ ); +}; diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx index c12fae07c0..e8f88d857a 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -1 +1,33 @@ -export const TodoList = () => {}; +import React from 'react'; +import { TodoInfo } from '../TodoInfo'; +import { getUserById } from '../../serveses/user'; + +type Todo = { + id: number; + title: string; + completed: boolean; + userId: number; +}; + +type User = { + id: number; + name: string; + username: string; + email: string; +}; + +type Props = { + todos: Todo[]; + users: User[]; +}; +export const TodoList: React.FC = ({ todos, users }) => { + return ( +
+ {todos.map(todo => { + const user = getUserById(users, todo.userId); + + return user && ; + })} +
+ ); +}; diff --git a/src/components/UserInfo/UserInfo.tsx b/src/components/UserInfo/UserInfo.tsx index f7bf0410ec..58023f17c8 100644 --- a/src/components/UserInfo/UserInfo.tsx +++ b/src/components/UserInfo/UserInfo.tsx @@ -1 +1,20 @@ -export const UserInfo = () => {}; +import React from 'react'; + +type User = { + id: number; + name: string; + username: string; + email: string; +}; + +type Props = { + user: User; +}; + +export const UserInfo: React.FC = ({ user }) => { + return ( + + {user.name} + + ); +}; diff --git a/src/serveses/todos.ts b/src/serveses/todos.ts new file mode 100644 index 0000000000..5ee462eac5 --- /dev/null +++ b/src/serveses/todos.ts @@ -0,0 +1,10 @@ +type Todo = { + id: number; + title: string; + completed: boolean; + userId: number; +}; + +export const getHigherId = (todos: Todo[]) => { + return Math.max(...todos.map(todo => todo.id)) + 1; +}; diff --git a/src/serveses/user.ts b/src/serveses/user.ts new file mode 100644 index 0000000000..a6e959e750 --- /dev/null +++ b/src/serveses/user.ts @@ -0,0 +1,10 @@ +type User = { + id: number; + name: string; + username: string; + email: string; +}; + +export const getUserById = (users: User[], id: number) => { + return users.find(user => user.id === id); +}; From 4053a79963c85b2789e93c94de48ae36a1aa5dff Mon Sep 17 00:00:00 2001 From: Alex Shmyhelskyi Date: Thu, 9 Jan 2025 11:17:32 +0200 Subject: [PATCH 2/5] Message --- src/App.tsx | 4 ++-- src/components/TodoInfo/TodoInfo.tsx | 20 ++++++++--------- src/components/TodoList/TodoList.tsx | 33 ++++++++++++++++++---------- 3 files changed, 34 insertions(+), 23 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 1cb9bbf8d6..5e02b1d8d5 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -36,11 +36,11 @@ export const App = () => { event.preventDefault(); if (!title) { - setHasTitleError('Write a title'); + setHasTitleError('Please enter a title'); } if (!selectedUserId) { - setHasUserError('Choose some User'); + setHasUserError('Please choose a user'); } if (!title || !selectedUserId) { diff --git a/src/components/TodoInfo/TodoInfo.tsx b/src/components/TodoInfo/TodoInfo.tsx index 7728494fe8..0fa8b15421 100644 --- a/src/components/TodoInfo/TodoInfo.tsx +++ b/src/components/TodoInfo/TodoInfo.tsx @@ -2,13 +2,6 @@ import classNames from 'classnames'; import React from 'react'; import { UserInfo } from '../UserInfo'; -type Todo = { - id: number; - title: string; - completed: boolean; - userId: number; -}; - type User = { id: number; name: string; @@ -16,12 +9,19 @@ type User = { email: string; }; +type Todo = { + id: number; + title: string; + completed: boolean; + userId: number; + user: User; +}; + type Props = { todo: Todo; - user: User; }; -export const TodoInfo: React.FC = ({ todo, user }) => { +export const TodoInfo: React.FC = ({ todo }) => { return (
= ({ todo, user }) => { })} >

{todo.title}

- +
); }; diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx index e8f88d857a..1e3ba103d7 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -2,13 +2,6 @@ import React from 'react'; import { TodoInfo } from '../TodoInfo'; import { getUserById } from '../../serveses/user'; -type Todo = { - id: number; - title: string; - completed: boolean; - userId: number; -}; - type User = { id: number; name: string; @@ -16,17 +9,35 @@ type User = { email: string; }; +type Todo = { + id: number; + title: string; + completed: boolean; + userId: number; +}; + type Props = { todos: Todo[]; users: User[]; }; export const TodoList: React.FC = ({ todos, users }) => { + const completedTodos = todos.map(todo => { + const user = getUserById(users, todo.userId); + + if (user) { + return { + ...todo, + user, + }; + } + + return todo; + }); + return (
- {todos.map(todo => { - const user = getUserById(users, todo.userId); - - return user && ; + {completedTodos.map(todo => { + return ; })}
); From d35b3f5bab7fdcb3749db2b61e4e05dba0df5ae3 Mon Sep 17 00:00:00 2001 From: Alex Shmyhelskyi Date: Thu, 9 Jan 2025 12:24:08 +0200 Subject: [PATCH 3/5] Message --- src/App.tsx | 28 ++++++++++++++++------- src/components/TodoInfo/TodoInfo.tsx | 16 +------------ src/components/TodoList/TodoList.tsx | 34 +++------------------------- src/serveses/types.ts | 14 ++++++++++++ 4 files changed, 38 insertions(+), 54 deletions(-) create mode 100644 src/serveses/types.ts diff --git a/src/App.tsx b/src/App.tsx index 5e02b1d8d5..033bdd7b28 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -5,13 +5,22 @@ import usersFromServer from './api/users'; import todosFromServer from './api/todos'; import { TodoList } from './components/TodoList'; import { getHigherId } from './serveses/todos'; +import { getUserById } from './serveses/user'; -type User = { - id: number; - name: string; - username: string; - email: string; -}; +import { User } from './serveses/types'; + +const completedTodos = todosFromServer.map(todo => { + const user = getUserById(usersFromServer, todo.userId); + + if (user) { + return { + ...todo, + user, + }; + } + + return todo; +}); export const App = () => { const [title, setTitle] = useState(''); @@ -20,7 +29,7 @@ export const App = () => { const [selectedUserId, setSelectedUserId] = useState(0); const [hasUserError, setHasUserError] = useState(''); - const [todos, setTodos] = useState(todosFromServer); + const [todos, setTodos] = useState(completedTodos); const handleTitle = (event: React.ChangeEvent) => { setTitle(event.target.value); @@ -48,11 +57,14 @@ export const App = () => { } setTodos(currentTodos => { + const user = getUserById(usersFromServer, selectedUserId); + const todo = { id: getHigherId(todos), title, completed: false, userId: +selectedUserId, + user, }; return [...currentTodos, todo]; @@ -108,7 +120,7 @@ export const App = () => { - + ); }; diff --git a/src/components/TodoInfo/TodoInfo.tsx b/src/components/TodoInfo/TodoInfo.tsx index 0fa8b15421..a764fca3fc 100644 --- a/src/components/TodoInfo/TodoInfo.tsx +++ b/src/components/TodoInfo/TodoInfo.tsx @@ -1,21 +1,7 @@ import classNames from 'classnames'; import React from 'react'; import { UserInfo } from '../UserInfo'; - -type User = { - id: number; - name: string; - username: string; - email: string; -}; - -type Todo = { - id: number; - title: string; - completed: boolean; - userId: number; - user: User; -}; +import { Todo } from '../../serveses/types'; type Props = { todo: Todo; diff --git a/src/components/TodoList/TodoList.tsx b/src/components/TodoList/TodoList.tsx index 1e3ba103d7..49bc4ef8cd 100644 --- a/src/components/TodoList/TodoList.tsx +++ b/src/components/TodoList/TodoList.tsx @@ -1,42 +1,14 @@ import React from 'react'; import { TodoInfo } from '../TodoInfo'; -import { getUserById } from '../../serveses/user'; - -type User = { - id: number; - name: string; - username: string; - email: string; -}; - -type Todo = { - id: number; - title: string; - completed: boolean; - userId: number; -}; +import { Todo } from '../../serveses/types'; type Props = { todos: Todo[]; - users: User[]; }; -export const TodoList: React.FC = ({ todos, users }) => { - const completedTodos = todos.map(todo => { - const user = getUserById(users, todo.userId); - - if (user) { - return { - ...todo, - user, - }; - } - - return todo; - }); - +export const TodoList: React.FC = ({ todos }) => { return (
- {completedTodos.map(todo => { + {todos.map(todo => { return ; })}
diff --git a/src/serveses/types.ts b/src/serveses/types.ts new file mode 100644 index 0000000000..e21644f85d --- /dev/null +++ b/src/serveses/types.ts @@ -0,0 +1,14 @@ +export type User = { + id: number; + name: string; + username: string; + email: string; +}; + +export type Todo = { + id: number; + title: string; + completed: boolean; + userId: number; + user: User; +}; From c7acea6a7c6d0f54230c5560e5afb47fa6df46b3 Mon Sep 17 00:00:00 2001 From: Alex Shmyhelskyi Date: Thu, 9 Jan 2025 12:44:43 +0200 Subject: [PATCH 4/5] add solution --- src/App.tsx | 12 ++++-------- src/components/UserInfo/UserInfo.tsx | 8 +------- src/serveses/types.ts | 2 +- src/serveses/user.ts | 2 +- 4 files changed, 7 insertions(+), 17 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 033bdd7b28..bf2f158269 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -12,14 +12,10 @@ import { User } from './serveses/types'; const completedTodos = todosFromServer.map(todo => { const user = getUserById(usersFromServer, todo.userId); - if (user) { - return { - ...todo, - user, - }; - } - - return todo; + return { + ...todo, + user, + }; }); export const App = () => { diff --git a/src/components/UserInfo/UserInfo.tsx b/src/components/UserInfo/UserInfo.tsx index 58023f17c8..f1c82ba5f8 100644 --- a/src/components/UserInfo/UserInfo.tsx +++ b/src/components/UserInfo/UserInfo.tsx @@ -1,11 +1,5 @@ import React from 'react'; - -type User = { - id: number; - name: string; - username: string; - email: string; -}; +import { User } from '../../serveses/types'; type Props = { user: User; diff --git a/src/serveses/types.ts b/src/serveses/types.ts index e21644f85d..6b110ad41a 100644 --- a/src/serveses/types.ts +++ b/src/serveses/types.ts @@ -10,5 +10,5 @@ export type Todo = { title: string; completed: boolean; userId: number; - user: User; + user: User | null; }; diff --git a/src/serveses/user.ts b/src/serveses/user.ts index a6e959e750..21bc7d440f 100644 --- a/src/serveses/user.ts +++ b/src/serveses/user.ts @@ -6,5 +6,5 @@ type User = { }; export const getUserById = (users: User[], id: number) => { - return users.find(user => user.id === id); + return users.find(user => user.id === id) || null; }; From 4501eb4422cde9843ca8e1bad082f8866e122b36 Mon Sep 17 00:00:00 2001 From: Alex Shmyhelskyi Date: Fri, 10 Jan 2025 07:56:41 +0200 Subject: [PATCH 5/5] add solution --- src/App.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index bf2f158269..e8166cfaca 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -20,32 +20,32 @@ const completedTodos = todosFromServer.map(todo => { export const App = () => { const [title, setTitle] = useState(''); - const [hasTitleError, setHasTitleError] = useState(''); + const [hasTitleError, setHasTitleError] = useState(false); const [selectedUserId, setSelectedUserId] = useState(0); - const [hasUserError, setHasUserError] = useState(''); + const [hasUserError, setHasUserError] = useState(false); const [todos, setTodos] = useState(completedTodos); const handleTitle = (event: React.ChangeEvent) => { setTitle(event.target.value); - setHasTitleError(''); + setHasTitleError(false); }; - const handlUser = (event: React.ChangeEvent) => { + const handleUser = (event: React.ChangeEvent) => { setSelectedUserId(+event.target.value); - setHasUserError(''); + setHasUserError(false); }; const addTodo = (event: React.FormEvent) => { event.preventDefault(); if (!title) { - setHasTitleError('Please enter a title'); + setHasTitleError(true); } if (!selectedUserId) { - setHasUserError('Please choose a user'); + setHasUserError(true); } if (!title || !selectedUserId) { @@ -87,14 +87,14 @@ export const App = () => { /> - {hasTitleError && {hasTitleError}} + {hasTitleError && Please enter a title}
- {hasUserError && {hasUserError}} + {hasUserError && Please choose a user}