Skip to content

Commit

Permalink
add solution
Browse files Browse the repository at this point in the history
  • Loading branch information
Yurii Mosin committed Jan 8, 2025
1 parent 8d91581 commit f0f3674
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 37 deletions.
133 changes: 99 additions & 34 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,126 @@
import { useState } from 'react';
import './App.scss';
import usersFromServer from './api/users';
import todosFromServer from './api/todos';
import { TodoList } from './components/TodoList';
import { Todo } from './types/Todo';

// import usersFromServer from './api/users';
// import todosFromServer from './api/todos';
const findUserById = (userId: number) => {
return usersFromServer.find(user => user.id === userId);
};

const getPreparedTodos = () => {
return todosFromServer.map(todo => {
return {
...todo,
user: findUserById(todo.userId) || null,
};
});
};

export const App = () => {
const [todos, setTodos] = useState<Todo[]>(getPreparedTodos());
const [selectedUserId, setSelectedUserId] = useState(0);
const [title, setTitle] = useState('');
const [titleInputError, setTitleInputError] = useState(false);
const [userSelectError, setUserSelectError] = useState(false);

const reset = () => {
setTitle('');
setSelectedUserId(0);
setTitleInputError(false);
setUserSelectError(false);
};

function handleSubmit(event: React.FormEvent<HTMLFormElement>): void {
event.preventDefault();

setTitleInputError(!title);
setUserSelectError(!selectedUserId);

if (!title || !selectedUserId) {
return;
}

setTodos(currentTodos => {
const newId = Math.max(...currentTodos.map(todo => todo.id)) + 1;

const user = findUserById(selectedUserId);

const newTodo: Todo = {
id: newId,
title: title,
userId: selectedUserId,
completed: false,
user: user || null,
};

return [...currentTodos, newTodo];
});

reset();
}

function handleTitleInput(event: React.ChangeEvent<HTMLInputElement>): void {
setTitle(event.target.value.trimStart());
setTitleInputError(false);
}

function handleUserSelect(event: React.ChangeEvent<HTMLSelectElement>): void {
setSelectedUserId(Number(event.target.value));
setUserSelectError(false);
}

return (
<div className="App">
<h1>Add todo form</h1>

<form action="/api/todos" method="POST">
<form action="/api/todos" method="POST" onSubmit={handleSubmit}>
<div className="field">
<input type="text" data-cy="titleInput" />
<span className="error">Please enter a title</span>
<label htmlFor="titleInput">Title: </label>
<input
type="text"
id="titleInput"
data-cy="titleInput"
value={title}
onChange={handleTitleInput}
placeholder="title"
/>
{titleInputError && (
<span className="error">Please enter a title</span>
)}
</div>

<div className="field">
<select data-cy="userSelect">
<label htmlFor="userSelect">User: </label>
<select
id="userSelect"
data-cy="userSelect"
onChange={handleUserSelect}
value={selectedUserId}
>
<option value="0" disabled>
Choose a user
</option>

{usersFromServer.map(user => (
<option key={user.id} value={user.id}>
{user.name}
</option>
))}
</select>

<span className="error">Please choose a user</span>
{userSelectError && (
<span className="error">Please choose a user</span>
)}
</div>

<button type="submit" data-cy="submitButton">
Add
</button>
</form>

<section className="TodoList">
<article data-id="1" className="TodoInfo TodoInfo--completed">
<h2 className="TodoInfo__title">delectus aut autem</h2>

<a className="UserInfo" href="mailto:Sincere@april.biz">
Leanne Graham
</a>
</article>

<article data-id="15" className="TodoInfo TodoInfo--completed">
<h2 className="TodoInfo__title">delectus aut autem</h2>

<a className="UserInfo" href="mailto:Sincere@april.biz">
Leanne Graham
</a>
</article>

<article data-id="2" className="TodoInfo">
<h2 className="TodoInfo__title">
quis ut nam facilis et officia qui
</h2>

<a className="UserInfo" href="mailto:Julianne.OConner@kory.org">
Patricia Lebsack
</a>
</article>
</section>
<TodoList todos={todos} />
</div>
);
};
24 changes: 23 additions & 1 deletion src/components/TodoInfo/TodoInfo.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,23 @@
export const TodoInfo = () => {};
import React from 'react';
import { Todo } from '../../types/Todo';
import { UserInfo } from '../UserInfo';
import classNames from 'classnames';

interface Props {
todo: Todo;
}

export const TodoInfo: React.FC<Props> = ({ todo }) => {
return (
<article
data-id={todo.id}
className={classNames('TodoInfo', {
'TodoInfo--completed': todo.completed,
})}
>
<h2 className="TodoInfo__title">{todo.title}</h2>

{todo.user && <UserInfo user={todo.user} />}
</article>
);
};
18 changes: 17 additions & 1 deletion src/components/TodoList/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,17 @@
export const TodoList = () => {};
import React from 'react';
import { Todo } from '../../types/Todo';
import { TodoInfo } from '../TodoInfo';

interface Props {
todos: Todo[];
}

export const TodoList: React.FC<Props> = ({ todos }) => {
return (
<section className="TodoList">
{todos.map(todo => (
<TodoInfo key={todo.id} todo={todo} />
))}
</section>
);
};
15 changes: 14 additions & 1 deletion src/components/UserInfo/UserInfo.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,14 @@
export const UserInfo = () => {};
import React from 'react';
import { User } from '../../types/User';

interface Props {
user: User;
}

export const UserInfo: React.FC<Props> = ({ user }) => {
return (
<a className="UserInfo" href={`mailto:${user.email}`}>
{user.name}
</a>
);
};
8 changes: 8 additions & 0 deletions src/types/Todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { User } from './User';
export interface Todo {
id: number;
title: string;
completed: boolean;
userId: number;
user: User | null;
}
6 changes: 6 additions & 0 deletions src/types/User.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface User {
id: number;
name: string;
username: string;
email: string;
}

0 comments on commit f0f3674

Please sign in to comment.