Skip to content

Commit

Permalink
add solution
Browse files Browse the repository at this point in the history
  • Loading branch information
purpleFade committed Nov 2, 2023
1 parent d3a183a commit 25ae5ca
Show file tree
Hide file tree
Showing 9 changed files with 263 additions and 42 deletions.
139 changes: 101 additions & 38 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,61 +1,124 @@
import React, { useContext } from 'react';
import { TodoList } from './components/TodoList';

import './App.scss';

// import usersFromServer from './api/users';
// import todosFromServer from './api/todos';
import { appContext } from './context/AppContext';
import { getUserFrom } from './utils/getUser';

const allowedChars = /[а-яА-ЯІіЇїЄєҐґ'a-zA-Z0-9\s]+/;

export const App = () => {
const {
query,
setQuery,
userId,
setUserId,
visibleTodos,
setVisibleTodos,
users,
userError,
setUserError,
inputError,
setInputError,
} = useContext(appContext);

const handleInputOnChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;

if (!allowedChars.test(value) && value !== '') {
return;
}

setQuery(value);
setInputError('');
};

const handleUserOnChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
const { value } = e.target;

setUserId(+value);
setUserError('');
};

const handleSumbit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();

if (!query.trim()) {
setInputError('Please enter a title');
}

if (!userId) {
setUserError('Please choose a user');
}

if (!userId || !query.trim()) {
return;
}

const ids = visibleTodos.map((todo) => todo.id);

const newTodo = {
id: Math.max(...ids) + 1,
title: query,
userId: +userId,
completed: false,
user: getUserFrom(+userId, users),
};

setVisibleTodos([...visibleTodos, newTodo]);

setQuery('');
setUserId(0);
};

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

<form action="/api/todos" method="POST">
<form action="/api/todos" method="POST" onSubmit={handleSumbit}>
<div className="field">
<input type="text" data-cy="titleInput" />
<span className="error">Please enter a title</span>
<label>
{'Title: '}
<input
type="text"
data-cy="titleInput"
value={query}
onChange={handleInputOnChange}
placeholder="Enter a title"
/>
</label>
<span className="error">{inputError}</span>
</div>

<div className="field">
<select data-cy="userSelect">
<option value="0" disabled>Choose a user</option>
</select>
<label>
{'User: '}
<select
data-cy="userSelect"
onChange={handleUserOnChange}
value={userId}
>
<option value="0" disabled>
Choose a user
</option>
{users.map((user) => (
<option value={user.id} key={user.id}>
{user.name}
</option>
))}
</select>
</label>

<span className="error">Please choose a user</span>
<span className="error">{userError}</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={visibleTodos} />
</div>
);
};
25 changes: 24 additions & 1 deletion src/components/TodoInfo/TodoInfo.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,24 @@
export const TodoInfo = () => {};
import React from 'react';
import classNames from 'classnames';
import { Todo } from '../../types/Todo';
import { UserInfo } from '../UserInfo';
import { User } from '../../types/User';

type 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>

<UserInfo user={todo.user || ({} as 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';

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

export const TodoList: React.FC<Props> = ({ todos }) => {
return (
<section className="TodoList">
{todos.map((todo) => (
<TodoInfo todo={todo} key={todo.id} />
))}
</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';

type Props = {
user: User;
};

export const UserInfo: React.FC<Props> = ({ user }) => {
return (
<a className="UserInfo" href={`mailto:${user.email}`}>
{user.name}
</a>
);
};
80 changes: 80 additions & 0 deletions src/context/AppContext.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, {
ReactNode, createContext, useEffect, useState,
} from 'react';

import usersFromServer from '../api/users';
import todosFromServer from '../api/todos';
import { Todo } from '../types/Todo';
import { User } from '../types/User';

type Props = {
children: ReactNode;
};

type AppContextType = {
query: string;
setQuery: React.Dispatch<React.SetStateAction<string>>;
userId: number;
setUserId: React.Dispatch<React.SetStateAction<number>>;
visibleTodos: Todo[];
setVisibleTodos: React.Dispatch<React.SetStateAction<Todo[]>>;
inputError: string;
setInputError: React.Dispatch<React.SetStateAction<string>>;
userError: string;
setUserError: React.Dispatch<React.SetStateAction<string>>;
users: User[];
};

const defaultContextValues: AppContextType = {
query: '',
setQuery: () => null,
userId: 0,
setUserId: () => null,
visibleTodos: [],
setVisibleTodos: () => null,
inputError: '',
setInputError: () => null,
userError: '',
setUserError: () => null,
users: [],
};

export const appContext = createContext<AppContextType>(defaultContextValues);

export const AppContextProvider: React.FC<Props> = ({ children }) => {
const [query, setQuery] = useState('');
const [userId, setUserId] = useState(0);
const [visibleTodos, setVisibleTodos] = useState<Todo[]>(todosFromServer);
const [users] = useState(usersFromServer);
const [inputError, setInputError] = useState('');
const [userError, setUserError] = useState('');

useEffect(() => {
const todosWithUsers = visibleTodos.map((todo) => {
const todoWithUser: Todo = {
...todo,
user: users.find((user) => user.id === todo.userId),
};

return todoWithUser;
});

setVisibleTodos(todosWithUsers);
}, []);

const state = {
query,
setQuery,
userId,
setUserId,
visibleTodos,
setVisibleTodos,
inputError,
setInputError,
users,
userError,
setUserError,
};

return <appContext.Provider value={state}>{children}</appContext.Provider>;
};
8 changes: 7 additions & 1 deletion src/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import ReactDOM from 'react-dom';
import { App } from './App';
import { AppContextProvider } from './context/AppContext';

ReactDOM.render(<App />, document.getElementById('root'));
ReactDOM.render(
<AppContextProvider>
<App />
</AppContextProvider>,
document.getElementById('root'),
);
9 changes: 9 additions & 0 deletions src/types/Todo.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { User } from './User';

export type Todo = {
id: number;
title: string;
completed: boolean;
userId: number;
user?: User;
};
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 type User = {
id: number;
name: string;
email: string;
username: string;
};
5 changes: 5 additions & 0 deletions src/utils/getUser.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { User } from '../types/User';

export const getUserFrom = (userId: number, users: User[]) => {
return users.find((user) => user.id === userId);
};

0 comments on commit 25ae5ca

Please sign in to comment.