feat: select payment methods dialog

This commit is contained in:
Ahmed Bouhuolia
2024-09-18 10:43:21 +02:00
parent eb48f66f6e
commit 5270e99de8
9 changed files with 264 additions and 7 deletions

View File

@@ -53,6 +53,7 @@ import { ExportDialog } from '@/containers/Dialogs/ExportDialog';
import { RuleFormDialog } from '@/containers/Banking/Rules/RuleFormDialog/RuleFormDialog';
import { DisconnectBankAccountDialog } from '@/containers/CashFlow/AccountTransactions/dialogs/DisconnectBankAccountDialog/DisconnectBankAccountDialog';
import { SharePaymentLinkDialog } from '@/containers/PaymentLink/dialogs/SharePaymentLinkDialog/SharePaymentLinkDialog';
import { SelectPaymentMethodsDialog } from '@/containers/PaymentLink/dialogs/SelectPaymentMethodsDialog/SelectPaymentMethodsDialog';
/**
* Dialogs container.
@@ -153,6 +154,9 @@ export default function DialogsContainer() {
dialogName={DialogsName.DisconnectBankAccountConfirmation}
/>
<SharePaymentLinkDialog dialogName={DialogsName.SharePaymentLink} />
<SelectPaymentMethodsDialog
dialogName={DialogsName.SelectPaymentMethod}
/>
</div>
);
}

View File

@@ -77,5 +77,6 @@ export enum DialogsName {
Export = 'Export',
BankRuleForm = 'BankRuleForm',
DisconnectBankAccountConfirmation = 'DisconnectBankAccountConfirmation',
SharePaymentLink = 'SharePaymentLink'
SharePaymentLink = 'SharePaymentLink',
SelectPaymentMethod = 'SelectPaymentMethodsDialog',
}

View File

@@ -7,6 +7,8 @@ import {
Classes,
NavbarDivider,
Intent,
Tooltip,
Position,
} from '@blueprintjs/core';
import { useInvoiceDetailDrawerContext } from './InvoiceDetailDrawerProvider';
@@ -32,6 +34,7 @@ import { compose } from '@/utils';
import { BadDebtMenuItem } from './utils';
import { DRAWERS } from '@/constants/drawers';
import { DialogsName } from '@/constants/dialogs';
import { ArrowBottomLeft } from '@/icons/ArrowBottomLeft';
/**
* Invoice details action bar.
@@ -126,7 +129,7 @@ function InvoiceDetailActionsBar({
<If condition={invoice.is_delivered && !invoice.is_fully_paid}>
<Button
className={Classes.MINIMAL}
icon={<Icon icon="arrow-downward" iconSize={18} />}
icon={<ArrowBottomLeft size={16} />}
text={<T id={'add_payment'} />}
onClick={handleQuickPaymentInvoice}
/>
@@ -157,11 +160,15 @@ function InvoiceDetailActionsBar({
onClick={handleDeleteInvoice}
/>
</Can>
<Button
className={Classes.MINIMAL}
text={'Share'}
onClick={handleShareButtonClick}
/>
<NavbarDivider />
<Tooltip content="Share" position={Position.BOTTOM} minimal>
<Button
className={Classes.MINIMAL}
icon={<Icon icon={'share'} iconSize={16} />}
onClick={handleShareButtonClick}
/>
</Tooltip>
<Can I={SaleInvoiceAction.Writeoff} a={AbilitySubject.Invoice}>
<NavbarDivider />
<BadDebtMenuItem

View File

@@ -0,0 +1,18 @@
import React from 'react';
import { Formik, Form } from 'formik';
interface SelectPaymentMethodsFormValues {}
const initialValues: SelectPaymentMethodsFormValues = {};
export const SelectPaymentMethodsForm: React.FC<{
children: React.ReactNode;
}> = ({ children }) => {
const handleSubmit = (values: SelectPaymentMethodsFormValues) => {};
return (
<Formik initialValues={initialValues} onSubmit={handleSubmit}>
<Form>{children}</Form>
</Formik>
);
};

View File

@@ -0,0 +1,35 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
interface SelectPaymentMethodsContextType {}
const SelectPaymentMethodsContext =
createContext<SelectPaymentMethodsContextType>(
{} as SelectPaymentMethodsContextType,
);
export const useSelectPaymentMethods = () => {
const context = useContext(SelectPaymentMethodsContext);
if (!context) {
throw new Error(
'useSelectPaymentMethods must be used within a SelectPaymentMethodsProvider',
);
}
return context;
};
interface SelectPaymentMethodsProviderProps {
children: ReactNode;
}
export const SelectPaymentMethodsBoot: React.FC<
SelectPaymentMethodsProviderProps
> = ({ children }) => {
return (
<SelectPaymentMethodsContext.Provider
value={{ }}
>
{children}
</SelectPaymentMethodsContext.Provider>
);
};

View File

@@ -0,0 +1,114 @@
import { SelectPaymentMethodsBoot } from './SelectPaymentMethodsBoot';
import { SelectPaymentMethodsForm } from './SelectPaymemtMethodsForm';
import styled from 'styled-components';
import { Group, Stack } from '@/components';
import {
DialogFooter,
Button,
Checkbox,
DialogBody,
Intent,
Text,
} from '@blueprintjs/core';
import { useDialogActions } from '@/hooks/state';
import { DialogsName } from '@/constants/dialogs';
import { useUncontrolled } from '@/hooks/useUncontrolled';
export function SelectPaymentMethodsContent() {
const { closeDialog } = useDialogActions();
const handleCancelBtnClick = () => {
closeDialog(DialogsName.SelectPaymentMethod);
};
return (
<SelectPaymentMethodsBoot>
<SelectPaymentMethodsForm>
<DialogBody>
<Stack spacing={12}>
<PaymentMethodSelect
label={'Card (Including Apple Pay, Google Pay and Link)'}
/>
<PaymentMethodSelect
label={'Card (Including Apple Pay, Google Pay and Link)'}
/>
<PaymentMethodSelect
label={'Card (Including Apple Pay, Google Pay and Link)'}
/>
<PaymentMethodSelect
label={'Card (Including Apple Pay, Google Pay and Link)'}
/>
<PaymentMethodSelect
label={'Card (Including Apple Pay, Google Pay and Link)'}
/>
<PaymentMethodSelect
label={'Card (Including Apple Pay, Google Pay and Link)'}
/>
</Stack>
</DialogBody>
<DialogFooter
actions={
<>
<Button onClick={handleCancelBtnClick}>Cancel</Button>
<Button intent={Intent.PRIMARY}>Submit</Button>
</>
}
></DialogFooter>
</SelectPaymentMethodsForm>
</SelectPaymentMethodsBoot>
);
}
interface PaymentMethodSelectProps {
label: string;
value?: boolean;
initialValue?: boolean;
onChange?: (value: boolean) => void;
}
function PaymentMethodSelect({
value,
initialValue,
onChange,
label,
}: PaymentMethodSelectProps) {
const [_value, handleChange] = useUncontrolled<boolean>({
value,
initialValue,
finalValue: false,
onChange,
});
const handleClick = () => {
handleChange(!_value);
};
return (
<PaymentMethodSelectRoot onClick={handleClick}>
<PaymentMethodCheckbox
label={''}
checked={_value}
onClick={handleClick}
/>
<PaymentMethodText>{label}</PaymentMethodText>
</PaymentMethodSelectRoot>
);
}
const PaymentMethodSelectRoot = styled(Group)`
border: 1px solid #d3d8de;
border-radius: 5px;
padding: 10px;
gap: 0;
cursor: pointer;
`;
const PaymentMethodCheckbox = styled(Checkbox)`
margin: 0;
&.bp4-control .bp4-control-indicator {
box-shadow: 0 0 0 1px #c5cbd3;
}
`;
const PaymentMethodText = styled(Text)`
color: #404854;
`;

View File

@@ -0,0 +1,38 @@
// @ts-nocheck
import React from 'react';
import { Dialog, DialogSuspense } from '@/components';
import withDialogRedux from '@/components/DialogReduxConnect';
import { compose } from '@/utils';
const SelectPaymentMethodsDialogContent = React.lazy(() =>
import('./SelectPaymentMethodsContent').then((module) => ({
default: module.SelectPaymentMethodsContent,
})),
);
/**
* Select payment methods dialogs.
*/
function SelectPaymentMethodsDialogRoot({ dialogName, payload, isOpen }) {
return (
<Dialog
name={dialogName}
isOpen={isOpen}
payload={payload}
title={'Share Link'}
canEscapeJeyClose={true}
autoFocus={true}
style={{ width: 570 }}
>
<DialogSuspense>
<SelectPaymentMethodsDialogContent />
</DialogSuspense>
</Dialog>
);
}
export const SelectPaymentMethodsDialog = compose(withDialogRedux())(
SelectPaymentMethodsDialogRoot,
);
SelectPaymentMethodsDialog.displayName = 'SelectPaymentMethodsDialog';

