From 9ddd3cef737c506e0db1dcec94ec50ed83376638 Mon Sep 17 00:00:00 2001 From: Ayush Chothe Date: Fri, 6 Sep 2024 23:42:55 +0530 Subject: [PATCH] feat: add Cashfree Payment Provider --- .../customers/CustomerMainInfos.tsx | 9 + .../customers/addDrawer/AddCustomerDrawer.tsx | 9 +- .../addDrawer/ExternalAppsAccordion.tsx | 20 +- .../settings/integrations/AddAdyenDialog.tsx | 5 + .../settings/integrations/AddAnrokDialog.tsx | 2 + .../integrations/AddCashfreeDialog.tsx | 291 +++++++ .../AddEditDeleteSuccessRedirectUrlDialog.tsx | 75 +- .../integrations/AddGocardlessDialog.tsx | 5 + .../integrations/AddHubspotDialog.tsx | 2 + .../AddLagoTaxManagementDialog.tsx | 9 +- .../integrations/AddNetsuiteDialog.tsx | 2 + .../integrations/AddSalesforceDialog.tsx | 2 + .../settings/integrations/AddStripeDialog.tsx | 5 + .../settings/integrations/AddXeroDialog.tsx | 2 + .../integrations/AnrokIntegrationSettings.tsx | 12 +- .../DeleteCashfreeIntegrationDialog.tsx | 85 ++ .../NetsuiteIntegrationSettings.tsx | 12 +- .../integrations/XeroIntegrationSettings.tsx | 12 +- src/core/router/SettingRoutes.tsx | 29 +- src/generated/graphql.tsx | 493 +++++++++++- src/layouts/Settings.tsx | 7 +- .../settings/AdyenIntegrationDetails.tsx | 18 +- src/pages/settings/AdyenIntegrations.tsx | 16 +- .../settings/AnrokIntegrationDetails.tsx | 18 +- src/pages/settings/AnrokIntegrations.tsx | 15 +- .../settings/CashfreeIntegrationDetails.tsx | 385 ++++++++++ src/pages/settings/CashfreeIntegrations.tsx | 274 +++++++ .../settings/GocardlessIntegrationDetails.tsx | 18 +- .../GocardlessIntegrationOauthCallback.tsx | 19 +- src/pages/settings/GocardlessIntegrations.tsx | 16 +- .../settings/HubspotIntegrationDetails.tsx | 18 +- src/pages/settings/HubspotIntegrations.tsx | 16 +- src/pages/settings/Integrations.tsx | 723 ++++++++++-------- .../settings/LagoTaxManagementIntegration.tsx | 14 +- .../settings/NetsuiteIntegrationDetails.tsx | 18 +- src/pages/settings/NetsuiteIntegrations.tsx | 15 +- .../settings/SalesforceIntegrationDetails.tsx | 18 +- src/pages/settings/SalesforceIntegrations.tsx | 16 +- .../settings/StripeIntegrationDetails.tsx | 18 +- src/pages/settings/StripeIntegrations.tsx | 16 +- src/pages/settings/XeroIntegrationDetails.tsx | 18 +- src/pages/settings/XeroIntegrations.tsx | 15 +- src/public/images/Cashfree.svg | 14 + translations/base.json | 21 +- 44 files changed, 2391 insertions(+), 416 deletions(-) create mode 100644 src/components/settings/integrations/AddCashfreeDialog.tsx create mode 100644 src/components/settings/integrations/DeleteCashfreeIntegrationDialog.tsx create mode 100644 src/pages/settings/CashfreeIntegrationDetails.tsx create mode 100644 src/pages/settings/CashfreeIntegrations.tsx create mode 100644 src/public/images/Cashfree.svg diff --git a/src/components/customers/CustomerMainInfos.tsx b/src/components/customers/CustomerMainInfos.tsx index e86b82e32..f6f7e63ff 100644 --- a/src/components/customers/CustomerMainInfos.tsx +++ b/src/components/customers/CustomerMainInfos.tsx @@ -32,6 +32,7 @@ import { import { useInternationalization } from '~/hooks/core/useInternationalization' import Adyen from '~/public/images/adyen.svg' import Anrok from '~/public/images/anrok.svg' +import Cashfree from '~/public/images/cashfree.svg' import Gocardless from '~/public/images/gocardless.svg' import Hubspot from '~/public/images/hubspot.svg' import Netsuite from '~/public/images/netsuite.svg' @@ -134,6 +135,12 @@ gql` code } + ... on CashfreeProvider { + id + name + code + } + ... on AdyenProvider { id name @@ -458,6 +465,8 @@ export const CustomerMainInfos = ({ loading, customer, onEdit }: CustomerMainInf ) : paymentProvider === ProviderTypeEnum?.Adyen ? ( + ) : paymentProvider === ProviderTypeEnum?.Cashfree ? ( + ) : null} {linkedProvider?.name} diff --git a/src/components/customers/addDrawer/AddCustomerDrawer.tsx b/src/components/customers/addDrawer/AddCustomerDrawer.tsx index eac536b91..e1418d40e 100644 --- a/src/components/customers/addDrawer/AddCustomerDrawer.tsx +++ b/src/components/customers/addDrawer/AddCustomerDrawer.tsx @@ -44,6 +44,7 @@ import { NetsuiteCustomer, ProviderCustomer, ProviderPaymentMethodsEnum, + ProviderTypeEnum, SalesforceCustomer, TimezoneEnum, UpdateCustomerInput, @@ -141,9 +142,11 @@ export const AddCustomerDrawer = forwardRef((_, ref) => { return false } - // if syncWithProvider is false, providerCustomerId is required - if (!value?.syncWithProvider && !value?.providerCustomerId) { - return false + if (from?.[1].value.paymentProvider !== ProviderTypeEnum.Cashfree) { + // if syncWithProvider is false, providerCustomerId is required + if (!value?.syncWithProvider && !value?.providerCustomerId) { + return false + } } return true diff --git a/src/components/customers/addDrawer/ExternalAppsAccordion.tsx b/src/components/customers/addDrawer/ExternalAppsAccordion.tsx index c6c873f51..e44da6153 100644 --- a/src/components/customers/addDrawer/ExternalAppsAccordion.tsx +++ b/src/components/customers/addDrawer/ExternalAppsAccordion.tsx @@ -52,6 +52,7 @@ import { import { useInternationalization } from '~/hooks/core/useInternationalization' import Adyen from '~/public/images/adyen.svg' import Anrok from '~/public/images/anrok.svg' +import Cashfree from '~/public/images/cashfree.svg' import GoCardless from '~/public/images/gocardless.svg' import Hubspot from '~/public/images/hubspot.svg' import Netsuite from '~/public/images/netsuite.svg' @@ -130,6 +131,13 @@ gql` code } + ... on CashfreeProvider { + __typename + id + name + code + } + ... on AdyenProvider { __typename id @@ -303,6 +311,14 @@ export const ExternalAppsAccordion = ({ formikProps, isEdition }: TExternalAppsA }) const isSyncWithProviderDisabled = !!formikProps.values.providerCustomer?.syncWithProvider + + const isSyncWithProviderSupported = useMemo(() => { + if (!formikProps.values.paymentProvider) return false + const unsupportedPaymentProviders: ProviderTypeEnum[] = [ProviderTypeEnum.Cashfree] + + return !unsupportedPaymentProviders.includes(formikProps.values.paymentProvider) + }, [formikProps.values.paymentProvider]) + const hadInitialNetsuiteIntegrationCustomer = !!formikProps.initialValues.integrationCustomers?.find( (i) => i.integrationType === IntegrationTypeEnum.Netsuite, @@ -504,6 +520,8 @@ export const ExternalAppsAccordion = ({ formikProps, isEdition }: TExternalAppsA ) : formikProps.values.paymentProvider === ProviderTypeEnum?.Adyen ? ( + ) : formikProps.values.paymentProvider === ProviderTypeEnum?.Cashfree ? ( + ) : null} ) : ( @@ -571,7 +589,7 @@ export const ExternalAppsAccordion = ({ formikProps, isEdition }: TExternalAppsA }} /> - {!!formikProps.values.paymentProviderCode && ( + {!!formikProps.values.paymentProviderCode && isSyncWithProviderSupported && ( <> ((_, ref) => { navigate( generatePath(ADYEN_INTEGRATION_DETAILS_ROUTE, { integrationId: addAdyenPaymentProvider.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AddAnrokDialog.tsx b/src/components/settings/integrations/AddAnrokDialog.tsx index 70bb59994..82b1fb0c2 100644 --- a/src/components/settings/integrations/AddAnrokDialog.tsx +++ b/src/components/settings/integrations/AddAnrokDialog.tsx @@ -23,6 +23,7 @@ import { } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' import { AnrokIntegrationDetailsTabs } from '~/pages/settings/AnrokIntegrationDetails' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { theme } from '~/styles' import { DeleteAnrokIntegrationDialogRef } from './DeleteAnrokIntegrationDialog' @@ -82,6 +83,7 @@ export const AddAnrokDialog = forwardRef((_, ref) => { generatePath(ANROK_INTEGRATION_DETAILS_ROUTE, { integrationId: createAnrokIntegration.id, tab: AnrokIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AddCashfreeDialog.tsx b/src/components/settings/integrations/AddCashfreeDialog.tsx new file mode 100644 index 000000000..12670ba91 --- /dev/null +++ b/src/components/settings/integrations/AddCashfreeDialog.tsx @@ -0,0 +1,291 @@ +import { gql } from '@apollo/client' +import { Stack } from '@mui/material' +import { useFormik } from 'formik' +import { forwardRef, RefObject, useImperativeHandle, useRef, useState } from 'react' +import { useNavigate } from 'react-router' +import { generatePath } from 'react-router-dom' +import { object, string } from 'yup' + +import { Button, Dialog, DialogRef } from '~/components/designSystem' +import { TextInputField } from '~/components/form' +import { addToast } from '~/core/apolloClient' +import { CASHFREE_INTEGRATION_DETAILS_ROUTE } from '~/core/router' +import { + AddCashfreePaymentProviderInput, + AddCashfreeProviderDialogFragment, + CashfreeIntegrationDetailsFragmentDoc, + LagoApiError, + useAddCashfreeApiKeyMutation, + useGetProviderByCodeForCashfreeLazyQuery, + useUpdateCashfreeApiKeyMutation, +} from '~/generated/graphql' +import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' + +import { DeleteCashfreeIntegrationDialogRef } from './DeleteCashfreeIntegrationDialog' + +gql` + fragment AddCashfreeProviderDialog on CashfreeProvider { + id + name + code + clientId + clientSecret + successRedirectUrl + } + + query getProviderByCodeForCashfree($code: String) { + paymentProvider(code: $code) { + ... on CashfreeProvider { + id + } + ... on GocardlessProvider { + id + } + ... on AdyenProvider { + id + } + ... on StripeProvider { + id + } + } + } + + mutation addCashfreeApiKey($input: AddCashfreePaymentProviderInput!) { + addCashfreePaymentProvider(input: $input) { + id + ...AddCashfreeProviderDialog + ...CashfreeIntegrationDetails + } + } + + mutation updateCashfreeApiKey($input: UpdateCashfreePaymentProviderInput!) { + updateCashfreePaymentProvider(input: $input) { + id + ...AddCashfreeProviderDialog + ...CashfreeIntegrationDetails + } + } + + ${CashfreeIntegrationDetailsFragmentDoc} +` + +type TAddCashfreeDialogProps = Partial<{ + deleteModalRef: RefObject + provider: AddCashfreeProviderDialogFragment + deleteDialogCallback: Function +}> + +export interface AddCashfreeDialogRef { + openDialog: (props?: TAddCashfreeDialogProps) => unknown + closeDialog: () => unknown +} + +export const AddCashfreeDialog = forwardRef((_, ref) => { + const navigate = useNavigate() + const dialogRef = useRef(null) + + const { translate } = useInternationalization() + const [localData, setLocalData] = useState(undefined) + const cashfreeProvider = localData?.provider + const isEdition = !!cashfreeProvider + + const [addApiKey] = useAddCashfreeApiKeyMutation({ + onCompleted({ addCashfreePaymentProvider }) { + if (addCashfreePaymentProvider?.id) { + navigate( + generatePath(CASHFREE_INTEGRATION_DETAILS_ROUTE, { + integrationId: addCashfreePaymentProvider.id, + integrationGroup: IntegrationsTabsOptionsEnum.Community, + }), + ) + + addToast({ + message: translate('text_17276219350329d36mgsotee'), + severity: 'success', + }) + } + }, + }) + + const [updateApiKey] = useUpdateCashfreeApiKeyMutation({ + onCompleted({ updateCashfreePaymentProvider }) { + if (updateCashfreePaymentProvider?.id) { + navigate( + generatePath(CASHFREE_INTEGRATION_DETAILS_ROUTE, { + integrationId: updateCashfreePaymentProvider.id, + integrationGroup: IntegrationsTabsOptionsEnum.Community, + }), + ) + + addToast({ + message: translate('text_1727621947600tg14usmdbb0'), + severity: 'success', + }) + } + }, + }) + + const [getCashfreeProviderByCode] = useGetProviderByCodeForCashfreeLazyQuery() + + const formikProps = useFormik({ + initialValues: { + code: cashfreeProvider?.code || '', + name: cashfreeProvider?.name || '', + clientId: cashfreeProvider?.clientId || '', + clientSecret: cashfreeProvider?.clientSecret || '', + successRedirectUrl: cashfreeProvider?.successRedirectUrl || '', + }, + validationSchema: object().shape({ + name: string(), + code: string().required(''), + clientId: string().required(''), + clientSecret: string().required(''), + successRedirectUrl: string(), + }), + onSubmit: async ({ clientId, clientSecret, ...values }, formikBag) => { + const res = await getCashfreeProviderByCode({ + context: { silentErrorCodes: [LagoApiError.NotFound] }, + variables: { + code: values.code, + }, + }) + const isNotAllowedToMutate = + (!!res.data?.paymentProvider?.id && !isEdition) || + (isEdition && + !!res.data?.paymentProvider?.id && + res.data?.paymentProvider?.id !== cashfreeProvider?.id) + + if (isNotAllowedToMutate) { + formikBag.setFieldError('code', translate('text_632a2d437e341dcc76817556')) + return + } + + if (isEdition) { + await updateApiKey({ + variables: { + input: { + id: cashfreeProvider?.id || '', + ...values, + }, + }, + }) + } else { + await addApiKey({ + variables: { + input: { clientId, clientSecret, ...values }, + }, + }) + } + dialogRef.current?.closeDialog() + }, + validateOnMount: true, + enableReinitialize: true, + }) + + useImperativeHandle(ref, () => ({ + openDialog: (data) => { + setLocalData(data) + dialogRef.current?.openDialog() + }, + closeDialog: () => dialogRef.current?.closeDialog(), + })) + + return ( + { + formikProps.resetForm() + }} + actions={({ closeDialog }) => ( + + {isEdition && ( + + )} + + + + + + )} + > +
+
+ + +
+ + + +
+
+ ) +}) + +AddCashfreeDialog.displayName = 'AddCashfreeDialog' diff --git a/src/components/settings/integrations/AddEditDeleteSuccessRedirectUrlDialog.tsx b/src/components/settings/integrations/AddEditDeleteSuccessRedirectUrlDialog.tsx index 2069004d7..7ca82ba70 100644 --- a/src/components/settings/integrations/AddEditDeleteSuccessRedirectUrlDialog.tsx +++ b/src/components/settings/integrations/AddEditDeleteSuccessRedirectUrlDialog.tsx @@ -1,7 +1,6 @@ import { gql } from '@apollo/client' import { useFormik } from 'formik' import { forwardRef, useImperativeHandle, useMemo, useRef, useState } from 'react' -import styled from 'styled-components' import { object, string } from 'yup' import { Button, Dialog, DialogRef, Typography } from '~/components/designSystem' @@ -10,17 +9,19 @@ import { addToast, hasDefinedGQLError } from '~/core/apolloClient' import { ADYEN_SUCCESS_LINK_SPEC_URL } from '~/core/constants/externalUrls' import { AdyenForCreateAndEditSuccessRedirectUrlFragment, + CashfreeForCreateAndEditSuccessRedirectUrlFragment, GocardlessForCreateAndEditSuccessRedirectUrlFragment, StripeForCreateAndEditSuccessRedirectUrlFragment, UpdateAdyenPaymentProviderInput, + UpdateCashfreePaymentProviderInput, UpdateGocardlessPaymentProviderInput, UpdateStripePaymentProviderInput, useUpdateAdyenPaymentProviderMutation, + useUpdateCashfreePaymentProviderMutation, useUpdateGocardlessPaymentProviderMutation, useUpdateStripePaymentProviderMutation, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' -import { theme } from '~/styles' gql` fragment AdyenForCreateAndEditSuccessRedirectUrl on AdyenProvider { @@ -28,6 +29,11 @@ gql` successRedirectUrl } + fragment CashfreeForCreateAndEditSuccessRedirectUrl on CashfreeProvider { + id + successRedirectUrl + } + fragment gocardlessForCreateAndEditSuccessRedirectUrl on GocardlessProvider { id successRedirectUrl @@ -45,6 +51,13 @@ gql` } } + mutation updateCashfreePaymentProvider($input: UpdateCashfreePaymentProviderInput!) { + updateCashfreePaymentProvider(input: $input) { + id + successRedirectUrl + } + } + mutation updateGocardlessPaymentProvider($input: UpdateGocardlessPaymentProviderInput!) { updateGocardlessPaymentProvider(input: $input) { id @@ -70,6 +83,7 @@ const AddEditDeleteSuccessRedirectUrlDialogProviderType = { Adyen: 'Adyen', Stripe: 'Stripe', GoCardless: 'GoCardless', + Cashfree: 'Cashfree', } as const type LocalProviderType = { @@ -77,6 +91,7 @@ type LocalProviderType = { type: keyof typeof AddEditDeleteSuccessRedirectUrlDialogProviderType provider?: | AdyenForCreateAndEditSuccessRedirectUrlFragment + | CashfreeForCreateAndEditSuccessRedirectUrlFragment | GocardlessForCreateAndEditSuccessRedirectUrlFragment | StripeForCreateAndEditSuccessRedirectUrlFragment | null @@ -110,6 +125,17 @@ export const AddEditDeleteSuccessRedirectUrlDialog = }, }) + const [updateCashfreeProvider] = useUpdateCashfreePaymentProviderMutation({ + onCompleted(data) { + if (data && data.updateCashfreePaymentProvider) { + addToast({ + message: successToastMessage, + severity: 'success', + }) + } + }, + }) + const [updateGocardlessProvider] = useUpdateGocardlessPaymentProviderMutation({ onCompleted(data) { if (data && data.updateGocardlessPaymentProvider) { @@ -134,6 +160,7 @@ export const AddEditDeleteSuccessRedirectUrlDialog = const formikProps = useFormik< | UpdateAdyenPaymentProviderInput + | UpdateCashfreePaymentProviderInput | UpdateGocardlessPaymentProviderInput | UpdateStripePaymentProviderInput >({ @@ -151,6 +178,7 @@ export const AddEditDeleteSuccessRedirectUrlDialog = [AddEditDeleteSuccessRedirectUrlDialogProviderType.Adyen]: updateAdyenProvider, [AddEditDeleteSuccessRedirectUrlDialogProviderType.Stripe]: updateStripeProvider, [AddEditDeleteSuccessRedirectUrlDialogProviderType.GoCardless]: updateGocardlessProvider, + [AddEditDeleteSuccessRedirectUrlDialogProviderType.Cashfree]: updateCashfreeProvider, } const method = methodLoojup[localData?.type as LocalProviderType['type']] @@ -255,35 +283,26 @@ export const AddEditDeleteSuccessRedirectUrlDialog = )} > {localData?.mode !== AddEditDeleteSuccessRedirectUrlDialogMode.Delete && ( - - - } - formikProps={formikProps} - /> - + + } + formikProps={formikProps} + /> )} ) }) -const Content = styled.div` - margin-bottom: ${theme.spacing(8)}; - - > *:not(:last-child) { - margin-bottom: ${theme.spacing(6)}; - } -` - AddEditDeleteSuccessRedirectUrlDialog.displayName = 'forwardRef' diff --git a/src/components/settings/integrations/AddGocardlessDialog.tsx b/src/components/settings/integrations/AddGocardlessDialog.tsx index e5625e1aa..b12b1df4a 100644 --- a/src/components/settings/integrations/AddGocardlessDialog.tsx +++ b/src/components/settings/integrations/AddGocardlessDialog.tsx @@ -21,6 +21,7 @@ import { useUpdateGocardlessApiKeyMutation, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { theme } from '~/styles' import { DeleteGocardlessIntegrationDialogRef } from './DeleteGocardlessIntegrationDialog' @@ -37,6 +38,9 @@ gql` ... on GocardlessProvider { id } + ... on CashfreeProvider { + id + } ... on AdyenProvider { id } @@ -84,6 +88,7 @@ export const AddGocardlessDialog = forwardRef((_, ref) = navigate( generatePath(GOCARDLESS_INTEGRATION_DETAILS_ROUTE, { integrationId: updateGocardlessPaymentProvider.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AddHubspotDialog.tsx b/src/components/settings/integrations/AddHubspotDialog.tsx index c3f0d5f44..d8de247f3 100644 --- a/src/components/settings/integrations/AddHubspotDialog.tsx +++ b/src/components/settings/integrations/AddHubspotDialog.tsx @@ -21,6 +21,7 @@ import { useUpdateHubspotIntegrationMutation, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { tw } from '~/styles/utils' gql` @@ -78,6 +79,7 @@ export const AddHubspotDialog = forwardRef((_, ref) => { navigate( generatePath(HUBSPOT_INTEGRATION_DETAILS_ROUTE, { integrationId: createHubspotIntegration.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AddLagoTaxManagementDialog.tsx b/src/components/settings/integrations/AddLagoTaxManagementDialog.tsx index 3855d6914..add1efd42 100644 --- a/src/components/settings/integrations/AddLagoTaxManagementDialog.tsx +++ b/src/components/settings/integrations/AddLagoTaxManagementDialog.tsx @@ -1,7 +1,7 @@ import { gql } from '@apollo/client' import { useFormik } from 'formik' import { forwardRef } from 'react' -import { useNavigate } from 'react-router' +import { generatePath, useNavigate } from 'react-router' import styled from 'styled-components' import { object, string } from 'yup' @@ -18,6 +18,7 @@ import { } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' import { useIntegrations } from '~/hooks/useIntegrations' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { theme } from '~/styles' gql` @@ -62,7 +63,11 @@ export const AddLagoTaxManagementDialog = forwardRef((_, ref) => { generatePath(NETSUITE_INTEGRATION_DETAILS_ROUTE, { integrationId: createNetsuiteIntegration.id, tab: NetsuiteIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AddSalesforceDialog.tsx b/src/components/settings/integrations/AddSalesforceDialog.tsx index 6b73e6c3b..0d843c3aa 100644 --- a/src/components/settings/integrations/AddSalesforceDialog.tsx +++ b/src/components/settings/integrations/AddSalesforceDialog.tsx @@ -17,6 +17,7 @@ import { useUpdateSalesforceIntegrationMutation, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { tw } from '~/styles/utils' import { DeleteSalesforceIntegrationDialogRef } from './DeleteSalesforceIntegrationDialog' @@ -72,6 +73,7 @@ export const AddSalesforceDialog = forwardRef((_, ref) = navigate( generatePath(SALESFORCE_INTEGRATION_DETAILS_ROUTE, { integrationId: createSalesforceIntegration.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AddStripeDialog.tsx b/src/components/settings/integrations/AddStripeDialog.tsx index e0f4769b0..690cbaaa6 100644 --- a/src/components/settings/integrations/AddStripeDialog.tsx +++ b/src/components/settings/integrations/AddStripeDialog.tsx @@ -21,6 +21,7 @@ import { useUpdateStripeApiKeyMutation, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { theme } from '~/styles' import { DeleteStripeIntegrationDialogRef } from './DeleteStripeIntegrationDialog' @@ -41,6 +42,9 @@ gql` ... on GocardlessProvider { id } + ... on CashfreeProvider { + id + } ... on AdyenProvider { id } @@ -91,6 +95,7 @@ export const AddStripeDialog = forwardRef((_, ref) => { navigate( generatePath(STRIPE_INTEGRATION_DETAILS_ROUTE, { integrationId: addStripePaymentProvider.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AddXeroDialog.tsx b/src/components/settings/integrations/AddXeroDialog.tsx index 0df5ab2c4..8c21141c6 100644 --- a/src/components/settings/integrations/AddXeroDialog.tsx +++ b/src/components/settings/integrations/AddXeroDialog.tsx @@ -20,6 +20,7 @@ import { XeroForCreateDialogDialogFragment, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { XeroIntegrationDetailsTabs } from '~/pages/settings/XeroIntegrationDetails' import { theme } from '~/styles' @@ -80,6 +81,7 @@ export const AddXeroDialog = forwardRef((_, ref) => { generatePath(XERO_INTEGRATION_DETAILS_ROUTE, { integrationId: createXeroIntegration.id, tab: XeroIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) diff --git a/src/components/settings/integrations/AnrokIntegrationSettings.tsx b/src/components/settings/integrations/AnrokIntegrationSettings.tsx index 9cc7facf0..6101d1acd 100644 --- a/src/components/settings/integrations/AnrokIntegrationSettings.tsx +++ b/src/components/settings/integrations/AnrokIntegrationSettings.tsx @@ -20,6 +20,7 @@ import { } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' import { AnrokIntegrationDetailsTabs } from '~/pages/settings/AnrokIntegrationDetails' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { NAV_HEIGHT, theme } from '~/styles' import { AddAnrokDialog, AddAnrokDialogRef } from './AddAnrokDialog' @@ -103,9 +104,15 @@ const AnrokIntegrationSettings = () => { const anrokIntegration = data?.integration as AnrokIntegrationSettingsFragment | undefined const deleteDialogCallback = () => { if ((data?.integrations?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(ANROK_INTEGRATION_ROUTE) + navigate( + generatePath(ANROK_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -122,6 +129,7 @@ const AnrokIntegrationSettings = () => { generatePath(ANROK_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: AnrokIntegrationDetailsTabs.Items, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) }, diff --git a/src/components/settings/integrations/DeleteCashfreeIntegrationDialog.tsx b/src/components/settings/integrations/DeleteCashfreeIntegrationDialog.tsx new file mode 100644 index 000000000..77a928b6b --- /dev/null +++ b/src/components/settings/integrations/DeleteCashfreeIntegrationDialog.tsx @@ -0,0 +1,85 @@ +import { gql } from '@apollo/client' +import { forwardRef, useImperativeHandle, useRef, useState } from 'react' + +import { WarningDialog, WarningDialogRef } from '~/components/WarningDialog' +import { addToast } from '~/core/apolloClient' +import { + DeleteCashfreeIntegrationDialogFragment, + useDeleteCashfreeMutation, +} from '~/generated/graphql' +import { useInternationalization } from '~/hooks/core/useInternationalization' + +gql` + fragment DeleteCashfreeIntegrationDialog on CashfreeProvider { + id + name + } + + mutation deleteCashfree($input: DestroyPaymentProviderInput!) { + destroyPaymentProvider(input: $input) { + id + } + } +` + +type TDeleteCashfreeIntegrationDialogProps = { + provider: DeleteCashfreeIntegrationDialogFragment | null + callback?: Function +} + +export interface DeleteCashfreeIntegrationDialogRef { + openDialog: ({ provider, callback }: TDeleteCashfreeIntegrationDialogProps) => unknown + closeDialog: () => unknown +} + +export const DeleteCashfreeIntegrationDialog = forwardRef( + (_, ref) => { + const { translate } = useInternationalization() + + const dialogRef = useRef(null) + const [localData, setLocalData] = useState( + undefined, + ) + + const cashfreeProvider = localData?.provider + + const [deleteCashfree] = useDeleteCashfreeMutation({ + onCompleted(data) { + if (data && data.destroyPaymentProvider) { + dialogRef.current?.closeDialog() + localData?.callback?.() + addToast({ + message: translate('text_1727621949511zk6kkl99pzk'), + severity: 'success', + }) + } + }, + update(cache) { + cache.evict({ id: `CashfreeProvider:${cashfreeProvider?.id}` }) + }, + }) + + useImperativeHandle(ref, () => ({ + openDialog: (data) => { + setLocalData(data) + dialogRef.current?.openDialog() + }, + closeDialog: () => dialogRef.current?.closeDialog(), + })) + + return ( + + await deleteCashfree({ variables: { input: { id: cashfreeProvider?.id as string } } }) + } + continueText={translate('text_659d5de7c9b7f51394f7f3fd')} + /> + ) + }, +) + +DeleteCashfreeIntegrationDialog.displayName = 'DeleteCashfreeIntegrationDialog' diff --git a/src/components/settings/integrations/NetsuiteIntegrationSettings.tsx b/src/components/settings/integrations/NetsuiteIntegrationSettings.tsx index d845545e4..120cdb580 100644 --- a/src/components/settings/integrations/NetsuiteIntegrationSettings.tsx +++ b/src/components/settings/integrations/NetsuiteIntegrationSettings.tsx @@ -17,6 +17,7 @@ import { useGetNetsuiteIntegrationsSettingsQuery, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { NetsuiteIntegrationDetailsTabs } from '~/pages/settings/NetsuiteIntegrationDetails' import { NAV_HEIGHT, theme } from '~/styles' @@ -110,9 +111,15 @@ const NetsuiteIntegrationSettings = () => { const netsuiteIntegration = data?.integration as NetsuiteIntegrationSettingsFragment | undefined const deleteDialogCallback = () => { if ((data?.integrations?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(NETSUITE_INTEGRATION_ROUTE) + navigate( + generatePath(NETSUITE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -129,6 +136,7 @@ const NetsuiteIntegrationSettings = () => { generatePath(NETSUITE_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: NetsuiteIntegrationDetailsTabs.Items, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) }, diff --git a/src/components/settings/integrations/XeroIntegrationSettings.tsx b/src/components/settings/integrations/XeroIntegrationSettings.tsx index ec7d43536..443739d70 100644 --- a/src/components/settings/integrations/XeroIntegrationSettings.tsx +++ b/src/components/settings/integrations/XeroIntegrationSettings.tsx @@ -17,6 +17,7 @@ import { XeroIntegrationSettingsFragment, } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { XeroIntegrationDetailsTabs } from '~/pages/settings/XeroIntegrationDetails' import { NAV_HEIGHT, theme } from '~/styles' @@ -111,9 +112,15 @@ const XeroIntegrationSettings = () => { const xeroIntegration = data?.integration as XeroIntegrationSettingsFragment | undefined const deleteDialogCallback = () => { if ((data?.integrations?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(XERO_INTEGRATION_ROUTE) + navigate( + generatePath(XERO_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -130,6 +137,7 @@ const XeroIntegrationSettings = () => { generatePath(XERO_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: XeroIntegrationDetailsTabs.Items, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) }, diff --git a/src/core/router/SettingRoutes.tsx b/src/core/router/SettingRoutes.tsx index 90b7aa0c6..ad2b354d4 100644 --- a/src/core/router/SettingRoutes.tsx +++ b/src/core/router/SettingRoutes.tsx @@ -87,6 +87,16 @@ const StripeIntegrationDetails = lazyLoad( /* webpackChunkName: 'stripe-integration-details' */ '~/pages/settings/StripeIntegrationDetails' ), ) +const CashfreeIntegrations = lazyLoad( + () => + import(/* webpackChunkName: 'cashfree-integrations' */ '~/pages/settings/CashfreeIntegrations'), +) +const CashfreeIntegrationDetails = lazyLoad( + () => + import( + /* webpackChunkName: 'cashfree-integration-details' */ '~/pages/settings/CashfreeIntegrationDetails' + ), +) const GocardlessIntegrationOauthCallback = lazyLoad( () => import( @@ -139,7 +149,8 @@ export const SETTINGS_ROUTE = '/settings' export const INVOICE_SETTINGS_ROUTE = `${SETTINGS_ROUTE}/invoice` export const TAXES_SETTINGS_ROUTE = `${SETTINGS_ROUTE}/taxes` export const ORGANIZATION_INFORMATIONS_ROUTE = `${SETTINGS_ROUTE}/organization-informations` -export const INTEGRATIONS_ROUTE = `${SETTINGS_ROUTE}/integrations` +export const DO_NOT_USE_INTEGRATIONS_ROUTE = `${SETTINGS_ROUTE}/integrations/` +export const INTEGRATIONS_ROUTE = `${SETTINGS_ROUTE}/integrations/:integrationGroup` export const AUTHENTICATION_ROUTE = `${SETTINGS_ROUTE}/authentication` export const OKTA_AUTHENTICATION_ROUTE = `${AUTHENTICATION_ROUTE}/okta/:integrationId` export const ANROK_INTEGRATION_ROUTE = `${INTEGRATIONS_ROUTE}/anrok` @@ -154,8 +165,10 @@ export const SALESFORCE_INTEGRATION_ROUTE = `${INTEGRATIONS_ROUTE}/salesforce` export const SALESFORCE_INTEGRATION_DETAILS_ROUTE = `${INTEGRATIONS_ROUTE}/salesforce/:integrationId` export const STRIPE_INTEGRATION_ROUTE = `${INTEGRATIONS_ROUTE}/stripe` export const STRIPE_INTEGRATION_DETAILS_ROUTE = `${INTEGRATIONS_ROUTE}/stripe/:integrationId` +export const CASHFREE_INTEGRATION_ROUTE = `${INTEGRATIONS_ROUTE}/cashfree` +export const CASHFREE_INTEGRATION_DETAILS_ROUTE = `${INTEGRATIONS_ROUTE}/cashfree/:integrationId` export const GOCARDLESS_INTEGRATION_ROUTE = `${INTEGRATIONS_ROUTE}/gocardless` -export const GOCARDLESS_INTEGRATION_OAUTH_CALLBACK_ROUTE = `${INTEGRATIONS_ROUTE}/gocardless/callback` +export const GOCARDLESS_INTEGRATION_OAUTH_CALLBACK_ROUTE = `${DO_NOT_USE_INTEGRATIONS_ROUTE}/gocardless/callback` export const GOCARDLESS_INTEGRATION_DETAILS_ROUTE = `${INTEGRATIONS_ROUTE}/gocardless/:integrationId` export const TAX_MANAGEMENT_INTEGRATION_ROUTE = `${INTEGRATIONS_ROUTE}/lago-tax-management` export const MEMBERS_ROUTE = `${SETTINGS_ROUTE}/members` @@ -298,6 +311,18 @@ export const settingRoutes: CustomRouteObject[] = [ element: , permissions: ['organizationIntegrationsView'], }, + { + path: CASHFREE_INTEGRATION_ROUTE, + private: true, + element: , + permissions: ['organizationIntegrationsView'], + }, + { + path: CASHFREE_INTEGRATION_DETAILS_ROUTE, + private: true, + element: , + permissions: ['organizationIntegrationsView'], + }, { path: GOCARDLESS_INTEGRATION_ROUTE, private: true, diff --git a/src/generated/graphql.tsx b/src/generated/graphql.tsx index f594643ba..3799345cd 100644 --- a/src/generated/graphql.tsx +++ b/src/generated/graphql.tsx @@ -49,6 +49,17 @@ export type AddAdyenPaymentProviderInput = { successRedirectUrl?: InputMaybe; }; +/** Cashfree input arguments */ +export type AddCashfreePaymentProviderInput = { + clientId: Scalars['String']['input']; + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + clientSecret: Scalars['String']['input']; + code: Scalars['String']['input']; + name: Scalars['String']['input']; + successRedirectUrl?: InputMaybe; +}; + /** Gocardless input arguments */ export type AddGocardlessPaymentProviderInput = { accessCode?: InputMaybe; @@ -297,6 +308,16 @@ export enum BillingTimeEnum { Calendar = 'calendar' } +export type CashfreeProvider = { + __typename?: 'CashfreeProvider'; + clientId?: Maybe; + clientSecret?: Maybe; + code: Scalars['String']['output']; + id: Scalars['ID']['output']; + name: Scalars['String']['output']; + successRedirectUrl?: Maybe; +}; + export type Charge = { __typename?: 'Charge'; billableMetric: BillableMetric; @@ -1047,6 +1068,7 @@ export type CreateApiKeyInput = { /** A unique identifier for the client performing the mutation. */ clientMutationId?: InputMaybe; name?: InputMaybe; + permissions?: InputMaybe; }; /** Autogenerated input type of CreateAppliedCoupon */ @@ -1784,6 +1806,7 @@ export type CurrentOrganization = { apiKey?: Maybe; appliedDunningCampaign?: Maybe; billingConfiguration?: Maybe; + cashfreePaymentProviders?: Maybe>; city?: Maybe; country?: Maybe; createdAt: Scalars['ISO8601DateTime']['output']; @@ -3162,6 +3185,8 @@ export type Mutation = { acceptInvite?: Maybe; /** Add Adyen payment provider */ addAdyenPaymentProvider?: Maybe; + /** Add or update Cashfree payment provider */ + addCashfreePaymentProvider?: Maybe; /** Add or update Gocardless payment provider */ addGocardlessPaymentProvider?: Maybe; /** Add Stripe API keys to the organization */ @@ -3335,6 +3360,8 @@ export type Mutation = { updateApiKey?: Maybe; /** Updates an existing Billable metric */ updateBillableMetric?: Maybe; + /** Update Cashfree payment provider */ + updateCashfreePaymentProvider?: Maybe; /** Update an existing coupon */ updateCoupon?: Maybe; /** Updates an existing Credit Note */ @@ -3400,6 +3427,11 @@ export type MutationAddAdyenPaymentProviderArgs = { }; +export type MutationAddCashfreePaymentProviderArgs = { + input: AddCashfreePaymentProviderInput; +}; + + export type MutationAddGocardlessPaymentProviderArgs = { input: AddGocardlessPaymentProviderInput; }; @@ -3840,6 +3872,11 @@ export type MutationUpdateBillableMetricArgs = { }; +export type MutationUpdateCashfreePaymentProviderArgs = { + input: UpdateCashfreePaymentProviderInput; +}; + + export type MutationUpdateCouponArgs = { input: UpdateCouponInput; }; @@ -4076,7 +4113,7 @@ export type OverdueBalanceCollection = { metadata: CollectionMetadata; }; -export type PaymentProvider = AdyenProvider | GocardlessProvider | StripeProvider; +export type PaymentProvider = AdyenProvider | CashfreeProvider | GocardlessProvider | StripeProvider; /** PaymentProviderCollection type */ export type PaymentProviderCollection = { @@ -4325,6 +4362,7 @@ export enum ProviderPaymentMethodsEnum { export enum ProviderTypeEnum { Adyen = 'adyen', + Cashfree = 'cashfree', Gocardless = 'gocardless', Stripe = 'stripe' } @@ -5609,6 +5647,16 @@ export type UpdateBillableMetricInput = { weightedInterval?: InputMaybe; }; +/** Update input arguments */ +export type UpdateCashfreePaymentProviderInput = { + /** A unique identifier for the client performing the mutation. */ + clientMutationId?: InputMaybe; + code?: InputMaybe; + id: Scalars['ID']['input']; + name?: InputMaybe; + successRedirectUrl?: InputMaybe; +}; + /** Autogenerated input type of UpdateCoupon */ export type UpdateCouponInput = { amountCents?: InputMaybe; @@ -6459,7 +6507,7 @@ export type PaymentProvidersListForCustomerMainInfosQueryVariables = Exact<{ }>; -export type PaymentProvidersListForCustomerMainInfosQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string, name: string, code: string } | { __typename?: 'GocardlessProvider', id: string, name: string, code: string } | { __typename?: 'StripeProvider', id: string, name: string, code: string }> } | null }; +export type PaymentProvidersListForCustomerMainInfosQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string, name: string, code: string } | { __typename?: 'CashfreeProvider', id: string, name: string, code: string } | { __typename?: 'GocardlessProvider', id: string, name: string, code: string } | { __typename?: 'StripeProvider', id: string, name: string, code: string }> } | null }; export type IntegrationsListForCustomerMainInfosQueryVariables = Exact<{ limit?: InputMaybe; @@ -6594,7 +6642,7 @@ export type PaymentProvidersListForCustomerCreateEditExternalAppsAccordionQueryV }>; -export type PaymentProvidersListForCustomerCreateEditExternalAppsAccordionQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename: 'AdyenProvider', id: string, name: string, code: string } | { __typename: 'GocardlessProvider', id: string, name: string, code: string } | { __typename: 'StripeProvider', id: string, name: string, code: string }> } | null }; +export type PaymentProvidersListForCustomerCreateEditExternalAppsAccordionQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename: 'AdyenProvider', id: string, name: string, code: string } | { __typename: 'CashfreeProvider', id: string, name: string, code: string } | { __typename: 'GocardlessProvider', id: string, name: string, code: string } | { __typename: 'StripeProvider', id: string, name: string, code: string }> } | null }; export type AccountingIntegrationsListForCustomerEditExternalAppsAccordionQueryVariables = Exact<{ limit?: InputMaybe; @@ -7080,7 +7128,7 @@ export type GetProviderByCodeForAdyenQueryVariables = Exact<{ }>; -export type GetProviderByCodeForAdyenQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string } | null }; +export type GetProviderByCodeForAdyenQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string } | { __typename?: 'CashfreeProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string } | null }; export type AddAdyenApiKeyMutationVariables = Exact<{ input: AddAdyenPaymentProviderInput; @@ -7112,8 +7160,33 @@ export type UpdateAnrokIntegrationMutationVariables = Exact<{ export type UpdateAnrokIntegrationMutation = { __typename?: 'Mutation', updateAnrokIntegration?: { __typename?: 'AnrokIntegration', id: string, name: string, code: string, apiKey: string } | null }; +export type AddCashfreeProviderDialogFragment = { __typename?: 'CashfreeProvider', id: string, name: string, code: string, clientId?: string | null, clientSecret?: string | null, successRedirectUrl?: string | null }; + +export type GetProviderByCodeForCashfreeQueryVariables = Exact<{ + code?: InputMaybe; +}>; + + +export type GetProviderByCodeForCashfreeQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string } | { __typename?: 'CashfreeProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string } | null }; + +export type AddCashfreeApiKeyMutationVariables = Exact<{ + input: AddCashfreePaymentProviderInput; +}>; + + +export type AddCashfreeApiKeyMutation = { __typename?: 'Mutation', addCashfreePaymentProvider?: { __typename?: 'CashfreeProvider', id: string, name: string, code: string, clientId?: string | null, clientSecret?: string | null, successRedirectUrl?: string | null } | null }; + +export type UpdateCashfreeApiKeyMutationVariables = Exact<{ + input: UpdateCashfreePaymentProviderInput; +}>; + + +export type UpdateCashfreeApiKeyMutation = { __typename?: 'Mutation', updateCashfreePaymentProvider?: { __typename?: 'CashfreeProvider', id: string, name: string, code: string, clientId?: string | null, clientSecret?: string | null, successRedirectUrl?: string | null } | null }; + export type AdyenForCreateAndEditSuccessRedirectUrlFragment = { __typename?: 'AdyenProvider', id: string, successRedirectUrl?: string | null }; +export type CashfreeForCreateAndEditSuccessRedirectUrlFragment = { __typename?: 'CashfreeProvider', id: string, successRedirectUrl?: string | null }; + export type GocardlessForCreateAndEditSuccessRedirectUrlFragment = { __typename?: 'GocardlessProvider', id: string, successRedirectUrl?: string | null }; export type StripeForCreateAndEditSuccessRedirectUrlFragment = { __typename?: 'StripeProvider', id: string, successRedirectUrl?: string | null }; @@ -7125,6 +7198,13 @@ export type UpdateAdyenPaymentProviderMutationVariables = Exact<{ export type UpdateAdyenPaymentProviderMutation = { __typename?: 'Mutation', updateAdyenPaymentProvider?: { __typename?: 'AdyenProvider', id: string, successRedirectUrl?: string | null } | null }; +export type UpdateCashfreePaymentProviderMutationVariables = Exact<{ + input: UpdateCashfreePaymentProviderInput; +}>; + + +export type UpdateCashfreePaymentProviderMutation = { __typename?: 'Mutation', updateCashfreePaymentProvider?: { __typename?: 'CashfreeProvider', id: string, successRedirectUrl?: string | null } | null }; + export type UpdateGocardlessPaymentProviderMutationVariables = Exact<{ input: UpdateGocardlessPaymentProviderInput; }>; @@ -7146,7 +7226,7 @@ export type GetProviderByCodeForGocardlessQueryVariables = Exact<{ }>; -export type GetProviderByCodeForGocardlessQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string } | null }; +export type GetProviderByCodeForGocardlessQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string } | { __typename?: 'CashfreeProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string } | null }; export type UpdateGocardlessApiKeyMutationVariables = Exact<{ input: UpdateGocardlessPaymentProviderInput; @@ -7217,7 +7297,7 @@ export type GetProviderByCodeForStripeQueryVariables = Exact<{ }>; -export type GetProviderByCodeForStripeQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string } | null }; +export type GetProviderByCodeForStripeQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string } | { __typename?: 'CashfreeProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string } | null }; export type AddStripeApiKeyMutationVariables = Exact<{ input: AddStripePaymentProviderInput; @@ -7367,6 +7447,15 @@ export type DestroyNangoIntegrationMutationVariables = Exact<{ export type DestroyNangoIntegrationMutation = { __typename?: 'Mutation', destroyIntegration?: { __typename?: 'DestroyIntegrationPayload', id?: string | null } | null }; +export type DeleteCashfreeIntegrationDialogFragment = { __typename?: 'CashfreeProvider', id: string, name: string }; + +export type DeleteCashfreeMutationVariables = Exact<{ + input: DestroyPaymentProviderInput; +}>; + + +export type DeleteCashfreeMutation = { __typename?: 'Mutation', destroyPaymentProvider?: { __typename?: 'DestroyPaymentProviderPayload', id?: string | null } | null }; + export type DeleteGocardlessIntegrationDialogFragment = { __typename?: 'GocardlessProvider', id: string, name: string }; export type DeleteGocardlessMutationVariables = Exact<{ @@ -7996,7 +8085,7 @@ export type GetSingleCampaignQuery = { __typename?: 'Query', dunningCampaign: { export type CreateDunningCampaignPaymentProviderQueryVariables = Exact<{ [key: string]: never; }>; -export type CreateDunningCampaignPaymentProviderQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename: 'AdyenProvider' } | { __typename: 'GocardlessProvider' } | { __typename: 'StripeProvider' }> } | null }; +export type CreateDunningCampaignPaymentProviderQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename: 'AdyenProvider' } | { __typename: 'CashfreeProvider' } | { __typename: 'GocardlessProvider' } | { __typename: 'StripeProvider' }> } | null }; export type CreateDunningCampaignMutationVariables = Exact<{ input: CreateDunningCampaignInput; @@ -8646,7 +8735,7 @@ export type GetAdyenIntegrationsDetailsQueryVariables = Exact<{ }>; -export type GetAdyenIntegrationsDetailsQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string, apiKey?: string | null, code: string, hmacKey?: string | null, livePrefix?: string | null, merchantAccount?: string | null, successRedirectUrl?: string | null, name: string } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' }> } | null }; +export type GetAdyenIntegrationsDetailsQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider', id: string, apiKey?: string | null, code: string, hmacKey?: string | null, livePrefix?: string | null, merchantAccount?: string | null, successRedirectUrl?: string | null, name: string } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' }> } | null }; export type AdyenIntegrationsFragment = { __typename?: 'AdyenProvider', id: string, name: string, code: string }; @@ -8656,7 +8745,7 @@ export type GetAdyenIntegrationsListQueryVariables = Exact<{ }>; -export type GetAdyenIntegrationsListQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string, name: string, code: string, apiKey?: string | null, hmacKey?: string | null, livePrefix?: string | null, merchantAccount?: string | null } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' }> } | null }; +export type GetAdyenIntegrationsListQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string, name: string, code: string, apiKey?: string | null, hmacKey?: string | null, livePrefix?: string | null, merchantAccount?: string | null } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' }> } | null }; export type AnrokIntegrationDetailsFragment = { __typename?: 'AnrokIntegration', id: string, name: string, code: string, apiKey: string }; @@ -8695,6 +8784,27 @@ export type GetOktaIntegrationQueryVariables = Exact<{ export type GetOktaIntegrationQuery = { __typename?: 'Query', integration?: { __typename?: 'AnrokIntegration' } | { __typename?: 'HubspotIntegration' } | { __typename?: 'NetsuiteIntegration' } | { __typename?: 'OktaIntegration', id: string, clientId?: string | null, clientSecret?: string | null, code: string, organizationName: string, domain: string, name: string } | { __typename?: 'SalesforceIntegration' } | { __typename?: 'XeroIntegration' } | null }; +export type CashfreeIntegrationDetailsFragment = { __typename?: 'CashfreeProvider', id: string, code: string, name: string, clientId?: string | null, clientSecret?: string | null, successRedirectUrl?: string | null }; + +export type GetCashfreeIntegrationsDetailsQueryVariables = Exact<{ + id: Scalars['ID']['input']; + limit?: InputMaybe; + type?: InputMaybe; +}>; + + +export type GetCashfreeIntegrationsDetailsQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider', id: string, code: string, name: string, clientId?: string | null, clientSecret?: string | null, successRedirectUrl?: string | null } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider', id: string } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' }> } | null }; + +export type CashfreeIntegrationsFragment = { __typename?: 'CashfreeProvider', id: string, name: string, code: string }; + +export type GetCashfreeIntegrationsListQueryVariables = Exact<{ + limit?: InputMaybe; + type?: InputMaybe; +}>; + + +export type GetCashfreeIntegrationsListQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider', id: string, name: string, code: string, clientId?: string | null, clientSecret?: string | null, successRedirectUrl?: string | null } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider' }> } | null }; + export type DunningCampaignItemFragment = { __typename?: 'DunningCampaign', id: string, name: string, code: string, appliedToOrganization: boolean }; export type GetDunningCampaignsQueryVariables = Exact<{ @@ -8721,7 +8831,7 @@ export type GetGocardlessIntegrationsDetailsQueryVariables = Exact<{ }>; -export type GetGocardlessIntegrationsDetailsQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider' } | { __typename?: 'GocardlessProvider', id: string, code: string, name: string, successRedirectUrl?: string | null, webhookSecret?: string | null } | { __typename?: 'StripeProvider' } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider' }> } | null }; +export type GetGocardlessIntegrationsDetailsQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider', id: string, code: string, name: string, successRedirectUrl?: string | null, webhookSecret?: string | null } | { __typename?: 'StripeProvider' } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider' }> } | null }; export type GocardlessIntegrationOauthCallbackFragment = { __typename?: 'GocardlessProvider', id: string, name: string, code: string }; @@ -8740,7 +8850,7 @@ export type GetGocardlessIntegrationsListQueryVariables = Exact<{ }>; -export type GetGocardlessIntegrationsListQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'GocardlessProvider', id: string, name: string, code: string } | { __typename?: 'StripeProvider' }> } | null }; +export type GetGocardlessIntegrationsListQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider', id: string, name: string, code: string } | { __typename?: 'StripeProvider' }> } | null }; export type HubspotIntegrationDetailsFragment = { __typename?: 'HubspotIntegration', id: string, name: string, code: string, defaultTargetedObject: HubspotTargetedObjectsEnum, syncInvoices?: boolean | null, syncSubscriptions?: boolean | null }; @@ -8768,7 +8878,7 @@ export type IntegrationsSettingQueryVariables = Exact<{ }>; -export type IntegrationsSettingQuery = { __typename?: 'Query', organization?: { __typename?: 'CurrentOrganization', id: string, euTaxManagement: boolean, country?: CountryCode | null } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string }> } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration', id: string } | { __typename?: 'HubspotIntegration', id: string } | { __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' } | { __typename?: 'SalesforceIntegration', id: string } | { __typename?: 'XeroIntegration', id: string }> } | null }; +export type IntegrationsSettingQuery = { __typename?: 'Query', organization?: { __typename?: 'CurrentOrganization', id: string, euTaxManagement: boolean, country?: CountryCode | null } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider', id: string } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider', id: string } | { __typename?: 'StripeProvider', id: string }> } | null, integrations?: { __typename?: 'IntegrationCollection', collection: Array<{ __typename?: 'AnrokIntegration', id: string } | { __typename?: 'HubspotIntegration', id: string } | { __typename?: 'NetsuiteIntegration', id: string } | { __typename?: 'OktaIntegration' } | { __typename?: 'SalesforceIntegration', id: string } | { __typename?: 'XeroIntegration', id: string }> } | null }; export type GetOrganizationSettingsQueryVariables = Exact<{ appliedToOrganization?: InputMaybe; @@ -8872,7 +8982,7 @@ export type GetStripeIntegrationsDetailsQueryVariables = Exact<{ }>; -export type GetStripeIntegrationsDetailsQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider', id: string, code: string, name: string, secretKey?: string | null, successRedirectUrl?: string | null } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider', id: string }> } | null }; +export type GetStripeIntegrationsDetailsQuery = { __typename?: 'Query', paymentProvider?: { __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider', id: string, code: string, name: string, secretKey?: string | null, successRedirectUrl?: string | null } | null, paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider', id: string }> } | null }; export type StripeIntegrationsFragment = { __typename?: 'StripeProvider', id: string, name: string, code: string }; @@ -8882,7 +8992,7 @@ export type GetStripeIntegrationsListQueryVariables = Exact<{ }>; -export type GetStripeIntegrationsListQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider', id: string, name: string, code: string, secretKey?: string | null }> } | null }; +export type GetStripeIntegrationsListQuery = { __typename?: 'Query', paymentProviders?: { __typename?: 'PaymentProviderCollection', collection: Array<{ __typename?: 'AdyenProvider' } | { __typename?: 'CashfreeProvider' } | { __typename?: 'GocardlessProvider' } | { __typename?: 'StripeProvider', id: string, name: string, code: string, secretKey?: string | null }> } | null }; export type TaxItemForTaxSettingsFragment = { __typename?: 'Tax', id: string, code: string, name: string, rate: number, autoGenerated: boolean, customersCount: number }; @@ -9693,12 +9803,28 @@ export const AddAdyenProviderDialogFragmentDoc = gql` merchantAccount } `; +export const AddCashfreeProviderDialogFragmentDoc = gql` + fragment AddCashfreeProviderDialog on CashfreeProvider { + id + name + code + clientId + clientSecret + successRedirectUrl +} + `; export const AdyenForCreateAndEditSuccessRedirectUrlFragmentDoc = gql` fragment AdyenForCreateAndEditSuccessRedirectUrl on AdyenProvider { id successRedirectUrl } `; +export const CashfreeForCreateAndEditSuccessRedirectUrlFragmentDoc = gql` + fragment CashfreeForCreateAndEditSuccessRedirectUrl on CashfreeProvider { + id + successRedirectUrl +} + `; export const GocardlessForCreateAndEditSuccessRedirectUrlFragmentDoc = gql` fragment gocardlessForCreateAndEditSuccessRedirectUrl on GocardlessProvider { id @@ -9804,6 +9930,12 @@ export const DeleteAdyenIntegrationDialogFragmentDoc = gql` name } `; +export const DeleteCashfreeIntegrationDialogFragmentDoc = gql` + fragment DeleteCashfreeIntegrationDialog on CashfreeProvider { + id + name +} + `; export const DeleteGocardlessIntegrationDialogFragmentDoc = gql` fragment DeleteGocardlessIntegrationDialog on GocardlessProvider { id @@ -11671,6 +11803,23 @@ export const OktaIntegrationDetailsFragmentDoc = gql` name } `; +export const CashfreeIntegrationDetailsFragmentDoc = gql` + fragment CashfreeIntegrationDetails on CashfreeProvider { + id + code + name + clientId + clientSecret + successRedirectUrl +} + `; +export const CashfreeIntegrationsFragmentDoc = gql` + fragment CashfreeIntegrations on CashfreeProvider { + id + name + code +} + `; export const DunningCampaignItemFragmentDoc = gql` fragment DunningCampaignItem on DunningCampaign { id @@ -13281,6 +13430,11 @@ export const PaymentProvidersListForCustomerMainInfosDocument = gql` name code } + ... on CashfreeProvider { + id + name + code + } ... on AdyenProvider { id name @@ -13937,6 +14091,12 @@ export const PaymentProvidersListForCustomerCreateEditExternalAppsAccordionDocum name code } + ... on CashfreeProvider { + __typename + id + name + code + } ... on AdyenProvider { __typename id @@ -15900,6 +16060,9 @@ export const GetProviderByCodeForAdyenDocument = gql` ... on GocardlessProvider { id } + ... on CashfreeProvider { + id + } ... on StripeProvider { id } @@ -16083,6 +16246,129 @@ export function useUpdateAnrokIntegrationMutation(baseOptions?: Apollo.MutationH export type UpdateAnrokIntegrationMutationHookResult = ReturnType; export type UpdateAnrokIntegrationMutationResult = Apollo.MutationResult; export type UpdateAnrokIntegrationMutationOptions = Apollo.BaseMutationOptions; +export const GetProviderByCodeForCashfreeDocument = gql` + query getProviderByCodeForCashfree($code: String) { + paymentProvider(code: $code) { + ... on CashfreeProvider { + id + } + ... on GocardlessProvider { + id + } + ... on AdyenProvider { + id + } + ... on StripeProvider { + id + } + } +} + `; + +/** + * __useGetProviderByCodeForCashfreeQuery__ + * + * To run a query within a React component, call `useGetProviderByCodeForCashfreeQuery` and pass it any options that fit your needs. + * When your component renders, `useGetProviderByCodeForCashfreeQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetProviderByCodeForCashfreeQuery({ + * variables: { + * code: // value for 'code' + * }, + * }); + */ +export function useGetProviderByCodeForCashfreeQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(GetProviderByCodeForCashfreeDocument, options); + } +export function useGetProviderByCodeForCashfreeLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(GetProviderByCodeForCashfreeDocument, options); + } +export function useGetProviderByCodeForCashfreeSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions) { + const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(GetProviderByCodeForCashfreeDocument, options); + } +export type GetProviderByCodeForCashfreeQueryHookResult = ReturnType; +export type GetProviderByCodeForCashfreeLazyQueryHookResult = ReturnType; +export type GetProviderByCodeForCashfreeSuspenseQueryHookResult = ReturnType; +export type GetProviderByCodeForCashfreeQueryResult = Apollo.QueryResult; +export const AddCashfreeApiKeyDocument = gql` + mutation addCashfreeApiKey($input: AddCashfreePaymentProviderInput!) { + addCashfreePaymentProvider(input: $input) { + id + ...AddCashfreeProviderDialog + ...CashfreeIntegrationDetails + } +} + ${AddCashfreeProviderDialogFragmentDoc} +${CashfreeIntegrationDetailsFragmentDoc}`; +export type AddCashfreeApiKeyMutationFn = Apollo.MutationFunction; + +/** + * __useAddCashfreeApiKeyMutation__ + * + * To run a mutation, you first call `useAddCashfreeApiKeyMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useAddCashfreeApiKeyMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [addCashfreeApiKeyMutation, { data, loading, error }] = useAddCashfreeApiKeyMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useAddCashfreeApiKeyMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(AddCashfreeApiKeyDocument, options); + } +export type AddCashfreeApiKeyMutationHookResult = ReturnType; +export type AddCashfreeApiKeyMutationResult = Apollo.MutationResult; +export type AddCashfreeApiKeyMutationOptions = Apollo.BaseMutationOptions; +export const UpdateCashfreeApiKeyDocument = gql` + mutation updateCashfreeApiKey($input: UpdateCashfreePaymentProviderInput!) { + updateCashfreePaymentProvider(input: $input) { + id + ...AddCashfreeProviderDialog + ...CashfreeIntegrationDetails + } +} + ${AddCashfreeProviderDialogFragmentDoc} +${CashfreeIntegrationDetailsFragmentDoc}`; +export type UpdateCashfreeApiKeyMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateCashfreeApiKeyMutation__ + * + * To run a mutation, you first call `useUpdateCashfreeApiKeyMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateCashfreeApiKeyMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateCashfreeApiKeyMutation, { data, loading, error }] = useUpdateCashfreeApiKeyMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useUpdateCashfreeApiKeyMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateCashfreeApiKeyDocument, options); + } +export type UpdateCashfreeApiKeyMutationHookResult = ReturnType; +export type UpdateCashfreeApiKeyMutationResult = Apollo.MutationResult; +export type UpdateCashfreeApiKeyMutationOptions = Apollo.BaseMutationOptions; export const UpdateAdyenPaymentProviderDocument = gql` mutation updateAdyenPaymentProvider($input: UpdateAdyenPaymentProviderInput!) { updateAdyenPaymentProvider(input: $input) { @@ -16117,6 +16403,40 @@ export function useUpdateAdyenPaymentProviderMutation(baseOptions?: Apollo.Mutat export type UpdateAdyenPaymentProviderMutationHookResult = ReturnType; export type UpdateAdyenPaymentProviderMutationResult = Apollo.MutationResult; export type UpdateAdyenPaymentProviderMutationOptions = Apollo.BaseMutationOptions; +export const UpdateCashfreePaymentProviderDocument = gql` + mutation updateCashfreePaymentProvider($input: UpdateCashfreePaymentProviderInput!) { + updateCashfreePaymentProvider(input: $input) { + id + successRedirectUrl + } +} + `; +export type UpdateCashfreePaymentProviderMutationFn = Apollo.MutationFunction; + +/** + * __useUpdateCashfreePaymentProviderMutation__ + * + * To run a mutation, you first call `useUpdateCashfreePaymentProviderMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useUpdateCashfreePaymentProviderMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [updateCashfreePaymentProviderMutation, { data, loading, error }] = useUpdateCashfreePaymentProviderMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useUpdateCashfreePaymentProviderMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(UpdateCashfreePaymentProviderDocument, options); + } +export type UpdateCashfreePaymentProviderMutationHookResult = ReturnType; +export type UpdateCashfreePaymentProviderMutationResult = Apollo.MutationResult; +export type UpdateCashfreePaymentProviderMutationOptions = Apollo.BaseMutationOptions; export const UpdateGocardlessPaymentProviderDocument = gql` mutation updateGocardlessPaymentProvider($input: UpdateGocardlessPaymentProviderInput!) { updateGocardlessPaymentProvider(input: $input) { @@ -16191,6 +16511,9 @@ export const GetProviderByCodeForGocardlessDocument = gql` ... on GocardlessProvider { id } + ... on CashfreeProvider { + id + } ... on AdyenProvider { id } @@ -16509,6 +16832,9 @@ export const GetProviderByCodeForStripeDocument = gql` ... on GocardlessProvider { id } + ... on CashfreeProvider { + id + } ... on AdyenProvider { id } @@ -17185,6 +17511,39 @@ export function useDestroyNangoIntegrationMutation(baseOptions?: Apollo.Mutation export type DestroyNangoIntegrationMutationHookResult = ReturnType; export type DestroyNangoIntegrationMutationResult = Apollo.MutationResult; export type DestroyNangoIntegrationMutationOptions = Apollo.BaseMutationOptions; +export const DeleteCashfreeDocument = gql` + mutation deleteCashfree($input: DestroyPaymentProviderInput!) { + destroyPaymentProvider(input: $input) { + id + } +} + `; +export type DeleteCashfreeMutationFn = Apollo.MutationFunction; + +/** + * __useDeleteCashfreeMutation__ + * + * To run a mutation, you first call `useDeleteCashfreeMutation` within a React component and pass it any options that fit your needs. + * When your component renders, `useDeleteCashfreeMutation` returns a tuple that includes: + * - A mutate function that you can call at any time to execute the mutation + * - An object with fields that represent the current status of the mutation's execution + * + * @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2; + * + * @example + * const [deleteCashfreeMutation, { data, loading, error }] = useDeleteCashfreeMutation({ + * variables: { + * input: // value for 'input' + * }, + * }); + */ +export function useDeleteCashfreeMutation(baseOptions?: Apollo.MutationHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useMutation(DeleteCashfreeDocument, options); + } +export type DeleteCashfreeMutationHookResult = ReturnType; +export type DeleteCashfreeMutationResult = Apollo.MutationResult; +export type DeleteCashfreeMutationOptions = Apollo.BaseMutationOptions; export const DeleteGocardlessDocument = gql` mutation deleteGocardless($input: DestroyPaymentProviderInput!) { destroyPaymentProvider(input: $input) { @@ -23720,6 +24079,112 @@ export type GetOktaIntegrationQueryHookResult = ReturnType; export type GetOktaIntegrationSuspenseQueryHookResult = ReturnType; export type GetOktaIntegrationQueryResult = Apollo.QueryResult; +export const GetCashfreeIntegrationsDetailsDocument = gql` + query getCashfreeIntegrationsDetails($id: ID!, $limit: Int, $type: ProviderTypeEnum) { + paymentProvider(id: $id) { + ... on CashfreeProvider { + id + ...CashfreeIntegrationDetails + ...DeleteCashfreeIntegrationDialog + ...AddCashfreeProviderDialog + } + } + paymentProviders(limit: $limit, type: $type) { + collection { + ... on CashfreeProvider { + id + } + } + } +} + ${CashfreeIntegrationDetailsFragmentDoc} +${DeleteCashfreeIntegrationDialogFragmentDoc} +${AddCashfreeProviderDialogFragmentDoc}`; + +/** + * __useGetCashfreeIntegrationsDetailsQuery__ + * + * To run a query within a React component, call `useGetCashfreeIntegrationsDetailsQuery` and pass it any options that fit your needs. + * When your component renders, `useGetCashfreeIntegrationsDetailsQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetCashfreeIntegrationsDetailsQuery({ + * variables: { + * id: // value for 'id' + * limit: // value for 'limit' + * type: // value for 'type' + * }, + * }); + */ +export function useGetCashfreeIntegrationsDetailsQuery(baseOptions: Apollo.QueryHookOptions & ({ variables: GetCashfreeIntegrationsDetailsQueryVariables; skip?: boolean; } | { skip: boolean; }) ) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(GetCashfreeIntegrationsDetailsDocument, options); + } +export function useGetCashfreeIntegrationsDetailsLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(GetCashfreeIntegrationsDetailsDocument, options); + } +export function useGetCashfreeIntegrationsDetailsSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions) { + const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(GetCashfreeIntegrationsDetailsDocument, options); + } +export type GetCashfreeIntegrationsDetailsQueryHookResult = ReturnType; +export type GetCashfreeIntegrationsDetailsLazyQueryHookResult = ReturnType; +export type GetCashfreeIntegrationsDetailsSuspenseQueryHookResult = ReturnType; +export type GetCashfreeIntegrationsDetailsQueryResult = Apollo.QueryResult; +export const GetCashfreeIntegrationsListDocument = gql` + query getCashfreeIntegrationsList($limit: Int, $type: ProviderTypeEnum) { + paymentProviders(limit: $limit, type: $type) { + collection { + ... on CashfreeProvider { + id + ...CashfreeIntegrations + ...AddCashfreeProviderDialog + ...DeleteCashfreeIntegrationDialog + } + } + } +} + ${CashfreeIntegrationsFragmentDoc} +${AddCashfreeProviderDialogFragmentDoc} +${DeleteCashfreeIntegrationDialogFragmentDoc}`; + +/** + * __useGetCashfreeIntegrationsListQuery__ + * + * To run a query within a React component, call `useGetCashfreeIntegrationsListQuery` and pass it any options that fit your needs. + * When your component renders, `useGetCashfreeIntegrationsListQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetCashfreeIntegrationsListQuery({ + * variables: { + * limit: // value for 'limit' + * type: // value for 'type' + * }, + * }); + */ +export function useGetCashfreeIntegrationsListQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(GetCashfreeIntegrationsListDocument, options); + } +export function useGetCashfreeIntegrationsListLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(GetCashfreeIntegrationsListDocument, options); + } +export function useGetCashfreeIntegrationsListSuspenseQuery(baseOptions?: Apollo.SkipToken | Apollo.SuspenseQueryHookOptions) { + const options = baseOptions === Apollo.skipToken ? baseOptions : {...defaultOptions, ...baseOptions} + return Apollo.useSuspenseQuery(GetCashfreeIntegrationsListDocument, options); + } +export type GetCashfreeIntegrationsListQueryHookResult = ReturnType; +export type GetCashfreeIntegrationsListLazyQueryHookResult = ReturnType; +export type GetCashfreeIntegrationsListSuspenseQueryHookResult = ReturnType; +export type GetCashfreeIntegrationsListQueryResult = Apollo.QueryResult; export const GetDunningCampaignsDocument = gql` query getDunningCampaigns($limit: Int, $page: Int) { dunningCampaigns(limit: $limit, page: $page, order: "name") { diff --git a/src/layouts/Settings.tsx b/src/layouts/Settings.tsx index 521792845..50e6aac61 100644 --- a/src/layouts/Settings.tsx +++ b/src/layouts/Settings.tsx @@ -1,6 +1,6 @@ import { ClickAwayListener, Stack } from '@mui/material' import { useState } from 'react' -import { Outlet } from 'react-router-dom' +import { generatePath, Outlet } from 'react-router-dom' import styled from 'styled-components' import { Button, Typography, VerticalMenu } from '~/components/designSystem' @@ -24,6 +24,7 @@ import { import { useInternationalization } from '~/hooks/core/useInternationalization' import { useLocationHistory } from '~/hooks/core/useLocationHistory' import { usePermissions } from '~/hooks/usePermissions' +import { IntegrationsTabsOptionsEnum } from '~/pages/settings/Integrations' import { theme } from '~/styles' const NAV_WIDTH = 240 @@ -117,7 +118,9 @@ const Settings = () => { }, { title: translate('text_62b1edddbf5f461ab9712733'), - link: INTEGRATIONS_ROUTE, + link: generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), hidden: !hasPermissions(['organizationIntegrationsView']), }, { diff --git a/src/pages/settings/AdyenIntegrationDetails.tsx b/src/pages/settings/AdyenIntegrationDetails.tsx index b22c35c4a..e7aed3f70 100644 --- a/src/pages/settings/AdyenIntegrationDetails.tsx +++ b/src/pages/settings/AdyenIntegrationDetails.tsx @@ -1,6 +1,6 @@ import { gql } from '@apollo/client' import { useRef } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { generatePath, useNavigate, useParams } from 'react-router-dom' import styled from 'styled-components' import { @@ -40,6 +40,8 @@ import { usePermissions } from '~/hooks/usePermissions' import Adyen from '~/public/images/adyen.svg' import { MenuPopper, NAV_HEIGHT, PageHeader, PopperOpener, theme } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 gql` @@ -98,9 +100,15 @@ const AdyenIntegrationDetails = () => { const adyenPaymentProvider = data?.paymentProvider as AdyenIntegrationDetailsFragment const deleteDialogCallback = () => { if ((data?.paymentProviders?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(ADYEN_INTEGRATION_ROUTE) + navigate( + generatePath(ADYEN_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } const canEditIntegration = hasPermissions(['organizationIntegrationsUpdate']) @@ -111,7 +119,9 @@ const AdyenIntegrationDetails = () => { diff --git a/src/pages/settings/AdyenIntegrations.tsx b/src/pages/settings/AdyenIntegrations.tsx index 59fe71ef9..0b8b847f9 100644 --- a/src/pages/settings/AdyenIntegrations.tsx +++ b/src/pages/settings/AdyenIntegrations.tsx @@ -49,6 +49,8 @@ import { theme, } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + gql` fragment AdyenIntegrations on AdyenProvider { id @@ -86,7 +88,14 @@ const AdyenIntegrations = () => { }) const connections = data?.paymentProviders?.collection as AdyenProvider[] | undefined const deleteDialogCallback = - connections && connections.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined const canCreateIntegration = hasPermissions(['organizationIntegrationsCreate']) const canEditIntegration = hasPermissions(['organizationIntegrationsUpdate']) @@ -97,7 +106,9 @@ const AdyenIntegrations = () => { @@ -172,6 +183,7 @@ const AdyenIntegrations = () => { tabIndex={0} to={generatePath(ADYEN_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} > diff --git a/src/pages/settings/AnrokIntegrationDetails.tsx b/src/pages/settings/AnrokIntegrationDetails.tsx index 6080281a2..55149d531 100644 --- a/src/pages/settings/AnrokIntegrationDetails.tsx +++ b/src/pages/settings/AnrokIntegrationDetails.tsx @@ -44,6 +44,8 @@ import { useInternationalization } from '~/hooks/core/useInternationalization' import Anrok from '~/public/images/anrok.svg' import { MenuPopper, PageHeader, theme } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 export enum AnrokIntegrationDetailsTabs { @@ -104,9 +106,15 @@ const AnrokIntegrationDetails = () => { const anrokIntegration = data?.integration as AnrokIntegrationDetailsFragment const deleteDialogCallback = () => { if ((data?.integrations?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(ANROK_INTEGRATION_ROUTE) + navigate( + generatePath(ANROK_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -115,7 +123,9 @@ const AnrokIntegrationDetails = () => { @@ -204,6 +214,7 @@ const AnrokIntegrationDetails = () => { link: generatePath(ANROK_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: AnrokIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), component: , }, @@ -212,6 +223,7 @@ const AnrokIntegrationDetails = () => { link: generatePath(ANROK_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: AnrokIntegrationDetailsTabs.Items, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), component: , }, diff --git a/src/pages/settings/AnrokIntegrations.tsx b/src/pages/settings/AnrokIntegrations.tsx index 4efecc0db..80d67cf02 100644 --- a/src/pages/settings/AnrokIntegrations.tsx +++ b/src/pages/settings/AnrokIntegrations.tsx @@ -44,6 +44,7 @@ import { } from '~/styles' import { AnrokIntegrationDetailsTabs } from './AnrokIntegrationDetails' +import { IntegrationsTabsOptionsEnum } from './Integrations' gql` fragment AnrokIntegrations on AnrokIntegration { @@ -80,14 +81,23 @@ const AnrokIntegrations = () => { }) const connections = data?.integrations?.collection as AnrokIntegrationsFragment[] | undefined const deleteDialogCallback = - connections && connections?.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections?.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined return ( <> @@ -160,6 +170,7 @@ const AnrokIntegrations = () => { to={generatePath(ANROK_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, tab: AnrokIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} > diff --git a/src/pages/settings/CashfreeIntegrationDetails.tsx b/src/pages/settings/CashfreeIntegrationDetails.tsx new file mode 100644 index 000000000..8651f43f2 --- /dev/null +++ b/src/pages/settings/CashfreeIntegrationDetails.tsx @@ -0,0 +1,385 @@ +import { gql } from '@apollo/client' +import { Stack } from '@mui/material' +import { useMemo, useRef } from 'react' +import { generatePath, useNavigate, useParams } from 'react-router-dom' + +import { + Alert, + Avatar, + Button, + ButtonLink, + Chip, + Icon, + Popper, + Skeleton, + Tooltip, + Typography, +} from '~/components/designSystem' +import { + AddCashfreeDialog, + AddCashfreeDialogRef, +} from '~/components/settings/integrations/AddCashfreeDialog' +import { + DeleteCashfreeIntegrationDialog, + DeleteCashfreeIntegrationDialogRef, +} from '~/components/settings/integrations/DeleteCashfreeIntegrationDialog' +import { addToast, envGlobalVar, getItemFromLS, ORGANIZATION_LS_KEY_ID } from '~/core/apolloClient' +import { CASHFREE_INTEGRATION_ROUTE, INTEGRATIONS_ROUTE } from '~/core/router' +import { copyToClipboard } from '~/core/utils/copyToClipboard' +import { + AddCashfreeProviderDialogFragmentDoc, + CashfreeIntegrationDetailsFragment, + DeleteCashfreeIntegrationDialogFragmentDoc, + ProviderTypeEnum, + useGetCashfreeIntegrationsDetailsQuery, +} from '~/generated/graphql' +import { useInternationalization } from '~/hooks/core/useInternationalization' +import { usePermissions } from '~/hooks/usePermissions' +import Cashfree from '~/public/images/cashfree.svg' +import { MenuPopper, PageHeader } from '~/styles' + +import { IntegrationsTabsOptionsEnum } from './Integrations' + +const PROVIDER_CONNECTION_LIMIT = 2 + +gql` + fragment CashfreeIntegrationDetails on CashfreeProvider { + id + code + name + clientId + clientSecret + successRedirectUrl + } + + query getCashfreeIntegrationsDetails($id: ID!, $limit: Int, $type: ProviderTypeEnum) { + paymentProvider(id: $id) { + ... on CashfreeProvider { + id + ...CashfreeIntegrationDetails + ...DeleteCashfreeIntegrationDialog + ...AddCashfreeProviderDialog + } + } + + paymentProviders(limit: $limit, type: $type) { + collection { + ... on CashfreeProvider { + id + } + } + } + } + + ${DeleteCashfreeIntegrationDialogFragmentDoc} + ${AddCashfreeProviderDialogFragmentDoc} +` + +const CashfreeIntegrationDetails = () => { + const navigate = useNavigate() + const { integrationId } = useParams() + const { hasPermissions } = usePermissions() + const addDialogRef = useRef(null) + const deleteDialogRef = useRef(null) + const { apiUrl } = envGlobalVar() + const currentOrganizationId = getItemFromLS(ORGANIZATION_LS_KEY_ID) + const { translate } = useInternationalization() + const { data, loading } = useGetCashfreeIntegrationsDetailsQuery({ + variables: { + id: integrationId as string, + limit: PROVIDER_CONNECTION_LIMIT, + type: ProviderTypeEnum.Cashfree, + }, + skip: !integrationId, + }) + const cashfreePaymentProvider = data?.paymentProvider as CashfreeIntegrationDetailsFragment + const deleteDialogCallback = () => { + if ((data?.paymentProviders?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { + navigate( + generatePath(CASHFREE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Community, + }), + ) + } else { + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Community, + }), + ) + } + } + + const canEditIntegration = hasPermissions(['organizationIntegrationsUpdate']) + const canDeleteIntegration = hasPermissions(['organizationIntegrationsDelete']) + + const webhookUrl = useMemo( + () => + `${apiUrl}/webhooks/cashfree/${currentOrganizationId}?code=${cashfreePaymentProvider?.code}`, + [apiUrl, currentOrganizationId, cashfreePaymentProvider?.code], + ) + + return ( +
+ +
+ + {loading ? ( + + ) : ( + + {cashfreePaymentProvider?.name} + + )} +
+ {(canEditIntegration || canDeleteIntegration) && ( + {translate('text_626162c62f790600f850b6fe')} + } + > + {({ closePopper }) => ( + + {canEditIntegration && ( + <> + + + )} + + {canDeleteIntegration && ( + + )} + + )} + + )} +
+
+ {loading ? ( + <> + +
+ + +
+ + ) : ( + <> + + + +
+
+ + {cashfreePaymentProvider?.name} + + +
+ + {translate('text_1727619878796wmgcntkfycn')} â€¢  + {translate('text_62b1edddbf5f461ab971271f')} + +
+ + )} +
+ +
+ {translate('text_1733303404277q80b216p5zr')} + +
+
+ + {translate('text_664c732c264d7eed1c74fdc5')} + + + {canEditIntegration && ( + + )} +
+ {loading ? ( + <> + {[0, 1, 2].map((i) => ( +
+ + +
+ ))} +
+ + + ) : ( + <> +
+ + + + + + {translate('text_626162c62f790600f850b76a')} + + + {cashfreePaymentProvider.name} + + +
+ +
+ + + + + + {translate('text_62876e85e32e0300e1803127')}{' '} + + + {cashfreePaymentProvider.code} + + +
+ +
+ + + + + + + {translate('text_1727620558031ftsky1vpr55')} + + + {cashfreePaymentProvider.clientId} + + + +
+ +
+ + + + + + + {translate('text_1727620574228qfyoqtsdih7')} + + + {cashfreePaymentProvider.clientSecret} + + + +
+ +
+ + + + + + + {translate('text_65367cb78324b77fcb6af21c')} + + + {cashfreePaymentProvider.successRedirectUrl || '-'} + + + +
+ +
+ + + + + + + {translate('text_6271200984178801ba8bdf22')} + + + {webhookUrl} + + + + + + +
+ + )} + {!loading && ( + + )} +
+
+ + + +
+ ) +} + +export default CashfreeIntegrationDetails diff --git a/src/pages/settings/CashfreeIntegrations.tsx b/src/pages/settings/CashfreeIntegrations.tsx new file mode 100644 index 000000000..bec17a9ab --- /dev/null +++ b/src/pages/settings/CashfreeIntegrations.tsx @@ -0,0 +1,274 @@ +import { gql } from '@apollo/client' +import { useRef } from 'react' +import { generatePath, useNavigate } from 'react-router-dom' + +import { + Avatar, + Button, + ButtonLink, + Chip, + Icon, + Popper, + Skeleton, + Tooltip, + Typography, +} from '~/components/designSystem' +import { + AddCashfreeDialog, + AddCashfreeDialogRef, +} from '~/components/settings/integrations/AddCashfreeDialog' +import { + AddEditDeleteSuccessRedirectUrlDialog, + AddEditDeleteSuccessRedirectUrlDialogRef, +} from '~/components/settings/integrations/AddEditDeleteSuccessRedirectUrlDialog' +import { + DeleteCashfreeIntegrationDialog, + DeleteCashfreeIntegrationDialogRef, +} from '~/components/settings/integrations/DeleteCashfreeIntegrationDialog' +import { CASHFREE_INTEGRATION_DETAILS_ROUTE, INTEGRATIONS_ROUTE } from '~/core/router' +import { + AddCashfreeProviderDialogFragmentDoc, + CashfreeForCreateAndEditSuccessRedirectUrlFragmentDoc, + CashfreeProvider, + DeleteCashfreeIntegrationDialogFragmentDoc, + ProviderTypeEnum, + useGetCashfreeIntegrationsListQuery, +} from '~/generated/graphql' +import { useInternationalization } from '~/hooks/core/useInternationalization' +import { usePermissions } from '~/hooks/usePermissions' +import Cashfree from '~/public/images/cashfree.svg' +import { ListItemLink, MenuPopper, PageHeader, PopperOpener } from '~/styles' + +import { IntegrationsTabsOptionsEnum } from './Integrations' + +gql` + fragment CashfreeIntegrations on CashfreeProvider { + id + name + code + } + + query getCashfreeIntegrationsList($limit: Int, $type: ProviderTypeEnum) { + paymentProviders(limit: $limit, type: $type) { + collection { + ... on CashfreeProvider { + id + ...CashfreeIntegrations + ...AddCashfreeProviderDialog + ...DeleteCashfreeIntegrationDialog + } + } + } + } + ${CashfreeForCreateAndEditSuccessRedirectUrlFragmentDoc} + ${DeleteCashfreeIntegrationDialogFragmentDoc} + ${AddCashfreeProviderDialogFragmentDoc} +` + +const CashfreeIntegrations = () => { + const navigate = useNavigate() + const { hasPermissions } = usePermissions() + const addCashfreeDialogRef = useRef(null) + const deleteDialogRef = useRef(null) + const successRedirectUrlDialogRef = useRef(null) + const { translate } = useInternationalization() + const { data, loading } = useGetCashfreeIntegrationsListQuery({ + variables: { limit: 1000, type: ProviderTypeEnum.Cashfree }, + }) + const connections = data?.paymentProviders?.collection as CashfreeProvider[] | undefined + const deleteDialogCallback = + connections && connections.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined + + const canCreateIntegration = hasPermissions(['organizationIntegrationsCreate']) + const canEditIntegration = hasPermissions(['organizationIntegrationsUpdate']) + const canDeleteIntegration = hasPermissions(['organizationIntegrationsDelete']) + + return ( + <> + +
+ + {loading ? ( + + ) : ( + + {translate('text_1727619878796wmgcntkfycn')} + + )} +
+ + {canCreateIntegration && ( + + )} +
+
+ {loading ? ( + <> + +
+ + +
+ + ) : ( + <> + + + +
+ + {translate('text_1727619878796wmgcntkfycn')} + + +
+ {translate('text_62b1edddbf5f461ab971271f')} + + )} +
+ +
+
+ + {translate('text_65846763e6140b469140e239')} + + + <> + {loading ? ( + <> + {[1, 2].map((i) => ( +
+ + +
+ ))} + + ) : ( + <> + {connections?.map((connection, index) => { + return ( +
+ +
+ + + +
+ + {connection.name} + + + {connection.code} + +
+
+
+ + {(canEditIntegration || canDeleteIntegration) && ( + ( + // right-0 used to align the popper to the right + + + + )} + + {canDeleteIntegration && ( + + )} + + )} + + )} +
+ ) + })} + + )} + +
+
+ + + + + + ) +} + +export default CashfreeIntegrations diff --git a/src/pages/settings/GocardlessIntegrationDetails.tsx b/src/pages/settings/GocardlessIntegrationDetails.tsx index 13d7c5406..9ede7d3d1 100644 --- a/src/pages/settings/GocardlessIntegrationDetails.tsx +++ b/src/pages/settings/GocardlessIntegrationDetails.tsx @@ -1,7 +1,7 @@ import { gql } from '@apollo/client' import { Stack } from '@mui/material' import { useRef } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { generatePath, useNavigate, useParams } from 'react-router-dom' import styled from 'styled-components' import { @@ -43,6 +43,8 @@ import { usePermissions } from '~/hooks/usePermissions' import GoCardless from '~/public/images/gocardless-large.svg' import { MenuPopper, NAV_HEIGHT, PageHeader, PopperOpener, theme } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 gql` @@ -98,9 +100,15 @@ const GocardlessIntegrationDetails = () => { const isConnectionEstablished = !!gocardlessPaymentProvider?.webhookSecret const deleteDialogCallback = () => { if ((data?.paymentProviders?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(GOCARDLESS_INTEGRATION_ROUTE) + navigate( + generatePath(GOCARDLESS_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -112,7 +120,9 @@ const GocardlessIntegrationDetails = () => { diff --git a/src/pages/settings/GocardlessIntegrationOauthCallback.tsx b/src/pages/settings/GocardlessIntegrationOauthCallback.tsx index 30dc3a977..8e37c7fa8 100644 --- a/src/pages/settings/GocardlessIntegrationOauthCallback.tsx +++ b/src/pages/settings/GocardlessIntegrationOauthCallback.tsx @@ -16,6 +16,8 @@ import Gocardless from '~/public/images/gocardless.svg' import ErrorImage from '~/public/images/maneki/error.svg' import { PageHeader, theme } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + gql` fragment GocardlessIntegrationOauthCallback on GocardlessProvider { id @@ -59,6 +61,7 @@ const GocardlessIntegrationOauthCallback = () => { navigate( generatePath(GOCARDLESS_INTEGRATION_DETAILS_ROUTE, { integrationId: res.data?.addGocardlessPaymentProvider?.id as string, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), ) } @@ -66,7 +69,9 @@ const GocardlessIntegrationOauthCallback = () => { if (!!code && !!accessCode && !!name) { createIntegration() } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) addToast({ severity: 'danger', @@ -83,7 +88,9 @@ const GocardlessIntegrationOauthCallback = () => { @@ -132,7 +139,13 @@ const GocardlessIntegrationOauthCallback = () => { title={translate('text_62bac37900192b773560e82d')} subtitle={translate('text_62bac37900192b773560e82f')} buttonTitle={translate('text_62bac37900192b773560e831')} - buttonAction={() => navigate(INTEGRATIONS_ROUTE)} + buttonAction={() => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } /> )} diff --git a/src/pages/settings/GocardlessIntegrations.tsx b/src/pages/settings/GocardlessIntegrations.tsx index 7c132e6fc..589866a7f 100644 --- a/src/pages/settings/GocardlessIntegrations.tsx +++ b/src/pages/settings/GocardlessIntegrations.tsx @@ -49,6 +49,8 @@ import { theme, } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + gql` fragment GocardlessIntegrations on GocardlessProvider { id @@ -86,7 +88,14 @@ const GocardlessIntegrations = () => { }) const connections = data?.paymentProviders?.collection as GocardlessProvider[] | undefined const deleteDialogCallback = - connections && connections.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined const canCreateIntegration = hasPermissions(['organizationIntegrationsCreate']) const canEditIntegration = hasPermissions(['organizationIntegrationsUpdate']) @@ -97,7 +106,9 @@ const GocardlessIntegrations = () => { @@ -172,6 +183,7 @@ const GocardlessIntegrations = () => { tabIndex={0} to={generatePath(GOCARDLESS_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} > diff --git a/src/pages/settings/HubspotIntegrationDetails.tsx b/src/pages/settings/HubspotIntegrationDetails.tsx index 0014e5e79..1ff005081 100644 --- a/src/pages/settings/HubspotIntegrationDetails.tsx +++ b/src/pages/settings/HubspotIntegrationDetails.tsx @@ -1,6 +1,6 @@ import { gql } from '@apollo/client' import { FC, useRef } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { generatePath, useNavigate, useParams } from 'react-router-dom' import { Avatar, @@ -33,6 +33,8 @@ import { useInternationalization } from '~/hooks/core/useInternationalization' import Hubspot from '~/public/images/hubspot.svg' import { MenuPopper, PageHeader } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 gql` @@ -95,9 +97,15 @@ const HubspotIntegrationDetails = () => { const integrations = data?.integrations?.collection || [] if (integrations.length >= PROVIDER_CONNECTION_LIMIT) { - navigate(HUBSPOT_INTEGRATION_ROUTE) + navigate( + generatePath(HUBSPOT_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -106,7 +114,9 @@ const HubspotIntegrationDetails = () => {
diff --git a/src/pages/settings/HubspotIntegrations.tsx b/src/pages/settings/HubspotIntegrations.tsx index c013297c5..4b3055125 100644 --- a/src/pages/settings/HubspotIntegrations.tsx +++ b/src/pages/settings/HubspotIntegrations.tsx @@ -32,6 +32,8 @@ import { useInternationalization } from '~/hooks/core/useInternationalization' import Hubspot from '~/public/images/hubspot.svg' import { ListItemLink, MenuPopper, PageHeader, PopperOpener } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + gql` fragment HubspotIntegrations on HubspotIntegration { id @@ -68,14 +70,23 @@ const HubspotIntegrations = () => { const connections = data?.integrations?.collection as HubspotIntegrationsFragment[] | undefined const deleteDialogCallback = - connections && connections?.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections?.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined return ( <>
@@ -151,6 +162,7 @@ const HubspotIntegrations = () => { className="p-0" to={generatePath(HUBSPOT_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} >
diff --git a/src/pages/settings/Integrations.tsx b/src/pages/settings/Integrations.tsx index 91af00bda..550f9db52 100644 --- a/src/pages/settings/Integrations.tsx +++ b/src/pages/settings/Integrations.tsx @@ -1,8 +1,8 @@ import { gql } from '@apollo/client' import { useRef } from 'react' -import { useNavigate } from 'react-router' +import { generatePath, useNavigate } from 'react-router' -import { Avatar, Chip, Selector, Typography } from '~/components/designSystem' +import { Alert, Avatar, Chip, NavigationTab, Selector, Typography } from '~/components/designSystem' import { PageBannerHeaderWithBurgerMenu } from '~/components/layouts/Pages' import { SettingsListItem, @@ -20,6 +20,10 @@ import { AddAnrokDialog, AddAnrokDialogRef, } from '~/components/settings/integrations/AddAnrokDialog' +import { + AddCashfreeDialog, + AddCashfreeDialogRef, +} from '~/components/settings/integrations/AddCashfreeDialog' import { AddGocardlessDialog, AddGocardlessDialogRef, @@ -54,8 +58,10 @@ import { import { ADYEN_INTEGRATION_ROUTE, ANROK_INTEGRATION_ROUTE, + CASHFREE_INTEGRATION_ROUTE, GOCARDLESS_INTEGRATION_ROUTE, HUBSPOT_INTEGRATION_ROUTE, + INTEGRATIONS_ROUTE, NETSUITE_INTEGRATION_ROUTE, SALESFORCE_INTEGRATION_ROUTE, STRIPE_INTEGRATION_ROUTE, @@ -70,6 +76,7 @@ import { useOrganizationInfos } from '~/hooks/useOrganizationInfos' import Adyen from '~/public/images/adyen.svg' import Airbyte from '~/public/images/airbyte.svg' import Anrok from '~/public/images/anrok.svg' +import Cashfree from '~/public/images/cashfree.svg' import GoCardless from '~/public/images/gocardless.svg' import HightTouch from '~/public/images/hightouch.svg' import Hubspot from '~/public/images/hubspot.svg' @@ -81,6 +88,11 @@ import Segment from '~/public/images/segment.svg' import Stripe from '~/public/images/stripe.svg' import Xero from '~/public/images/xero.svg' +export enum IntegrationsTabsOptionsEnum { + Lago = 'lago', + Community = 'community', +} + gql` query integrationsSetting($limit: Int) { organization { @@ -139,6 +151,7 @@ const Integrations = () => { const addStripeDialogRef = useRef(null) const addAdyenDialogRef = useRef(null) const addGocardlessDialogRef = useRef(null) + const addCashfreeDialogRef = useRef(null) const addLagoTaxManagementDialog = useRef(null) const addNetsuiteDialogRef = useRef(null) const addSalesforceDialogRef = useRef(null) @@ -159,6 +172,9 @@ const Integrations = () => { const hasGocardlessIntegration = data?.paymentProviders?.collection?.some( (provider) => provider?.__typename === 'GocardlessProvider', ) + const hasCashfreeIntegration = data?.paymentProviders?.collection?.some( + (provider) => provider?.__typename === 'CashfreeProvider', + ) const hasTaxManagement = !!organization?.euTaxManagement const hasAccessToNetsuitePremiumIntegration = !!premiumIntegrations?.includes( PremiumIntegrationTypeEnum.Netsuite, @@ -196,322 +212,417 @@ const Integrations = () => { - + {translate('text_62b1edddbf5f461ab9712750')} {translate('text_62b1edddbf5f461ab9712765')} - - {!!loading ? ( - - ) : ( - - - ) : undefined - } - icon={ - - {} - - } - onClick={() => { - if (!isPremium) { - premiumWarningDialogRef.current?.openDialog({ - title: translate('text_661ff6e56ef7e1b7c542b1ea'), - description: translate('text_661ff6e56ef7e1b7c542b1f6'), - mailtoSubject: translate('text_666887641443e4a75b9ead3d'), - mailtoBody: translate('text_666887641443e4a75b9ead3e'), - }) - } else if (hasAnrokIntegration) { - navigate(ANROK_INTEGRATION_ROUTE) - } else { - addAnrokDialogRef.current?.openDialog() - } - }} - /> - - - - } - endIcon={ - hasAdyenIntegration ? ( - - ) : undefined - } - onClick={() => { - if (hasAdyenIntegration) { - navigate(ADYEN_INTEGRATION_ROUTE) - } else { - const element = document.activeElement as HTMLElement + + {!!loading ? ( + + ) : ( + + + ) : undefined + } + icon={ + + {} + + } + onClick={() => { + if (!isPremium) { + premiumWarningDialogRef.current?.openDialog({ + title: translate('text_661ff6e56ef7e1b7c542b1ea'), + description: translate('text_661ff6e56ef7e1b7c542b1f6'), + mailtoSubject: translate('text_666887641443e4a75b9ead3d'), + mailtoBody: translate('text_666887641443e4a75b9ead3e'), + }) + } else if (hasAnrokIntegration) { + navigate( + generatePath(ANROK_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + addAnrokDialogRef.current?.openDialog() + } + }} + /> + + + + } + endIcon={ + hasAdyenIntegration ? ( + + ) : undefined + } + onClick={() => { + if (hasAdyenIntegration) { + navigate( + generatePath(ADYEN_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + const element = document.activeElement as HTMLElement - element.blur && element.blur() - addAdyenDialogRef.current?.openDialog() - } - }} - fullWidth - /> - - {} - - } - onClick={() => { - window.open(DOCUMENTATION_AIRBYTE, '_blank') - }} - fullWidth - /> - - {} - - } - onClick={() => { - window.open(DOCUMENTATION_OSO, '_blank') - }} - fullWidth - /> - - - - } - endIcon={ - hasGocardlessIntegration ? ( - - ) : undefined - } - onClick={() => { - if (hasGocardlessIntegration) { - navigate(GOCARDLESS_INTEGRATION_ROUTE) - } else { - addGocardlessDialogRef.current?.openDialog() - } - }} - fullWidth - /> - - {} - - } - onClick={() => { - window.open(DOCUMENTATION_HIGHTTOUCH, '_blank') - }} - fullWidth - /> - - {} - - } - endIcon={ - !hasAccessToHubspotPremiumIntegration ? ( - 'sparkles' - ) : hasHubspotIntegration ? ( - - ) : undefined - } - onClick={() => { - if (!hasAccessToHubspotPremiumIntegration) { - premiumWarningDialogRef.current?.openDialog({ - title: translate('text_661ff6e56ef7e1b7c542b1ea'), - description: translate('text_661ff6e56ef7e1b7c542b1f6'), - mailtoSubject: translate('text_172718956805392syzumhdlm'), - mailtoBody: translate('text_1727189568053f91r4b3f4rl'), - }) - } else if (hasHubspotIntegration) { - navigate(HUBSPOT_INTEGRATION_ROUTE) - } else { - addHubspotDialogRef.current?.openDialog() - } - }} - fullWidth - /> - - {} - - } - endIcon={ - hasTaxManagement ? ( - - ) : undefined - } - onClick={() => { - if (hasTaxManagement) { - navigate(TAX_MANAGEMENT_INTEGRATION_ROUTE) - } else { - addLagoTaxManagementDialog.current?.openDialog() - } - }} - /> - - ) : undefined - } - icon={ - - {} - - } - onClick={() => { - if (!hasAccessToNetsuitePremiumIntegration) { - premiumWarningDialogRef.current?.openDialog({ - title: translate('text_661ff6e56ef7e1b7c542b1ea'), - description: translate('text_661ff6e56ef7e1b7c542b1f6'), - mailtoSubject: translate('text_661ff6e56ef7e1b7c542b220'), - mailtoBody: translate('text_661ff6e56ef7e1b7c542b238'), - }) - } else if (hasNetsuiteIntegration) { - navigate(NETSUITE_INTEGRATION_ROUTE) - } else { - addNetsuiteDialogRef.current?.openDialog() - } - }} - /> - {isFeatureSalesforceEnabled && ( - - {} - - } - endIcon={!hasAccessToSalesforcePremiumIntegration ? 'sparkles' : undefined} - onClick={() => { - if (!hasAccessToSalesforcePremiumIntegration) { - premiumWarningDialogRef.current?.openDialog({ - title: translate('text_661ff6e56ef7e1b7c542b1ea'), - description: translate('text_661ff6e56ef7e1b7c542b1f6'), - mailtoSubject: translate('text_173150719524652xb2nd3f7r'), - mailtoBody: translate('text_1731507195246xxr17pdnb7s'), - }) - } else if (hasSalesforceIntegration) { - navigate(SALESFORCE_INTEGRATION_ROUTE) - } else { - addSalesforceDialogRef.current?.openDialog() - } - }} - /> - )} - - {} - - } - onClick={() => { - window.open(DOCUMENTATION_SEGMENT, '_blank') - }} - fullWidth - /> - - - - } - endIcon={ - hasStripeIntegration ? ( - - ) : undefined - } - onClick={() => { - if (hasStripeIntegration) { - navigate(STRIPE_INTEGRATION_ROUTE) - } else { - const element = document.activeElement as HTMLElement + element.blur && element.blur() + addAdyenDialogRef.current?.openDialog() + } + }} + fullWidth + /> + + {} + + } + onClick={() => { + window.open(DOCUMENTATION_AIRBYTE, '_blank') + }} + fullWidth + /> + + {} + + } + onClick={() => { + window.open(DOCUMENTATION_OSO, '_blank') + }} + fullWidth + /> + + + + } + endIcon={ + hasGocardlessIntegration ? ( + + ) : undefined + } + onClick={() => { + if (hasGocardlessIntegration) { + navigate( + generatePath(GOCARDLESS_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + addGocardlessDialogRef.current?.openDialog() + } + }} + fullWidth + /> + + {} + + } + onClick={() => { + window.open(DOCUMENTATION_HIGHTTOUCH, '_blank') + }} + fullWidth + /> + + {} + + } + endIcon={ + !hasAccessToHubspotPremiumIntegration ? ( + 'sparkles' + ) : hasHubspotIntegration ? ( + + ) : undefined + } + onClick={() => { + if (!hasAccessToHubspotPremiumIntegration) { + premiumWarningDialogRef.current?.openDialog({ + title: translate('text_661ff6e56ef7e1b7c542b1ea'), + description: translate('text_661ff6e56ef7e1b7c542b1f6'), + mailtoSubject: translate('text_172718956805392syzumhdlm'), + mailtoBody: translate('text_1727189568053f91r4b3f4rl'), + }) + } else if (hasHubspotIntegration) { + navigate( + generatePath(HUBSPOT_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + addHubspotDialogRef.current?.openDialog() + } + }} + fullWidth + /> + + {} + + } + endIcon={ + hasTaxManagement ? ( + + ) : undefined + } + onClick={() => { + if (hasTaxManagement) { + navigate( + generatePath(TAX_MANAGEMENT_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + addLagoTaxManagementDialog.current?.openDialog() + } + }} + /> + + ) : undefined + } + icon={ + + {} + + } + onClick={() => { + if (!hasAccessToNetsuitePremiumIntegration) { + premiumWarningDialogRef.current?.openDialog({ + title: translate('text_661ff6e56ef7e1b7c542b1ea'), + description: translate('text_661ff6e56ef7e1b7c542b1f6'), + mailtoSubject: translate('text_661ff6e56ef7e1b7c542b220'), + mailtoBody: translate('text_661ff6e56ef7e1b7c542b238'), + }) + } else if (hasNetsuiteIntegration) { + navigate( + generatePath(NETSUITE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + addNetsuiteDialogRef.current?.openDialog() + } + }} + /> + {isFeatureSalesforceEnabled && ( + + {} + + } + endIcon={ + !hasAccessToSalesforcePremiumIntegration ? 'sparkles' : undefined + } + onClick={() => { + if (!hasAccessToSalesforcePremiumIntegration) { + premiumWarningDialogRef.current?.openDialog({ + title: translate('text_661ff6e56ef7e1b7c542b1ea'), + description: translate('text_661ff6e56ef7e1b7c542b1f6'), + mailtoSubject: translate('text_173150719524652xb2nd3f7r'), + mailtoBody: translate('text_1731507195246xxr17pdnb7s'), + }) + } else if (hasSalesforceIntegration) { + navigate( + generatePath(SALESFORCE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + addSalesforceDialogRef.current?.openDialog() + } + }} + /> + )} + + {} + + } + onClick={() => { + window.open(DOCUMENTATION_SEGMENT, '_blank') + }} + fullWidth + /> + + + + } + endIcon={ + hasStripeIntegration ? ( + + ) : undefined + } + onClick={() => { + if (hasStripeIntegration) { + navigate( + generatePath(STRIPE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + const element = document.activeElement as HTMLElement - element.blur && element.blur() - addStripeDialogRef.current?.openDialog() - } - }} - fullWidth - /> - - ) : undefined - } - icon={ - - {} - - } - onClick={() => { - if (!hasAccessToXeroPremiumIntegration) { - premiumWarningDialogRef.current?.openDialog({ - title: translate('text_661ff6e56ef7e1b7c542b1ea'), - description: translate('text_661ff6e56ef7e1b7c542b1f6'), - mailtoSubject: translate('text_6672ebb8b1b50be550ecca09'), - mailtoBody: translate('text_6672ebb8b1b50be550ecca13'), - }) - } else if (hasXeroIntegration) { - navigate(XERO_INTEGRATION_ROUTE) - } else { - addXeroDialogRef.current?.openDialog() - } - }} - /> - - )} - + element.blur && element.blur() + addStripeDialogRef.current?.openDialog() + } + }} + fullWidth + /> + + ) : undefined + } + icon={ + + {} + + } + onClick={() => { + if (!hasAccessToXeroPremiumIntegration) { + premiumWarningDialogRef.current?.openDialog({ + title: translate('text_661ff6e56ef7e1b7c542b1ea'), + description: translate('text_661ff6e56ef7e1b7c542b1f6'), + mailtoSubject: translate('text_6672ebb8b1b50be550ecca09'), + mailtoBody: translate('text_6672ebb8b1b50be550ecca13'), + }) + } else if (hasXeroIntegration) { + navigate( + generatePath(XERO_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + } else { + addXeroDialogRef.current?.openDialog() + } + }} + /> + + )} + + ), + }, + { + title: translate('text_173330340427732b341qnuny'), + link: generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Community, + }), + component: ( + + {translate('text_1733303404277q80b216p5zr')} + {!!loading ? ( + + ) : ( + + + + + } + endIcon={ + hasCashfreeIntegration ? ( + + ) : undefined + } + onClick={() => { + if (hasCashfreeIntegration) { + navigate( + generatePath(CASHFREE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Community, + }), + ) + } else { + addCashfreeDialogRef.current?.openDialog() + } + }} + /> + + )} + + ), + }, + ]} + loading={loading} + /> + { @@ -243,7 +247,11 @@ const LagoTaxManagementIntegration = () => { }, }) - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) }} /> diff --git a/src/pages/settings/NetsuiteIntegrationDetails.tsx b/src/pages/settings/NetsuiteIntegrationDetails.tsx index 9e89a4157..e3514a44b 100644 --- a/src/pages/settings/NetsuiteIntegrationDetails.tsx +++ b/src/pages/settings/NetsuiteIntegrationDetails.tsx @@ -44,6 +44,8 @@ import { useInternationalization } from '~/hooks/core/useInternationalization' import Netsuite from '~/public/images/netsuite.svg' import { MenuPopper, PageHeader, theme } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 export enum NetsuiteIntegrationDetailsTabs { @@ -104,9 +106,15 @@ const NetsuiteIntegrationDetails = () => { const netsuiteIntegration = data?.integration as NetsuiteIntegrationDetailsFragment const deleteDialogCallback = () => { if ((data?.integrations?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(NETSUITE_INTEGRATION_ROUTE) + navigate( + generatePath(NETSUITE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -115,7 +123,9 @@ const NetsuiteIntegrationDetails = () => { @@ -204,6 +214,7 @@ const NetsuiteIntegrationDetails = () => { link: generatePath(NETSUITE_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: NetsuiteIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), component: , }, @@ -212,6 +223,7 @@ const NetsuiteIntegrationDetails = () => { link: generatePath(NETSUITE_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: NetsuiteIntegrationDetailsTabs.Items, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), component: , }, diff --git a/src/pages/settings/NetsuiteIntegrations.tsx b/src/pages/settings/NetsuiteIntegrations.tsx index 7be8c90c6..b206e9598 100644 --- a/src/pages/settings/NetsuiteIntegrations.tsx +++ b/src/pages/settings/NetsuiteIntegrations.tsx @@ -43,6 +43,7 @@ import { theme, } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' import { NetsuiteIntegrationDetailsTabs } from './NetsuiteIntegrationDetails' gql` @@ -80,14 +81,23 @@ const NetsuiteIntegrations = () => { }) const connections = data?.integrations?.collection as NetsuiteIntegrationsFragment[] | undefined const deleteDialogCallback = - connections && connections?.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections?.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined return ( <> @@ -160,6 +170,7 @@ const NetsuiteIntegrations = () => { to={generatePath(NETSUITE_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, tab: NetsuiteIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} > diff --git a/src/pages/settings/SalesforceIntegrationDetails.tsx b/src/pages/settings/SalesforceIntegrationDetails.tsx index cae56d194..a1d229a3f 100644 --- a/src/pages/settings/SalesforceIntegrationDetails.tsx +++ b/src/pages/settings/SalesforceIntegrationDetails.tsx @@ -1,6 +1,6 @@ import { gql } from '@apollo/client' import { FC, useRef } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { generatePath, useNavigate, useParams } from 'react-router-dom' import { Avatar, @@ -33,6 +33,8 @@ import { useInternationalization } from '~/hooks/core/useInternationalization' import Salesforce from '~/public/images/salesforce.svg' import { MenuPopper, PageHeader } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 gql` @@ -95,9 +97,15 @@ const SalesforceIntegrationDetails = () => { const integrations = data?.integrations?.collection || [] if (integrations.length >= PROVIDER_CONNECTION_LIMIT) { - navigate(SALESFORCE_INTEGRATION_ROUTE) + navigate( + generatePath(SALESFORCE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -106,7 +114,9 @@ const SalesforceIntegrationDetails = () => {
diff --git a/src/pages/settings/SalesforceIntegrations.tsx b/src/pages/settings/SalesforceIntegrations.tsx index 9ed9cf377..a155b9350 100644 --- a/src/pages/settings/SalesforceIntegrations.tsx +++ b/src/pages/settings/SalesforceIntegrations.tsx @@ -33,6 +33,8 @@ import { useInternationalization } from '~/hooks/core/useInternationalization' import Salesforce from '~/public/images/salesforce.svg' import { ListItemLink, MenuPopper, PageHeader, PopperOpener } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + gql` fragment SalesforceIntegrations on SalesforceIntegration { id @@ -71,14 +73,23 @@ const SalesforceIntegrations = () => { const connections = data?.integrations?.collection as SalesforceIntegrationsFragment[] | undefined const deleteDialogCallback = - connections && connections?.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections?.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined return ( <>
@@ -154,6 +165,7 @@ const SalesforceIntegrations = () => { className="p-0" to={generatePath(SALESFORCE_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} >
diff --git a/src/pages/settings/StripeIntegrationDetails.tsx b/src/pages/settings/StripeIntegrationDetails.tsx index 58eae20f2..e7874e8d2 100644 --- a/src/pages/settings/StripeIntegrationDetails.tsx +++ b/src/pages/settings/StripeIntegrationDetails.tsx @@ -1,7 +1,7 @@ import { gql } from '@apollo/client' import { Stack } from '@mui/material' import { useRef } from 'react' -import { useNavigate, useParams } from 'react-router-dom' +import { generatePath, useNavigate, useParams } from 'react-router-dom' import styled from 'styled-components' import { @@ -41,6 +41,8 @@ import { usePermissions } from '~/hooks/usePermissions' import Stripe from '~/public/images/stripe.svg' import { MenuPopper, NAV_HEIGHT, PageHeader, PopperOpener, theme } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 gql` @@ -96,9 +98,15 @@ const StripeIntegrationDetails = () => { const stripePaymentProvider = data?.paymentProvider as StripeIntegrationDetailsFragment const deleteDialogCallback = () => { if ((data?.paymentProviders?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(STRIPE_INTEGRATION_ROUTE) + navigate( + generatePath(STRIPE_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -110,7 +118,9 @@ const StripeIntegrationDetails = () => { diff --git a/src/pages/settings/StripeIntegrations.tsx b/src/pages/settings/StripeIntegrations.tsx index 8f2847932..7d9d9c46b 100644 --- a/src/pages/settings/StripeIntegrations.tsx +++ b/src/pages/settings/StripeIntegrations.tsx @@ -49,6 +49,8 @@ import { theme, } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + gql` fragment StripeIntegrations on StripeProvider { id @@ -86,7 +88,14 @@ const StripeIntegrations = () => { }) const connections = data?.paymentProviders?.collection as StripeProvider[] | undefined const deleteDialogCallback = - connections && connections.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined const canCreateIntegration = hasPermissions(['organizationIntegrationsCreate']) const canEditIntegration = hasPermissions(['organizationIntegrationsUpdate']) @@ -97,7 +106,9 @@ const StripeIntegrations = () => { @@ -172,6 +183,7 @@ const StripeIntegrations = () => { tabIndex={0} to={generatePath(STRIPE_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} > diff --git a/src/pages/settings/XeroIntegrationDetails.tsx b/src/pages/settings/XeroIntegrationDetails.tsx index 9efac245e..52e2b9a10 100644 --- a/src/pages/settings/XeroIntegrationDetails.tsx +++ b/src/pages/settings/XeroIntegrationDetails.tsx @@ -41,6 +41,8 @@ import { useInternationalization } from '~/hooks/core/useInternationalization' import Xero from '~/public/images/xero.svg' import { MenuPopper, PageHeader, theme } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' + const PROVIDER_CONNECTION_LIMIT = 2 export enum XeroIntegrationDetailsTabs { @@ -97,9 +99,15 @@ const XeroIntegrationDetails = () => { const xeroIntegration = data?.integration as XeroIntegrationDetailsFragment const deleteDialogCallback = () => { if ((data?.integrations?.collection.length || 0) >= PROVIDER_CONNECTION_LIMIT) { - navigate(XERO_INTEGRATION_ROUTE) + navigate( + generatePath(XERO_INTEGRATION_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) } else { - navigate(INTEGRATIONS_ROUTE) + navigate( + generatePath(INTEGRATIONS_ROUTE, { integrationGroup: IntegrationsTabsOptionsEnum.Lago }), + ) } } @@ -108,7 +116,9 @@ const XeroIntegrationDetails = () => { @@ -197,6 +207,7 @@ const XeroIntegrationDetails = () => { link: generatePath(XERO_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: XeroIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), component: , }, @@ -205,6 +216,7 @@ const XeroIntegrationDetails = () => { link: generatePath(XERO_INTEGRATION_DETAILS_ROUTE, { integrationId, tab: XeroIntegrationDetailsTabs.Items, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, }), component: , }, diff --git a/src/pages/settings/XeroIntegrations.tsx b/src/pages/settings/XeroIntegrations.tsx index 7d6a837bb..66ec6172e 100644 --- a/src/pages/settings/XeroIntegrations.tsx +++ b/src/pages/settings/XeroIntegrations.tsx @@ -40,6 +40,7 @@ import { theme, } from '~/styles' +import { IntegrationsTabsOptionsEnum } from './Integrations' import { XeroIntegrationDetailsTabs } from './XeroIntegrationDetails' gql` @@ -77,14 +78,23 @@ const XeroIntegrations = () => { }) const connections = data?.integrations?.collection as XeroIntegrationsFragment[] | undefined const deleteDialogCallback = - connections && connections?.length === 1 ? () => navigate(INTEGRATIONS_ROUTE) : undefined + connections && connections?.length === 1 + ? () => + navigate( + generatePath(INTEGRATIONS_ROUTE, { + integrationGroup: IntegrationsTabsOptionsEnum.Lago, + }), + ) + : undefined return ( <> @@ -157,6 +167,7 @@ const XeroIntegrations = () => { to={generatePath(XERO_INTEGRATION_DETAILS_ROUTE, { integrationId: connection.id, tab: XeroIntegrationDetailsTabs.Settings, + integrationGroup: IntegrationsTabsOptionsEnum.Lago, })} > diff --git a/src/public/images/Cashfree.svg b/src/public/images/Cashfree.svg new file mode 100644 index 000000000..167b8a593 --- /dev/null +++ b/src/public/images/Cashfree.svg @@ -0,0 +1,14 @@ + + + + + diff --git a/translations/base.json b/translations/base.json index 7e87963fc..8c96ac5de 100644 --- a/translations/base.json +++ b/translations/base.json @@ -2707,5 +2707,24 @@ "text_1732522865354i0r12i6z9mu": "Add API key", "text_17325256621362d6ocmq1lhw": "API key successfully deleted", "text_1733136185960mb0c4wf2ekq": "Add-on", - "text_17331361859605azhkvgofv3": "One off" + "text_17331361859605azhkvgofv3": "One off", + "text_172450747075633492aqpbm2": "Connect to Cashfree Payments", + "text_17245079170372xxmw737fhf": "To connect to Cashfree Payments, please enter the following information.", + "text_1724507963056bu20ky8z98g": "Edit information linked to this Cashfree Payments connection.", + "text_1727619878796wmgcntkfycn": "Cashfree Payments", + "text_1727620558031ftsky1vpr55": "Client ID", + "text_1727620574228qfyoqtsdih7": "Client secret", + "text_1727621816788cygs13tsdyv": "By deleting the connection, it will not be used anymore and upcoming data will not be synchronized to the connected Cashfree Payments account. Are you sure?", + "text_17276219350329d36mgsotee": "Cashfree Payments connection successfully created", + "text_1727621947600tg14usmdbb0": "Cashfree Payments connection successfully edited", + "text_1727621949511zk6kkl99pzk": "Cashfree Payments connection successfully deleted", + "text_1727623090069kyp9o88hpqe": "Webhook URL copied to clipboard", + "text_1727623127072q52kj0u3xql": "Copy Webhook URL", + "text_1727623232636ys8hnp8a3su": "Add the following Webhook URL in the Webhooks > Payment Link section of the Cashfree Payments dashboard.", + "text_1727624537843s2ublm4rsyj": "Type a client id", + "text_17276245391922l9540z7f78": "Type a client secret", + "text_1733303404276jppxvximavl": "Built by Lago", + "text_173330340427732b341qnuny": "Built by community", + "text_1733303404277q80b216p5zr": "This integration is not officially developed by Lago. Please note that the Lago team will not be able to provide support if any issues arise.", + "text_1733303818769298k0fvsgcz": "Type a redirect URL" }