Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature: update donation summary hook with helpful form state values #7600

Draft
wants to merge 19 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
f42422a
feature: add donation summary state values to hook for unified access
Nov 1, 2024
51bd5e6
feature: add initial subscription values
Nov 1, 2024
13837af
refactor: update variable names
Nov 1, 2024
04c7ca5
feature: add subscription installments
Nov 1, 2024
8fd681c
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Nov 1, 2024
e38f8a3
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Nov 4, 2024
ff9d671
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Nov 7, 2024
94b0ecf
refactor: rename dollarsToCents to amountToMinorUnit
Nov 8, 2024
44a2ddd
refactor: create new hook useFormData and leave useDonationSummary le…
Nov 8, 2024
af2bb33
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Nov 22, 2024
b1fa053
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Dec 6, 2024
5af8277
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Dec 23, 2024
1037fd0
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Jan 3, 2025
e6108c9
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Jan 10, 2025
f4725d5
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Jan 17, 2025
0656842
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Feb 7, 2025
5b79368
refactor: simplify and reduce terms down to just amount and amountInM…
Feb 7, 2025
5c7e25b
feature: add phone
Feb 7, 2025
0d2904b
Merge branch 'develop' into feature/donation-summary-hook-store
jonwaldstein Feb 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 17 additions & 3 deletions src/DonationForms/resources/app/hooks/useDonationSummary.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -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])
};
}
132 changes: 132 additions & 0 deletions src/DonationForms/resources/app/hooks/useFormData.ts
Original file line number Diff line number Diff line change
@@ -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,
};
}
2 changes: 2 additions & 0 deletions src/DonationForms/resources/app/utilities/mountWindowData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
*
Expand All @@ -17,5 +18,6 @@ export default function mountWindowData(): void {
useCurrencyFormatter,
useDonationSummary,
useDonationFormSettings,
useFormData
};
}
2 changes: 2 additions & 0 deletions src/DonationForms/resources/registrars/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -21,6 +22,7 @@ declare global {
useCurrencyFormatter: typeof useCurrencyFormatter;
useDonationSummary: typeof useDonationSummary;
useDonationFormSettings: typeof useDonationFormSettings;
useFormData: typeof useFormData;
};
};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,40 +3,31 @@ 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)) {
const subscriptionPeriod = new SubscriptionPeriod(period);

if (frequency > 1) {
return createInterpolateElement(__('Every <period />', 'give'), {
period: <span>{`${frequency} ${subscriptionPeriod.label().plural()}`}</span>,
period: <span>{`${frequency} ${subscriptionPeriod.label().plural()}`}</span>
});
}

Expand All @@ -49,18 +40,18 @@ export default function DonationSummary() {
const amountItem = {
id: 'amount',
label: __('Payment Amount', 'give'),
value: formatter.format(Number(amount)),
value: formatter.format(donationAmount)
};

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 (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<ul className="givewp-elements-donationSummary__list">
Expand Down
Loading