Skip to content

Commit

Permalink
add task solution
Browse files Browse the repository at this point in the history
  • Loading branch information
Sonya-Miss committed Dec 25, 2024
1 parent 8d91581 commit b305474
Show file tree
Hide file tree
Showing 4 changed files with 196 additions and 40 deletions.
167 changes: 130 additions & 37 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,154 @@
import React, { useState } from 'react';
import './App.scss';
import usersFromServer from './api/users';
import todosFromServer from './api/todos';
import { TodoList } from './components/TodoList/TodoList';

// import usersFromServer from './api/users';
// import todosFromServer from './api/todos';
type User = {
id: number;
name: string;
email: string;
};

type Todo = {
id: number;
title: string;
user: User;
completed: boolean;
};

export const App: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>(
todosFromServer.map(todo => {
const user = usersFromServer.find(u => u.id === todo.userId) || {
id: 0,
name: 'Unknown',
email: '',
};

return {
id: todo.id,
title: todo.title,
user,
completed: todo.completed,
};
}),
);

const [errors, setErrors] = useState<{
title: string | null;
user: string | null;
}>({
title: null,
user: null,
});

const addTodo = (todo: Omit<Todo, 'id'>) => {
const maxId = todos.length > 0 ? Math.max(...todos.map(t => t.id)) : 0;

const newTodo: Todo = {
id: maxId + 1,
...todo,
};

setTodos(prevTodos => [...prevTodos, newTodo]);
};

export const App = () => {
return (
<div className="App">
<h1>Add todo form</h1>

<form action="/api/todos" method="POST">
<form
onSubmit={e => {
e.preventDefault();
const form = e.currentTarget;

const titleElement = form.elements.namedItem('title');
const userElement = form.elements.namedItem('user');

if (
!(titleElement instanceof HTMLInputElement) ||
!(userElement instanceof HTMLSelectElement)
) {
return;
}

const title = titleElement.value.trim();
const userId = parseInt(userElement.value, 10);
const user = usersFromServer.find(u => u.id === userId);

const newErrors = {
title: title ? null : 'Please enter a title',
user: user ? null : 'Please choose a user',
};

setErrors(newErrors);

if (newErrors.title || newErrors.user) {
return;
}

addTodo({
title,
user: user as User,
completed: false,
});

setErrors({ title: null, user: null });
form.reset();
}}
>
<div className="field">
<input type="text" data-cy="titleInput" />
<span className="error">Please enter a title</span>
Title:
<input
type="text"
name="title"
data-cy="titleInput"
placeholder="Enter a title"
onChange={() => {
setErrors(prev => ({ ...prev, title: null }));
}}
/>
</div>

<div className="field">
<select data-cy="userSelect">
<option value="0" disabled>
User:
<select
name="user"
data-cy="userSelect"
defaultValue=""
onChange={() => {
setErrors(prev => ({ ...prev, user: null }));
}}
>
<option value="" 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>
</div>

{errors.title && (
<p className="error" data-cy="errorMessage">
{errors.title}
</p>
)}
{errors.user && (
<p className="error" data-cy="errorMessage">
{errors.user}
</p>
)}

<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>
);
};
26 changes: 25 additions & 1 deletion src/components/TodoInfo/TodoInfo.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,25 @@
export const TodoInfo = () => {};
import React from 'react';
import { UserInfo } from '../UserInfo/UserInfo';

type Todo = {
id: number;
title: string;
user: { name: string; email: string };
completed: boolean;
};

type Props = {
todo: Todo;
};

export const TodoInfo: React.FC<Props> = ({ todo }) => {
return (
<article
className={`TodoInfo ${todo.completed ? 'TodoInfo--completed' : ''}`}
data-id={todo.id}
>
<h2 className="TodoInfo__title">{todo.title}</h2>
<UserInfo user={todo.user} />
</article>
);
};
29 changes: 28 additions & 1 deletion src/components/TodoList/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,28 @@
export const TodoList = () => {};
import React from 'react';
import { TodoInfo } from '../TodoInfo/TodoInfo';

export type User = {
name: string;
email: string;
};

export type Todo = {
id: number;
title: string;
user: User;
completed: boolean;
};

type Props = {
todos: Todo[];
};

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

type Props = {
user: { name: string; email: string };
};

export const UserInfo: React.FC<Props> = ({ user }) => {
return (
<a className="UserInfo" href={`mailto:${user.email}`}>
{user.name}
</a>
);
};

0 comments on commit b305474

Please sign in to comment.