Skip to content

Commit

Permalink
Epic: add donation amount level descriptions (#7364)
Browse files Browse the repository at this point in the history
Co-authored-by: Paulo Iankoski <[email protected]>
Co-authored-by: Jon Waldstein <[email protected]>
  • Loading branch information
3 people authored May 14, 2024
1 parent 6a76b0b commit 4293f07
Show file tree
Hide file tree
Showing 33 changed files with 968 additions and 537 deletions.
688 changes: 342 additions & 346 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@
"@fortawesome/free-solid-svg-icons": "^5.15.1",
"@fortawesome/react-fontawesome": "^0.1.12",
"@givewp/design-system-foundation": "^1.1.0",
"@givewp/form-builder-library": "^1.2.1",
"@givewp/form-builder-library": "^1.6.0",
"@hookform/error-message": "^2.0.1",
"@hookform/resolvers": "^2.9.10",
"@paypal/paypal-js": "^5.1.4",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,19 +56,25 @@ public function __invoke(DonationAmountBlockModel $block, string $currency): Don

/** @var Amount $amountNode */
$amountNode = $group->getNodeByName('amount');
$defaultLevel = $block->getDefaultLevel() > 0 ? $block->getDefaultLevel() : 10;
$amountNode
->label($block->getLabel())
->levels(...$block->getLevels())
->allowLevels($block->getPriceOption() === 'multi')
->allowCustomAmount($block->isCustomAmountEnabled())
->fixedAmountValue($block->getSetPrice())
->defaultValue(
$block->getPriceOption() === 'set' ?
$block->getSetPrice() : $defaultLevel
)
->rules(...$amountRules);

$priceOptions = $block->getPriceOption();
if ($priceOptions === 'multi') {
['levels' => $levels, 'checked' => $checked] = $this->prepareLevelsArray($block);

$amountNode
->allowLevels()
->levels(...$levels)
->defaultValue($checked);
} else {
$amountNode
->fixedAmountValue($block->getSetPrice())
->defaultValue($block->getSetPrice());
}

/** @var Hidden $currencyNode */
$currencyNode = $group->getNodeByName('currency');
$currencyNode
Expand Down Expand Up @@ -164,4 +170,41 @@ protected function getRecurringAmountPeriodField(DonationAmountBlockModel $block
->options(...$options)
->rules(new SubscriptionPeriodRule());
}

/**
* Prepares the options array to be used in the field.
*
* @unreleased
*
* @return array ['options' => ['label' => string, 'value' => string][], 'checked' => string]
*/
private function prepareLevelsArray(DonationAmountBlockModel $block): array
{
$checked = null;
$levels = array_values(
array_filter(
array_map(
function ($item) use (&$checked, $block) {
if (isset($item['checked']) && $item['checked']) {
$checked = $item['value'];
}

return [
'value' => $item['value'] ?? '',
'label' => $block->isDescriptionEnabled() ? $item['label'] : '',
];
},
$block->getLevels()
),
function ($item) {
return $item['value'] !== '';
}
)
);

return [
'levels' => $levels,
'checked' => $checked,
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public function __invoke(DonationForm $donationForm)
}

/**
* @unreleased Update to store donation levels with descriptions
* @since 3.0.0 update with dynamic values from amount field
* @since 3.0.0
*/
Expand All @@ -43,7 +44,8 @@ public function storeDonationLevels(DonationForm $donationForm)
'_give_id' => [
'level_id' => $index,
],
'_give_amount' => $donationLevel
'_give_amount' => $donationLevel['value'],
'_give_text' => $donationLevel['label'] ?? '',
];
}, $donationLevels, array_keys($donationLevels));

Expand Down
80 changes: 80 additions & 0 deletions src/DonationForms/Migrations/UpdateDonationLevelsSchema.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
<?php

namespace Give\DonationForms\Migrations;

use Give\DonationForms\Repositories\DonationFormRepository;
use Give\Framework\Migrations\Contracts\Migration;

/**
* Update the donation levels schema to support descriptions
*
* @unreleased
*/
class UpdateDonationLevelsSchema extends Migration
{
/**
* @inheritdoc
*/
public static function id()
{
return 'donation-forms-donation-levels-schema';
}

/**
* @inheritdoc
*/
public static function title()
{
return 'Update Donation Levels schema to support descriptions';
}

/**
* @inheritdoc
*/
public static function timestamp()
{
return strtotime('2024-04-22');
}

/**
* @unreleased
*/
public function run()
{
$forms = give(DonationFormRepository::class)
->prepareQuery()
->whereIsNotNull("give_formmeta_attach_meta_fields.meta_value")
->getAll();

if ( ! $forms) {
return;
}

foreach ($forms as $form) {
$amountBlock = $form->blocks->findByName('givewp/donation-amount');

if ( ! $amountBlock) {
continue;
}

$blockAttributes = $amountBlock->getAttributes();
$levels = $blockAttributes["levels"];
$defaultLevel = $blockAttributes["defaultLevel"] ?? 0;

if ( ! is_array($levels) || empty($levels) || isset($levels[0]['value'])) {
continue;
}

$levels = array_map(function($level) use ($defaultLevel) {
return [
'value' => $level,
'label' => '',
'checked' => $level === $defaultLevel,
];
}, $levels);

$amountBlock->setAttribute('levels', $levels);
$form->save();
}
}
}
4 changes: 3 additions & 1 deletion src/DonationForms/ServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@
use Give\DonationForms\DataTransferObjects\DonationFormPreviewRouteData;
use Give\DonationForms\DataTransferObjects\DonationFormViewRouteData;
use Give\DonationForms\FormDesigns\ClassicFormDesign\ClassicFormDesign;
use Give\DonationForms\FormDesigns\TwoPanelStepsFormLayout\TwoPanelStepsFormLayout;
use Give\DonationForms\FormDesigns\MultiStepFormDesign\MultiStepFormDesign;
use Give\DonationForms\FormDesigns\TwoPanelStepsFormLayout\TwoPanelStepsFormLayout;
use Give\DonationForms\FormPage\TemplateHandler;
use Give\DonationForms\Migrations\CleanMultipleSlashesOnDB;
use Give\DonationForms\Migrations\RemoveDuplicateMeta;
use Give\DonationForms\Migrations\UpdateDonationLevelsSchema;
use Give\DonationForms\Repositories\DonationFormRepository;
use Give\DonationForms\Routes\AuthenticationRoute;
use Give\DonationForms\Routes\DonateRoute;
Expand Down Expand Up @@ -76,6 +77,7 @@ public function boot()
give(MigrationsRegister::class)->addMigrations([
CleanMultipleSlashesOnDB::class,
RemoveDuplicateMeta::class,
UpdateDonationLevelsSchema::class,
]);
}

