Added payments schema and connected to stripe BOD-146 BOD-147
This commit is contained in:
@@ -26,6 +26,8 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
dispatch(setModalContext({ context: context, modal: "invoiceEnter" })),
|
||||
setTimeTicketContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "timeTicket" })),
|
||||
setPaymentContext: (context) =>
|
||||
dispatch(setModalContext({ context: context, modal: "payment" })),
|
||||
});
|
||||
|
||||
function Header({
|
||||
@@ -37,6 +39,7 @@ function Header({
|
||||
signOutStart,
|
||||
setInvoiceEnterContext,
|
||||
setTimeTicketContext,
|
||||
setPaymentContext,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
//TODO Add
|
||||
@@ -199,6 +202,18 @@ function Header({
|
||||
</span>
|
||||
}
|
||||
>
|
||||
<Menu.Item
|
||||
key="enterpayments"
|
||||
onClick={() => {
|
||||
setPaymentContext({
|
||||
actions: {},
|
||||
context: {},
|
||||
});
|
||||
}}
|
||||
>
|
||||
{t("menus.header.enterpayment")}
|
||||
</Menu.Item>
|
||||
|
||||
<Menu.Item
|
||||
key="enterinvoices"
|
||||
onClick={() => {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {
|
||||
import { handleUpload } from "../documents-upload/documents-upload.utility";
|
||||
import InvoiceFormContainer from "../invoice-form/invoice-form.container";
|
||||
import { UPDATE_JOB_LINE_STATUS } from "../../graphql/jobs-lines.queries";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
invoiceEnterModal: selectInvoiceEnterModal,
|
||||
bodyshop: selectBodyshop,
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Form } from "antd";
|
||||
import React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import CurrencyInput from "../form-items-formatted/currency-form-item.component";
|
||||
import JobSearchSelect from "../job-search-select/job-search-select.component";
|
||||
import { CardElement } from "@stripe/react-stripe-js";
|
||||
|
||||
export default function PaymentFormComponent({
|
||||
form,
|
||||
roAutoCompleteOptions,
|
||||
stripeStateArr,
|
||||
}) {
|
||||
const [stripeState, setStripeState] = stripeStateArr;
|
||||
console.log("stripeState", stripeState);
|
||||
const { t } = useTranslation();
|
||||
const handleStripeChange = (e) => {
|
||||
setStripeState({ error: e.error, cardComplete: e.complete });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="invoice-form-wrapper">
|
||||
<Form.Item
|
||||
name="jobid"
|
||||
label={t("invoices.fields.ro_number")}
|
||||
rules={[
|
||||
{
|
||||
required: true,
|
||||
message: t("general.validation.required"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<JobSearchSelect options={roAutoCompleteOptions} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t("payment.fields.amount")} name="amount">
|
||||
<CurrencyInput />
|
||||
</Form.Item>
|
||||
<CardElement
|
||||
options={{
|
||||
style: {
|
||||
base: {
|
||||
color: "#32325d",
|
||||
fontFamily: '"Helvetica Neue", Helvetica, sans-serif',
|
||||
fontSmoothing: "antialiased",
|
||||
fontSize: "16px",
|
||||
"::placeholder": {
|
||||
color: "#aab7c4",
|
||||
},
|
||||
},
|
||||
invalid: {
|
||||
color: "#fa755a",
|
||||
iconColor: "#fa755a",
|
||||
},
|
||||
},
|
||||
}}
|
||||
onChange={handleStripeChange}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useQuery } from "@apollo/react-hooks";
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { ACTIVE_JOBS_FOR_AUTOCOMPLETE } from "../../graphql/jobs.queries";
|
||||
import { selectBodyshop } from "../../redux/user/user.selectors";
|
||||
import PaymentFormComponent from "./payment-form.component";
|
||||
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
bodyshop: selectBodyshop,
|
||||
});
|
||||
|
||||
export function PaymentFormContainer({ bodyshop, form,stripeStateArr }) {
|
||||
const { data: RoAutoCompleteData } = useQuery(ACTIVE_JOBS_FOR_AUTOCOMPLETE, {
|
||||
variables: { statuses: bodyshop.md_ro_statuses.open_statuses || ["Open*"] },
|
||||
});
|
||||
|
||||
return (
|
||||
<div>
|
||||
<PaymentFormComponent
|
||||
form={form}
|
||||
roAutoCompleteOptions={RoAutoCompleteData && RoAutoCompleteData.jobs}
|
||||
stripeStateArr={stripeStateArr}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
export default connect(mapStateToProps, null)(PaymentFormContainer);
|
||||
125
client/src/components/payment-modal/payment-modal.container.jsx
Normal file
125
client/src/components/payment-modal/payment-modal.container.jsx
Normal file
@@ -0,0 +1,125 @@
|
||||
import { useElements, useStripe, CardElement } from "@stripe/react-stripe-js";
|
||||
import { Form, Modal } from "antd";
|
||||
import React, { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { connect } from "react-redux";
|
||||
import { createStructuredSelector } from "reselect";
|
||||
import { toggleModalVisible } from "../../redux/modals/modals.actions";
|
||||
import { selectPayment } from "../../redux/modals/modals.selectors";
|
||||
import {
|
||||
selectBodyshop,
|
||||
selectCurrentUser,
|
||||
} from "../../redux/user/user.selectors";
|
||||
import PaymentForm from "../payment-form/payment-form.container";
|
||||
import axios from "axios";
|
||||
const mapStateToProps = createStructuredSelector({
|
||||
paymentModal: selectPayment,
|
||||
bodyshop: selectBodyshop,
|
||||
currentUser: selectCurrentUser,
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
toggleModalVisible: () => dispatch(toggleModalVisible("payment")),
|
||||
});
|
||||
|
||||
function InvoiceEnterModalContainer({
|
||||
paymentModal,
|
||||
toggleModalVisible,
|
||||
bodyshop,
|
||||
currentUser,
|
||||
}) {
|
||||
const [form] = Form.useForm();
|
||||
const stripe = useStripe();
|
||||
const elements = useElements();
|
||||
const { t } = useTranslation();
|
||||
const { context, actions, visible } = paymentModal;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const stripeStateArr = useState({
|
||||
error: null,
|
||||
cardComplete: false,
|
||||
});
|
||||
const [stripeState, setStripeState] = stripeStateArr;
|
||||
|
||||
const cardValid = !!!stripeState.error && stripeState.cardComplete;
|
||||
|
||||
const handleFinish = async (values) => {
|
||||
if (!cardValid) return;
|
||||
|
||||
if (!stripe || !elements) {
|
||||
// Stripe.js has not yet loaded.
|
||||
// Make sure to disable form submission until Stripe.js has loaded.
|
||||
return;
|
||||
}
|
||||
setLoading(true);
|
||||
try {
|
||||
const pr = stripe.paymentRequest({
|
||||
country: "US",
|
||||
currency: "usd",
|
||||
total: {
|
||||
label: "Demo total",
|
||||
amount: 1099,
|
||||
},
|
||||
requestPayerName: true,
|
||||
requestPayerEmail: true,
|
||||
});
|
||||
console.log("handleFinish -> pr", pr);
|
||||
|
||||
// const secretKey = await axios.post("/stripe/payment", {
|
||||
// amount: Math.round(values.amount * 100),
|
||||
// stripe_acct_id: bodyshop.stripe_acct_id,
|
||||
// });
|
||||
// console.log("handleFinish -> secretKey", secretKey);
|
||||
// const result = await stripe.confirmCardPayment(
|
||||
// secretKey.data.clientSecret,
|
||||
// {
|
||||
// payment_method: {
|
||||
// card: elements.getElement(CardElement),
|
||||
// billing_details: {
|
||||
// name: "Jenny Rosen",
|
||||
// },
|
||||
// },
|
||||
// }
|
||||
// );
|
||||
// console.log("handleFinish -> result", result);
|
||||
|
||||
if (actions.refetch) actions.refetch();
|
||||
} catch (error) {
|
||||
console.log("error", error);
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
// toggleModalVisible();
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
toggleModalVisible();
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={t("payments.labels.new")}
|
||||
width={"90%"}
|
||||
visible={visible}
|
||||
okText={t("general.actions.save")}
|
||||
onOk={() => form.submit()}
|
||||
onCancel={handleCancel}
|
||||
afterClose={() => form.resetFields()}
|
||||
okButtonProps={{ loading: loading, disabled: !cardValid }}
|
||||
destroyOnClose
|
||||
>
|
||||
<Form
|
||||
onFinish={handleFinish}
|
||||
autoComplete={"off"}
|
||||
form={form}
|
||||
initialValues={{ jobid: context.jobId }}
|
||||
>
|
||||
<PaymentForm form={form} stripeStateArr={stripeStateArr} />
|
||||
</Form>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)(InvoiceEnterModalContainer);
|
||||
Reference in New Issue
Block a user