Skip to content

Commit

Permalink
Refactor users page and users
Browse files Browse the repository at this point in the history
  • Loading branch information
EMaksy committed Apr 25, 2024
1 parent d248360 commit f476d16
Show file tree
Hide file tree
Showing 7 changed files with 104 additions and 102 deletions.
4 changes: 0 additions & 4 deletions assets/js/common/Button/Button.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,6 @@ const getButtonClasses = (type) => {
return 'bg-white hover:opacity-75 focus:outline-none text-red-500 border border-red-500 transition ease-in duration-200 text-center font-semibold rounded shadow';
case 'danger-bold':
return 'bg-red-500 hover:opacity-75 focus:outline-none text-white border border-red-500 transition ease-in duration-200 text-center font-semibold rounded shadow';
case 'danger-no-border':
return 'bg-none hover:opacity-75 focus:outline-none text-red-500 border border-none transition ease-in duration-200 text-center font-semibold';
case 'danger-op-50-no-border':
return 'text-red-500 opacity-50';
default:
return 'bg-jungle-green-500 hover:opacity-75 focus:outline-none text-white w-full transition ease-in duration-200 text-center font-semibold rounded shadow disabled:bg-gray-400 disabled:text-gray-200';
}
Expand Down
2 changes: 1 addition & 1 deletion assets/js/lib/api/users.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ import { networkClient } from '@lib/network';

export const listUsers = () => networkClient.get('/users');

export const deleteUser = (userId) => networkClient.delete(`/users/${userId}`);
export const deleteUser = (userID) => networkClient.delete(`/users/${userID}`);
1 change: 0 additions & 1 deletion assets/js/lib/test-utils/factories/users.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
/* eslint-disable import/no-extraneous-dependencies */
import { faker } from '@faker-js/faker';

Check failure on line 1 in assets/js/lib/test-utils/factories/users.js

View workflow job for this annotation

GitHub Actions / Static Code Analysis

'@faker-js/faker' should be listed in the project's dependencies, not devDependencies
import { Factory } from 'fishery';

Check failure on line 2 in assets/js/lib/test-utils/factories/users.js

View workflow job for this annotation

GitHub Actions / Static Code Analysis

'fishery' should be listed in the project's dependencies, not devDependencies

