diff --git a/src/components/Accounts/ApprovedAccounts.jsx b/src/components/Accounts/ApprovedAccounts.jsx index 927c1b4..d2de376 100644 --- a/src/components/Accounts/ApprovedAccounts.jsx +++ b/src/components/Accounts/ApprovedAccounts.jsx @@ -1,67 +1,177 @@ import { NPOBackend } from '../../utils/auth_utils.js'; -import { useEffect, useState } from 'react'; +import { useEffect, useState, useCallback } from 'react'; import { Box, Table, Thead, Tbody, Tr, Th, Td, TableContainer, Button, Checkbox, useDisclosure } from '@chakra-ui/react' import { CloseIcon } from '@chakra-ui/icons' import DeleteAccountModal from './DeleteAccountModal.jsx'; import PropTypes from 'prop-types'; +import PaginationFooter from "../../components/Catalog/PaginationFooter/PaginationFooter"; +import { usePagination } from '@ajna/pagination'; -const ApprovedAccounts = ( {accountType} ) => { +const ApprovedAccounts = ( {accountType, searchQuery} ) => { const [approvedAccounts, setApprovedAccounts] = useState([]); const { isOpen: isDeleteOpen, onOpen: onDeleteOpen, onClose: onDeleteClose } = useDisclosure(); - const [deleteItemId, setDeleteItemId] = useState(""); + const [deleteItemId, setDeleteItemId] = useState([]); + const [totalRowCount, setTotalRowCount] = useState(0); + const { currentPage, setCurrentPage, pagesCount, offset, pageSize, setPageSize} = usePagination({ + initialState: { currentPage: 1, pageSize: 10 }, + total: totalRowCount, + }); + const [individualChecked, setIndividualChecked] = useState(new Array(approvedAccounts.length).fill(false)); + const [checkedAccountIds, setCheckedAccountIds] = useState([]); + const [dataShouldRevalidate, setDataShouldRevalidate] = useState(false); + + const fetchTableData = useCallback(async () => { + try { + const { data } = await NPOBackend.get(`/users/approved-accounts`, { + params: { + keyword: (searchQuery && searchQuery.length) && searchQuery, + page: currentPage, + limit: pageSize, + accountType: accountType} + }); + setApprovedAccounts(data.accounts); + setTotalRowCount(Number(data.count[0].count)); + } catch (error) { + console.error('Error fetching data:', error); + } + }, [searchQuery, currentPage, pageSize, accountType]); + + useEffect(() => { + fetchTableData(); + }, [fetchTableData]); + + useEffect(() => { + if (dataShouldRevalidate) { + fetchTableData(); + setDataShouldRevalidate(false); + setIndividualChecked(new Array(totalRowCount).fill(false)); + setCheckedAccountIds([]); + } + }, [dataShouldRevalidate, fetchTableData, totalRowCount]); + + useEffect(() => { + setCurrentPage(1); + }, [searchQuery, setCurrentPage, pageSize]); useEffect(() => { - const renderTable = async () => { - const { data } = await NPOBackend.get('/users/approved-accounts'); - setApprovedAccounts(data); - }; - renderTable(); - }, [approvedAccounts]) + setIndividualChecked(new Array(totalRowCount).fill(false)); + setCheckedAccountIds([]); + }, [searchQuery, currentPage, totalRowCount]); const handleDeleteClick = id => { setDeleteItemId(id); onDeleteOpen(); } + const updateAllCheckedAccountIds = (e) => { + setIndividualChecked(new Array(approvedAccounts.length).fill(e.target.checked)); + if (e.target.checked) { + let allIds = []; + for (let i = 0; i < approvedAccounts.length; i++) { + allIds.push(approvedAccounts[i].id) + } + setCheckedAccountIds(allIds); + } else { + setCheckedAccountIds([]); + } + } + + const updateIndividualCheckedAccountIds = (e, id, index) => { + const newIndividualChecked = [...individualChecked]; + newIndividualChecked[index] = e.target.checked; + setIndividualChecked(newIndividualChecked); + let newCheckedAccountIds = [... checkedAccountIds]; + if (e.target.checked) { + newCheckedAccountIds.push(id); + setCheckedAccountIds(newCheckedAccountIds); + } else { + let index = newCheckedAccountIds.indexOf(id); + newCheckedAccountIds.splice(index, 1); + setCheckedAccountIds(newCheckedAccountIds); + } + } + return ( - - - - - - - - - - - - { - approvedAccounts.map((account, i) => ( - accountType === account.type ? ( - - - - - - - ) : ( - <> - ) - )) + +
NameEmailDeactivate
{account.firstName} {account.lastName}{account.email} - -
+ + + + + + + {checkedAccountIds.length > 0 && + + } + + + + { + approvedAccounts.map((account, i) => ( + + + + + {checkedAccountIds.length > 0 && + } - -
{ updateAllCheckedAccountIds(e) }}/>NameEmailDeactivate + +
+ { updateIndividualCheckedAccountIds(e, account.id, i)}}> + + {account.firstName} {account.lastName}{account.email}
-
- + + + + + )) + } + + + + +
) } ApprovedAccounts.propTypes = { accountType: PropTypes.string.isRequired, + searchQuery: PropTypes.string }; -export default ApprovedAccounts; \ No newline at end of file +export default ApprovedAccounts; diff --git a/src/components/Accounts/DeleteAccountModal.jsx b/src/components/Accounts/DeleteAccountModal.jsx index 040ef34..dc15679 100644 --- a/src/components/Accounts/DeleteAccountModal.jsx +++ b/src/components/Accounts/DeleteAccountModal.jsx @@ -11,11 +11,14 @@ import { } from '@chakra-ui/react'; import { NPOBackend } from '../../utils/auth_utils.js'; -const DeleteAccountModal = ({ isOpen, onClose, deleteItemId}) => { +const DeleteAccountModal = ({ isOpen, onClose, deleteItemId, setDataShouldRevalidate}) => { const handleConfirmDelete = async idToDelete => { try { - await NPOBackend.delete(`/users/${idToDelete}`); + for (let i = 0; i < idToDelete.length; i++) { + await NPOBackend.delete(`/users/${idToDelete[i]}`); + } onClose(); + setDataShouldRevalidate(true); } catch (error) { console.error(error); } @@ -30,7 +33,7 @@ const DeleteAccountModal = ({ isOpen, onClose, deleteItemId}) => { Are you sure? You cannot undo this action afterwards. - + @@ -40,7 +43,8 @@ const DeleteAccountModal = ({ isOpen, onClose, deleteItemId}) => { DeleteAccountModal.propTypes = { isOpen: PropTypes.bool.isRequired, onClose: PropTypes.func.isRequired, - deleteItemId: PropTypes.string.isRequired, + deleteItemId: PropTypes.array.isRequired, + setDataShouldRevalidate: PropTypes.func.isRequired }; export default DeleteAccountModal; diff --git a/src/components/Accounts/PendingAccounts.jsx b/src/components/Accounts/PendingAccounts.jsx index bd775ae..52e71c8 100644 --- a/src/components/Accounts/PendingAccounts.jsx +++ b/src/components/Accounts/PendingAccounts.jsx @@ -4,60 +4,182 @@ import { Table, Thead, Tbody, Tr, Th, Td, TableContainer, Button } from '@chakra import { Checkbox } from '@chakra-ui/react' import PropTypes from 'prop-types'; -const PendingAccounts = ( {accountType} ) => { +const PendingAccounts = ( {accountType, setHasPendingAccounts} ) => { const [pendingAccounts, setPendingAccounts] = useState([]); + const [individualChecked, setIndividualChecked] = useState(new Array(pendingAccounts.length).fill(false)); + const [checkedAccountIds, setCheckedAccountIds] = useState([]); + const [accountStatus, setAccountStatus] = useState({}); + useEffect(() => { const renderTable = async () => { - const { data } = await NPOBackend.get('/users/pending-accounts'); + const { data } = await NPOBackend.get('/users/pending-accounts', {params: {accountType: accountType}}); setPendingAccounts(data); + setHasPendingAccounts(data.length !== 0); }; renderTable(); + }, [accountType, setHasPendingAccounts]) + + useEffect(() => { + const newAccountStatus = {} + for (let i = 0; i < pendingAccounts.length; i++) { + newAccountStatus[pendingAccounts[i]["id"]] = "pending"; + } + setAccountStatus(newAccountStatus); }, [pendingAccounts]) - const handleApproveUser = async (id) => { + const changeStatus = ((accountid, status) => { + let newAccountStatus = {...accountStatus}; + newAccountStatus[accountid] = status; + setAccountStatus(newAccountStatus); + }); + + const handleApproveDeclineUser = async (ids, option) => { try { - await NPOBackend.put(`/users/approve/${id}`); + for (let i = 0; i < ids.length; i++) { + if (option === "approve-option") { + await NPOBackend.put(`/users/approve/${ids[i]}`); + } + else if (option === "decline-option") { + await NPOBackend.delete(`/users/${ids[i]}`); + } + } + setIndividualChecked(new Array(pendingAccounts.length).fill(false)); } catch (error) { console.log(error); } } - const handleDeleteUser = async (id) => { - try { - await NPOBackend.delete(`/users/${id}`); - } catch (error) { - console.log(error); + const updateAllIndividualChecked = (e) => { + let newIndividualChecked = []; + for (let i = 0; i < pendingAccounts.length; i++) { + if (accountStatus[pendingAccounts[i].id] === "pending") { + newIndividualChecked.push(e.target.checked); + } + else { + newIndividualChecked.push(false); + } + } + setIndividualChecked(newIndividualChecked); + } + + const updateAllCheckedAccountIds = (e) => { + if (e.target.checked) { + let allIds = []; + for (let i = 0; i < pendingAccounts.length; i++) { + if (accountStatus[pendingAccounts[i].id] === "pending") { + allIds.push(pendingAccounts[i].id); + } + } + setCheckedAccountIds(allIds); + } else { + setCheckedAccountIds([]); + } + } + + const updateIndividualCheckedAccountIds = (e, id, index) => { + const newIndividualChecked = [...individualChecked]; + newIndividualChecked[index] = e.target.checked; + setIndividualChecked(newIndividualChecked); + let newCheckedAccountIds = [... checkedAccountIds]; + if (e.target.checked) { + newCheckedAccountIds.push(id); + setCheckedAccountIds(newCheckedAccountIds); + } else { + let index = newCheckedAccountIds.indexOf(id); + newCheckedAccountIds.splice(index, 1); + setCheckedAccountIds(newCheckedAccountIds); + } + } + + const acceptDeclineAllClick = (option) => { + handleApproveDeclineUser(checkedAccountIds, option); + const newAccountStatus = {... accountStatus}; + for (let i = 0; i < checkedAccountIds.length; i++) { + newAccountStatus[checkedAccountIds[i]] = option === "approve-option" ? "approved" : "declined"; } + setAccountStatus(newAccountStatus); + setCheckedAccountIds([]); } return ( - + - - - - + + + + + {checkedAccountIds.length > 0 && + + } { pendingAccounts.map((account, i) => ( - accountType === account.type ? ( - - - - - + + + + {checkedAccountIds.length > 0 && + + } + { + accountStatus[account.id] === "pending" ? ( + - - ) : ( - <> - ) + ) : accountStatus[account.id] === "approved" ? ( + + ) : ( + + ) + } + ) ) } @@ -69,6 +191,7 @@ const PendingAccounts = ( {accountType} ) => { PendingAccounts.propTypes = { accountType: PropTypes.string.isRequired, + setHasPendingAccounts: PropTypes.func.isRequired }; -export default PendingAccounts; \ No newline at end of file +export default PendingAccounts; diff --git a/src/components/Authentication/SignUp.jsx b/src/components/Authentication/SignUp.jsx index 95a1c98..5b9168c 100644 --- a/src/components/Authentication/SignUp.jsx +++ b/src/components/Authentication/SignUp.jsx @@ -6,7 +6,7 @@ import emailtemplate from '../EmailTemplates/emailtemplate'; import { Box, Heading, Text, FormControl, Button, Center, Link, Input, Alert, AlertDescription } from '@chakra-ui/react'; import AUTH_ROLES from '../../utils/auth_config'; -const { USER_ROLE } = AUTH_ROLES.AUTH_ROLES; +const { ADMIN_ROLE, USER_ROLE } = AUTH_ROLES.AUTH_ROLES; const SignUp = () => { const [firstName, setFirstName] = useState(); @@ -45,7 +45,7 @@ const SignUp = () => { } // register email and password - await registerWithEmailAndPassword(email, password, USER_ROLE, navigate, '/awaitConfirmation', firstName, lastName); + await registerWithEmailAndPassword(email, password, userType, navigate, '/awaitConfirmation', firstName, lastName); // send email to Debbie const subject = "New User Created Account"; @@ -86,7 +86,7 @@ const SignUp = () => { }} >
NameEmailAction {updateAllIndividualChecked(e); updateAllCheckedAccountIds(e);}}/>NameEmailAction + + +
{account.firstName} {account.lastName}{account.email} - - +
{updateIndividualCheckedAccountIds(e, account.id, i);}}> + + {account.firstName} {account.lastName}{account.email} + +
ApprovedDeclined