import React from 'react'; import { useClient } from 'urql'; import dayjs from 'dayjs'; import { Box, Flex, IconButton, NumberDecrementStepper, NumberIncrementStepper, NumberInput, NumberInputField, NumberInputStepper, Select, Table, Tag, Tbody, Td, Text, TableCaption, Th, Thead, Tooltip, Tr, Button, Center, Menu, MenuButton, MenuList, MenuItem, useToast, Spinner, TableContainer, } from '@chakra-ui/react'; import { FaAngleLeft, FaAngleRight, FaAngleDoubleLeft, FaAngleDoubleRight, FaExclamationCircle, FaAngleDown, } from 'react-icons/fa'; import { EmailVerificationQuery, UserDetailsQuery } from '../graphql/queries'; import { EnableAccess, RevokeAccess, UpdateUser } from '../graphql/mutation'; import EditUserModal from '../components/EditUserModal'; import DeleteUserModal from '../components/DeleteUserModal'; import InviteMembersModal from '../components/InviteMembersModal'; interface paginationPropTypes { limit: number; page: number; offset: number; total: number; maxPages: number; } interface userDataTypes { id: string; email: string; email_verified: boolean; given_name: string; family_name: string; middle_name: string; nickname: string; gender: string; birthdate: string; phone_number: string; picture: string; signup_methods: string; roles: [string]; created_at: number; revoked_timestamp: number; is_multi_factor_auth_enabled?: boolean; } const enum updateAccessActions { REVOKE = 'REVOKE', ENABLE = 'ENABLE', } const getMaxPages = (pagination: paginationPropTypes) => { const { limit, total } = pagination; if (total > 1) { return total % limit === 0 ? total / limit : parseInt(`${total / limit}`) + 1; } else return 1; }; const getLimits = (pagination: paginationPropTypes) => { const { total } = pagination; const limits = [5]; if (total > 10) { for (let i = 10; i <= total && limits.length <= 10; i += 5) { limits.push(i); } } return limits; }; export default function Users() { const client = useClient(); const toast = useToast(); const [paginationProps, setPaginationProps] = React.useState({ limit: 5, page: 1, offset: 0, total: 0, maxPages: 1, }); const [userList, setUserList] = React.useState([]); const [loading, setLoading] = React.useState(false); const [disableInviteMembers, setDisableInviteMembers] = React.useState(true); const updateUserList = async () => { setLoading(true); const { data } = await client .query(UserDetailsQuery, { params: { pagination: { limit: paginationProps.limit, page: paginationProps.page, }, }, }) .toPromise(); if (data?._users) { const { pagination, users } = data._users; const maxPages = getMaxPages(pagination); if (users && users.length > 0) { setPaginationProps({ ...paginationProps, ...pagination, maxPages }); setUserList(users); } else { if (paginationProps.page !== 1) { setPaginationProps({ ...paginationProps, ...pagination, maxPages, page: 1, }); } } } setLoading(false); }; const checkEmailVerification = async () => { setLoading(true); const { data } = await client.query(EmailVerificationQuery).toPromise(); if (data?._env) { const { DISABLE_EMAIL_VERIFICATION } = data._env; setDisableInviteMembers(DISABLE_EMAIL_VERIFICATION); } setLoading(false); }; React.useEffect(() => { updateUserList(); checkEmailVerification(); }, []); React.useEffect(() => { updateUserList(); }, [paginationProps.page, paginationProps.limit]); const paginationHandler = (value: Record) => { setPaginationProps({ ...paginationProps, ...value }); }; const userVerificationHandler = async (user: userDataTypes) => { const { id, email, phone_number } = user; let params = {}; if (email) { params = { id, email, email_verified: true, }; } if (phone_number) { params = { id, phone_number, phone_number_verified: true, }; } const res = await client .mutation(UpdateUser, { params, }) .toPromise(); if (res.error) { toast({ title: 'User verification failed', isClosable: true, status: 'error', position: 'top-right', }); } else if (res.data?._update_user?.id) { toast({ title: 'User verification successful', isClosable: true, status: 'success', position: 'top-right', }); } updateUserList(); }; const updateAccessHandler = async ( id: string, action: updateAccessActions, ) => { switch (action) { case updateAccessActions.ENABLE: const enableAccessRes = await client .mutation(EnableAccess, { param: { user_id: id, }, }) .toPromise(); if (enableAccessRes.error) { toast({ title: 'User access enable failed', isClosable: true, status: 'error', position: 'top-right', }); } else { toast({ title: 'User access enabled successfully', isClosable: true, status: 'success', position: 'top-right', }); } updateUserList(); break; case updateAccessActions.REVOKE: const revokeAccessRes = await client .mutation(RevokeAccess, { param: { user_id: id, }, }) .toPromise(); if (revokeAccessRes.error) { toast({ title: 'User access revoke failed', isClosable: true, status: 'error', position: 'top-right', }); } else { toast({ title: 'User access revoked successfully', isClosable: true, status: 'success', position: 'top-right', }); } updateUserList(); break; default: break; } }; const multiFactorAuthUpdateHandler = async (user: userDataTypes) => { const res = await client .mutation(UpdateUser, { params: { id: user.id, is_multi_factor_auth_enabled: !user.is_multi_factor_auth_enabled, }, }) .toPromise(); if (res.data?._update_user?.id) { toast({ title: `Multi factor authentication ${ user.is_multi_factor_auth_enabled ? 'disabled' : 'enabled' } for user`, isClosable: true, status: 'success', position: 'top-right', }); updateUserList(); return; } toast({ title: 'Multi factor authentication update failed for user', isClosable: true, status: 'error', position: 'top-right', }); }; return ( Users {!loading ? ( userList.length > 0 ? ( {userList.map((user: userDataTypes) => { const { email_verified, phone_number_verified, created_at, ...rest }: any = user; return ( ); })} {(paginationProps.maxPages > 1 || paginationProps.total >= 5) && ( paginationHandler({ page: 1, }) } isDisabled={paginationProps.page <= 1} mr={4} icon={} /> paginationHandler({ page: paginationProps.page - 1, }) } isDisabled={paginationProps.page <= 1} icon={} /> Page{' '} {paginationProps.page} {' '} of{' '} {paginationProps.maxPages} Go to page:{' '} paginationHandler({ page: parseInt(value), }) } value={paginationProps.page} > paginationHandler({ page: paginationProps.page + 1, }) } isDisabled={ paginationProps.page >= paginationProps.maxPages } icon={} /> paginationHandler({ page: paginationProps.maxPages, }) } isDisabled={ paginationProps.page >= paginationProps.maxPages } ml={4} icon={} /> )}
Email / Phone Created At Signup Methods Roles Verified Access MFA Actions
{user.email || user.phone_number} {dayjs(user.created_at * 1000).format('MMM DD, YYYY')} {user.signup_methods} {user.roles.join(', ')} {( user.email_verified || user.phone_number_verified ).toString()} {user.revoked_timestamp ? 'Revoked' : 'Enabled'} {user.is_multi_factor_auth_enabled ? 'Enabled' : 'Disabled'} Menu {!user.email_verified && !user.phone_number_verified && ( userVerificationHandler(user)} > Verify User )} {user.revoked_timestamp ? ( updateAccessHandler( user.id, updateAccessActions.ENABLE, ) } > Enable Access ) : ( updateAccessHandler( user.id, updateAccessActions.REVOKE, ) } > Revoke Access )} {user.is_multi_factor_auth_enabled ? ( multiFactorAuthUpdateHandler(user) } > Disable MultiFactor Authentication ) : ( multiFactorAuthUpdateHandler(user) } > Enable MultiFactor Authentication )}
) : (
No Data
) ) : (
)}
); }