Expand Down
6 changes: 5 additions & 1 deletion assets/js/pages/Layout/Layout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,11 @@ const navigation = [
href: '/catalog',
icon: EOS_LIST,
},
{ name: 'Users', href: '/users', icon: EOS_SUPERVISED_USER_CIRCLE_OUTLINED },
{
name: 'Users',
href: '/users',
icon: EOS_SUPERVISED_USER_CIRCLE_OUTLINED,
},
{
name: 'Settings',
href: '/settings',
Expand Down
155 changes: 81 additions & 74 deletions assets/js/pages/Users/Users.jsx
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
import React from 'react';
import React, { useState } from 'react';
import { Link } from 'react-router-dom';
import { noop, find } from 'lodash';
import { format, parseISO } from 'date-fns';

import Button from '@common/Button';
import Table from '@common/Table';
import PageHeader from '@common/PageHeader';
import Modal from '@common/Modal';
import Tooltip from '@common/Tooltip';
import Banner from '@common/Banners/Banner';
import { EOS_LOADING_ANIMATED } from 'eos-icons-react';

const USER_CREATE_ROUTE = '/users/new';
const defaultUsers = [];

function getUserByID(users, userID) {
return find(users, { id: userID }) || null;
}

function Users({
handleDeleteUser = () => {},
navigate = () => {},
setModalOpen = () => {},
setDeleteUserId = () => {},
deleteUserId = 0,
modalOpen = false,
users = [],
onDeleteUser = noop,
navigate = noop,
users = defaultUsers,
loading = false,
}) {
const [modalOpen, setModalOpen] = useState(false);
const [deleteUserID, setDeleteUserID] = useState(null);

const usersTableConfig = {
pagination: true,
usePadding: false,
Expand Down Expand Up @@ -47,79 +52,44 @@ function Users({
{
title: 'Status',
key: 'enabled',
render: (content, item) => (item.enabled ? 'Enabled' : 'Disabled'),
},
{
title: 'Created',
key: 'created',
key: 'created_at',
render: (content, item) =>
format(parseISO(item.created_at), 'MMMM dd, yyyy'),
},

{
title: 'Actions',
key: 'actions',
render: (content, item) => (
<>
{item.id !== 1 ? (
<div>
<Tooltip
content="Admin user can not be deleted"
isEnabled={item.id === 1}
>
<Button
type="danger-no-border"
className="text-red-500"
size="small"
type="transparent"
disabled={item.id === 1}
onClick={() => {
setModalOpen(true);
setDeleteUserId(item.id);
setDeleteUserID(item.id);
}}
>
Delete
</Button>
) : (
<Tooltip content="Admin user can not be deleted">
<Button type="danger-op-50-no-border">Delete</Button>
</Tooltip>
)}

{modalOpen && deleteUserId === item.id && (
<Modal
open={modalOpen}
className="!w-3/4 !max-w-3xl"
onClose={() => setModalOpen(false)}
title="Delete User"
>
<div className="flex flex-col my-2">
<Banner type="warning">
<span className="text-sm">
This Action cannot be undone
</span>
</Banner>
<span className="my-1 text-gray-500">
Are you sure you want to delete the following user account?
</span>
<span className="my-1 mb-4 text-gray-600">
{item.username}
</span>

<div className="w-1/6 h-4/5 flex">
<Button
type="danger-bold"
className=" mr-4"
onClick={() => handleDeleteUser(deleteUserId)}
>
Delete
</Button>

<Button
type="primary-white"
className="w-1/6"
onClick={() => setModalOpen(false)}
>
Cancel
</Button>
</div>
</div>
</Modal>
)}
</>
</Tooltip>
</div>
),
},
],
};

const user = getUserByID(users, deleteUserID);

return (
<div className="flex flex-wrap">
<div className="flex w-1/2 h-auto overflow-hidden overflow-ellipsis break-words">
Expand All @@ -130,23 +100,60 @@ function Users({
<Button
className="inline-block mx-1 border-green-500 border"
size="small"
onClick={() => navigate(USER_CREATE_ROUTE)}
onClick={() => navigate('/users/new')}
>
Create User
</Button>
</div>
</div>
{loading ? (
<div className="flex flex-col items-center justify-center w-full">
<EOS_LOADING_ANIMATED
size="xxl"
className="inline align-bottom fill-green-400"
/>
Loading...
</div>
) : (
<Table config={usersTableConfig} data={users} />
{modalOpen && (
<Modal
open={modalOpen}
className="!w-3/4 !max-w-3xl"
onClose={() => setModalOpen(false)}
title="Delete User"
>
<div className="flex flex-col my-2">
<Banner type="warning">
<span className="text-sm">This Action cannot be undone</span>
</Banner>
<span className="my-1 text-gray-500">
Are you sure you want to delete the following user account?
</span>
{user ? (
<span className="my-1 mb-4 text-gray-600">{user.username}</span>
) : (
<span className="my-1 mb-4 text-gray-600">User not found</span>
)}

<div className="w-1/6 h-4/5 flex">
<Button
type="danger-bold"
className=" mr-4"
onClick={() => {
onDeleteUser(deleteUserID);
setModalOpen(false);
}}
>
Delete
</Button>

<Button
type="primary-white"
className="w-1/6"
onClick={() => setModalOpen(false)}
>
Cancel
</Button>
</div>
</div>
</Modal>
)}
<Table
config={usersTableConfig}
data={users}
emptyStateText={loading ? 'Loading...' : 'No data available'}
/>
</div>
);
}
Expand Down
4 changes: 2 additions & 2 deletions assets/js/pages/Users/Users.stories.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,10 @@ export default {
},
},
argTypes: {
handleDeleteUser: {
onDeleteUser: {
description: 'Function to handle deleting a user',
control: { type: 'function' },
action: 'handleDeleteUser',
action: 'onDeleteUser',
},
navigate: {
description: 'Function to navigate pages',
Expand Down
34 changes: 15 additions & 19 deletions assets/js/pages/Users/UsersPage.jsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
import React, { useState, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { listUsers, deleteUser } from '@lib/api/users';
import { format, parseISO } from 'date-fns';

import { toast } from 'react-hot-toast';

import Users from './Users';

const successDeleteMessage = 'User deleted successfully';
const errorLoadingMessage = 'An error occurred during loading users';
const errorDeletingMessage = 'An error occurred during deleting user';

function UsersPage() {
const [modalOpen, setModalOpen] = useState(false);
const [deleteUserId, setDeleteUserId] = useState(null);
const [userUpdateTrigger, setUserUpdateTrigger] = useState(false);
const [loading, setLoading] = useState(false);
const [userData, setUserData] = useState([]);
const [error, setError] = useState(null);
Expand All @@ -22,29 +22,30 @@ function UsersPage() {
listUsers()
.then((response) => {
setUserData(response.data);
setLoading(false);
})
.catch((_error) => {
setError('An error occurred during loading users');
setError(errorLoadingMessage);
setUserData([]);
})
.finally(() => {
setLoading(false);
});
};

const handleDeleteUser = (userId) => {
const onDeleteUser = (userId) => {
deleteUser(userId)
.then(() => {
setUserUpdateTrigger(true);
fetchUsers();
toast.success(successDeleteMessage);
})
.catch((_error) => {
setError('An error occurred during deleting user');
setError(errorDeletingMessage);
});
};

useEffect(() => {
fetchUsers();
setUserUpdateTrigger(false);
}, [userUpdateTrigger]);
}, []);

useEffect(() => {
if (error) {
Expand All @@ -57,22 +58,17 @@ function UsersPage() {
({ id, username, created_at, enabled, fullname, email }) => ({
id,
username,
created: format(parseISO(created_at), 'MMMM dd, yyyy'),
actions: 'Delete',
enabled: enabled ? 'Enabled' : 'Disabled',
created_at,
enabled,
fullname,
email,
})
);

return (
<Users
handleDeleteUser={handleDeleteUser}
onDeleteUser={onDeleteUser}
navigate={navigate}
setModalOpen={setModalOpen}
setDeleteUserId={setDeleteUserId}
deleteUserId={deleteUserId}
modalOpen={modalOpen}
users={usersTableData}
loading={loading}
/>
Expand Down

0 comments on commit f476d16

Please sign in to comment.