mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-16 04:40:32 +00:00
feat: Control the payment method from invoice form
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { FCheckbox, Group } from '@/components';
|
||||
import { useUncontrolled } from '@/hooks/useUncontrolled';
|
||||
import { Checkbox, Text } from '@blueprintjs/core';
|
||||
import { useFormikContext } from 'formik';
|
||||
import { get } from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
export interface PaymentMethodSelectProps {
|
||||
label: string;
|
||||
value?: boolean;
|
||||
initialValue?: boolean;
|
||||
onChange?: (value: boolean) => void;
|
||||
}
|
||||
export 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>
|
||||
);
|
||||
}
|
||||
|
||||
export interface PaymentMethodSelectFieldProps
|
||||
extends Partial<PaymentMethodSelectProps> {
|
||||
label: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export function PaymentMethodSelectField({
|
||||
name,
|
||||
...props
|
||||
}: PaymentMethodSelectFieldProps) {
|
||||
const { values, setFieldValue } = useFormikContext();
|
||||
const value = useMemo(() => get(values, name), [values, name]);
|
||||
|
||||
const handleChange = (newValue: boolean) => {
|
||||
setFieldValue(name, newValue);
|
||||
};
|
||||
|
||||
return (
|
||||
<PaymentMethodSelect value={value} onChange={handleChange} {...props} />
|
||||
);
|
||||
}
|
||||
|
||||
const PaymentMethodSelectRoot = styled(Group)`
|
||||
border: 1px solid #d3d8de;
|
||||
border-radius: 3px;
|
||||
padding: 8px;
|
||||
gap: 0;
|
||||
min-width: 200px;
|
||||
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;
|
||||
`;
|
||||
@@ -0,0 +1,52 @@
|
||||
import styled from 'styled-components';
|
||||
import React from 'react';
|
||||
import {
|
||||
Classes,
|
||||
Popover,
|
||||
PopoverInteractionKind,
|
||||
Position,
|
||||
} from '@blueprintjs/core';
|
||||
import { Stack } from '@/components';
|
||||
import { PaymentMethodSelectField } from './PaymentMethodSelect';
|
||||
|
||||
interface PaymentOptionsButtonPopverProps {
|
||||
paymentMethods: Array<any>;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
export function PaymentOptionsButtonPopver({
|
||||
paymentMethods,
|
||||
children,
|
||||
}: PaymentOptionsButtonPopverProps) {
|
||||
return (
|
||||
<Popover
|
||||
interactionKind={PopoverInteractionKind.HOVER}
|
||||
position={Position.TOP_RIGHT}
|
||||
popoverClassName={Classes.POPOVER_CONTENT_SIZING}
|
||||
minimal={true}
|
||||
content={
|
||||
<Stack spacing={8}>
|
||||
<PaymentMethodsTitle>Payment Options</PaymentMethodsTitle>
|
||||
|
||||
<Stack spacing={8}>
|
||||
{paymentMethods?.map((service, key) => (
|
||||
<PaymentMethodSelectField
|
||||
name={`payment_methods.${service.id}.enable`}
|
||||
label={'Card (Stripe)'}
|
||||
key={key}
|
||||
/>
|
||||
))}
|
||||
</Stack>
|
||||
</Stack>
|
||||
}
|
||||
>
|
||||
{children}
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
const PaymentMethodsTitle = styled('h6')`
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
margin: 0;
|
||||
color: rgb(95, 107, 124);
|
||||
`;
|
||||
@@ -2,23 +2,25 @@
|
||||
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';
|
||||
import { Button, Intent } from '@blueprintjs/core';
|
||||
import {
|
||||
FFormGroup,
|
||||
FEditableText,
|
||||
FormattedMessage as T,
|
||||
Box,
|
||||
Group,
|
||||
Stack,
|
||||
} from '@/components';
|
||||
import { VisaIcon } from '@/icons/Visa';
|
||||
import { MastercardIcon } from '@/icons/Mastercard';
|
||||
import { useInvoiceFormContext } from './InvoiceFormProvider';
|
||||
import { PaymentOptionsButtonPopver } from '@/containers/PaymentMethods/SelectPaymentMethodPopover';
|
||||
|
||||
export function InvoiceFormFooterLeft() {
|
||||
const { openDialog } = useDialogActions();
|
||||
|
||||
const handleSelectPaymentMethodsClick = () => {
|
||||
openDialog(DialogsName.SelectPaymentMethod, {});
|
||||
}
|
||||
const { paymentServices } = useInvoiceFormContext();
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<FFormGroup label={'Payment Options'} name={'payment_method_id'}>
|
||||
<a href={'#'} onClick={handleSelectPaymentMethodsClick}>Payment Options</a>
|
||||
</FFormGroup>
|
||||
|
||||
<Stack spacing={20}>
|
||||
{/* --------- Invoice message --------- */}
|
||||
<InvoiceMsgFormGroup
|
||||
name={'invoice_message'}
|
||||
@@ -46,14 +48,31 @@ export function InvoiceFormFooterLeft() {
|
||||
fastField
|
||||
/>
|
||||
</TermsConditsFormGroup>
|
||||
</React.Fragment>
|
||||
|
||||
{/* --------- Payment Options --------- */}
|
||||
<PaymentOptionsFormGroup
|
||||
label={'Payment Options'}
|
||||
name={'payment_method_id'}
|
||||
>
|
||||
<PaymentOptionsText>
|
||||
Select an online payment option to get paid faster{' '}
|
||||
<Group spacing={6} style={{ marginLeft: 8 }}>
|
||||
<VisaIcon />
|
||||
<MastercardIcon />
|
||||
</Group>
|
||||
<PaymentOptionsButtonPopver paymentMethods={paymentServices}>
|
||||
<PaymentOptionsButton intent={Intent.PRIMARY} small minimal>
|
||||
Payment Options
|
||||
</PaymentOptionsButton>
|
||||
</PaymentOptionsButtonPopver>
|
||||
</PaymentOptionsText>
|
||||
</PaymentOptionsFormGroup>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
const InvoiceMsgFormGroup = styled(FFormGroup)`
|
||||
&.bp4-form-group {
|
||||
margin-bottom: 40px;
|
||||
|
||||
.bp4-label {
|
||||
font-size: 12px;
|
||||
margin-bottom: 12px;
|
||||
@@ -75,3 +94,29 @@ const TermsConditsFormGroup = styled(FFormGroup)`
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const PaymentOptionsFormGroup = styled(FFormGroup)`
|
||||
&.bp4-form-group {
|
||||
.bp4-label {
|
||||
font-weight: 500;
|
||||
font-size: 12px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const PaymentOptionsText = styled(Box)`
|
||||
font-size: 13px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
color: #5f6b7c;
|
||||
`;
|
||||
|
||||
const PaymentOptionsButton = styled(Button)`
|
||||
font-size: 13px;
|
||||
margin-left: 4px;
|
||||
|
||||
&.bp4-minimal.bp4-intent-primary {
|
||||
color: #0052cc;
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -150,6 +150,10 @@ function InvoiceFormProvider({ invoiceId, baseCurrency, ...props }) {
|
||||
editInvoiceMutate,
|
||||
setSubmitPayload,
|
||||
isNewMode,
|
||||
|
||||
// Payment Services
|
||||
paymentServices,
|
||||
isPaymentServicesLoading,
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -69,6 +69,7 @@ export const defaultInvoice = {
|
||||
pdf_template_id: '',
|
||||
entries: [...repeatValue(defaultInvoiceEntry, MIN_LINES_NUMBER)],
|
||||
attachments: [],
|
||||
payment_methods: {},
|
||||
};
|
||||
|
||||
// Invoice entry request schema.
|
||||
@@ -223,9 +224,19 @@ export function transformValueToRequest(values) {
|
||||
entries: transformEntriesToRequest(values.entries),
|
||||
delivered: false,
|
||||
attachments: transformAttachmentsToRequest(values),
|
||||
payment_methods: transformPaymentMethodsToRequest(values?.payment_methods),
|
||||
};
|
||||
}
|
||||
|
||||
const transformPaymentMethodsToRequest = (
|
||||
paymentMethods: Record<string, { enable: boolean }>,
|
||||
): Array<{ payment_integration_id: string; enable: boolean }> => {
|
||||
return Object.entries(paymentMethods).map(([paymentMethodId, method]) => ({
|
||||
payment_integration_id: paymentMethodId,
|
||||
enable: method.enable,
|
||||
}));
|
||||
};
|
||||
|
||||
export const useSetPrimaryWarehouseToForm = () => {
|
||||
const { setFieldValue } = useFormikContext();
|
||||
const { warehouses, isWarehousesSuccess } = useInvoiceFormContext();
|
||||
|
||||
Reference in New Issue
Block a user