View File

@@ -3,10 +3,22 @@ import React from 'react';
import intl from 'react-intl-universal';
import styled from 'styled-components';
import { FFormGroup, FEditableText, FormattedMessage as T } from '@/components';
import { useDialogActions } from '@/hooks/state';
import { DialogsName } from '@/constants/dialogs';
export function InvoiceFormFooterLeft() {
const { openDialog } = useDialogActions();
const handleSelectPaymentMethodsClick = () => {
openDialog(DialogsName.SelectPaymentMethod, {});
}
return (
<React.Fragment>
<FFormGroup label={'Payment Options'} name={'payment_method_id'}>
<a href={'#'} onClick={handleSelectPaymentMethodsClick}>Payment Options</a>
</FFormGroup>
{/* --------- Invoice message --------- */}
<InvoiceMsgFormGroup
name={'invoice_message'}

View File

@@ -0,0 +1,28 @@
import React from 'react';
interface ArrowBottomLeftProps extends React.SVGProps<SVGSVGElement> {
size?: number;
}
export const ArrowBottomLeft: React.FC<ArrowBottomLeftProps> = ({
size = 16,
...props
}) => {
return (
<svg
width={size}
height={size}
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
{...props}
>
<path
fillRule="evenodd"
clipRule="evenodd"
d="M14 3C14 2.45 13.55 2 13 2C12.72 2 12.47 2.11 12.29 2.29L4 10.59V6C4 5.45 3.55 5 3 5S2 5.45 2 6V13C2 13.55 2.45 14 3 14H10C10.55 14 11 13.55 11 13C11 12.45 10.55 12 10 12H5.41L13.7 3.71C13.89 3.53 14 3.28 14 3Z"
fill="currentColor"
/>
</svg>
);
};