From f42422acb5fe971752745272ac406e76de237a5a Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Fri, 1 Nov 2024 10:44:11 -0400 Subject: [PATCH 1/6] feature: add donation summary state values to hook for unified access --- .../resources/app/hooks/useDonationSummary.ts | 83 ++++++++++++++++++- .../templates/elements/DonationSummary.tsx | 37 +++------ 2 files changed, 93 insertions(+), 27 deletions(-) diff --git a/src/DonationForms/resources/app/hooks/useDonationSummary.ts b/src/DonationForms/resources/app/hooks/useDonationSummary.ts index 6770683263..a46ecf4b61 100644 --- a/src/DonationForms/resources/app/hooks/useDonationSummary.ts +++ b/src/DonationForms/resources/app/hooks/useDonationSummary.ts @@ -1,22 +1,88 @@ 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'; /** + * 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 dollarsToCents = (amount: string, currency: string) => { + if (zeroDecimalCurrencies.includes(currency)) { + return Math.round(parseFloat(amount)); + } + + return Math.round(parseFloat(amount) * 100); +}; + +/** + * @unreleased + */ +const getDonationTotal = (totals: any, amount: any) => + Number( + Object.values({ + ...totals, + amount: Number(amount), + }).reduce((total: number, amount: number) => { + return total + amount; + }, 0) + ); + +/** + * 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. + * + * Although the initial intent for this hook was to be used in the DonationSummary component for visual reasons, it is also recommended to be used in others + * areas like gateways to get the total donation amount and currency. + * + * @unreleased added currency, donationAmountBase, donationAmountTotal * @since 3.0.0 */ export default function useDonationSummary() { - const {items, totals} = useDonationSummaryContext(); + const { items, totals } = useDonationSummaryContext(); const dispatch = useDonationSummaryDispatch(); + const { useWatch } = window.givewp.form.hooks; + const amount = useWatch({ name: 'amount' }); + const currency = useWatch({ name: 'currency' }); + const period = useWatch({name: 'subscriptionPeriod'}); + const frequency = useWatch({name: 'subscriptionFrequency'}); + const donationType = useWatch({name: 'donationType'}); return { items, @@ -28,5 +94,16 @@ export default function useDonationSummary() { [dispatch] ), removeFromTotal: useCallback((itemId: string) => dispatch(removeAmountFromTotal(itemId)), [dispatch]), + state: { + currency, + donationAmountBase: Number(amount), + donationAmountBaseMinor: dollarsToCents(amount, currency), + donationAmountTotal: getDonationTotal(totals, amount), + donationAmountTotalMinor: dollarsToCents(getDonationTotal(totals, amount).toString(), currency), + donationIsOneTime: donationType === 'single', + donationIsRecurring: donationType === 'subscription', + subscriptionPeriod: period, + subscriptionFrequency: frequency, + }, }; } diff --git a/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx b/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx index 85a0ea3d32..de4b164fc4 100644 --- a/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx +++ b/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx @@ -3,40 +3,29 @@ 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 { useCurrencyFormatter, useDonationSummary } = window.givewp.form.hooks; + const { items, state } = useDonationSummary(); + const { + currency, + donationAmountBase, + donationAmountTotal, + subscriptionPeriod: period, + subscriptionFrequency: frequency + } = state; const formatter = useCurrencyFormatter(currency); - const amount = useWatch({name: 'amount'}); - const period = useWatch({name: 'subscriptionPeriod'}); - const frequency = useWatch({name: 'subscriptionFrequency'}); - const givingFrequency = useMemo(() => { if (isSubscriptionPeriod(period)) { const subscriptionPeriod = new SubscriptionPeriod(period); if (frequency > 1) { return createInterpolateElement(__('Every ', 'give'), { - period: {`${frequency} ${subscriptionPeriod.label().plural()}`}, + period: {`${frequency} ${subscriptionPeriod.label().plural()}`} }); } @@ -49,18 +38,18 @@ export default function DonationSummary() { const amountItem = { id: 'amount', label: __('Payment Amount', 'give'), - value: formatter.format(Number(amount)), + value: formatter.format(donationAmountBase) }; const frequencyItem = { id: 'frequency', label: __('Giving Frequency', 'give'), - value: givingFrequency, + value: givingFrequency }; const donationSummaryItems = [amountItem, frequencyItem, ...Object.values(items)]; - const donationTotal = formatter.format(getDonationTotal(totals, amount)); + const donationTotal = formatter.format(donationAmountTotal); return ( <> From 51bd5e6211e513dc1736ce985a98b5761a4fd514 Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Fri, 1 Nov 2024 12:10:08 -0400 Subject: [PATCH 2/6] feature: add initial subscription values --- .../resources/app/hooks/useDonationSummary.ts | 55 +++++++++++++++---- 1 file changed, 43 insertions(+), 12 deletions(-) diff --git a/src/DonationForms/resources/app/hooks/useDonationSummary.ts b/src/DonationForms/resources/app/hooks/useDonationSummary.ts index a46ecf4b61..47116260f5 100644 --- a/src/DonationForms/resources/app/hooks/useDonationSummary.ts +++ b/src/DonationForms/resources/app/hooks/useDonationSummary.ts @@ -1,5 +1,5 @@ import { - DonationSummaryLineItem, + DonationSummaryLineItem, DonationTotals, useDonationSummaryContext, useDonationSummaryDispatch } from '@givewp/forms/app/store/donation-summary'; @@ -10,6 +10,9 @@ import { removeItem } from '@givewp/forms/app/store/donation-summary/reducer'; import {useCallback} from '@wordpress/element'; +import type { + subscriptionPeriod +} from '@givewp/forms/registrars/templates/groups/DonationAmount/subscriptionPeriod'; /** * Zero decimal currencies are currencies that do not have a minor unit. @@ -51,18 +54,40 @@ const dollarsToCents = (amount: string, currency: string) => { }; /** + * Donation total calculation + * * @unreleased */ -const getDonationTotal = (totals: any, amount: any) => +const getDonationTotal = (totals: DonationTotals, amount: number) => Number( Object.values({ ...totals, - amount: Number(amount), + 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); +} /** * 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. @@ -78,11 +103,15 @@ export default function useDonationSummary() { const { items, totals } = useDonationSummaryContext(); const dispatch = useDonationSummaryDispatch(); const { useWatch } = window.givewp.form.hooks; - const amount = useWatch({ name: 'amount' }); - const currency = useWatch({ name: 'currency' }); - const period = useWatch({name: 'subscriptionPeriod'}); - const frequency = useWatch({name: 'subscriptionFrequency'}); - const donationType = useWatch({name: 'donationType'}); + + const amount = useWatch({ name: 'amount' }) as string; + const currency = useWatch({ name: 'currency' }) as string; + const period = useWatch({name: 'subscriptionPeriod'}) as subscriptionPeriod | undefined; + const frequency = useWatch({name: 'subscriptionFrequency'}) as number | undefined; + const donationType = useWatch({name: 'donationType'}) as "single" | "subscription" | undefined; + + const donationAmountTotal = getDonationTotal(totals, Number(amount)); + const subscriptionAmount = getSubscriptionTotal(totals, Number(amount)) return { items, @@ -96,10 +125,12 @@ export default function useDonationSummary() { removeFromTotal: useCallback((itemId: string) => dispatch(removeAmountFromTotal(itemId)), [dispatch]), state: { currency, - donationAmountBase: Number(amount), - donationAmountBaseMinor: dollarsToCents(amount, currency), - donationAmountTotal: getDonationTotal(totals, amount), - donationAmountTotalMinor: dollarsToCents(getDonationTotal(totals, amount).toString(), currency), + donationAmount: Number(amount), + donationAmountMinor: dollarsToCents(amount, currency), + donationAmountTotal, + donationAmountTotalMinor: dollarsToCents(donationAmountTotal.toString(), currency), + subscriptionAmount, + subscriptionAmountMinor: dollarsToCents(subscriptionAmount.toString(), currency), donationIsOneTime: donationType === 'single', donationIsRecurring: donationType === 'subscription', subscriptionPeriod: period, From 13837af7892a863b990ddd7878dcd0ea7b22db01 Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Fri, 1 Nov 2024 12:16:28 -0400 Subject: [PATCH 3/6] refactor: update variable names --- .../resources/app/hooks/useDonationSummary.ts | 2 +- .../templates/elements/DonationSummary.tsx | 12 ++++++------ .../templates/layouts/DonationSummaryItems.tsx | 3 +++ 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/DonationForms/resources/app/hooks/useDonationSummary.ts b/src/DonationForms/resources/app/hooks/useDonationSummary.ts index 47116260f5..2e894d7219 100644 --- a/src/DonationForms/resources/app/hooks/useDonationSummary.ts +++ b/src/DonationForms/resources/app/hooks/useDonationSummary.ts @@ -96,7 +96,7 @@ const getSubscriptionTotal = (totals: DonationTotals, amount: number) => { * Although the initial intent for this hook was to be used in the DonationSummary component for visual reasons, it is also recommended to be used in others * areas like gateways to get the total donation amount and currency. * - * @unreleased added currency, donationAmountBase, donationAmountTotal + * @unreleased added state object * @since 3.0.0 */ export default function useDonationSummary() { diff --git a/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx b/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx index de4b164fc4..78ce91744d 100644 --- a/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx +++ b/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx @@ -9,14 +9,14 @@ import {createInterpolateElement} from '@wordpress/element'; export default function DonationSummary() { const DonationSummaryItemsTemplate = window.givewp.form.templates.layouts.donationSummaryItems; const { useCurrencyFormatter, useDonationSummary } = window.givewp.form.hooks; - const { items, state } = useDonationSummary(); - const { + const { items } = useDonationSummary(); + const {state: { currency, - donationAmountBase, + donationAmount, donationAmountTotal, subscriptionPeriod: period, - subscriptionFrequency: frequency - } = state; + subscriptionFrequency: frequency, + }} = window.givewp.form.hooks.useDonationSummary(); const formatter = useCurrencyFormatter(currency); const givingFrequency = useMemo(() => { @@ -38,7 +38,7 @@ export default function DonationSummary() { const amountItem = { id: 'amount', label: __('Payment Amount', 'give'), - value: formatter.format(donationAmountBase) + value: formatter.format(donationAmount) }; const frequencyItem = { diff --git a/src/DonationForms/resources/registrars/templates/layouts/DonationSummaryItems.tsx b/src/DonationForms/resources/registrars/templates/layouts/DonationSummaryItems.tsx index 9b109365c7..6b5231755c 100644 --- a/src/DonationForms/resources/registrars/templates/layouts/DonationSummaryItems.tsx +++ b/src/DonationForms/resources/registrars/templates/layouts/DonationSummaryItems.tsx @@ -32,6 +32,9 @@ const LineItem = ({id, label, value, description, className}: LineItem) => { ); }; +/** + * TODO: account for when the total donation amount is different than subscription amount + */ export default function DonationSummaryItems({items, total}) { return (
    From 04c7ca5a078ccfe9c195e71ef09f7fd7ff7c85e9 Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Fri, 1 Nov 2024 12:36:25 -0400 Subject: [PATCH 4/6] feature: add subscription installments --- .../resources/app/hooks/useDonationSummary.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/DonationForms/resources/app/hooks/useDonationSummary.ts b/src/DonationForms/resources/app/hooks/useDonationSummary.ts index 2e894d7219..c6ddbe4488 100644 --- a/src/DonationForms/resources/app/hooks/useDonationSummary.ts +++ b/src/DonationForms/resources/app/hooks/useDonationSummary.ts @@ -106,8 +106,9 @@ export default function useDonationSummary() { const amount = useWatch({ name: 'amount' }) as string; const currency = useWatch({ name: 'currency' }) as string; - const period = useWatch({name: 'subscriptionPeriod'}) as subscriptionPeriod | undefined; - const frequency = useWatch({name: 'subscriptionFrequency'}) as number | undefined; + 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 donationAmountTotal = getDonationTotal(totals, Number(amount)); @@ -133,8 +134,9 @@ export default function useDonationSummary() { subscriptionAmountMinor: dollarsToCents(subscriptionAmount.toString(), currency), donationIsOneTime: donationType === 'single', donationIsRecurring: donationType === 'subscription', - subscriptionPeriod: period, - subscriptionFrequency: frequency, + subscriptionPeriod, + subscriptionFrequency, + subscriptionInstallments, }, }; } From 94b0ecf3627deb0550aba6d65e6a2db711e577e0 Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Fri, 8 Nov 2024 10:51:24 -0500 Subject: [PATCH 5/6] refactor: rename dollarsToCents to amountToMinorUnit --- .../resources/app/hooks/useDonationSummary.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/DonationForms/resources/app/hooks/useDonationSummary.ts b/src/DonationForms/resources/app/hooks/useDonationSummary.ts index c6ddbe4488..c63b0522db 100644 --- a/src/DonationForms/resources/app/hooks/useDonationSummary.ts +++ b/src/DonationForms/resources/app/hooks/useDonationSummary.ts @@ -45,7 +45,7 @@ const zeroDecimalCurrencies = [ * * @unreleased */ -const dollarsToCents = (amount: string, currency: string) => { +const amountToMinorUnit = (amount: string, currency: string) => { if (zeroDecimalCurrencies.includes(currency)) { return Math.round(parseFloat(amount)); } @@ -127,11 +127,11 @@ export default function useDonationSummary() { state: { currency, donationAmount: Number(amount), - donationAmountMinor: dollarsToCents(amount, currency), + donationAmountMinor: amountToMinorUnit(amount, currency), donationAmountTotal, - donationAmountTotalMinor: dollarsToCents(donationAmountTotal.toString(), currency), + donationAmountTotalMinor: amountToMinorUnit(donationAmountTotal.toString(), currency), subscriptionAmount, - subscriptionAmountMinor: dollarsToCents(subscriptionAmount.toString(), currency), + subscriptionAmountMinor: amountToMinorUnit(subscriptionAmount.toString(), currency), donationIsOneTime: donationType === 'single', donationIsRecurring: donationType === 'subscription', subscriptionPeriod, From 44a2ddd75dc0539df502f6380c10ca5afd056abe Mon Sep 17 00:00:00 2001 From: Jon Waldstein Date: Fri, 8 Nov 2024 11:40:21 -0500 Subject: [PATCH 6/6] refactor: create new hook useFormData and leave useDonationSummary leaner --- .../resources/app/hooks/useDonationSummary.ts | 118 ++-------------- .../resources/app/hooks/useFormData.ts | 129 ++++++++++++++++++ .../resources/registrars/index.ts | 2 + .../templates/elements/DonationSummary.tsx | 12 +- 4 files changed, 149 insertions(+), 112 deletions(-) create mode 100644 src/DonationForms/resources/app/hooks/useFormData.ts diff --git a/src/DonationForms/resources/app/hooks/useDonationSummary.ts b/src/DonationForms/resources/app/hooks/useDonationSummary.ts index c63b0522db..bd14bdc6f4 100644 --- a/src/DonationForms/resources/app/hooks/useDonationSummary.ts +++ b/src/DonationForms/resources/app/hooks/useDonationSummary.ts @@ -1,5 +1,5 @@ import { - DonationSummaryLineItem, DonationTotals, + DonationSummaryLineItem, useDonationSummaryContext, useDonationSummaryDispatch } from '@givewp/forms/app/store/donation-summary'; @@ -10,109 +10,18 @@ import { removeItem } from '@givewp/forms/app/store/donation-summary/reducer'; import {useCallback} from '@wordpress/element'; -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 getDonationTotal = (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); -} /** * 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. * - * Although the initial intent for this hook was to be used in the DonationSummary component for visual reasons, it is also recommended to be used in others - * areas like gateways to get the total donation amount and currency. - * - * @unreleased added state object + * @unreleased added getTotalSum * @since 3.0.0 */ export default function useDonationSummary() { const { items, totals } = useDonationSummaryContext(); const dispatch = useDonationSummaryDispatch(); - const { useWatch } = window.givewp.form.hooks; - - 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 donationAmountTotal = getDonationTotal(totals, Number(amount)); - const subscriptionAmount = getSubscriptionTotal(totals, Number(amount)) return { items, @@ -124,19 +33,14 @@ export default function useDonationSummary() { [dispatch] ), removeFromTotal: useCallback((itemId: string) => dispatch(removeAmountFromTotal(itemId)), [dispatch]), - state: { - currency, - donationAmount: Number(amount), - donationAmountMinor: amountToMinorUnit(amount, currency), - donationAmountTotal, - donationAmountTotalMinor: amountToMinorUnit(donationAmountTotal.toString(), currency), - subscriptionAmount, - subscriptionAmountMinor: amountToMinorUnit(subscriptionAmount.toString(), currency), - donationIsOneTime: donationType === 'single', - donationIsRecurring: donationType === 'subscription', - subscriptionPeriod, - subscriptionFrequency, - subscriptionInstallments, - }, + 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..18e67687ff --- /dev/null +++ b/src/DonationForms/resources/app/hooks/useFormData.ts @@ -0,0 +1,129 @@ +import type {DonationTotals} from '@givewp/forms/app/store/donation-summary'; +import type { + subscriptionPeriod +} from '@givewp/forms/registrars/templates/groups/DonationAmount/subscriptionPeriod'; +import { + useDonationSummaryContext, +} from '@givewp/forms/app/store/donation-summary'; + +/** + * 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 getDonationTotal = (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 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 donationAmountTotal = getDonationTotal(totals, Number(amount)); + const subscriptionAmount = getSubscriptionTotal(totals, Number(amount)) + + return { + firstName, + lastName, + email, + billingAddress, + currency, + donationAmount: Number(amount), + donationAmountMinor: amountToMinorUnit(amount, currency), + donationAmountTotal, + donationAmountTotalMinor: amountToMinorUnit(donationAmountTotal.toString(), currency), + subscriptionAmount, + subscriptionAmountMinor: amountToMinorUnit(subscriptionAmount.toString(), currency), + donationIsOneTime: donationType === 'single', + donationIsRecurring: donationType === 'subscription', + subscriptionPeriod, + subscriptionFrequency, + subscriptionInstallments, + }; +} 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 78ce91744d..857d693ec4 100644 --- a/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx +++ b/src/DonationForms/resources/registrars/templates/elements/DonationSummary.tsx @@ -9,14 +9,16 @@ import {createInterpolateElement} from '@wordpress/element'; export default function DonationSummary() { const DonationSummaryItemsTemplate = window.givewp.form.templates.layouts.donationSummaryItems; const { useCurrencyFormatter, useDonationSummary } = window.givewp.form.hooks; - const { items } = useDonationSummary(); - const {state: { + const { items, getTotalSum } = useDonationSummary(); + const { currency, donationAmount, - donationAmountTotal, subscriptionPeriod: period, - subscriptionFrequency: frequency, - }} = window.givewp.form.hooks.useDonationSummary(); + subscriptionFrequency: frequency + } = window.givewp.form.hooks.useFormData(); + + const donationAmountTotal = getTotalSum(donationAmount); + const formatter = useCurrencyFormatter(currency); const givingFrequency = useMemo(() => {