Skip to content

Commit

Permalink
solution
Browse files Browse the repository at this point in the history
  • Loading branch information
AlenaLoik committed Feb 4, 2025
1 parent 8bf0f2b commit 165ed3b
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 114 deletions.
72 changes: 67 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,63 @@
/* eslint-disable max-len */
import React from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import 'bulma/css/bulma.css';
import '@fortawesome/fontawesome-free/css/all.css';

import { TodoList } from './components/TodoList';
import { TodoFilter } from './components/TodoFilter';
import { TodoModal } from './components/TodoModal';
import { Loader } from './components/Loader';
import { Todo } from './types/Todo';
import { getTodos } from './api';
import { Filter } from './types/Filter';

export const App: React.FC = () => {
const [todos, setTodos] = useState([] as Todo[]);
const [loading, setLoading] = useState(false);
const [selectedTodo, setSelectedTodo] = useState(null as Todo | null);
const [query, setQuery] = useState('');
const [filterParam, setFilterParam] = useState(Filter.all);

const handleOpenModal = (item: Todo) => {
setSelectedTodo(item);
};

const handleCloseModal = () => setSelectedTodo(null);

const filteredTodos = useMemo(() => {
switch (filterParam) {
case Filter.all:
return todos.filter(el =>
el.title.toLowerCase().includes(query.toLowerCase()),
);
case Filter.completed:
return todos.filter(
el =>
el.title.toLowerCase().includes(query.toLowerCase()) &&
el.completed,
);
case Filter.active:
return todos.filter(
el =>
el.title.toLowerCase().includes(query.toLowerCase()) &&
!el.completed,
);
default:
return todos.filter(el =>
el.title.toLowerCase().includes(query.toLowerCase()),
);
}
}, [filterParam, query, todos]);

useEffect(() => {
setLoading(true);
getTodos()
.then(todosFromServer => {
setTodos(todosFromServer);
})
.finally(() => setLoading(false));
}, []);

return (
<>
<div className="section">
Expand All @@ -17,18 +66,31 @@ export const App: React.FC = () => {
<h1 className="title">Todos:</h1>

<div className="block">
<TodoFilter />
<TodoFilter
query={query}
searchTodo={setQuery}
handleFiltered={setFilterParam}
/>
</div>

<div className="block">
<Loader />
<TodoList />
{loading ? (
<Loader />
) : (
<TodoList
todos={filteredTodos}
todo={selectedTodo}
handleSelect={handleOpenModal}
/>
)}
</div>
</div>
</div>
</div>

<TodoModal />
{selectedTodo && (
<TodoModal todo={selectedTodo} closeModal={handleCloseModal} />
)}
</>
);
};
42 changes: 36 additions & 6 deletions src/components/TodoFilter/TodoFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
export const TodoFilter = () => (
import { Filter } from '../../types/Filter';

type Props = {
query: string;
searchTodo: (query: string) => void;
handleFiltered: (filterType: Filter) => void;
};

export const TodoFilter: React.FC<Props> = ({
query,
searchTodo,
handleFiltered,
}) => (
<form className="field has-addons">
<p className="control">
<span className="select">
<select data-cy="statusSelect">
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
<select
data-cy="statusSelect"
onChange={(event: React.FormEvent<HTMLSelectElement>) =>
handleFiltered(event.currentTarget.value as Filter)
}
>
<option value={Filter.all}>All</option>
<option value={Filter.active}>Active</option>
<option value={Filter.completed}>Completed</option>
</select>
</span>
</p>
Expand All @@ -15,15 +32,28 @@ export const TodoFilter = () => (
data-cy="searchInput"
type="text"
className="input"
value={query}
placeholder="Search..."
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
searchTodo(event.target.value);
}}
/>
<span className="icon is-left">
<i className="fas fa-magnifying-glass" />
</span>

<span className="icon is-right" style={{ pointerEvents: 'all' }}>
{/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}
<button data-cy="clearSearchButton" type="button" className="delete" />
{query.length && (
<button
data-cy="clearSearchButton"
type="button"
className="delete"
onClick={() => {
searchTodo('');
}}
/>
)}
</span>
</p>
</form>
Expand Down
163 changes: 69 additions & 94 deletions src/components/TodoList/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,75 @@
import React from 'react';
import { Todo } from '../../types/Todo';

export const TodoList: React.FC = () => (
<table className="table is-narrow is-fullwidth">
<thead>
<tr>
<th>#</th>
<th>
<span className="icon">
<i className="fas fa-check" />
</span>
</th>
<th>Title</th>
<th> </th>
</tr>
</thead>
type Props = {
todos: Todo[];
todo: Todo | null;
handleSelect: (todo: Todo) => void;
};

<tbody>
<tr data-cy="todo" className="">
<td className="is-vcentered">1</td>
<td className="is-vcentered" />
<td className="is-vcentered is-expanded">
<p className="has-text-danger">delectus aut autem</p>
</td>
<td className="has-text-right is-vcentered">
<button data-cy="selectButton" className="button" type="button">
export const TodoList: React.FC<Props> = ({ todos, todo, handleSelect }) => {
return (
<table className="table is-narrow is-fullwidth">
<thead>
<tr>
<th>#</th>
<th>
<span className="icon">
<i className="far fa-eye" />
<i className="fas fa-check" />
</span>
</button>
</td>
</tr>
<tr data-cy="todo" className="has-background-info-light">
<td className="is-vcentered">2</td>
<td className="is-vcentered" />
<td className="is-vcentered is-expanded">
<p className="has-text-danger">quis ut nam facilis et officia qui</p>
</td>
<td className="has-text-right is-vcentered">
<button data-cy="selectButton" className="button" type="button">
<span className="icon">
<i className="far fa-eye-slash" />
</span>
</button>
</td>
</tr>

<tr data-cy="todo" className="">
<td className="is-vcentered">1</td>
<td className="is-vcentered" />
<td className="is-vcentered is-expanded">
<p className="has-text-danger">delectus aut autem</p>
</td>
<td className="has-text-right is-vcentered">
<button data-cy="selectButton" className="button" type="button">
<span className="icon">
<i className="far fa-eye" />
</span>
</button>
</td>
</tr>
</th>
<th>Title</th>
<th> </th>
</tr>
</thead>

<tr data-cy="todo" className="">
<td className="is-vcentered">6</td>
<td className="is-vcentered" />
<td className="is-vcentered is-expanded">
<p className="has-text-danger">
qui ullam ratione quibusdam voluptatem quia omnis
</p>
</td>
<td className="has-text-right is-vcentered">
<button data-cy="selectButton" className="button" type="button">
<span className="icon">
<i className="far fa-eye" />
</span>
</button>
</td>
</tr>

<tr data-cy="todo" className="">
<td className="is-vcentered">8</td>
<td className="is-vcentered">
<span className="icon" data-cy="iconCompleted">
<i className="fas fa-check" />
</span>
</td>
<td className="is-vcentered is-expanded">
<p className="has-text-success">quo adipisci enim quam ut ab</p>
</td>
<td className="has-text-right is-vcentered">
<button data-cy="selectButton" className="button" type="button">
<span className="icon">
<i className="far fa-eye" />
</span>
</button>
</td>
</tr>
</tbody>
</table>
);
<tbody>
{todos.map(el => {
return (
<tr
key={el.id}
data-cy="todo"
className={el.id === todo?.id ? 'has-background-info-light' : ''}
>
<td className="is-vcentered">{el.id}</td>
{el.completed ? (
<td className="is-vcentered">
<span className="icon" data-cy="iconCompleted">
<i className="fas fa-check" />
</span>
</td>
) : (
<td className="is-vcentered" />
)}
<td className="is-vcentered is-expanded">
<p
className={
el.completed ? ' has-text-success' : 'has-text-danger'
}
>
{el.title}
</p>
</td>
<td className="has-text-right is-vcentered">
<button
data-cy="selectButton"
className="button"
type="button"
onClick={() => handleSelect(el)}
>
<span className="icon">
{el.id === todo?.id ? (
<i className="far fa-eye-slash" />
) : (
<i className="far fa-eye" />
)}
</span>
</button>
</td>
</tr>
);
})}
</tbody>
</table>
);
};
Loading

0 comments on commit 165ed3b

Please sign in to comment.