From 3129c76c308107c5e1a607bfebfd60167e1f0b6a Mon Sep 17 00:00:00 2001 From: Ahmed Bouhuolia Date: Sun, 22 Sep 2024 14:30:47 +0200 Subject: [PATCH] feat: Delete Stripe payment method --- .../PaymentServicesController.ts | 2 +- .../PaymentServices/GetPaymentMethodsState.ts | 2 + .../src/services/PaymentServices/types.ts | 1 + .../containers/AlertsContainer/registered.tsx | 2 + .../PreferencesPaymentMethodsPage.tsx | 63 ++++++++++++++--- .../alerts/DeleteStripeConnectionAlert.tsx | 69 +++++++++++++++++++ .../alerts/PaymentMethodsAlerts.ts | 13 ++++ .../drawers/StripeIntegrationEditForm.tsx | 30 ++++++++ .../StripeIntegrationEditFormContent.tsx | 8 ++- .../src/hooks/query/payment-services.ts | 51 +++++++++++++- packages/webapp/src/hooks/state/dashboard.tsx | 9 +++ packages/webapp/src/icons/More.tsx | 26 +++++++ 12 files changed, 262 insertions(+), 14 deletions(-) create mode 100644 packages/webapp/src/containers/Preferences/PaymentMethods/alerts/DeleteStripeConnectionAlert.tsx create mode 100644 packages/webapp/src/containers/Preferences/PaymentMethods/alerts/PaymentMethodsAlerts.ts create mode 100644 packages/webapp/src/icons/More.tsx diff --git a/packages/server/src/api/controllers/PaymentServices/PaymentServicesController.ts b/packages/server/src/api/controllers/PaymentServices/PaymentServicesController.ts index 52250db51..11be0f93a 100644 --- a/packages/server/src/api/controllers/PaymentServices/PaymentServicesController.ts +++ b/packages/server/src/api/controllers/PaymentServices/PaymentServicesController.ts @@ -27,7 +27,7 @@ export class PaymentServicesController extends BaseController { param('paymentMethodId').exists(), body('name').optional().isString(), body('options.bankAccountId').optional().isNumeric(), - body('options.clearningAccountId').optional().isNumeric(), + body('options.clearingAccountId').optional().isNumeric(), body('options.showVisa').optional().isBoolean(), body('options.showMasterCard').optional().isBoolean(), body('options.showDiscover').optional().isBoolean(), diff --git a/packages/server/src/services/PaymentServices/GetPaymentMethodsState.ts b/packages/server/src/services/PaymentServices/GetPaymentMethodsState.ts index bb95fadfc..06ab5466a 100644 --- a/packages/server/src/services/PaymentServices/GetPaymentMethodsState.ts +++ b/packages/server/src/services/PaymentServices/GetPaymentMethodsState.ts @@ -26,6 +26,7 @@ export class GetPaymentMethodsStateService { const isStripeAccountCreated = !!stripePayment; const isStripePaymentActive = !!(stripePayment?.active || null); + const stripePaymentMethodId = stripePayment?.id || null; const stripeAccountId = stripePayment?.accountId || null; const stripePublishableKey = config.stripePayment.publishableKey; const stripeCurrencies = ['USD', 'EUR']; @@ -36,6 +37,7 @@ export class GetPaymentMethodsStateService { isStripeAccountCreated, isStripePaymentActive, stripeAccountId, + stripePaymentMethodId, stripePublishableKey, stripeCurrencies, stripeRedirectUrl, diff --git a/packages/server/src/services/PaymentServices/types.ts b/packages/server/src/services/PaymentServices/types.ts index 42d444897..f23041b16 100644 --- a/packages/server/src/services/PaymentServices/types.ts +++ b/packages/server/src/services/PaymentServices/types.ts @@ -18,6 +18,7 @@ export interface GetPaymentMethodsPOJO { isStripeAccountCreated: boolean; isStripePaymentActive: boolean; stripeAccountId: string | null; + stripePaymentMethodId: number | null; stripePublishableKey: string | null; stripeCurrencies: Array; stripeRedirectUrl: string | null; diff --git a/packages/webapp/src/containers/AlertsContainer/registered.tsx b/packages/webapp/src/containers/AlertsContainer/registered.tsx index a4180e25b..8f503746f 100644 --- a/packages/webapp/src/containers/AlertsContainer/registered.tsx +++ b/packages/webapp/src/containers/AlertsContainer/registered.tsx @@ -30,6 +30,7 @@ import { BankRulesAlerts } from '../Banking/Rules/RulesList/BankRulesAlerts'; import { SubscriptionAlerts } from '../Subscriptions/alerts/alerts'; import { BankAccountAlerts } from '@/containers/CashFlow/AccountTransactions/alerts'; import { BrandingTemplatesAlerts } from '../BrandingTemplates/alerts/BrandingTemplatesAlerts'; +import { PaymentMethodsAlerts } from '../Preferences/PaymentMethods/alerts/PaymentMethodsAlerts'; export default [ ...AccountsAlerts, @@ -63,4 +64,5 @@ export default [ ...SubscriptionAlerts, ...BankAccountAlerts, ...BrandingTemplatesAlerts, + ...PaymentMethodsAlerts, ]; diff --git a/packages/webapp/src/containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage.tsx b/packages/webapp/src/containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage.tsx index d46b28b95..960f4d279 100644 --- a/packages/webapp/src/containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage.tsx +++ b/packages/webapp/src/containers/Preferences/PaymentMethods/PreferencesPaymentMethodsPage.tsx @@ -1,6 +1,15 @@ // @ts-nocheck import styled from 'styled-components'; -import { Button, Classes, Intent, Text } from '@blueprintjs/core'; +import { + Button, + Classes, + Intent, + Menu, + MenuItem, + Popover, + Tag, + Text, +} from '@blueprintjs/core'; import { AppToaster, Box, Card, Group, Stack } from '@/components'; import { StripeLogo } from '@/icons/StripeLogo'; import { @@ -9,10 +18,15 @@ import { } from './PreferencesPaymentMethodsBoot'; import { StripePreSetupDialog } from './dialogs/StripePreSetupDialog/StripePreSetupDialog'; import { DialogsName } from '@/constants/dialogs'; -import { useDialogActions, useDrawerActions } from '@/hooks/state'; +import { + useAlertActions, + useDialogActions, + useDrawerActions, +} from '@/hooks/state'; import { useCreateStripeAccountLink } from '@/hooks/query/stripe-integration'; import { StripeIntegrationEditDrawer } from './drawers/StripeIntegrationEditDrawer'; import { DRAWERS } from '@/constants/drawers'; +import { MoreIcon } from '@/icons/More'; export default function PreferencesPaymentMethodsPage() { return ( @@ -26,12 +40,12 @@ export default function PreferencesPaymentMethodsPage() { - - - + + + ); } @@ -39,12 +53,15 @@ export default function PreferencesPaymentMethodsPage() { function StripePaymentMethod() { const { openDialog } = useDialogActions(); const { openDrawer } = useDrawerActions(); + const { openAlert } = useAlertActions(); + const { paymentMethodsState } = usePaymentMethodsBoot(); const stripeState = paymentMethodsState?.stripe; const isAccountCreated = stripeState?.isStripeAccountCreated; const isAccountActive = stripeState?.isStripePaymentActive; const stripeAccountId = stripeState?.stripeAccountId; + const stripePaymentMethodId = stripeState?.stripePaymentMethodId; const { mutateAsync: createStripeAccountLink, @@ -78,10 +95,24 @@ function StripePaymentMethod() { openDrawer(DRAWERS.STRIPE_PAYMENT_INTEGRATION_EDIT); }; + const handleDeleteConnectionClick = () => { + openAlert('delete-stripe-payment-method', { + paymentMethodId: stripePaymentMethodId, + }); + }; + return ( - + + + + {isAccountActive && ( + + Active + + )} + )} + + {isAccountCreated && ( + + + + } + > + diff --git a/packages/webapp/src/hooks/query/payment-services.ts b/packages/webapp/src/hooks/query/payment-services.ts index e9893b5de..d9eef51b9 100644 --- a/packages/webapp/src/hooks/query/payment-services.ts +++ b/packages/webapp/src/hooks/query/payment-services.ts @@ -1,7 +1,12 @@ // @ts-nocheck -import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query'; +import { + useMutation, + useQuery, + UseQueryOptions, + UseQueryResult, +} from 'react-query'; import useApiRequest from '../useRequest'; -import { transformToCamelCase } from '@/utils'; +import { transformToCamelCase, transfromToSnakeCase } from '@/utils'; const PaymentServicesQueryKey = 'PaymentServices'; @@ -37,7 +42,8 @@ export interface GetPaymentServicesStateResponse { stripe: { isStripeAccountCreated: boolean; isStripePaymentActive: boolean; - stripeAccountId: string; + stripeAccountId: string | null; + stripePaymentMethodId: number | null; stripeCurrencies: string[]; stripePublishableKey: string; stripeRedirectUrl: string; @@ -69,3 +75,42 @@ export const useGetPaymentServicesState = ( }, ); }; + +interface UpdatePaymentMethodResponse { + id: number; + message: string; +} +interface UpdatePaymentMethodValues { + paymentMethodId: string | number; + values: { + name: string; + bankAccountId: number; + clearingAccountId: number; + }; +} +/** + * Updates a payment method. + * @returns {UseMutationResult} + */ +export const useUpdatePaymentMethod = (): UseMutationResult< + UpdatePaymentMethodResponse, + Error, + UpdatePaymentMethodValues, + unknown +> => { + const apiRequest = useApiRequest(); + + return useMutation< + UpdatePaymentMethodResponse, + Error, + UpdatePaymentMethodValues, + unknown + >((data: UpdatePaymentMethodValues) => + apiRequest + .post( + `/payment-services/${data.paymentMethodId}`, + transfromToSnakeCase(data.values), + ) + .then((response) => response.data), + ); +}; diff --git a/packages/webapp/src/hooks/state/dashboard.tsx b/packages/webapp/src/hooks/state/dashboard.tsx index 6f424191b..8b5f118de 100644 --- a/packages/webapp/src/hooks/state/dashboard.tsx +++ b/packages/webapp/src/hooks/state/dashboard.tsx @@ -12,6 +12,8 @@ import { closeDialog, openDrawer, closeDrawer, + openAlert, + closeAlert, } from '@/store/dashboard/dashboard.actions'; export const useDispatchAction = (action) => { @@ -86,3 +88,10 @@ export const useDrawerActions = () => { closeDrawer: useDispatchAction(closeDrawer), }; }; + +export const useAlertActions = () => { + return { + openAlert: useDispatchAction(openAlert), + closeAlert: useDispatchAction(closeAlert), + }; +}; diff --git a/packages/webapp/src/icons/More.tsx b/packages/webapp/src/icons/More.tsx new file mode 100644 index 000000000..1bab862c4 --- /dev/null +++ b/packages/webapp/src/icons/More.tsx @@ -0,0 +1,26 @@ +import React from 'react'; + +interface MoreIconProps extends React.SVGProps { + size?: number; +} + +export const MoreIcon: React.FC = ({ size = 16, ...props }) => ( + + + + + + + +);