diff --git a/dashboard/package-lock.json b/dashboard/package-lock.json index c04cac0..41d31f9 100644 --- a/dashboard/package-lock.json +++ b/dashboard/package-lock.json @@ -2529,8 +2529,7 @@ "@chakra-ui/css-reset": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@chakra-ui/css-reset/-/css-reset-1.1.1.tgz", - "integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==", - "requires": {} + "integrity": "sha512-+KNNHL4OWqeKia5SL858K3Qbd8WxMij9mWIilBzLD4j2KFrl/+aWFw8syMKth3NmgIibrjsljo+PU3fy2o50dg==" }, "@chakra-ui/descendant": { "version": "2.1.1", @@ -3134,8 +3133,7 @@ "@graphql-typed-document-node/core": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz", - "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==", - "requires": {} + "integrity": "sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==" }, "@popperjs/core": { "version": "2.11.0", @@ -3845,8 +3843,7 @@ "react-icons": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.3.1.tgz", - "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==", - "requires": {} + "integrity": "sha512-cB10MXLTs3gVuXimblAdI71jrJx8njrJZmNMEMC+sQu5B/BIOmlsAjskdqpn81y8UBVEGuHODd7/ci5DvoSzTQ==" }, "react-is": { "version": "16.13.1", @@ -4032,8 +4029,7 @@ "use-callback-ref": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.2.5.tgz", - "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==", - "requires": {} + "integrity": "sha512-gN3vgMISAgacF7sqsLPByqoePooY3n2emTH59Ur5d/M8eg4WTWu1xp8i8DHjohftIyEx0S08RiYxbffr4j8Peg==" }, "use-sidecar": { "version": "1.0.5", diff --git a/dashboard/src/components/Menu.tsx b/dashboard/src/components/Menu.tsx index e424a0f..7e59043 100644 --- a/dashboard/src/components/Menu.tsx +++ b/dashboard/src/components/Menu.tsx @@ -31,6 +31,7 @@ import { FiUsers, FiChevronDown, FiLink, + FiFileText, } from 'react-icons/fi'; import { BiCustomize } from 'react-icons/bi'; import { AiOutlineKey } from 'react-icons/ai'; @@ -113,6 +114,7 @@ const LinkItems: Array = [ }, { name: 'Users', icon: FiUsers, route: '/users' }, { name: 'Webhooks', icon: FiLink, route: '/webhooks' }, + { name: 'Email Templates', icon: FiFileText, route: '/email-templates' }, ]; interface SidebarProps extends BoxProps { diff --git a/dashboard/src/components/UpdateEmailTemplateModal.tsx b/dashboard/src/components/UpdateEmailTemplateModal.tsx new file mode 100644 index 0000000..0d7ff05 --- /dev/null +++ b/dashboard/src/components/UpdateEmailTemplateModal.tsx @@ -0,0 +1,327 @@ +import React, { useEffect, useState } from 'react'; +import { + Button, + Center, + Flex, + Input, + InputGroup, + MenuItem, + Modal, + ModalBody, + ModalCloseButton, + ModalContent, + ModalFooter, + ModalHeader, + ModalOverlay, + Select, + useDisclosure, + useToast, +} from '@chakra-ui/react'; +import { FaPlus } from 'react-icons/fa'; +import { useClient } from 'urql'; +import { + UpdateModalViews, + EmailTemplateInputDataFields, + emailTemplateEventNames, +} from '../constants'; +import { capitalizeFirstLetter } from '../utils'; +import { AddEmailTemplate, EditEmailTemplate } from '../graphql/mutation'; + +interface selectedEmailTemplateDataTypes { + [EmailTemplateInputDataFields.ID]: string; + [EmailTemplateInputDataFields.EVENT_NAME]: string; + [EmailTemplateInputDataFields.SUBJECT]: string; + [EmailTemplateInputDataFields.CREATED_AT]: number; + [EmailTemplateInputDataFields.TEMPLATE]: string; +} + +interface UpdateEmailTemplateInputPropTypes { + view: UpdateModalViews; + selectedTemplate?: selectedEmailTemplateDataTypes; + fetchEmailTemplatesData: Function; +} + +interface emailTemplateDataType { + [EmailTemplateInputDataFields.EVENT_NAME]: string; + [EmailTemplateInputDataFields.SUBJECT]: string; + [EmailTemplateInputDataFields.TEMPLATE]: string; +} + +interface validatorDataType { + [EmailTemplateInputDataFields.SUBJECT]: boolean; + [EmailTemplateInputDataFields.TEMPLATE]: boolean; +} + +const initTemplateData: emailTemplateDataType = { + [EmailTemplateInputDataFields.EVENT_NAME]: + emailTemplateEventNames.BASIC_AUTH_SIGNUP, + [EmailTemplateInputDataFields.SUBJECT]: '', + [EmailTemplateInputDataFields.TEMPLATE]: '', +}; + +const initTemplateValidatorData: validatorDataType = { + [EmailTemplateInputDataFields.SUBJECT]: true, + [EmailTemplateInputDataFields.TEMPLATE]: true, +}; + +const UpdateEmailTemplate = ({ + view, + selectedTemplate, + fetchEmailTemplatesData, +}: UpdateEmailTemplateInputPropTypes) => { + const client = useClient(); + const toast = useToast(); + const { isOpen, onOpen, onClose } = useDisclosure(); + const [loading, setLoading] = useState(false); + const [templateData, setTemplateData] = useState({ + ...initTemplateData, + }); + const [validator, setValidator] = useState({ + ...initTemplateValidatorData, + }); + const inputChangehandler = (inputType: string, value: any) => { + if (inputType !== EmailTemplateInputDataFields.EVENT_NAME) { + setValidator({ + ...validator, + [inputType]: value?.trim().length, + }); + } + setTemplateData({ ...templateData, [inputType]: value }); + }; + + const validateData = () => { + return ( + !loading && + templateData[EmailTemplateInputDataFields.EVENT_NAME].length > 0 && + templateData[EmailTemplateInputDataFields.TEMPLATE].length > 0 && + templateData[EmailTemplateInputDataFields.SUBJECT].length > 0 && + validator[EmailTemplateInputDataFields.TEMPLATE] && + validator[EmailTemplateInputDataFields.SUBJECT] + ); + }; + + const saveData = async () => { + if (!validateData()) return; + setLoading(true); + const params = { + [EmailTemplateInputDataFields.EVENT_NAME]: + templateData[EmailTemplateInputDataFields.EVENT_NAME], + [EmailTemplateInputDataFields.SUBJECT]: + templateData[EmailTemplateInputDataFields.SUBJECT], + [EmailTemplateInputDataFields.TEMPLATE]: + templateData[EmailTemplateInputDataFields.TEMPLATE], + }; + let res: any = {}; + if ( + view === UpdateModalViews.Edit && + selectedTemplate?.[EmailTemplateInputDataFields.ID] + ) { + res = await client + .mutation(EditEmailTemplate, { + params: { + ...params, + id: selectedTemplate[EmailTemplateInputDataFields.ID], + }, + }) + .toPromise(); + } else { + res = await client.mutation(AddEmailTemplate, { params }).toPromise(); + } + setLoading(false); + if (res.error) { + toast({ + title: capitalizeFirstLetter(res.error.message), + isClosable: true, + status: 'error', + position: 'bottom-right', + }); + } else if ( + res.data?._add_email_template || + res.data?._update_email_template + ) { + toast({ + title: capitalizeFirstLetter( + res.data?._add_email_template?.message || + res.data?._update_email_template?.message + ), + isClosable: true, + status: 'success', + position: 'bottom-right', + }); + setTemplateData({ + ...initTemplateData, + }); + setValidator({ ...initTemplateValidatorData }); + fetchEmailTemplatesData(); + } + view === UpdateModalViews.ADD && onClose(); + }; + const resetData = () => { + if (selectedTemplate) { + setTemplateData(selectedTemplate); + } else { + setTemplateData({ ...initTemplateData }); + } + }; + useEffect(() => { + if ( + isOpen && + view === UpdateModalViews.Edit && + selectedTemplate && + Object.keys(selectedTemplate || {}).length + ) { + const { id, created_at, ...rest } = selectedTemplate; + setTemplateData(rest); + } + }, [isOpen]); + return ( + <> + {view === UpdateModalViews.ADD ? ( + + ) : ( + Edit + )} + + + + + {view === UpdateModalViews.ADD + ? 'Add New Email Template' + : 'Edit Email Template'} + + + + + + Event Name + + + + + + Subject + + + + inputChangehandler( + EmailTemplateInputDataFields.SUBJECT, + e.currentTarget.value + ) + } + /> + + + + + Template Body + + + + + + inputChangehandler( + EmailTemplateInputDataFields.TEMPLATE, + e.currentTarget.value + ) + } + /> + + + + + + + + + + + + + ); +}; + +export default UpdateEmailTemplate; diff --git a/dashboard/src/components/UpdateWebhookModal.tsx b/dashboard/src/components/UpdateWebhookModal.tsx index 60590f2..d399c62 100644 --- a/dashboard/src/components/UpdateWebhookModal.tsx +++ b/dashboard/src/components/UpdateWebhookModal.tsx @@ -27,12 +27,11 @@ import { ArrayInputOperations, WebhookInputDataFields, WebhookInputHeaderFields, - UpdateWebhookModalViews, + UpdateModalViews, webhookVerifiedStatus, } from '../constants'; import { capitalizeFirstLetter, validateURI } from '../utils'; import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation'; -import { rest } from 'lodash'; import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi'; interface headersDataType { @@ -54,7 +53,7 @@ interface selecetdWebhookDataTypes { } interface UpdateWebhookModalInputPropTypes { - view: UpdateWebhookModalViews; + view: UpdateModalViews; selectedWebhook?: selecetdWebhookDataTypes; fetchWebookData: Function; } @@ -254,7 +253,7 @@ const UpdateWebhookModal = ({ const params = getParams(); let res: any = {}; if ( - view === UpdateWebhookModalViews.Edit && + view === UpdateModalViews.Edit && selectedWebhook?.[WebhookInputDataFields.ID] ) { res = await client @@ -292,12 +291,12 @@ const UpdateWebhookModal = ({ setValidator({ ...initWebhookValidatorData }); fetchWebookData(); } - view === UpdateWebhookModalViews.ADD && onClose(); + view === UpdateModalViews.ADD && onClose(); }; useEffect(() => { if ( isOpen && - view === UpdateWebhookModalViews.Edit && + view === UpdateModalViews.Edit && selectedWebhook && Object.keys(selectedWebhook || {}).length ) { @@ -347,7 +346,7 @@ const UpdateWebhookModal = ({ }; return ( <> - {view === UpdateWebhookModalViews.ADD ? ( + {view === UpdateModalViews.ADD ? (