Skip to content

Commit

Permalink
v1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
debrajhyper committed Feb 12, 2022
1 parent 43b74bf commit 2cf8f8c
Show file tree
Hide file tree
Showing 32 changed files with 819 additions and 79 deletions.
69 changes: 68 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.1",
"@testing-library/react": "^12.1.2",
"@testing-library/user-event": "^13.5.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
Expand Down Expand Up @@ -38,8 +37,11 @@
]
},
"devDependencies": {
"@testing-library/react": "^12.1.2",
"autoprefixer": "^10.4.1",
"jest-dom": "^4.0.0",
"postcss": "^8.4.5",
"react-test-renderer": "^17.0.2",
"tailwindcss": "^3.0.8"
}
}
124 changes: 70 additions & 54 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,20 @@ import React, { useEffect, useState, Fragment } from 'react'
import "./App.css"
import Api from "./Services/Api";
import ThemeToggle from "./Components/DarkMode/ThemeToggle";
import { DeleteSelectedBtn, SearchUser, EditableRow, DisplayRows, Pagination } from "./Components";
import icon from "./icon.png";
import { DeleteSelectedBtn, SearchUser, EditableRow, DisplayRows, Pagination, Modal, Skeleton } from "./Components";
import icon from "./Images/icon.png";

const App = () => {

// Use state to show initial loading skeleton state.
const [loading, setLoading] = useState(true);

// Use state to store the users data from the API call and set it to an empty array initially.
const [users, setUsers] = useState([]);

// Use state to set the error to be true if the API call fails and set it to false if the API call succeeds.
const [error, setError] = useState(false);

// Use state to store the edited user data and set it to an empty object initially.
const [editUserForm, setEditUserForm] = useState({
id: '',
Expand All @@ -34,8 +40,12 @@ const App = () => {
// Get the users from the API when the component mounts and set the users to the state using the setUsers() method.
useEffect(() => {
Api.getUsers()
.then(data => setUsers(data))
.catch(error => console.log(error));
.then(data => {
setError(false)
setUsers(data)
})
.catch(error => setError(true))
.finally(setLoading(false));
}, []); // [] is used to tell React that this effect should only run once.

// Checked all the users in the current page and set the users to the state using the setUsers() method.
Expand Down Expand Up @@ -83,41 +93,47 @@ const App = () => {
const currentItems = filteredUsers.slice(indexOfFirstItem, indexOfLastItem); // Get the users in the current page.

return (
<div className="min-h-screen min-w-screen bg-background dark:bg-background-dark dark:text-white transition-all ease-out text-gray text-center flex justify-center items-center overflow-hidden">
<div className=" h-full sm:h-auto w-full sm:w-auto mt-0 sm:mt-4 px-2 sm:px-8 text-sm md:text-md">

<div className="py-1 pt-0 w-full flex justify-between items-center">
<DeleteSelectedBtn users={users} setUsers={setUsers} />
<div className="flex justify-center items-center">
<SearchUser searchResult={searchResult} setSearchResult={setSearchResult} />
<ThemeToggle />
</div>
<div className="relative min-h-screen min-w-screen bg-background dark:bg-background-dark dark:text-white transition-all ease-out text-gray text-center flex justify-center items-center overflow-hidden">
<div className="h-full sm:h-auto w-full sm:w-auto mt-0 sm:mt-4 px-2 sm:px-8 text-sm md:text-md">

{
error && <Modal />
}

<div className="py-1 pt-0 w-full flex justify-between items-center">
<DeleteSelectedBtn users={users} setUsers={setUsers} />
<div className="flex justify-center items-center">
<SearchUser searchResult={searchResult} setSearchResult={setSearchResult} />
<ThemeToggle />
</div>

<div className="flex flex-col mt-1 min-h-[75vh] min-w-auto md:min-w-[75vw] xl:min-w-[55vw]">
<div className="py-2 pb-0 overflow-auto">
<form onSubmit={handleUserEditFormSubmit}>
<table className="w-full sm:min-w-full table-auto">
<thead className="relative">
<tr className="text-md font-medium text-gray-deep uppercase tracking-wider">
<th scope="col" className="px-6 py-2 w-auto text-center">
<input
type="checkbox"
value="checkedAll"
className="form-checkbox h-4 w-4"
onChange={handleAllChecked}
checked={ users.slice(indexOfFirstItem, indexOfLastItem).filter( user => user?.isChecked !== true ).length < 1 }
/>
</th>
<th scope="col" className="px-6 py-2 text-left">Name</th>
<th scope="col" className="px-6 py-2 text-left">Email</th>
<th scope="col" className="px-6 py-2 text-left">Role</th>
<th scope="col" className="px-6 py-2 text-center">Action</th>
</tr>
</thead>
<tbody className="relative text-md divide-y-4 divide-transparent">
{
users && users.length > 0 && filteredUsers.length > 0
</div>

<div className="flex flex-col mt-1 min-h-[75vh] min-w-auto md:min-w-[75vw] xl:min-w-[55vw]">
<div className="py-2 pb-0 overflow-auto">
<form onSubmit={handleUserEditFormSubmit}>
<table className="w-full sm:min-w-full table-auto">
<thead className="relative">
<tr className="text-md font-medium text-gray-deep uppercase tracking-wider">
<th scope="col" className="px-6 py-2 w-auto text-center">
<input
type="checkbox"
value="checkedAll"
className="form-checkbox h-4 w-4"
onChange={handleAllChecked}
checked={ users.slice(indexOfFirstItem, indexOfLastItem).filter( user => user?.isChecked !== true ).length < 1 }
/>
</th>
<th scope="col" className="px-6 py-2 text-left">Name</th>
<th scope="col" className="px-6 py-2 text-left">Email</th>
<th scope="col" className="px-6 py-2 text-left">Role</th>
<th scope="col" className="px-6 py-2 text-center">Action</th>
</tr>
</thead>
<tbody className="relative text-md divide-y-4 divide-transparent">
{
loading
? <Skeleton />
: users && users.length > 0 && filteredUsers.length > 0
? currentItems
.map((user, index) => (
<Fragment key={index}>
Expand Down Expand Up @@ -149,22 +165,22 @@ const App = () => {
</div>
</td>
</tr>
}
</tbody>
</table>
</form>
</div>
</div>

<div className={`${users.length !== 0 ? 'flex' : 'invisible' } justify-center items-center py-2 md:py-6`}>
<Pagination
filteredUsers={filteredUsers}
itemPerPage={itemPerPage}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
</div>
}
</tbody>
</table>
</form>
</div>
</div>

<div className={`${users.length !== 0 ? 'flex' : 'invisible' } justify-center items-center py-2 md:py-6`}>
<Pagination
filteredUsers={filteredUsers}
itemPerPage={itemPerPage}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
</div>

</div>
</div>
)
Expand Down
8 changes: 0 additions & 8 deletions src/App.test.js

This file was deleted.

22 changes: 13 additions & 9 deletions src/Components/DarkMode/ThemeToggle.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,24 @@ const Toggle = () => {
const { theme, setTheme } = useContext(ThemeContext);

return (
<div className="transition duration-500 ease-in-out p-1 mx-0 md:mx-16 ml-2 md:ml-5 border-dashed border-2 rounded-full border-yellow-300">
{theme === 'dark' ? (
<AiFillFire
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="text-amber-500 dark:text-amber-500 text-2xl cursor-pointer"
title='Dark Mode'
/>
) : (
<div data-testid="theme-toggle" className="transition duration-500 ease-in-out p-1 mx-0 md:mx-16 ml-2 md:ml-5 border-dashed border-2 rounded-full border-yellow-300">
{
theme === 'dark'
? (
<AiFillFire
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="text-amber-500 dark:text-amber-500 text-2xl cursor-pointer"
title='Dark Mode'
/>
)
: (
<AiOutlineFire
onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
className="text-amber-500 dark:text-amber-500 text-2xl cursor-pointer"
title='Light Mode'
/>
)}
)
}
</div>
);
};
Expand Down
1 change: 1 addition & 0 deletions src/Components/DeleteSelectedBtn.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const DeleteSelectedBtn = ({ users, setUsers }) => {

return (
<button
data-testid="delete-selected-btn"
id="first"
type="button"
title="Delete selected"
Expand Down
2 changes: 1 addition & 1 deletion src/Components/DisplayRows.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const DisplayRows = ({ user, users, setUsers, setEditUserId, setEditUserForm })
};

return (
<tr id={user.id} className={`data-row bg-white dark:bg-dark whitespace-nowrap hover:cursor-default hover:bg-blue-light dark:hover:bg-blue-dark hover:text-white ${user?.isChecked ? 'bg-gray-100 dark:bg-gray-800' : null}`}>
<tr data-testid="display-rows" id={user.id} className={`data-row bg-white dark:bg-dark whitespace-nowrap hover:cursor-default hover:bg-blue-light dark:hover:bg-blue-dark hover:text-white ${user?.isChecked ? 'bg-gray-100 dark:bg-gray-800' : null}`}>
<td className="px-6 py-2 whitespace-nowrap text-center">
<input
type="checkbox"
Expand Down
2 changes: 1 addition & 1 deletion src/Components/EditableRow.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ const EditableRow = ({ user, editUserForm ,setEditUserForm, setEditUserId }) =>
};

return (
<tr id={user.id} className="data-row bg-white dark:bg-dark whitespace-nowrap hover:cursor-default hover:bg-blue-light dark:hover:bg-blue-dark hover:text-gray">
<tr data-testid="editable-rows" id={user.id} className="data-row bg-white dark:bg-dark whitespace-nowrap hover:cursor-default hover:bg-blue-light dark:hover:bg-blue-dark hover:text-gray">
<td className="px-6 py-2 whitespace-nowrap text-center">
<input
type="checkbox"
Expand Down
18 changes: 18 additions & 0 deletions src/Components/Modal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import React from 'react'
import icon from "../Images/1140-error-outline.webp";

const Modal = () => {
return (
<div className="bg-slate-800 bg-opacity-50 flex justify-center items-center z-50 absolute top-0 right-0 bottom-0 left-0">
<div className="bg-red-100 text-red-600 border border-red-200 px-2 md:px-16 py-6 md:py-10 rounded-md text-center" role="alert">
<div className="flex justify-center items-center mb-4">
<img src={icon} alt="error" className="w-20" />
</div>
<h1 className="text-xl mb-4 font-bold">Failed to load Data from Server</h1>
<p className="text-sm">Please check your internet connection and try again</p>
</div>
</div>
)
}

export default Modal
Loading

1 comment on commit 2cf8f8c

@vercel
Copy link

@vercel vercel bot commented on 2cf8f8c Feb 12, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.