From ab01ff249d06c7490d408047880be115ec3e2fb4 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Tue, 15 Mar 2022 01:24:14 +0530 Subject: [PATCH 01/10] invite email modal added --- dashboard/package.json | 1 + dashboard/src/components/InviteEmailModal.tsx | 254 ++++++++++++++++++ dashboard/src/constants.ts | 7 + dashboard/src/pages/Users.tsx | 2 + dashboard/src/utils/index.ts | 11 + 5 files changed, 275 insertions(+) create mode 100644 dashboard/src/components/InviteEmailModal.tsx diff --git a/dashboard/package.json b/dashboard/package.json index af68d43..9f854ba 100644 --- a/dashboard/package.json +++ b/dashboard/package.json @@ -24,6 +24,7 @@ "lodash": "^4.17.21", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-dropzone": "^12.0.4", "react-icons": "^4.3.1", "react-router-dom": "^6.2.1", "typescript": "^4.5.4", diff --git a/dashboard/src/components/InviteEmailModal.tsx b/dashboard/src/components/InviteEmailModal.tsx new file mode 100644 index 0000000..7f69bd5 --- /dev/null +++ b/dashboard/src/components/InviteEmailModal.tsx @@ -0,0 +1,254 @@ +import React, { useState, useCallback } from 'react'; +import { + Button, + Center, + Flex, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + useDisclosure, + useToast, + Tabs, + TabList, + Tab, + TabPanels, + TabPanel, + InputGroup, + Input, + InputRightElement, + Text, + Link, +} from '@chakra-ui/react'; +import { useClient } from 'urql'; +import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa'; +import { useDropzone } from 'react-dropzone'; +import { escape } from 'lodash'; +import { validateEmail } from '../utils'; +import { UpdateUser } from '../graphql/mutation'; +import { ArrayInputOperations, csvDemoData } from '../constants'; + +interface emailDataTypes { + value: string; + isInvalid: boolean; +} + +const InviteEmailModal = () => { + const client = useClient(); + const toast = useToast(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const [emails, setEmails] = useState([ + { + value: '', + isInvalid: false, + }, + { + value: '', + isInvalid: false, + }, + { + value: '', + isInvalid: false, + }, + { + value: '', + isInvalid: false, + }, + ]); + const sendInviteHandler = async () => { + onClose(); + }; + const updateEmailListHandler = (operation: string, index: number = 0) => { + switch (operation) { + case ArrayInputOperations.APPEND: + setEmails([ + ...emails, + { + value: '', + isInvalid: false, + }, + ]); + 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); + }; + const onDrop = useCallback((acceptedFiles) => { + console.log(acceptedFiles); + }, []); + const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); + return ( + <> + + + + + Invite Email + + + + + Enter emails + Upload CSV + + + + + + Emails + + + + + {emails.map((emailData, index) => ( + + + + inputChangeHandler(e.currentTarget.value, index) + } + /> + + + + + + ))} + + + + + + {isDragActive ? ( + Drop the files here... + ) : ( + +
+ +
+ + Drag 'n' drop the csv file here, or click to select. + + + Download{' '} + e.stopPropagation()} + > + {' '} + sample.csv + {' '} + and modify it.{' '} + +
+ )} +
+
+
+
+
+ + + +
+
+ + ); +}; + +export default InviteEmailModal; diff --git a/dashboard/src/constants.ts b/dashboard/src/constants.ts index 64fa372..6900730 100644 --- a/dashboard/src/constants.ts +++ b/dashboard/src/constants.ts @@ -88,3 +88,10 @@ export const ECDSAEncryptionType = { ES384: 'ES384', ES512: 'ES512', }; + +export const csvDemoData = `email +lakhan.demo@contentment.org +john@gmail.com +anik@contentment.org +harry@potter.com +anikgh89@gmail.com`; diff --git a/dashboard/src/pages/Users.tsx b/dashboard/src/pages/Users.tsx index 5d36c5b..9b9e512 100644 --- a/dashboard/src/pages/Users.tsx +++ b/dashboard/src/pages/Users.tsx @@ -42,6 +42,7 @@ import { UserDetailsQuery } from '../graphql/queries'; import { UpdateUser } from '../graphql/mutation'; import EditUserModal from '../components/EditUserModal'; import DeleteUserModal from '../components/DeleteUserModal'; +import InviteEmailModal from '../components/InviteEmailModal'; interface paginationPropTypes { limit: number; @@ -177,6 +178,7 @@ export default function Users() { Users + {!loading ? ( userList.length > 0 ? ( diff --git a/dashboard/src/utils/index.ts b/dashboard/src/utils/index.ts index 64ca0bf..60f4906 100644 --- a/dashboard/src/utils/index.ts +++ b/dashboard/src/utils/index.ts @@ -64,3 +64,14 @@ export const getObjectDiff = (obj1: any, obj2: any) => { return diff; }; + +export const validateEmail = (email: string) => { + if (!email || email === '') return true; + return email + .toLowerCase() + .match( + /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ + ) + ? true + : false; +}; From e126bfddadf50a4b6aab03a4150555cf2e54c477 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Tue, 15 Mar 2022 20:31:54 +0530 Subject: [PATCH 02/10] invite email modal updated --- dashboard/src/components/InviteEmailModal.tsx | 69 ++++++++++++++----- dashboard/src/utils/index.ts | 11 +++ 2 files changed, 64 insertions(+), 16 deletions(-) diff --git a/dashboard/src/components/InviteEmailModal.tsx b/dashboard/src/components/InviteEmailModal.tsx index 7f69bd5..33bb792 100644 --- a/dashboard/src/components/InviteEmailModal.tsx +++ b/dashboard/src/components/InviteEmailModal.tsx @@ -27,11 +27,11 @@ import { useClient } from 'urql'; import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa'; import { useDropzone } from 'react-dropzone'; import { escape } from 'lodash'; -import { validateEmail } from '../utils'; +import { validateEmail, validateURI } from '../utils'; import { UpdateUser } from '../graphql/mutation'; import { ArrayInputOperations, csvDemoData } from '../constants'; -interface emailDataTypes { +interface stateDataTypes { value: string; isInvalid: boolean; } @@ -40,19 +40,12 @@ const InviteEmailModal = () => { const client = useClient(); const toast = useToast(); const { isOpen, onOpen, onClose } = useDisclosure(); - const [emails, setEmails] = useState([ - { - value: '', - isInvalid: false, - }, - { - value: '', - isInvalid: false, - }, - { - value: '', - isInvalid: false, - }, + const [tabIndex, setTabIndex] = useState(0); + const [redirectURI, setRedirectURI] = useState({ + value: '', + isInvalid: false, + }); + const [emails, setEmails] = useState([ { value: '', isInvalid: false, @@ -90,6 +83,18 @@ const InviteEmailModal = () => { const onDrop = useCallback((acceptedFiles) => { console.log(acceptedFiles); }, []); + const handleTabsChange = (index: number) => { + setTabIndex(index); + }; + const setRedirectURIHandler = (value: string) => { + const updatedRedirectURI: stateDataTypes = { + value: '', + isInvalid: false, + }; + updatedRedirectURI.value = value; + updatedRedirectURI.isInvalid = !validateURI(value); + setRedirectURI(updatedRedirectURI); + }; const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); return ( <> @@ -111,7 +116,12 @@ const InviteEmailModal = () => { Invite Email - + Enter emails Upload CSV @@ -124,6 +134,33 @@ const InviteEmailModal = () => { > + + Redirect URI + + + + + setRedirectURIHandler(e.currentTarget.value) + } + /> + + { ? true : false; }; + +export const validateURI = (uri: string) => { + if (!uri || uri === '') return true; + return uri + .toLowerCase() + .match( + /(?:^|\s)((https?:\/\/)?(?:localhost|[\w-]+(?:\.[\w-]+)+)(:\d+)?(\/\S*)?)/ + ) + ? true + : false; +}; From 2913fa06035b18c61fe8c2a121c6203c63200679 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Tue, 15 Mar 2022 23:51:54 +0530 Subject: [PATCH 03/10] updates --- dashboard/src/components/InviteEmailModal.tsx | 26 ++++++++++--- dashboard/src/constants.ts | 3 +- dashboard/src/utils/parseCSV.ts | 39 +++++++++++++++++++ 3 files changed, 61 insertions(+), 7 deletions(-) create mode 100644 dashboard/src/utils/parseCSV.ts diff --git a/dashboard/src/components/InviteEmailModal.tsx b/dashboard/src/components/InviteEmailModal.tsx index 33bb792..7cd9ad2 100644 --- a/dashboard/src/components/InviteEmailModal.tsx +++ b/dashboard/src/components/InviteEmailModal.tsx @@ -1,4 +1,4 @@ -import React, { useState, useCallback } from 'react'; +import React, { useState, useCallback, useEffect } from 'react'; import { Button, Center, @@ -30,6 +30,7 @@ import { escape } from 'lodash'; import { validateEmail, validateURI } from '../utils'; import { UpdateUser } from '../graphql/mutation'; import { ArrayInputOperations, csvDemoData } from '../constants'; +import parseCSV from '../utils/parseCSV'; interface stateDataTypes { value: string; @@ -51,6 +52,16 @@ const InviteEmailModal = () => { isInvalid: false, }, ]); + const [disableSendButton, setDisableSendButton] = useState(false); + useEffect(() => { + if (redirectURI.isInvalid) { + setDisableSendButton(true); + } else if (emails.some((emailData) => emailData.isInvalid)) { + setDisableSendButton(true); + } else { + setDisableSendButton(false); + } + }, [redirectURI, emails]); const sendInviteHandler = async () => { onClose(); }; @@ -80,8 +91,10 @@ const InviteEmailModal = () => { updatedEmailList[index].isInvalid = !validateEmail(value); setEmails(updatedEmailList); }; - const onDrop = useCallback((acceptedFiles) => { - console.log(acceptedFiles); + const onDrop = useCallback(async (acceptedFiles) => { + const result = await parseCSV(acceptedFiles[0], ','); + setEmails(result); + setTabIndex(0); }, []); const handleTabsChange = (index: number) => { setTabIndex(index); @@ -95,7 +108,10 @@ const InviteEmailModal = () => { updatedRedirectURI.isInvalid = !validateURI(value); setRedirectURI(updatedRedirectURI); }; - const { getRootProps, getInputProps, isDragActive } = useDropzone({ onDrop }); + const { getRootProps, getInputProps, isDragActive } = useDropzone({ + onDrop, + accept: 'text/csv', + }); return ( <> - Invite Email + Invite Members { ); }; -export default InviteEmailModal; +export default InviteMembersModal; diff --git a/dashboard/src/pages/Users.tsx b/dashboard/src/pages/Users.tsx index 9b9e512..c98df5e 100644 --- a/dashboard/src/pages/Users.tsx +++ b/dashboard/src/pages/Users.tsx @@ -42,7 +42,7 @@ import { UserDetailsQuery } from '../graphql/queries'; import { UpdateUser } from '../graphql/mutation'; import EditUserModal from '../components/EditUserModal'; import DeleteUserModal from '../components/DeleteUserModal'; -import InviteEmailModal from '../components/InviteEmailModal'; +import InviteMembersModal from '../components/InviteMembersModal'; interface paginationPropTypes { limit: number; @@ -178,7 +178,7 @@ export default function Users() { Users - + {!loading ? ( userList.length > 0 ? ( From 8ec52a90f1a68c0ef66da1bd5a250fce5a742533 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Wed, 16 Mar 2022 14:08:08 +0530 Subject: [PATCH 05/10] updates --- dashboard/src/components/InviteMembersModal.tsx | 14 +++++++------- dashboard/src/pages/Users.tsx | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/dashboard/src/components/InviteMembersModal.tsx b/dashboard/src/components/InviteMembersModal.tsx index 9bc159f..50a4c2c 100644 --- a/dashboard/src/components/InviteMembersModal.tsx +++ b/dashboard/src/components/InviteMembersModal.tsx @@ -37,7 +37,7 @@ interface stateDataTypes { isInvalid: boolean; } -const InviteMembersModal = () => { +const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { const client = useClient(); const toast = useToast(); const { isOpen, onOpen, onClose } = useDisclosure(); @@ -91,14 +91,14 @@ const InviteMembersModal = () => { updatedEmailList[index].isInvalid = !validateEmail(value); setEmails(updatedEmailList); }; + const changeTabsHandler = (index: number) => { + setTabIndex(index); + }; const onDrop = useCallback(async (acceptedFiles) => { const result = await parseCSV(acceptedFiles[0], ','); setEmails(result); - setTabIndex(0); + changeTabsHandler(0); }, []); - const handleTabsChange = (index: number) => { - setTabIndex(index); - }; const setRedirectURIHandler = (value: string) => { const updatedRedirectURI: stateDataTypes = { value: '', @@ -119,7 +119,7 @@ const InviteMembersModal = () => { colorScheme="blue" variant="solid" onClick={onOpen} - isDisabled={false} + isDisabled={disabled} size="sm" >
@@ -136,7 +136,7 @@ const InviteMembersModal = () => { isFitted variant="enclosed" index={tabIndex} - onChange={handleTabsChange} + onChange={changeTabsHandler} > Enter emails diff --git a/dashboard/src/pages/Users.tsx b/dashboard/src/pages/Users.tsx index c98df5e..552fbe8 100644 --- a/dashboard/src/pages/Users.tsx +++ b/dashboard/src/pages/Users.tsx @@ -178,7 +178,7 @@ export default function Users() { Users - + {!loading ? ( userList.length > 0 ? ( From 32f8c99a71b9fd7832039472d22f7467c8f6971f Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Wed, 16 Mar 2022 14:08:22 +0530 Subject: [PATCH 06/10] updates --- .../src/components/InviteMembersModal.tsx | 35 ++++++++++++++++--- dashboard/src/graphql/queries/index.ts | 8 +++++ dashboard/src/pages/Users.tsx | 16 +++++++-- 3 files changed, 52 insertions(+), 7 deletions(-) diff --git a/dashboard/src/components/InviteMembersModal.tsx b/dashboard/src/components/InviteMembersModal.tsx index 50a4c2c..c154d28 100644 --- a/dashboard/src/components/InviteMembersModal.tsx +++ b/dashboard/src/components/InviteMembersModal.tsx @@ -62,8 +62,22 @@ const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { setDisableSendButton(false); } }, [redirectURI, emails]); + useEffect(() => { + return () => { + setRedirectURI({ + value: '', + isInvalid: false, + }); + setEmails([ + { + value: '', + isInvalid: false, + }, + ]); + }; + }, []); const sendInviteHandler = async () => { - onClose(); + closeModalHandler(); }; const updateEmailListHandler = (operation: string, index: number = 0) => { switch (operation) { @@ -112,6 +126,19 @@ const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { onDrop, accept: 'text/csv', }); + const closeModalHandler = () => { + setRedirectURI({ + value: '', + isInvalid: false, + }); + setEmails([ + { + value: '', + isInvalid: false, + }, + ]); + onClose(); + }; return ( <> - + Invite Members diff --git a/dashboard/src/graphql/queries/index.ts b/dashboard/src/graphql/queries/index.ts index 8528f3f..698452e 100644 --- a/dashboard/src/graphql/queries/index.ts +++ b/dashboard/src/graphql/queries/index.ts @@ -84,3 +84,11 @@ export const UserDetailsQuery = ` } } `; + +export const EmailVerificationQuery = ` + query { + _env{ + DISABLE_EMAIL_VERIFICATION + } + } +`; diff --git a/dashboard/src/pages/Users.tsx b/dashboard/src/pages/Users.tsx index 552fbe8..7e542d1 100644 --- a/dashboard/src/pages/Users.tsx +++ b/dashboard/src/pages/Users.tsx @@ -38,7 +38,7 @@ import { FaExclamationCircle, FaAngleDown, } from 'react-icons/fa'; -import { UserDetailsQuery } from '../graphql/queries'; +import { EmailVerificationQuery, UserDetailsQuery } from '../graphql/queries'; import { UpdateUser } from '../graphql/mutation'; import EditUserModal from '../components/EditUserModal'; import DeleteUserModal from '../components/DeleteUserModal'; @@ -102,6 +102,8 @@ export default function Users() { }); 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 @@ -133,8 +135,18 @@ export default function Users() { } 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(); @@ -178,7 +190,7 @@ export default function Users() { Users - + {!loading ? ( userList.length > 0 ? ( From f65ea72944d3cb3b670cbe5eff10d35794cdb6f1 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Wed, 16 Mar 2022 14:10:55 +0530 Subject: [PATCH 07/10] package-lock.json --- dashboard/package-lock.json | 71 +++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index 864d9cb..7917cfa 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -22,6 +22,7 @@ "lodash": "^4.17.21", "react": "^17.0.2", "react-dom": "^17.0.2", + "react-dropzone": "^12.0.4", "react-icons": "^4.3.1", "react-router-dom": "^6.2.1", "typescript": "^4.5.4", @@ -1251,6 +1252,14 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" }, + "node_modules/attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==", + "engines": { + "node": ">=4" + } + }, "node_modules/babel-plugin-macros": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", @@ -1631,6 +1640,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/file-selector": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz", + "integrity": "sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==", + "dependencies": { + "tslib": "^2.0.3" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -1914,9 +1934,9 @@ } }, "node_modules/prop-types": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.0.tgz", - "integrity": "sha512-fDGekdaHh65eI3lMi5OnErU6a8Ighg2KjcjQxO7m8VHyWjcPyj5kiOgV1LQDOOOgVy3+5FgjXvdSSX7B8/5/4g==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -1959,6 +1979,22 @@ "react": "17.0.2" } }, + "node_modules/react-dropzone": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz", + "integrity": "sha512-fcqHEYe1MzAghU6/Hz86lHDlBNsA+lO48nAcm7/wA+kIzwS6uuJbUG33tBZjksj7GAZ1iUQ6NHwjUURPmSGang==", + "dependencies": { + "attr-accept": "^2.2.2", + "file-selector": "^0.4.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">= 10.13" + }, + "peerDependencies": { + "react": ">= 16.8" + } + }, "node_modules/react-fast-compare": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", @@ -3226,6 +3262,11 @@ } } }, + "attr-accept": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/attr-accept/-/attr-accept-2.2.2.tgz", + "integrity": "sha512-7prDjvt9HmqiZ0cl5CRjtS84sEyhsHP2coDkaZKRKVfCDo9s7iw7ChVmar78Gu9pC4SoR/28wFu/G5JJhTnqEg==" + }, "babel-plugin-macros": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-2.8.0.tgz", @@ -3478,6 +3519,14 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==" }, + "file-selector": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.4.0.tgz", + "integrity": "sha512-iACCiXeMYOvZqlF1kTiYINzgepRBymz1wwjiuup9u9nayhb6g4fSwiyJ/6adli+EPwrWtpgQAh2PoS7HukEGEg==", + "requires": { + "tslib": "^2.0.3" + } + }, "find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -3707,9 +3756,9 @@ } }, "prop-types": { - "version": "15.8.0", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.0.tgz", - "integrity": "sha512-fDGekdaHh65eI3lMi5OnErU6a8Ighg2KjcjQxO7m8VHyWjcPyj5kiOgV1LQDOOOgVy3+5FgjXvdSSX7B8/5/4g==", + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3743,6 +3792,16 @@ "scheduler": "^0.20.2" } }, + "react-dropzone": { + "version": "12.0.4", + "resolved": "https://registry.npmjs.org/react-dropzone/-/react-dropzone-12.0.4.tgz", + "integrity": "sha512-fcqHEYe1MzAghU6/Hz86lHDlBNsA+lO48nAcm7/wA+kIzwS6uuJbUG33tBZjksj7GAZ1iUQ6NHwjUURPmSGang==", + "requires": { + "attr-accept": "^2.2.2", + "file-selector": "^0.4.0", + "prop-types": "^15.8.1" + } + }, "react-fast-compare": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/react-fast-compare/-/react-fast-compare-3.2.0.tgz", From 2213619ed5f382faae3ea07d60cd764158cef3d3 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Wed, 16 Mar 2022 18:06:51 +0530 Subject: [PATCH 08/10] updates --- .../src/components/InviteMembersModal.tsx | 74 ++++++++++--------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/dashboard/src/components/InviteMembersModal.tsx b/dashboard/src/components/InviteMembersModal.tsx index c154d28..ed5b98f 100644 --- a/dashboard/src/components/InviteMembersModal.tsx +++ b/dashboard/src/components/InviteMembersModal.tsx @@ -224,42 +224,44 @@ const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { - {emails.map((emailData, index) => ( - - - - inputChangeHandler(e.currentTarget.value, index) - } - /> - - - - - - ))} + /> + + + + + + ))} + @@ -268,7 +270,7 @@ const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { align="center" textAlign="center" bg="#f0f0f0" - h={231} + h={230} p={50} m={2} borderRadius={5} From d709f53c47b7e3f86db854e167892462d1a279d5 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Wed, 16 Mar 2022 20:13:18 +0530 Subject: [PATCH 09/10] updates --- .../src/components/InviteMembersModal.tsx | 94 +++++++++++++------ dashboard/src/graphql/mutation/index.ts | 8 ++ dashboard/src/pages/Users.tsx | 5 +- 3 files changed, 78 insertions(+), 29 deletions(-) diff --git a/dashboard/src/components/InviteMembersModal.tsx b/dashboard/src/components/InviteMembersModal.tsx index ed5b98f..d267875 100644 --- a/dashboard/src/components/InviteMembersModal.tsx +++ b/dashboard/src/components/InviteMembersModal.tsx @@ -28,7 +28,7 @@ import { FaUserPlus, FaMinusCircle, FaPlus, FaUpload } from 'react-icons/fa'; import { useDropzone } from 'react-dropzone'; import { escape } from 'lodash'; import { validateEmail, validateURI } from '../utils'; -import { UpdateUser } from '../graphql/mutation'; +import { InviteMembers } from '../graphql/mutation'; import { ArrayInputOperations, csvDemoData } from '../constants'; import parseCSV from '../utils/parseCSV'; @@ -37,22 +37,33 @@ interface stateDataTypes { isInvalid: boolean; } -const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { +interface requestParamTypes { + emails: string[]; + redirect_uri?: string; +} + +const initData: stateDataTypes = { + value: '', + isInvalid: false, +}; + +const InviteMembersModal = ({ + updateUserList, + disabled = true, +}: { + updateUserList: Function; + disabled: boolean; +}) => { const client = useClient(); const toast = useToast(); const { isOpen, onOpen, onClose } = useDisclosure(); const [tabIndex, setTabIndex] = useState(0); const [redirectURI, setRedirectURI] = useState({ - value: '', - isInvalid: false, + ...initData, }); - const [emails, setEmails] = useState([ - { - value: '', - isInvalid: false, - }, - ]); + const [emails, setEmails] = useState([{ ...initData }]); const [disableSendButton, setDisableSendButton] = useState(false); + const [loading, setLoading] = React.useState(false); useEffect(() => { if (redirectURI.isInvalid) { setDisableSendButton(true); @@ -64,31 +75,58 @@ const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { }, [redirectURI, emails]); useEffect(() => { return () => { - setRedirectURI({ - value: '', - isInvalid: false, - }); - setEmails([ - { - value: '', - isInvalid: false, - }, - ]); + setRedirectURI({ ...initData }); + setEmails([{ ...initData }]); }; }, []); const sendInviteHandler = async () => { + 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; + } + if (emailList.length > 1) { + 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); + } closeModalHandler(); }; const updateEmailListHandler = (operation: string, index: number = 0) => { switch (operation) { case ArrayInputOperations.APPEND: - setEmails([ - ...emails, - { - value: '', - isInvalid: false, - }, - ]); + setEmails([...emails, { ...initData }]); break; case ArrayInputOperations.REMOVE: const updatedEmailList = [...emails]; @@ -318,7 +356,7 @@ const InviteMembersModal = ({ disabled = true }: { disabled: boolean }) => { colorScheme="blue" variant="solid" onClick={sendInviteHandler} - isDisabled={disableSendButton} + isDisabled={disableSendButton || loading} >
Send diff --git a/dashboard/src/graphql/mutation/index.ts b/dashboard/src/graphql/mutation/index.ts index 9736a01..df5db93 100644 --- a/dashboard/src/graphql/mutation/index.ts +++ b/dashboard/src/graphql/mutation/index.ts @@ -45,3 +45,11 @@ export const DeleteUser = ` } } `; + +export const InviteMembers = ` + mutation inviteMembers($params: InviteMemberInput!) { + _invite_members(params: $params) { + message + } + } +`; diff --git a/dashboard/src/pages/Users.tsx b/dashboard/src/pages/Users.tsx index 7e542d1..a9d1d05 100644 --- a/dashboard/src/pages/Users.tsx +++ b/dashboard/src/pages/Users.tsx @@ -190,7 +190,10 @@ export default function Users() { Users - + {!loading ? ( userList.length > 0 ? ( From df7837f44d32b2ebc40df7b9a81e3b9a394e28a7 Mon Sep 17 00:00:00 2001 From: Anik Ghosh Date: Wed, 16 Mar 2022 20:22:24 +0530 Subject: [PATCH 10/10] updates --- dashboard/src/components/InviteMembersModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dashboard/src/components/InviteMembersModal.tsx b/dashboard/src/components/InviteMembersModal.tsx index d267875..cf18f21 100644 --- a/dashboard/src/components/InviteMembersModal.tsx +++ b/dashboard/src/components/InviteMembersModal.tsx @@ -91,7 +91,7 @@ const InviteMembersModal = ({ if (redirectURI.value !== '' && !redirectURI.isInvalid) { params.redirect_uri = redirectURI.value; } - if (emailList.length > 1) { + if (emailList.length > 0) { const res = await client .mutation(InviteMembers, { params,