import React, { useEffect, useState } from 'react'; import { Button, Center, Flex, Input, InputGroup, InputRightElement, MenuItem, Modal, ModalBody, ModalCloseButton, ModalContent, ModalFooter, ModalHeader, ModalOverlay, Select, Switch, Text, useDisclosure, useToast, } from '@chakra-ui/react'; import { FaMinusCircle, FaPlus } from 'react-icons/fa'; import { useClient } from 'urql'; import { webhookEventNames, ArrayInputOperations, WebhookInputDataFields, WebhookInputHeaderFields, UpdateModalViews, webhookVerifiedStatus, } from '../constants'; import { capitalizeFirstLetter, validateURI } from '../utils'; import { AddWebhook, EditWebhook, TestEndpoint } from '../graphql/mutation'; import { BiCheckCircle, BiError, BiErrorCircle } from 'react-icons/bi'; interface headersDataType { [WebhookInputHeaderFields.KEY]: string; [WebhookInputHeaderFields.VALUE]: string; } interface headersValidatorDataType { [WebhookInputHeaderFields.KEY]: boolean; [WebhookInputHeaderFields.VALUE]: boolean; } interface selecetdWebhookDataTypes { [WebhookInputDataFields.ID]: string; [WebhookInputDataFields.EVENT_NAME]: string; [WebhookInputDataFields.ENDPOINT]: string; [WebhookInputDataFields.ENABLED]: boolean; [WebhookInputDataFields.HEADERS]?: Record; } interface UpdateWebhookModalInputPropTypes { view: UpdateModalViews; selectedWebhook?: selecetdWebhookDataTypes; fetchWebookData: Function; } const initHeadersData: headersDataType = { [WebhookInputHeaderFields.KEY]: '', [WebhookInputHeaderFields.VALUE]: '', }; const initHeadersValidatorData: headersValidatorDataType = { [WebhookInputHeaderFields.KEY]: true, [WebhookInputHeaderFields.VALUE]: true, }; interface webhookDataType { [WebhookInputDataFields.EVENT_NAME]: string; [WebhookInputDataFields.ENDPOINT]: string; [WebhookInputDataFields.ENABLED]: boolean; [WebhookInputDataFields.HEADERS]: headersDataType[]; } interface validatorDataType { [WebhookInputDataFields.ENDPOINT]: boolean; [WebhookInputDataFields.HEADERS]: headersValidatorDataType[]; } const initWebhookData: webhookDataType = { [WebhookInputDataFields.EVENT_NAME]: webhookEventNames.USER_LOGIN, [WebhookInputDataFields.ENDPOINT]: '', [WebhookInputDataFields.ENABLED]: true, [WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }], }; const initWebhookValidatorData: validatorDataType = { [WebhookInputDataFields.ENDPOINT]: true, [WebhookInputDataFields.HEADERS]: [{ ...initHeadersValidatorData }], }; const UpdateWebhookModal = ({ view, selectedWebhook, fetchWebookData, }: UpdateWebhookModalInputPropTypes) => { const client = useClient(); const toast = useToast(); const { isOpen, onOpen, onClose } = useDisclosure(); const [loading, setLoading] = useState(false); const [verifyingEndpoint, setVerifyingEndpoint] = useState(false); const [webhook, setWebhook] = useState({ ...initWebhookData, }); const [validator, setValidator] = useState({ ...initWebhookValidatorData, }); const [verifiedStatus, setVerifiedStatus] = useState( webhookVerifiedStatus.PENDING ); const inputChangehandler = ( inputType: string, value: any, headerInputType: string = WebhookInputHeaderFields.KEY, headerIndex: number = 0 ) => { if ( verifiedStatus !== webhookVerifiedStatus.PENDING && inputType !== WebhookInputDataFields.ENABLED ) { setVerifiedStatus(webhookVerifiedStatus.PENDING); } switch (inputType) { case WebhookInputDataFields.EVENT_NAME: setWebhook({ ...webhook, [inputType]: value }); break; case WebhookInputDataFields.ENDPOINT: setWebhook({ ...webhook, [inputType]: value }); setValidator({ ...validator, [WebhookInputDataFields.ENDPOINT]: validateURI(value), }); break; case WebhookInputDataFields.ENABLED: setWebhook({ ...webhook, [inputType]: value }); break; case WebhookInputDataFields.HEADERS: const updatedHeaders: any = [ ...webhook[WebhookInputDataFields.HEADERS], ]; const updatedHeadersValidatorData: any = [ ...validator[WebhookInputDataFields.HEADERS], ]; const otherHeaderInputType = headerInputType === WebhookInputHeaderFields.KEY ? WebhookInputHeaderFields.VALUE : WebhookInputHeaderFields.KEY; updatedHeaders[headerIndex][headerInputType] = value; updatedHeadersValidatorData[headerIndex][headerInputType] = value.length > 0 ? updatedHeaders[headerIndex][otherHeaderInputType].length > 0 : updatedHeaders[headerIndex][otherHeaderInputType].length === 0; updatedHeadersValidatorData[headerIndex][otherHeaderInputType] = value.length > 0 ? updatedHeaders[headerIndex][otherHeaderInputType].length > 0 : updatedHeaders[headerIndex][otherHeaderInputType].length === 0; setWebhook({ ...webhook, [inputType]: updatedHeaders }); setValidator({ ...validator, [inputType]: updatedHeadersValidatorData, }); break; default: break; } }; const updateHeaders = (operation: string, index: number = 0) => { if (verifiedStatus !== webhookVerifiedStatus.PENDING) { setVerifiedStatus(webhookVerifiedStatus.PENDING); } switch (operation) { case ArrayInputOperations.APPEND: setWebhook({ ...webhook, [WebhookInputDataFields.HEADERS]: [ ...(webhook?.[WebhookInputDataFields.HEADERS] || []), { ...initHeadersData }, ], }); setValidator({ ...validator, [WebhookInputDataFields.HEADERS]: [ ...(validator?.[WebhookInputDataFields.HEADERS] || []), { ...initHeadersValidatorData }, ], }); break; case ArrayInputOperations.REMOVE: if (webhook?.[WebhookInputDataFields.HEADERS]?.length) { const updatedHeaders = [...webhook[WebhookInputDataFields.HEADERS]]; updatedHeaders.splice(index, 1); setWebhook({ ...webhook, [WebhookInputDataFields.HEADERS]: updatedHeaders, }); } if (validator?.[WebhookInputDataFields.HEADERS]?.length) { const updatedHeadersData = [ ...validator[WebhookInputDataFields.HEADERS], ]; updatedHeadersData.splice(index, 1); setValidator({ ...validator, [WebhookInputDataFields.HEADERS]: updatedHeadersData, }); } break; default: break; } }; const validateData = () => { return ( !loading && !verifyingEndpoint && webhook[WebhookInputDataFields.EVENT_NAME].length > 0 && webhook[WebhookInputDataFields.ENDPOINT].length > 0 && validator[WebhookInputDataFields.ENDPOINT] && !validator[WebhookInputDataFields.HEADERS].some( (headerData: headersValidatorDataType) => !headerData.key || !headerData.value ) ); }; const getParams = () => { let params: any = { [WebhookInputDataFields.EVENT_NAME]: webhook[WebhookInputDataFields.EVENT_NAME], [WebhookInputDataFields.ENDPOINT]: webhook[WebhookInputDataFields.ENDPOINT], [WebhookInputDataFields.ENABLED]: webhook[WebhookInputDataFields.ENABLED], [WebhookInputDataFields.HEADERS]: {}, }; if (webhook[WebhookInputDataFields.HEADERS].length) { const headers = webhook[WebhookInputDataFields.HEADERS].reduce( (acc, data) => { return data.key ? { ...acc, [data.key]: data.value } : acc; }, {} ); if (Object.keys(headers).length) { params[WebhookInputDataFields.HEADERS] = headers; } } return params; }; const saveData = async () => { if (!validateData()) return; setLoading(true); const params = getParams(); let res: any = {}; if ( view === UpdateModalViews.Edit && selectedWebhook?.[WebhookInputDataFields.ID] ) { res = await client .mutation(EditWebhook, { params: { ...params, id: selectedWebhook[WebhookInputDataFields.ID], }, }) .toPromise(); } else { res = await client.mutation(AddWebhook, { 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_webhook || res.data?._update_webhook) { toast({ title: capitalizeFirstLetter( res.data?._add_webhook?.message || res.data?._update_webhook?.message ), isClosable: true, status: 'success', position: 'bottom-right', }); setWebhook({ ...initWebhookData, [WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }], }); setValidator({ ...initWebhookValidatorData }); fetchWebookData(); } view === UpdateModalViews.ADD && onClose(); }; useEffect(() => { if ( isOpen && view === UpdateModalViews.Edit && selectedWebhook && Object.keys(selectedWebhook || {}).length ) { const { headers, ...rest } = selectedWebhook; const headerItems = Object.entries(headers || {}); if (headerItems.length) { let formattedHeadersData = headerItems.map((headerData) => { return { [WebhookInputHeaderFields.KEY]: headerData[0], [WebhookInputHeaderFields.VALUE]: headerData[1], }; }); setWebhook({ ...rest, [WebhookInputDataFields.HEADERS]: formattedHeadersData, }); setValidator({ ...validator, [WebhookInputDataFields.HEADERS]: new Array( formattedHeadersData.length ) .fill({}) .map(() => ({ ...initHeadersValidatorData })), }); } else { setWebhook({ ...rest, [WebhookInputDataFields.HEADERS]: [{ ...initHeadersData }], }); } } }, [isOpen]); const verifyEndpoint = async () => { if (!validateData()) return; setVerifyingEndpoint(true); const { [WebhookInputDataFields.ENABLED]: _, ...params } = getParams(); const res = await client.mutation(TestEndpoint, { params }).toPromise(); if ( res.data?._test_endpoint?.http_status >= 200 && res.data?._test_endpoint?.http_status < 400 ) { setVerifiedStatus(webhookVerifiedStatus.VERIFIED); } else { setVerifiedStatus(webhookVerifiedStatus.NOT_VERIFIED); } setVerifyingEndpoint(false); }; return ( <> {view === UpdateModalViews.ADD ? ( ) : ( Edit )} {view === UpdateModalViews.ADD ? 'Add New Webhook' : 'Edit Webhook'} Event Name Endpoint inputChangehandler( WebhookInputDataFields.ENDPOINT, e.currentTarget.value ) } /> Enabled Off inputChangehandler( WebhookInputDataFields.ENABLED, !webhook[WebhookInputDataFields.ENABLED] ) } /> On Headers {webhook[WebhookInputDataFields.HEADERS]?.map( (headerData, index) => ( inputChangehandler( WebhookInputDataFields.HEADERS, e.target.value, WebhookInputHeaderFields.KEY, index ) } width="30%" marginRight="2%" />
:
inputChangehandler( WebhookInputDataFields.HEADERS, e.target.value, WebhookInputHeaderFields.VALUE, index ) } width="65%" />
) )}
); }; export default UpdateWebhookModal;