Skip to content

Commit

Permalink
add todo form solution
Browse files Browse the repository at this point in the history
  • Loading branch information
o-drozzdyk committed Oct 23, 2023
1 parent d3a183a commit 3d6b961
Show file tree
Hide file tree
Showing 13 changed files with 239 additions and 85 deletions.
32 changes: 0 additions & 32 deletions src/App.scss
Original file line number Diff line number Diff line change
Expand Up @@ -5,35 +5,3 @@ iframe {
html {
font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}

label,
button {
cursor: pointer;
}

.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;
}
73 changes: 23 additions & 50 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,34 @@
import './App.scss';

// import usersFromServer from './api/users';
// import todosFromServer from './api/todos';
import { useState } from 'react';
import { Form } from './components/Form';
import { TodoList } from './components/TodoList';
import { Todo } from './types/Todo';
import { getNewTodoId } from './services';

import todosFromServer from './api/todos';

export const App = () => {
const [todoList, setTodoList] = useState<Todo[]>(todosFromServer);

const addTodo = (todo: Todo) => {
const newTodo = {
...todo,
id: getNewTodoId(todoList),
};

setTodoList(currentList => [...currentList, newTodo]);
};

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

<form action="/api/todos" method="POST">
<div className="field">
<input type="text" data-cy="titleInput" />
<span className="error">Please enter a title</span>
</div>

<div className="field">
<select data-cy="userSelect">
<option value="0" disabled>Choose a user</option>
</select>

<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>
<Form
onAdd={addTodo}
/>

<TodoList todos={todoList} />
</div>
);
};
8 changes: 8 additions & 0 deletions src/components/Form/Form.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
label,
button {
cursor: pointer;
}

.error {
color: red;
}
98 changes: 98 additions & 0 deletions src/components/Form/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import React, { useState } from 'react';
import usersFromServer from '../../api/users';
import { Todo } from '../../types/Todo';

type Props = {
onAdd: (todo: Todo) => void;
};

export const Form: React.FC<Props> = ({ onAdd }) => {
const [count, setCount] = useState(0);
const [title, setTitle] = useState('');
const [hasTitleError, setHasTitleError] = useState(false);
const [selectedUser, setSelectedUser] = useState(0);
const [hasSelectError, setHasSelectError] = useState(false);

function handleTitleChange(event: React.ChangeEvent<HTMLInputElement>) {
setTitle(event.target.value);
setHasTitleError(false);
}

function handleUserChange(event: React.ChangeEvent<HTMLSelectElement>) {
setSelectedUser(+event.target.value);
setHasSelectError(false);
}

function handleSubmit(event: React.FormEvent) {
event.preventDefault();

setHasTitleError(!title);
setHasSelectError(!selectedUser);

if (title && selectedUser) {
onAdd({
id: 0,
title,
completed: false,
userId: selectedUser,
});
setCount(count + 1);
setTitle('');
setSelectedUser(0);
}
}

return (
<form key={count}>
<div className="field">
<label htmlFor="title">Title: </label>

<input
id="title"
type="text"
data-cy="titleInput"
placeholder="Enter a title"
value={title}
onChange={handleTitleChange}
/>

{hasTitleError && (
<span className="error">Please enter a title</span>
)}
</div>

<div className="field">
<label htmlFor="user">User: </label>

<select
id="user"
data-cy="userSelect"
value={selectedUser}
onChange={handleUserChange}
>
<option value="0" disabled>Choose a user</option>
{usersFromServer.map(user => (
<option
key={user.id}
value={user.id}
>
{user.name}
</option>
))}
</select>

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

<button
type="submit"
data-cy="submitButton"
onClick={handleSubmit}
>
Add
</button>
</form>
);
};
1 change: 1 addition & 0 deletions src/components/Form/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './Form';
18 changes: 18 additions & 0 deletions src/components/TodoInfo/TodoInfo.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
.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;
}
}
33 changes: 32 additions & 1 deletion src/components/TodoInfo/TodoInfo.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,32 @@
export const TodoInfo = () => {};
import './TodoInfo.scss';

import React from 'react';
import { UserInfo } from '../UserInfo';
import { Todo } from '../../types/Todo';
import { getUserById } from '../../services';

type Props = {
todo: Todo;
};

export const TodoInfo:React.FC<Props> = ({ todo }) => {
const {
title,
completed,
userId,
} = todo;

const user = getUserById(userId);

return (
<article
className={`TodoInfo ${completed && 'TodoInfo--completed'}`}
data-id={todo.id}
>
<h2 className="TodoInfo__title">{title}</h2>

{user && <UserInfo user={user} />}

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

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

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

type Props = {
user: User;
};

export const UserInfo:React.FC<Props> = ({ user }) => {
const {
name,
email,
} = user;

return (
<a className="UserInfo" href={`mailto:${email}`}>
{name}
</a>
);
};
11 changes: 11 additions & 0 deletions src/services.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { User } from './types/User';
import { Todo } from './types/Todo';
import usersFromServer from './api/users';

export function getUserById(userId: number): User | null {
return usersFromServer.find(user => user.id === userId) || null;
}

export function getNewTodoId(todos: Todo[]): number {
return Math.max(...todos.map(todo => todo.id)) + 1;
}
6 changes: 6 additions & 0 deletions src/types/Todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export interface Todo {
id: number;
title: string;
completed: boolean;
userId: number;
}
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 3d6b961

Please sign in to comment.