2022-03-15 18:21:54 +00:00
|
|
|
import React, { useState, useCallback, useEffect } from 'react';
|
2022-03-14 19:54:14 +00:00
|
|
|
import {
|
|
|
|
Button,
|
|
|
|
Center,
|
|
|
|
Flex,
|
|
|
|
Modal,
|
|
|
|
ModalBody,
|
|
|
|
ModalCloseButton,
|
|
|
|
ModalContent,
|
|
|
|
ModalFooter,
|
|
|
|
ModalHeader,
|
|
|
|
ModalOverlay,
|
|
|
|
useDisclosure,
|
|
|
|
useToast,
|
|
|
|
Tabs,
|
|
|
|
TabList,
|
|
|
|
Tab,
|
|
|
|
TabPanels,
|
|
|
|
TabPanel,
|
|
|
|
InputGroup,
|
|
|
|
Input,
|
|
|
|
InputRightElement,
|
|
|
|
Text,
|
|
|
|
Link,
|
2022-05-08 04:05:50 +00:00
|
|
|
Tooltip
|
2022-03-14 19:54:14 +00:00
|
|
|
} from '@chakra-ui/react';
|
|
|
|
import { useClient } from 'urql';
|
|
|
|
import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa';
|
|
|
|
import { useDropzone } from 'react-dropzone';
|
2022-03-15 15:01:54 +00:00
|
|
|
import { validateEmail, validateURI } from '../utils';
|
2022-03-16 14:43:18 +00:00
|
|
|
import { InviteMembers } from '../graphql/mutation';
|
2022-03-16 18:45:47 +00:00
|
|
|
import { ArrayInputOperations } from '../constants';
|
2022-03-15 18:21:54 +00:00
|
|
|
import parseCSV from '../utils/parseCSV';
|
2022-03-14 19:54:14 +00:00
|
|
|
|
2022-03-15 15:01:54 +00:00
|
|
|
interface stateDataTypes {
|
2022-03-14 19:54:14 +00:00
|
|
|
value: string;
|
|
|
|
isInvalid: boolean;
|
|
|
|
}
|
|
|
|
|
2022-03-16 14:43:18 +00:00
|
|
|
interface requestParamTypes {
|
|
|
|
emails: string[];
|
|
|
|
redirect_uri?: string;
|
|
|
|
}
|
|
|
|
|
|
|
|
const initData: stateDataTypes = {
|
|
|
|
value: '',
|
|
|
|
isInvalid: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
const InviteMembersModal = ({
|
|
|
|
updateUserList,
|
|
|
|
disabled = true,
|
|
|
|
}: {
|
|
|
|
updateUserList: Function;
|
|
|
|
disabled: boolean;
|
|
|
|
}) => {
|
2022-03-14 19:54:14 +00:00
|
|
|
const client = useClient();
|
|
|
|
const toast = useToast();
|
|
|
|
const { isOpen, onOpen, onClose } = useDisclosure();
|
2022-03-15 15:01:54 +00:00
|
|
|
const [tabIndex, setTabIndex] = useState<number>(0);
|
|
|
|
const [redirectURI, setRedirectURI] = useState<stateDataTypes>({
|
2022-03-16 14:43:18 +00:00
|
|
|
...initData,
|
2022-03-15 15:01:54 +00:00
|
|
|
});
|
2022-03-16 14:43:18 +00:00
|
|
|
const [emails, setEmails] = useState<stateDataTypes[]>([{ ...initData }]);
|
2022-03-15 18:21:54 +00:00
|
|
|
const [disableSendButton, setDisableSendButton] = useState<boolean>(false);
|
2022-03-16 14:43:18 +00:00
|
|
|
const [loading, setLoading] = React.useState<boolean>(false);
|
2022-03-15 18:21:54 +00:00
|
|
|
useEffect(() => {
|
|
|
|
if (redirectURI.isInvalid) {
|
|
|
|
setDisableSendButton(true);
|
|
|
|
} else if (emails.some((emailData) => emailData.isInvalid)) {
|
|
|
|
setDisableSendButton(true);
|
|
|
|
} else {
|
|
|
|
setDisableSendButton(false);
|
|
|
|
}
|
|
|
|
}, [redirectURI, emails]);
|
2022-03-16 08:38:22 +00:00
|
|
|
useEffect(() => {
|
|
|
|
return () => {
|
2022-03-16 14:43:18 +00:00
|
|
|
setRedirectURI({ ...initData });
|
|
|
|
setEmails([{ ...initData }]);
|
2022-03-16 08:38:22 +00:00
|
|
|
};
|
|
|
|
}, []);
|
2022-03-14 19:54:14 +00:00
|
|
|
const sendInviteHandler = async () => {
|
2022-03-16 14:43:18 +00:00
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
|
|
const emailList = emails
|
|
|
|
.filter((emailData) => !emailData.isInvalid)
|
|
|
|
.map((emailData) => emailData.value);
|
|
|
|
const params: requestParamTypes = {
|
|
|
|
emails: emailList,
|
|
|
|
};
|
|
|
|
if (redirectURI.value !== '' && !redirectURI.isInvalid) {
|
|
|
|
params.redirect_uri = redirectURI.value;
|
|
|
|
}
|
2022-03-16 14:52:24 +00:00
|
|
|
if (emailList.length > 0) {
|
2022-03-16 14:43:18 +00:00
|
|
|
const res = await client
|
|
|
|
.mutation(InviteMembers, {
|
|
|
|
params,
|
|
|
|
})
|
|
|
|
.toPromise();
|
|
|
|
if (res.error) {
|
|
|
|
throw new Error('Internal server error');
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
toast({
|
|
|
|
title: 'Invites sent successfully!',
|
|
|
|
isClosable: true,
|
|
|
|
status: 'success',
|
|
|
|
position: 'bottom-right',
|
|
|
|
});
|
|
|
|
setLoading(false);
|
|
|
|
updateUserList();
|
|
|
|
} else {
|
|
|
|
throw new Error('Please add emails');
|
|
|
|
}
|
|
|
|
} catch (error: any) {
|
|
|
|
toast({
|
|
|
|
title: error?.message || 'Error occurred, try again!',
|
|
|
|
isClosable: true,
|
|
|
|
status: 'error',
|
|
|
|
position: 'bottom-right',
|
|
|
|
});
|
|
|
|
setLoading(false);
|
|
|
|
}
|
2022-03-16 08:38:22 +00:00
|
|
|
closeModalHandler();
|
2022-03-14 19:54:14 +00:00
|
|
|
};
|
|
|
|
const updateEmailListHandler = (operation: string, index: number = 0) => {
|
|
|
|
switch (operation) {
|
|
|
|
case ArrayInputOperations.APPEND:
|
2022-03-16 14:43:18 +00:00
|
|
|
setEmails([...emails, { ...initData }]);
|
2022-03-14 19:54:14 +00:00
|
|
|
break;
|
|
|
|
case ArrayInputOperations.REMOVE:
|
|
|
|
const updatedEmailList = [...emails];
|
|
|
|
updatedEmailList.splice(index, 1);
|
|
|
|
setEmails(updatedEmailList);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const inputChangeHandler = (value: string, index: number) => {
|
|
|
|
const updatedEmailList = [...emails];
|
|
|
|
updatedEmailList[index].value = value;
|
|
|
|
updatedEmailList[index].isInvalid = !validateEmail(value);
|
|
|
|
setEmails(updatedEmailList);
|
|
|
|
};
|
2022-03-16 08:38:08 +00:00
|
|
|
const changeTabsHandler = (index: number) => {
|
|
|
|
setTabIndex(index);
|
|
|
|
};
|
2022-03-15 18:21:54 +00:00
|
|
|
const onDrop = useCallback(async (acceptedFiles) => {
|
|
|
|
const result = await parseCSV(acceptedFiles[0], ',');
|
|
|
|
setEmails(result);
|
2022-03-16 08:38:08 +00:00
|
|
|
changeTabsHandler(0);
|
2022-03-14 19:54:14 +00:00
|
|
|
}, []);
|
2022-03-15 15:01:54 +00:00
|
|
|
const setRedirectURIHandler = (value: string) => {
|
|
|
|
const updatedRedirectURI: stateDataTypes = {
|
|
|
|
value: '',
|
|
|
|
isInvalid: false,
|
|
|
|
};
|
|
|
|
updatedRedirectURI.value = value;
|
|
|
|
updatedRedirectURI.isInvalid = !validateURI(value);
|
|
|
|
setRedirectURI(updatedRedirectURI);
|
|
|
|
};
|
2022-03-15 18:21:54 +00:00
|
|
|
const { getRootProps, getInputProps, isDragActive } = useDropzone({
|
|
|
|
onDrop,
|
|
|
|
accept: 'text/csv',
|
|
|
|
});
|
2022-03-16 08:38:22 +00:00
|
|
|
const closeModalHandler = () => {
|
|
|
|
setRedirectURI({
|
|
|
|
value: '',
|
|
|
|
isInvalid: false,
|
|
|
|
});
|
|
|
|
setEmails([
|
|
|
|
{
|
|
|
|
value: '',
|
|
|
|
isInvalid: false,
|
|
|
|
},
|
|
|
|
]);
|
|
|
|
onClose();
|
|
|
|
};
|
2022-03-14 19:54:14 +00:00
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Button
|
|
|
|
leftIcon={<FaUserPlus />}
|
|
|
|
colorScheme="blue"
|
|
|
|
variant="solid"
|
|
|
|
onClick={onOpen}
|
2022-03-16 08:38:08 +00:00
|
|
|
isDisabled={disabled}
|
2022-03-14 19:54:14 +00:00
|
|
|
size="sm"
|
|
|
|
>
|
2022-05-08 04:05:50 +00:00
|
|
|
<Center h="100%">
|
|
|
|
{disabled ? (
|
|
|
|
<Tooltip
|
|
|
|
mr={8}
|
|
|
|
mt={1}
|
|
|
|
hasArrow
|
|
|
|
bg="gray.300"
|
|
|
|
color="black"
|
|
|
|
label="Email verification is disabled, refer to 'UI Customization' tab within 'Environment' to enable it."
|
|
|
|
>
|
|
|
|
Invite Members
|
|
|
|
</Tooltip>
|
|
|
|
) : (
|
|
|
|
"Invite Members"
|
|
|
|
)}
|
|
|
|
</Center>{" "}
|
2022-03-14 19:54:14 +00:00
|
|
|
</Button>
|
2022-03-16 08:38:22 +00:00
|
|
|
<Modal isOpen={isOpen} onClose={closeModalHandler} size="xl">
|
2022-03-14 19:54:14 +00:00
|
|
|
<ModalOverlay />
|
|
|
|
<ModalContent>
|
2022-03-15 18:37:58 +00:00
|
|
|
<ModalHeader>Invite Members</ModalHeader>
|
2022-03-14 19:54:14 +00:00
|
|
|
<ModalCloseButton />
|
|
|
|
<ModalBody>
|
2022-03-15 15:01:54 +00:00
|
|
|
<Tabs
|
|
|
|
isFitted
|
|
|
|
variant="enclosed"
|
|
|
|
index={tabIndex}
|
2022-03-16 08:38:08 +00:00
|
|
|
onChange={changeTabsHandler}
|
2022-03-15 15:01:54 +00:00
|
|
|
>
|
2022-03-14 19:54:14 +00:00
|
|
|
<TabList>
|
|
|
|
<Tab>Enter emails</Tab>
|
|
|
|
<Tab>Upload CSV</Tab>
|
|
|
|
</TabList>
|
|
|
|
<TabPanels
|
|
|
|
border="1px"
|
|
|
|
borderTop="0"
|
|
|
|
borderBottomRadius="5px"
|
|
|
|
borderColor="inherit"
|
|
|
|
>
|
|
|
|
<TabPanel>
|
|
|
|
<Flex flexDirection="column">
|
2022-03-15 15:01:54 +00:00
|
|
|
<Flex
|
|
|
|
width="100%"
|
|
|
|
justifyContent="start"
|
|
|
|
alignItems="center"
|
|
|
|
marginBottom="2%"
|
|
|
|
>
|
|
|
|
<Flex marginLeft="2.5%">Redirect URI</Flex>
|
|
|
|
</Flex>
|
|
|
|
<Flex
|
|
|
|
width="100%"
|
|
|
|
justifyContent="space-between"
|
|
|
|
alignItems="center"
|
|
|
|
marginBottom="2%"
|
|
|
|
>
|
|
|
|
<InputGroup size="md" marginBottom="2.5%">
|
|
|
|
<Input
|
|
|
|
pr="4.5rem"
|
|
|
|
type="text"
|
|
|
|
placeholder="https://domain.com/sign-up"
|
|
|
|
value={redirectURI.value}
|
|
|
|
isInvalid={redirectURI.isInvalid}
|
|
|
|
onChange={(e) =>
|
|
|
|
setRedirectURIHandler(e.currentTarget.value)
|
|
|
|
}
|
|
|
|
/>
|
|
|
|
</InputGroup>
|
|
|
|
</Flex>
|
2022-03-14 19:54:14 +00:00
|
|
|
<Flex
|
|
|
|
width="100%"
|
|
|
|
justifyContent="space-between"
|
|
|
|
alignItems="center"
|
|
|
|
marginBottom="2%"
|
|
|
|
>
|
|
|
|
<Flex marginLeft="2.5%">Emails</Flex>
|
|
|
|
<Flex>
|
|
|
|
<Button
|
|
|
|
leftIcon={<FaPlus />}
|
|
|
|
colorScheme="blue"
|
|
|
|
h="1.75rem"
|
|
|
|
size="sm"
|
|
|
|
variant="ghost"
|
|
|
|
onClick={() =>
|
|
|
|
updateEmailListHandler(ArrayInputOperations.APPEND)
|
|
|
|
}
|
|
|
|
>
|
|
|
|
Add more emails
|
|
|
|
</Button>
|
|
|
|
</Flex>
|
|
|
|
</Flex>
|
2022-03-16 12:36:51 +00:00
|
|
|
<Flex flexDirection="column" maxH={250} overflowY="scroll">
|
|
|
|
{emails.map((emailData, index) => (
|
|
|
|
<Flex
|
|
|
|
key={`email-data-${index}`}
|
|
|
|
justifyContent="center"
|
|
|
|
alignItems="center"
|
|
|
|
>
|
|
|
|
<InputGroup size="md" marginBottom="2.5%">
|
|
|
|
<Input
|
|
|
|
pr="4.5rem"
|
|
|
|
type="text"
|
|
|
|
placeholder="name@domain.com"
|
|
|
|
value={emailData.value}
|
|
|
|
isInvalid={emailData.isInvalid}
|
|
|
|
onChange={(e) =>
|
|
|
|
inputChangeHandler(e.currentTarget.value, index)
|
2022-03-14 19:54:14 +00:00
|
|
|
}
|
2022-03-16 12:36:51 +00:00
|
|
|
/>
|
|
|
|
<InputRightElement width="3rem">
|
|
|
|
<Button
|
|
|
|
h="1.75rem"
|
|
|
|
size="sm"
|
|
|
|
colorScheme="blackAlpha"
|
|
|
|
variant="ghost"
|
|
|
|
onClick={() =>
|
|
|
|
updateEmailListHandler(
|
|
|
|
ArrayInputOperations.REMOVE,
|
|
|
|
index
|
|
|
|
)
|
|
|
|
}
|
|
|
|
>
|
|
|
|
<FaMinusCircle />
|
|
|
|
</Button>
|
|
|
|
</InputRightElement>
|
|
|
|
</InputGroup>
|
|
|
|
</Flex>
|
|
|
|
))}
|
|
|
|
</Flex>
|
2022-03-14 19:54:14 +00:00
|
|
|
</Flex>
|
|
|
|
</TabPanel>
|
|
|
|
<TabPanel>
|
|
|
|
<Flex
|
|
|
|
justify="center"
|
|
|
|
align="center"
|
|
|
|
textAlign="center"
|
|
|
|
bg="#f0f0f0"
|
2022-03-16 12:36:51 +00:00
|
|
|
h={230}
|
2022-03-14 19:54:14 +00:00
|
|
|
p={50}
|
|
|
|
m={2}
|
|
|
|
borderRadius={5}
|
|
|
|
{...getRootProps()}
|
|
|
|
>
|
|
|
|
<input {...getInputProps()} />
|
|
|
|
{isDragActive ? (
|
|
|
|
<Text>Drop the files here...</Text>
|
|
|
|
) : (
|
|
|
|
<Flex
|
|
|
|
flexDirection="column"
|
|
|
|
justifyContent="center"
|
|
|
|
alignItems="center"
|
|
|
|
>
|
|
|
|
<Center boxSize="20" color="blackAlpha.500">
|
|
|
|
<FaUpload fontSize="40" />
|
|
|
|
</Center>
|
|
|
|
<Text>
|
|
|
|
Drag 'n' drop the csv file here, or click to select.
|
|
|
|
</Text>
|
|
|
|
<Text size="xs">
|
|
|
|
Download{' '}
|
|
|
|
<Link
|
2022-03-16 18:45:47 +00:00
|
|
|
href={`/dashboard/public/sample.csv`}
|
2022-03-14 19:54:14 +00:00
|
|
|
download="sample.csv"
|
|
|
|
color="blue.600"
|
|
|
|
onClick={(e) => e.stopPropagation()}
|
|
|
|
>
|
|
|
|
{' '}
|
|
|
|
sample.csv
|
|
|
|
</Link>{' '}
|
|
|
|
and modify it.{' '}
|
|
|
|
</Text>
|
|
|
|
</Flex>
|
|
|
|
)}
|
|
|
|
</Flex>
|
|
|
|
</TabPanel>
|
|
|
|
</TabPanels>
|
|
|
|
</Tabs>
|
|
|
|
</ModalBody>
|
|
|
|
<ModalFooter>
|
|
|
|
<Button
|
|
|
|
colorScheme="blue"
|
|
|
|
variant="solid"
|
|
|
|
onClick={sendInviteHandler}
|
2022-03-16 14:43:18 +00:00
|
|
|
isDisabled={disableSendButton || loading}
|
2022-03-14 19:54:14 +00:00
|
|
|
>
|
|
|
|
<Center h="100%" pt="5%">
|
|
|
|
Send
|
|
|
|
</Center>
|
|
|
|
</Button>
|
|
|
|
</ModalFooter>
|
|
|
|
</ModalContent>
|
|
|
|
</Modal>
|
|
|
|
</>
|
|
|
|
);
|
|
|
|
};
|
|
|
|
|
2022-03-15 18:37:58 +00:00
|
|
|
export default InviteMembersModal;
|