Skip to content

Commit

Permalink
My solution (test failing)
Browse files Browse the repository at this point in the history
  • Loading branch information
AsyaYeromina committed Nov 2, 2024
1 parent faa7ff1 commit a389398
Show file tree
Hide file tree
Showing 5 changed files with 254 additions and 140 deletions.
80 changes: 75 additions & 5 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,71 @@
/* eslint-disable max-len */
import React from 'react';
import React, { useEffect, 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 { getTodos, getUser } from './api';
import { Todo } from './types/Todo';
import { FilterStatus } from './types/FilterStatus';
import { TodoModal } from './components/TodoModal';
import { User } from './types/User';
import { Loader } from './components/Loader';

export const App: React.FC = () => {
const [todos, setTodos] = useState<Todo[]>([]);
const [filteredTodos, setFilteredTodos] = useState<Todo[]>([]);
const [selectedTodo, setSelectedTodo] = useState<Todo | null>(null);
const [currentUser, setCurrentUser] = useState<User | null>(null);
const [todosLoading, setTodoLoading] = useState(true);

useEffect(() => {
getTodos()
.then(fetchedTodos => {
setTodos(fetchedTodos);
setFilteredTodos(fetchedTodos);
})
.finally(() => {
setTodoLoading(false);
});
}, []);

const filterList = (status: FilterStatus, query: string) => {
const preparedTodos = todos.filter((todo: Todo) => {
const matchesStatus =
status === 'all' ||
(status === 'active' && !todo.completed) ||
(status === 'completed' && todo.completed);

const matchesTitle = todo.title
.trim()
.toLowerCase()
.includes(query.trim().toLowerCase());

return matchesStatus && matchesTitle;
});

return preparedTodos;
};

const handleFilterChange = (status: FilterStatus, query: string) => {
const newFilteredTodos = filterList(status, query);

setFilteredTodos(newFilteredTodos);
};

const handleTodoSelection = (todo: Todo) => {
setSelectedTodo(todo);
getUser(todo.userId).then(user => {
setCurrentUser(user);
});
};

const handleTodoClear = () => {
setSelectedTodo(null);
setCurrentUser(null);
};

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

<div className="block">
<TodoFilter />
<TodoFilter onFilterChange={handleFilterChange} />
</div>

<div className="block">
<Loader />
<TodoList />
{todosLoading ? (
<Loader />
) : (
<TodoList
todoList={filteredTodos}
selectedTodo={selectedTodo}
onTodoSelect={handleTodoSelection}
/>
)}
</div>
</div>
</div>
</div>

<TodoModal />
{selectedTodo && (
<TodoModal
onTodoClear={handleTodoClear}
todo={selectedTodo}
user={currentUser}
/>
)}
</>
);
};
109 changes: 79 additions & 30 deletions src/components/TodoFilter/TodoFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,79 @@
export const TodoFilter = () => (
<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>
</span>
</p>

<p className="control is-expanded has-icons-left has-icons-right">
<input
data-cy="searchInput"
type="text"
className="input"
placeholder="Search..."
/>
<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" />
</span>
</p>
</form>
);
import { useState } from 'react';
import { FilterStatus } from '../../types/FilterStatus';

interface TodoFilterProps {
onFilterChange: (status: FilterStatus, query: string) => void;
}

export const TodoFilter: React.FC<TodoFilterProps> = ({ onFilterChange }) => {
const [selectedStatus, setSelectedStatus] = useState<FilterStatus>('all');
const [searchQuery, setSearchQuery] = useState('');

const handleStatusChange = (event: React.ChangeEvent<HTMLSelectElement>) => {
const newStatus = event.target.value as FilterStatus;

if (newStatus !== selectedStatus) {
setSelectedStatus(newStatus);
onFilterChange(newStatus, searchQuery);
}
};

const handleQueryChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const newQuery = event.target.value;

if (newQuery !== searchQuery) {
setSearchQuery(newQuery);
onFilterChange(selectedStatus, newQuery);
}
};

const handleClearQuery = () => {
setSearchQuery('');
onFilterChange(selectedStatus, '');
};

return (
<form className="field has-addons">
<p className="control">
<span className="select">
<select
data-cy="statusSelect"
onChange={handleStatusChange}
value={selectedStatus}
>
<option value="all">All</option>
<option value="active">Active</option>
<option value="completed">Completed</option>
</select>
</span>
</p>

<p className="control is-expanded has-icons-left has-icons-right">
<input
data-cy="searchInput"
type="text"
className="input"
placeholder="Search..."
onChange={handleQueryChange}
value={searchQuery}
/>
<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 */}

{searchQuery && (
<button
data-cy="clearSearchButton"
type="button"
className="delete"
onClick={handleClearQuery}
/>
)}
</span>
</p>
</form>
);
};
171 changes: 75 additions & 96 deletions src/components/TodoList/TodoList.tsx
Original file line number Diff line number Diff line change
@@ -1,100 +1,79 @@
import React from 'react';
import { Todo } from '../../types/Todo';
import classNames from 'classnames';

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>
interface Props {
todoList: Todo[];
selectedTodo: Todo | null;
onTodoSelect: (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">
<span className="icon">
<i className="far fa-eye" />
export const TodoList: React.FC<Props> = ({
todoList,
selectedTodo,
onTodoSelect,
}) => {
return (
<table className="table is-narrow is-fullwidth">
<thead>
<tr>
<th>#</th>
<th>
<span className="icon" data-cy="iconCompleted">
<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>
{todoList.map((todo: Todo) => (
<tr
key={todo.id}
data-cy="todo"
className={classNames({
'has-background-info-light':
selectedTodo && todo.id === selectedTodo.id,
})}
>
<td className="is-vcentered">{todo.id}</td>
<td className="is-vcentered">
{todo.completed && (
<span className="icon" data-cy="iconCompleted">
<i className="fas fa-check" />
</span>
)}
</td>
<td className="is-vcentered is-expanded">
<p
className={classNames({
'has-text-danger': !todo.completed,
'has-text-success': todo.completed,
})}
>
{todo.title}
</p>
</td>
<td className="has-text-right is-vcentered">
<button
data-cy="selectButton"
className="button"
type="button"
onClick={() => {
onTodoSelect(todo);
}}
>
<span className="icon">
<i
className={`far ${selectedTodo?.id === todo.id ? 'fa-eye-slash' : 'fa-eye'}`}
/>
</span>
</button>
</td>
</tr>
))}
</tbody>
</table>
);
};
Loading

0 comments on commit a389398

Please sign in to comment.