@churchapps/apphelper-donations
Version:
Donation components for ChurchApps AppHelper
188 lines • 12.9 kB
JavaScript
"use client";
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useCallback, useState, useEffect } from "react";
import { InputBox, ErrorMessages } from "@churchapps/apphelper";
import { FundDonations } from ".";
import { DonationPreviewModal } from "../modals/DonationPreviewModal";
import { ApiHelper, CurrencyHelper, DateHelper } from "@churchapps/helpers";
import { Locale } from "../helpers";
import { Grid, InputLabel, MenuItem, Select, TextField, FormControl, Button, FormControlLabel, Checkbox, FormGroup, Typography } from "@mui/material";
import { DonationHelper } from "../helpers";
export const DonationForm = (props) => {
const [errorMessage, setErrorMessage] = useState();
const [fundDonations, setFundDonations] = useState();
const [funds, setFunds] = useState([]);
const [fundsTotal, setFundsTotal] = useState(0);
const [transactionFee, setTransactionFee] = useState(0);
const [payFee, setPayFee] = useState(0);
const [total, setTotal] = useState(0);
const [paymentMethodName, setPaymentMethodName] = useState(props?.paymentMethods?.length > 0 ? `${props.paymentMethods[0].name} ****${props.paymentMethods[0].last4}` : "");
const [donationType, setDonationType] = useState();
const [showDonationPreviewModal, setShowDonationPreviewModal] = useState(false);
const [interval, setInterval] = useState("one_month");
const [gateway, setGateway] = useState(null);
const [donation, setDonation] = useState({
id: props?.paymentMethods?.length > 0 ? props.paymentMethods[0].id : "",
type: props?.paymentMethods?.length > 0 ? props.paymentMethods[0].type : "",
customerId: props.customerId,
person: {
id: props.person?.id,
email: props.person?.contactInfo?.email,
name: props.person?.name?.display
},
amount: 0,
billing_cycle_anchor: +new Date(),
interval: {
interval_count: 1,
interval: "month"
},
funds: []
});
const loadData = useCallback(() => {
ApiHelper.get("/funds", "GivingApi").then((data) => {
setFunds(data);
if (data.length)
setFundDonations([{ fundId: data[0].id }]);
});
ApiHelper.get("/gateways", "GivingApi").then((data) => {
if (data.length !== 0)
setGateway(data[0]);
});
}, []);
const handleSave = useCallback(() => {
if (donation.amount < .5)
setErrorMessage(Locale.label("donation.donationForm.tooLow"));
else
setShowDonationPreviewModal(true);
}, [donation.amount]);
const handleKeyDown = useCallback((e) => { if (e.key === "Enter") {
e.preventDefault();
handleSave();
} }, [handleSave]);
const handleCheckChange = useCallback((e, checked) => {
const d = { ...donation };
d.amount = checked ? fundsTotal + transactionFee : fundsTotal;
const showFee = checked ? transactionFee : 0;
setTotal(d.amount);
setPayFee(showFee);
setDonation(d);
}, [donation, fundsTotal, transactionFee]);
const handleChange = useCallback((e) => {
setErrorMessage(null);
const d = { ...donation };
const value = e.target.value;
switch (e.target.name) {
case "method":
d.id = value;
const pm = props.paymentMethods.find(pm => pm.id === value);
d.type = pm.type;
setPaymentMethodName(`${pm.name} ****${pm.last4}`);
break;
case "type":
setDonationType(value);
break;
case "date":
d.billing_cycle_anchor = +new Date(value);
break;
case "interval":
setInterval(value);
d.interval = DonationHelper.getInterval(value);
break;
case "notes":
d.notes = value;
break;
case "transaction-fee":
const element = e.target;
d.amount = element.checked ? fundsTotal + transactionFee : fundsTotal;
const showFee = element.checked ? transactionFee : 0;
setTotal(d.amount);
setPayFee(showFee);
}
setDonation(d);
}, [donation, props.paymentMethods, fundsTotal, transactionFee]);
const handleCancel = useCallback(() => { setDonationType(null); }, []);
const handleDonationSelect = useCallback((type) => {
const dt = donationType === type ? null : type;
setDonationType(dt);
}, [donationType]);
const handleSingleDonationClick = useCallback(() => handleDonationSelect("once"), [handleDonationSelect]);
const handleRecurringDonationClick = useCallback(() => handleDonationSelect("recurring"), [handleDonationSelect]);
const makeDonation = useCallback(async (message) => {
let results;
const churchObj = {
name: props?.church?.name,
subDomain: props?.church?.subDomain,
churchURL: typeof window !== "undefined" && window.location.origin,
logo: props?.churchLogo
};
if (donationType === "once")
results = await ApiHelper.post("/donate/charge/", { ...donation, church: churchObj }, "GivingApi");
if (donationType === "recurring")
results = await ApiHelper.post("/donate/subscribe/", { ...donation, church: churchObj }, "GivingApi");
if (results?.status === "succeeded" || results?.status === "pending" || results?.status === "active") {
setShowDonationPreviewModal(false);
setDonationType(null);
props.donationSuccess(message);
}
if (results?.raw?.message) {
setShowDonationPreviewModal(false);
setErrorMessage(Locale.label("donation.common.error") + ": " + results?.raw?.message);
}
}, [donation, donationType, props.church?.name, props.church?.subDomain, props.churchLogo, props.donationSuccess]);
const handleFundDonationsChange = useCallback(async (fd) => {
setErrorMessage(null);
setFundDonations(fd);
let totalAmount = 0;
const selectedFunds = [];
for (const fundDonation of fd) {
totalAmount += fundDonation.amount || 0;
const fund = funds.find((fund) => fund.id === fundDonation.fundId);
selectedFunds.push({ id: fundDonation.fundId, amount: fundDonation.amount || 0, name: fund.name });
}
const d = { ...donation };
d.amount = totalAmount;
d.funds = selectedFunds;
setFundsTotal(totalAmount);
const fee = await getTransactionFee(totalAmount);
setTransactionFee(fee);
if (gateway && gateway.payFees === true) {
d.amount = totalAmount + fee;
setPayFee(fee);
}
setTotal(d.amount);
setDonation(d);
}, [donation, funds, gateway]);
const getTransactionFee = useCallback(async (amount) => {
if (amount > 0) {
let dt = "";
if (donation.type === "card")
dt = "creditCard";
if (donation.type === "bank")
dt = "ach";
try {
const response = await ApiHelper.post("/donate/fee?churchId=" + props?.church?.id, { type: dt, amount }, "GivingApi");
return response.calculatedFee;
}
catch (error) {
console.log("Error calculating transaction fee: ", error);
return 0;
}
}
else {
return 0;
}
}, [donation.type, props?.church?.id]);
useEffect(() => {
loadData();
}, [loadData, props.person?.id]);
if (!funds.length || !props?.paymentMethods?.length)
return null;
else {
return (_jsxs(_Fragment, { children: [_jsx(DonationPreviewModal, { show: showDonationPreviewModal, onHide: () => setShowDonationPreviewModal(false), handleDonate: makeDonation, donation: donation, donationType: donationType, payFee: payFee, paymentMethodName: paymentMethodName, funds: funds }), _jsxs(InputBox, { id: "donation-form", "aria-label": "donation-box", headerIcon: "volunteer_activism", headerText: Locale.label("donation.donationForm.donate"), ariaLabelSave: "save-button", cancelFunction: donationType ? handleCancel : undefined, saveFunction: donationType ? handleSave : undefined, saveText: Locale.label("donation.donationForm.preview"), children: [_jsxs(Grid, { id: "donation-type-selector", container: true, spacing: 3, children: [_jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(Button, { id: "single-donation-button", "aria-label": "single-donation", size: "small", fullWidth: true, style: { minHeight: "50px" }, variant: donationType === "once" ? "contained" : "outlined", onClick: handleSingleDonationClick, children: Locale.label("donation.donationForm.make") }) }), _jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(Button, { id: "recurring-donation-button", "aria-label": "recurring-donation", size: "small", fullWidth: true, style: { minHeight: "50px" }, variant: donationType === "recurring" ? "contained" : "outlined", onClick: handleRecurringDonationClick, children: Locale.label("donation.donationForm.makeRecurring") }) })] }), donationType
&& _jsxs("div", { id: "donation-details", style: { marginTop: "20px" }, children: [_jsx(Grid, { container: true, spacing: 3, children: _jsx(Grid, { size: 12, children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: Locale.label("donation.donationForm.method") }), _jsx(Select, { id: "payment-method-select", label: Locale.label("donation.donationForm.method"), name: "method", "aria-label": "method", value: donation.id, className: "capitalize", onChange: handleChange, children: props.paymentMethods.map((paymentMethod, i) => _jsxs(MenuItem, { value: paymentMethod.id, children: [paymentMethod.name, " ****", paymentMethod.last4] }, i)) })] }) }) }), donationType === "recurring"
&& _jsxs(Grid, { container: true, spacing: 3, style: { marginTop: 10 }, children: [_jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsx(TextField, { id: "start-date-field", fullWidth: true, name: "date", type: "date", "aria-label": "date", label: Locale.label("donation.donationForm.startDate"), value: DateHelper.formatHtml5Date(new Date(donation.billing_cycle_anchor)), onChange: handleChange, onKeyDown: handleKeyDown }) }), _jsx(Grid, { size: { xs: 12, md: 6 }, children: _jsxs(FormControl, { fullWidth: true, children: [_jsx(InputLabel, { children: Locale.label("donation.donationForm.frequency") }), _jsxs(Select, { id: "frequency-select", label: Locale.label("donation.donationForm.frequency"), name: "interval", "aria-label": "interval", value: interval, onChange: handleChange, children: [_jsx(MenuItem, { value: "one_week", children: Locale.label("donation.donationForm.weekly") }), _jsx(MenuItem, { value: "two_week", children: Locale.label("donation.donationForm.biWeekly") }), _jsx(MenuItem, { value: "one_month", children: Locale.label("donation.donationForm.monthly") }), _jsx(MenuItem, { value: "three_month", children: Locale.label("donation.donationForm.quarterly") }), _jsx(MenuItem, { value: "one_year", children: Locale.label("donation.donationForm.annually") })] })] }) })] }), _jsxs("div", { id: "fund-selection", className: "form-group", children: [funds && fundDonations
&& _jsxs(_Fragment, { children: [_jsx("h4", { children: Locale.label("donation.donationForm.fund") }), _jsx(FundDonations, { fundDonations: fundDonations, funds: funds, updatedFunction: handleFundDonationsChange })] }), fundsTotal > 0
&& _jsxs(_Fragment, { children: [(gateway && gateway.payFees === true) ? _jsxs(Typography, { fontSize: 14, fontStyle: "italic", children: ["*", Locale.label("donation.donationForm.fees").replace("{}", CurrencyHelper.formatCurrency(transactionFee))] }) : (_jsx(FormGroup, { children: _jsx(FormControlLabel, { control: _jsx(Checkbox, {}), name: "transaction-fee", label: Locale.label("donation.donationForm.cover").replace("{}", CurrencyHelper.formatCurrency(transactionFee)), onChange: handleCheckChange }) })), _jsxs("p", { children: [Locale.label("donation.donationForm.total"), ": $", total] })] }), _jsx(TextField, { id: "donation-notes", fullWidth: true, label: Locale.label("donation.donationForm.notes"), multiline: true, "aria-label": "note", name: "notes", value: donation.notes || "", onChange: handleChange, onKeyDown: handleKeyDown })] }), errorMessage && _jsx(ErrorMessages, { errors: [errorMessage] })] })] })] }));
}
};
//# sourceMappingURL=DonationForm.js.map