diff --git a/src/DonationForms/resources/app/hooks/useDonationSummary.ts b/src/DonationForms/resources/app/hooks/useDonationSummary.ts
index 6770683263..bd14bdc6f4 100644
--- a/src/DonationForms/resources/app/hooks/useDonationSummary.ts
+++ b/src/DonationForms/resources/app/hooks/useDonationSummary.ts
@@ -1,21 +1,26 @@
import {
DonationSummaryLineItem,
useDonationSummaryContext,
- useDonationSummaryDispatch,
+ useDonationSummaryDispatch
} from '@givewp/forms/app/store/donation-summary';
import {
addAmountToTotal,
addItem,
removeAmountFromTotal,
- removeItem,
+ removeItem
} from '@givewp/forms/app/store/donation-summary/reducer';
import {useCallback} from '@wordpress/element';
/**
+ * The donation summary hook is used to interact with the donation summary context which wraps around our donation form.
+ * It provides methods to add and remove items from the summary, as well as to add and remove amounts from the total.
+ * It also provides the current items and totals from the context, making it easier to access form values specific to donations.
+ *
+ * @unreleased added getTotalSum
* @since 3.0.0
*/
export default function useDonationSummary() {
- const {items, totals} = useDonationSummaryContext();
+ const { items, totals } = useDonationSummaryContext();
const dispatch = useDonationSummaryDispatch();
return {
@@ -28,5 +33,14 @@ export default function useDonationSummary() {
[dispatch]
),
removeFromTotal: useCallback((itemId: string) => dispatch(removeAmountFromTotal(itemId)), [dispatch]),
+ getTotalSum: useCallback((amount: number) =>
+ Number(
+ Object.values({
+ ...totals,
+ amount
+ }).reduce((total: number, amount: number) => {
+ return total + amount;
+ }, 0)
+ ), [totals])
};
}
diff --git a/src/DonationForms/resources/app/hooks/useFormData.ts b/src/DonationForms/resources/app/hooks/useFormData.ts
new file mode 100644
index 0000000000..8744cf4c97
--- /dev/null
+++ b/src/DonationForms/resources/app/hooks/useFormData.ts
@@ -0,0 +1,132 @@
+import type {DonationTotals} from '@givewp/forms/app/store/donation-summary';
+import {useDonationSummaryContext} from '@givewp/forms/app/store/donation-summary';
+import type {subscriptionPeriod} from '@givewp/forms/registrars/templates/groups/DonationAmount/subscriptionPeriod';
+
+/**
+ * Zero decimal currencies are currencies that do not have a minor unit.
+ * For example, the Japanese Yen (JPY) does not have a minor unit.
+ * @unreleased
+ *
+ * @see https://stripe.com/docs/currencies#zero-decimal
+ */
+const zeroDecimalCurrencies = [
+ 'BIF',
+ 'CLP',
+ 'DJF',
+ 'GNF',
+ 'JPY',
+ 'KMF',
+ 'KRW',
+ 'MGA',
+ 'PYG',
+ 'RWF',
+ 'UGX',
+ 'VND',
+ 'VUV',
+ 'XAF',
+ 'XOF',
+ 'XPF',
+];
+
+/**
+ * Takes in an amount value in dollar units and returns the calculated cents (minor) amount
+ *
+ * @unreleased
+ */
+const amountToMinorUnit = (amount: string, currency: string) => {
+ if (zeroDecimalCurrencies.includes(currency)) {
+ return Math.round(parseFloat(amount));
+ }
+
+ return Math.round(parseFloat(amount) * 100);
+};
+
+/**
+ * Donation total calculation
+ *
+ * @unreleased
+ */
+const getAmountTotal = (totals: DonationTotals, amount: number) =>
+ Number(
+ Object.values({
+ ...totals,
+ amount,
+ }).reduce((total: number, amount: number) => {
+ return total + amount;
+ }, 0)
+ );
+
+/**
+ * Subscription total calculation
+ * TODO: figure out which totals will be included in subscriptions
+ *
+ * @unreleased
+ */
+const getSubscriptionTotal = (totals: DonationTotals, amount: number) => {
+ let total = 0;
+
+ // Subscriptions currently only support donation amount (TODO: and potentially feeRecovery values)
+ const allowedKeys = ['feeRecovery'];
+
+ for (const [key, value] of Object.entries(totals)) {
+ if (allowedKeys.includes(key)) {
+ total += value;
+ }
+ }
+
+ return Number(total + amount);
+};
+
+/**
+ * @unreleased
+ */
+export default function useFormData() {
+ const {totals} = useDonationSummaryContext();
+ const {useWatch} = window.givewp.form.hooks;
+
+ const firstName = useWatch({name: 'firstName'}) as string;
+ const lastName = useWatch({name: 'lastName'}) as string | undefined;
+ const email = useWatch({name: 'email'}) as string;
+ const phone = useWatch({name: 'phone'}) as string | undefined;
+ const billingAddress = {
+ addressLine1: useWatch({name: 'address1'}) as string | undefined,
+ addressLine2: useWatch({name: 'address2'}) as string | undefined,
+ city: useWatch({name: 'city'}) as string | undefined,
+ state: useWatch({name: 'state'}) as string | undefined,
+ postalCode: useWatch({name: 'zip'}) as string | undefined,
+ country: useWatch({name: 'country'}) as string | undefined,
+ };
+ const amount = useWatch({name: 'amount'}) as string;
+ const currency = useWatch({name: 'currency'}) as string;
+ const subscriptionPeriod = useWatch({name: 'subscriptionPeriod'}) as subscriptionPeriod | undefined;
+ const subscriptionFrequency = useWatch({name: 'subscriptionFrequency'}) as number | undefined;
+ const subscriptionInstallments = useWatch({name: 'subscriptionInstallments'});
+ const donationType = useWatch({name: 'donationType'}) as 'single' | 'subscription' | undefined;
+
+ const amountTotal = getAmountTotal(totals, Number(amount));
+ const amountTotalInMinorUnits = amountToMinorUnit(amountTotal.toString(), currency);
+ const subscriptionAmountTotal = getSubscriptionTotal(totals, Number(amount));
+ const subscriptionAmountTotalInMinorUnits = amountToMinorUnit(subscriptionAmountTotal.toString(), currency);
+
+ const donationAmount = Number(amount);
+ const donationAmountMinor = amountToMinorUnit(amount, currency);
+
+ const isOneTime = donationType === 'single';
+ const isRecurring = donationType === 'subscription';
+
+ return {
+ firstName,
+ lastName,
+ email,
+ phone,
+ currency,
+ billingAddress,
+ amount: isOneTime ? amountTotal : subscriptionAmountTotal,
+ amountInMinorUnits: isOneTime ? amountTotalInMinorUnits : subscriptionAmountTotalInMinorUnits,
+ isOneTime,
+ isRecurring,
+ subscriptionPeriod,
+ subscriptionFrequency,
+ subscriptionInstallments,
+ };
+}
diff --git a/src/DonationForms/resources/app/utilities/mountWindowData.ts b/src/DonationForms/resources/app/utilities/mountWindowData.ts
index 84dd508185..dd424b0910 100644
--- a/src/DonationForms/resources/app/utilities/mountWindowData.ts
+++ b/src/DonationForms/resources/app/utilities/mountWindowData.ts
@@ -2,6 +2,7 @@ import {useFormContext, useFormState, useWatch} from 'react-hook-form';
import useCurrencyFormatter from '@givewp/forms/app/hooks/useCurrencyFormatter';
import useDonationSummary from '@givewp/forms/app/hooks/useDonationSummary';
import {useDonationFormSettings} from '@givewp/forms/app/store/form-settings';
+import useFormData from '@givewp/forms/app/hooks/useFormData';
/**
*
@@ -17,5 +18,6 @@ export default function mountWindowData(): void {
useCurrencyFormatter,
useDonationSummary,
useDonationFormSettings,
+ useFormData
};
}
diff --git a/src/DonationForms/resources/registrars/index.ts b/src/DonationForms/resources/registrars/index.ts
index 149d647e9d..80de7003f7 100644
--- a/src/DonationForms/resources/registrars/index.ts
+++ b/src/DonationForms/resources/registrars/index.ts
@@ -5,6 +5,7 @@ import defaultFormTemplates from './templates';
import useCurrencyFormatter from '@givewp/forms/app/hooks/useCurrencyFormatter';
import useDonationSummary from '@givewp/forms/app/hooks/useDonationSummary';
import {useDonationFormSettings} from '@givewp/forms/app/store/form-settings';
+import useFormData from '@givewp/forms/app/hooks/useFormData';
declare global {
interface Window {
@@ -21,6 +22,7 @@ declare global {
useCurrencyFormatter: typeof useCurrencyFormatter;
useDonationSummary: typeof useDonationSummary;
useDonationFormSettings: typeof useDonationFormSettings;
+ useFormData: typeof useFormData;
};
};
};
diff --git a/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx b/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx
index 85a0ea3d32..4b36b7a9bc 100644
--- a/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx
+++ b/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx
@@ -3,32 +3,23 @@ import {__} from '@wordpress/i18n';
import {isSubscriptionPeriod, SubscriptionPeriod} from '../groups/DonationAmount/subscriptionPeriod';
import {createInterpolateElement} from '@wordpress/element';
-/**
- * @since 3.0.0
- */
-const getDonationTotal = (totals: any, amount: any) =>
- Number(
- Object.values({
- ...totals,
- amount: Number(amount),
- }).reduce((total: number, amount: number) => {
- return total + amount;
- }, 0)
- );
-
/**
* @since 3.0.0
*/
export default function DonationSummary() {
const DonationSummaryItemsTemplate = window.givewp.form.templates.layouts.donationSummaryItems;
- const {useWatch, useCurrencyFormatter, useDonationSummary} = window.givewp.form.hooks;
- const {items, totals} = useDonationSummary();
- const currency = useWatch({name: 'currency'});
- const formatter = useCurrencyFormatter(currency);
+ const { useCurrencyFormatter, useDonationSummary, useWatch } = window.givewp.form.hooks;
+ const donationAmount = Number(useWatch({name: 'amount'}));
+ const { items, getTotalSum } = useDonationSummary();
+ const {
+ currency,
+ subscriptionPeriod: period,
+ subscriptionFrequency: frequency
+ } = window.givewp.form.hooks.useFormData();
- const amount = useWatch({name: 'amount'});
- const period = useWatch({name: 'subscriptionPeriod'});
- const frequency = useWatch({name: 'subscriptionFrequency'});
+ const donationAmountTotal = getTotalSum(donationAmount);
+
+ const formatter = useCurrencyFormatter(currency);
const givingFrequency = useMemo(() => {
if (isSubscriptionPeriod(period)) {
@@ -36,7 +27,7 @@ export default function DonationSummary() {
if (frequency > 1) {
return createInterpolateElement(__('Every