Expand Down
2 changes: 1 addition & 1 deletion src/DonationForms/resources/propTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ export interface DonationAmountProps extends GroupProps {
}

export interface AmountProps extends FieldProps {
levels: number[];
levels: {label: string; value: number}[];
allowLevels: boolean;
allowCustomAmount: boolean;
fixedAmountValue: number;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,41 +6,71 @@ import classNames from 'classnames';
type DonationAmountLevelsProps = {
name: string;
currency: string;
levels: number[];
levels: Level[];
onLevelClick?: (amount: number) => void;
};

type GroupedLevels = {
labeled: Level[];
unlabeled: Level[];
};

type Level = {label: string | null; value: number};

/**
* @unreleased add level descriptions.
* @since 3.0.0
*/
export default function DonationAmountLevels({
name,
currency,
levels,
onLevelClick,
}: DonationAmountLevelsProps) {
export default function DonationAmountLevels({name, currency, levels, onLevelClick}: DonationAmountLevelsProps) {
const {useWatch, useCurrencyFormatter} = window.givewp.form.hooks;
const amount = useWatch({name});
const formatter = useCurrencyFormatter(currency);

const groupedLevels: GroupedLevels = levels.reduce(
(acc: GroupedLevels, level) => {
const key = level.label ? 'labeled' : 'unlabeled';
acc[key].push(level);
return acc;
},
{labeled: [], unlabeled: []}
);

const allLevels = [...groupedLevels.labeled, ...groupedLevels.unlabeled];

return (
<div className="givewp-fields-amount__levels-container">
{levels.map((levelAmount, index) => {
const label = formatter.format(levelAmount);
const selected = levelAmount === amount;
<div
className={classNames('givewp-fields-amount__levels-container', {
'givewp-fields-amount__levels-container--has-descriptions': groupedLevels.labeled.length > 0,
})}
>
{allLevels.map((level, index) => {
const label = formatter.format(level.value);
const selected = level.value === amount;
const hasDescription = level.label;

return (
<button
className={classNames('givewp-fields-amount__level', {
'givewp-fields-amount__level--selected': selected,
<div
className={classNames('givewp-fields-amount__level-container', {
'givewp-fields-amount__level-container--col': hasDescription,
})}
type="button"
onClick={() => {
onLevelClick(levelAmount);
}}
key={index}
>
{label}
</button>
<button
className={classNames('givewp-fields-amount__level', {
'givewp-fields-amount__level--selected': selected,
'givewp-fields-amount__level--description': hasDescription,
})}
type="button"
onClick={() => {
onLevelClick(level.value);
}}
key={index}
>
{label}
</button>
{hasDescription && (
<span className={'givewp-fields-amount__level__description'}>{level.label}</span>
)}
</div>
);
})}
</div>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {useCallback} from '@wordpress/element';
import type {AmountProps} from '@givewp/forms/propTypes';
import CustomAmount from './CustomAmount';
import {useState} from 'react';
import getAmountLevelsWithCurrencySettings from './getAmountLevelsWithCurrencySettings';
import {useEffect, useState} from 'react';
import {getAmountLevelsWithCurrencySettings, getDefaultAmountWithCurrencySettings} from './withCurrencySettings';
import DonationAmountCurrency from './DonationAmountCurrency';
import DonationAmountLevels from './DonationAmountLevels';

/**
* @unreleased Update default level when having distinct default currency
* @since 3.0.0
*/
export default function Amount({
Expand All @@ -24,13 +25,26 @@ export default function Amount({
}: AmountProps) {
const isFixedAmount = !allowLevels;
const [customAmountValue, setCustomAmountValue] = useState<string>(
isFixedAmount ? fixedAmountValue.toString() : '');
isFixedAmount ? fixedAmountValue.toString() : ''
);
const {useWatch, useFormContext, useDonationFormSettings} = window.givewp.form.hooks;
const {setValue} = useFormContext();
const {currencySwitcherSettings} = useDonationFormSettings();

const currency = useWatch({name: 'currency'});

useEffect(() => {
if (!isFixedAmount) {
const defaultAmount = getDefaultAmountWithCurrencySettings(
levels,
defaultValue,
currency,
currencySwitcherSettings
);
setValue(name, defaultAmount);
}
}, []);

const getAmountLevels = useCallback(() => {
if (currencySwitcherSettings.length <= 1) {
return levels;
Expand Down Expand Up @@ -87,7 +101,6 @@ export default function Amount({
value={customAmountValue}
onValueChange={(value) => {
setCustomAmountValue(value);

setValue(name, Number(value) ?? null);
}}
/>
Expand Down
Loading

0 comments on commit 4293f07

Please sign in to comment.