diff --git a/src/Campaigns/Actions/AssociateCampaignPageWithCampaign.php b/src/Campaigns/Actions/AssociateCampaignPageWithCampaign.php new file mode 100644 index 0000000000..3f99574d30 --- /dev/null +++ b/src/Campaigns/Actions/AssociateCampaignPageWithCampaign.php @@ -0,0 +1,25 @@ +campaignId); + + if ($campaign) { + $campaign->pageId = $campaignPage->id; + $campaign->save(); + } + } +} diff --git a/src/Campaigns/Actions/ConvertQueryDataToCampaign.php b/src/Campaigns/Actions/ConvertQueryDataToCampaign.php index a064898b87..7293dc4c30 100644 --- a/src/Campaigns/Actions/ConvertQueryDataToCampaign.php +++ b/src/Campaigns/Actions/ConvertQueryDataToCampaign.php @@ -20,6 +20,7 @@ public function __invoke(object $queryObject): Campaign { return new Campaign([ 'id' => (int)$queryObject->id, + 'pageId' => (int)$queryObject->pageId, 'defaultFormId' => (int)$queryObject->defaultFormId, 'type' => new CampaignType($queryObject->type), 'enableCampaignPage' => (bool)$queryObject->enableCampaignPage, diff --git a/src/Campaigns/Actions/LoadCampaignDetailsAssets.php b/src/Campaigns/Actions/LoadCampaignDetailsAssets.php index 9f70322c04..8e0ec03ed0 100644 --- a/src/Campaigns/Actions/LoadCampaignDetailsAssets.php +++ b/src/Campaigns/Actions/LoadCampaignDetailsAssets.php @@ -27,14 +27,6 @@ public function __invoke() true ); - wp_localize_script($handleName, 'GiveCampaignDetails', - [ - 'adminUrl' => admin_url(), - 'currency' => give_get_currency(), - 'isRecurringEnabled' => defined('GIVE_RECURRING_VERSION') ? GIVE_RECURRING_VERSION : null - ] - ); - wp_enqueue_script($handleName); wp_enqueue_style('givewp-design-system-foundation'); wp_enqueue_style( diff --git a/src/Campaigns/Actions/LoadCampaignOptions.php b/src/Campaigns/Actions/LoadCampaignOptions.php new file mode 100644 index 0000000000..680325871a --- /dev/null +++ b/src/Campaigns/Actions/LoadCampaignOptions.php @@ -0,0 +1,30 @@ + admin_url(), + 'currency' => give_get_currency(), + 'currencySymbol' => give_currency_symbol(), + 'isRecurringEnabled' => defined('GIVE_RECURRING_VERSION') + ? GIVE_RECURRING_VERSION + : null, + ] + ); + + wp_enqueue_script('give-campaign-options'); + } +} diff --git a/src/Campaigns/Actions/RegisterCampaignBlocks.php b/src/Campaigns/Actions/RegisterCampaignBlocks.php index da87da9b1d..f7f6a42338 100644 --- a/src/Campaigns/Actions/RegisterCampaignBlocks.php +++ b/src/Campaigns/Actions/RegisterCampaignBlocks.php @@ -45,7 +45,6 @@ private function enqueueAdminBlocksAssets(): void wp_enqueue_style( $handleName, GIVE_PLUGIN_URL . 'build/campaignBlocks.css', - /** @see https://developer.wordpress.org/block-editor/reference-guides/packages/packages-components/#usage */ ['wp-components'], $scriptAsset['version'] ); diff --git a/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php b/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php index bc6f530c3c..9f9740a47e 100644 --- a/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php +++ b/src/Campaigns/Blocks/CampaignDonations/resources/views/render.php @@ -14,8 +14,8 @@ $blockInlineStyles = sprintf( '--givewp-primary-color: %s; --givewp-secondary-color: %s;', - esc_attr('#0b72d9'), - esc_attr('#27ae60') + esc_attr($campaign->primaryColor ?? '#0b72d9'), + esc_attr($campaign->secondaryColor ?? '#27ae60') ); ?>
primaryColor ?? '#0b72d9'), + esc_attr($campaign->secondaryColor ?? '#27ae60') ); ?>
{ + const {campaign, hasResolved} = useCampaign(campaignId); + + if (!hasResolved || !campaignId) { + return null; + } + + return ; +} + +/** + * @unreleased + */ +const nodeList = document.querySelectorAll('[data-givewp-campaign-goal]'); + +if (nodeList) { + const containers = Array.from(nodeList); + + containers.map((container: any) => { + return render(, container); + }); +} diff --git a/src/Campaigns/Blocks/CampaignGoal/app/index.tsx b/src/Campaigns/Blocks/CampaignGoal/app/index.tsx new file mode 100644 index 0000000000..37fa61bdcd --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/app/index.tsx @@ -0,0 +1,34 @@ +import {__} from '@wordpress/i18n'; +import {Campaign} from '@givewp/campaigns/admin/components/types'; +import {getGoalDescription, getGoalFormattedValue} from '../utils'; + +import './styles.scss'; + +export default ({campaign}: { campaign: Campaign }) => { + return ( +
+
+
+ {getGoalDescription(campaign.goalType)} + + {getGoalFormattedValue(campaign.goalType, campaign.goalStats.actual)} + +
+
+ {__('Our goal', 'give')} + + {getGoalFormattedValue(campaign.goalType, campaign.goal)} + +
+
+
+
+
+
+
+
+
+ ); +} diff --git a/src/Campaigns/Blocks/CampaignGoal/app/styles.scss b/src/Campaigns/Blocks/CampaignGoal/app/styles.scss new file mode 100644 index 0000000000..b98f4b8b2f --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/app/styles.scss @@ -0,0 +1,51 @@ +.givewp-campaign-goal { + display: flex; + flex-direction: column; + gap: 0.5rem; + + &__container { + display: flex; + flex-direction: row; + justify-content: space-between; + + &-item { + display: flex; + flex-direction: column; + gap: 0.2rem; + + span { + text-transform: uppercase; + font-size: 12px; + line-height: 18px; + color: #4b5563; + } + + strong { + font-size: 20px; + line-height: 32px; + color: #060c1a; + } + } + } + + &__progress-bar { + display: flex; + + &-container { + display: flex; + height: 8px; + flex-grow: 1; + border-radius: 14px; + box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.09); + background-color: #f2f2f2; + } + + &-progress { + display: flex; + height: 8px; + border-radius: 14px; + box-shadow: inset 0 1px 4px 0 rgba(0, 0, 0, 0.09); + background-color: #2d802f; + } + } +} diff --git a/src/Campaigns/Blocks/CampaignGoal/block.json b/src/Campaigns/Blocks/CampaignGoal/block.json new file mode 100644 index 0000000000..0145bccb61 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/block.json @@ -0,0 +1,24 @@ +{ + "$schema": "https://json.schemastore.org/block.json", + "apiVersion": 2, + "name": "givewp/campaign-goal", + "version": "1.0.0", + "title": "Campaign Goal", + "category": "give", + "description": "Displays the goal of the campaign.", + "supports": { + "align": [ + "wide", + "full" + ] + }, + "attributes": { + "campaignId": { + "type": "number" + } + }, + "textdomain": "give", + "viewScript": "file:../../../../build/campaignGoalBlockApp.js", + "style": "file:../../../../build/campaignGoalBlockApp.css", + "render": "file:./render.php" +} diff --git a/src/Campaigns/Blocks/CampaignGoal/edit.tsx b/src/Campaigns/Blocks/CampaignGoal/edit.tsx new file mode 100644 index 0000000000..ed200bbb18 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/edit.tsx @@ -0,0 +1,53 @@ +import {__} from '@wordpress/i18n'; +import {useSelect} from '@wordpress/data'; +import {InspectorControls, useBlockProps} from '@wordpress/block-editor'; +import {BlockEditProps} from '@wordpress/blocks'; +import {ExternalLink, PanelBody, TextControl} from '@wordpress/components'; +import useCampaign from '../shared/hooks/useCampaign'; +import CampaignGoalApp from './app/index'; +import {CampaignSelector} from '../shared/components/CampaignSelector'; +import {getGoalDescription} from './utils'; + +/** + * @unreleased + */ +export default function Edit({attributes, setAttributes}: BlockEditProps<{ + campaignId: number; + goalType: string; +}>) { + const {campaign, hasResolved} = useCampaign(attributes.campaignId); + + const blockProps = useBlockProps(); + + const adminBaseUrl = useSelect( + // @ts-ignore + (select) => select('core').getSite()?.url + '/wp-admin/edit.php?post_type=give_forms&page=give-campaigns', + [] + ); + + if (!hasResolved) { + return null; + } + + return ( +
+ + + + + {campaign?.id && ( + + + + + {__('Edit campaign goal settings', 'give')} + + + + )} +
+ ); +} diff --git a/src/Campaigns/Blocks/CampaignGoal/icon.jsx b/src/Campaigns/Blocks/CampaignGoal/icon.jsx new file mode 100644 index 0000000000..fd153a0135 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/icon.jsx @@ -0,0 +1,11 @@ +export default () => ( + + + + + +) diff --git a/src/Campaigns/Blocks/CampaignGoal/index.tsx b/src/Campaigns/Blocks/CampaignGoal/index.tsx new file mode 100644 index 0000000000..034f5fc8a5 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/index.tsx @@ -0,0 +1,15 @@ +import edit from './edit'; +import icon from './icon'; +import schema from './block.json'; + +/** + * @unreleased + */ +export default { + schema, + settings: { + icon, + edit + } +}; + diff --git a/src/Campaigns/Blocks/CampaignGoal/render.php b/src/Campaigns/Blocks/CampaignGoal/render.php new file mode 100644 index 0000000000..7b507bdf08 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/render.php @@ -0,0 +1,20 @@ +getById($attributes['campaignId']) +) { + return; +} + +?> + +
diff --git a/src/Campaigns/Blocks/CampaignGoal/utils.ts b/src/Campaigns/Blocks/CampaignGoal/utils.ts new file mode 100644 index 0000000000..8a81a38016 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignGoal/utils.ts @@ -0,0 +1,35 @@ +import {__} from '@wordpress/i18n'; +import {getCampaignOptionsWindowData, amountFormatter} from '@givewp/campaigns/utils'; + + +export const getGoalDescription = (goalType: string) => { + switch (goalType) { + case 'amount': + return __('Amount raised', 'give'); + case 'donations': + return __('Number of donations', 'give'); + case 'donors': + return __('Number of donors', 'give'); + case 'amountFromSubscriptions': + return __('Recurring amount raised', 'give'); + case 'subscriptions': + return __('Number of recurring donations', 'give'); + case 'donorsFromSubscriptions': + return __('Number of recurring donors', 'give'); + } +} + + +export const getGoalFormattedValue = (goalType: string, value: number) => { + switch (goalType) { + case 'amount': + case 'amountFromSubscriptions': + const {currency} = getCampaignOptionsWindowData() + const currencyFormatter = amountFormatter(currency); + + return currencyFormatter.format(value); + + default: + return value; + } +} diff --git a/src/Campaigns/Blocks/CampaignStats/Icon.tsx b/src/Campaigns/Blocks/CampaignStats/Icon.tsx new file mode 100644 index 0000000000..b8e68daa9f --- /dev/null +++ b/src/Campaigns/Blocks/CampaignStats/Icon.tsx @@ -0,0 +1,20 @@ +import {Path, SVG} from '@wordpress/components'; + +export function StatsIcon() { + return ( + + + + + ); +} diff --git a/src/Campaigns/Blocks/CampaignStats/app.tsx b/src/Campaigns/Blocks/CampaignStats/app.tsx new file mode 100644 index 0000000000..e39aaa19eb --- /dev/null +++ b/src/Campaigns/Blocks/CampaignStats/app.tsx @@ -0,0 +1 @@ +import './styles.scss'; diff --git a/src/Campaigns/Blocks/CampaignStats/block.json b/src/Campaigns/Blocks/CampaignStats/block.json new file mode 100644 index 0000000000..ee52e1e2bd --- /dev/null +++ b/src/Campaigns/Blocks/CampaignStats/block.json @@ -0,0 +1,71 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "givewp/campaign-stats-block", + "version": "1.0.0", + "title": "Campaign Statistics", + "category": "give", + "description": "Displays the campaign’s statistics.", + "attributes": { + "campaignId": { + "type": "integer" + }, + "statistic": { + "type": "string", + "enum": [ + "top-donation", + "average-donation" + ], + "default": "top-donation" + } + }, + "supports": { + "align": [ + "wide", + "full" + ], + "anchor": true, + "className": true, + "splitting": true, + "__experimentalBorder": { + "color": true, + "radius": true, + "style": true, + "width": true + }, + "color": { + "gradients": true, + "link": true, + "__experimentalDefaultControls": { + "background": true, + "text": true + } + }, + "spacing": { + "margin": true, + "padding": true, + "__experimentalDefaultControls": { + "margin": false, + "padding": false + } + }, + "typography": { + "fontSize": true, + "lineHeight": true, + "__experimentalFontFamily": true, + "__experimentalFontStyle": true, + "__experimentalFontWeight": true, + "__experimentalLetterSpacing": true, + "__experimentalTextTransform": true, + "__experimentalTextDecoration": true, + "__experimentalWritingMode": true, + "__experimentalDefaultControls": { + "fontSize": true + } + } + }, + "textdomain": "give", + "render": "file:./render.php", + "viewScript": "file:../../../../build/campaignStatsApp.js", + "style": "file:../../../../build/campaignStatsBlockApp.css" +} diff --git a/src/Campaigns/Blocks/CampaignStats/edit.tsx b/src/Campaigns/Blocks/CampaignStats/edit.tsx new file mode 100644 index 0000000000..785230cd50 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignStats/edit.tsx @@ -0,0 +1,51 @@ +import {InspectorControls, useBlockProps} from '@wordpress/block-editor'; +import {__} from '@wordpress/i18n'; +import {BlockEditProps} from '@wordpress/blocks'; +import {PanelBody, SelectControl} from '@wordpress/components'; +import {CampaignSelector} from '../shared/components/CampaignSelector'; +import ServerSideRender from '@wordpress/server-side-render'; +import useCampaign from '../shared/hooks/useCampaign'; + +import './styles.scss'; + +type statisticType = 'top-donation' | 'average-donation'; + +export default function Edit({ + attributes, + setAttributes, +}: BlockEditProps<{ + campaignId: number; + statistic: statisticType; +}>) { + const blockProps = useBlockProps(); + const {campaign, hasResolved} = useCampaign(attributes?.campaignId); + + const statsHelpText = attributes.statistic === 'top-donation' + ? __('Displays the top donation of the selected campaign.', 'give') + : __('Displays the average donation of the selected campaign.', 'give'); + + return ( +
+ + + + + {hasResolved && campaign?.id && ( + + + setAttributes({statistic: value})} + /> + + + )} +
+ ); +} diff --git a/src/Campaigns/Blocks/CampaignStats/index.tsx b/src/Campaigns/Blocks/CampaignStats/index.tsx new file mode 100644 index 0000000000..1e035d8a69 --- /dev/null +++ b/src/Campaigns/Blocks/CampaignStats/index.tsx @@ -0,0 +1,16 @@ +import schema from './block.json'; +import Edit from './edit'; +import {StatsIcon} from './Icon'; + +/** + * @unreleased + */ +const settings = { + icon: , + edit: Edit, +}; + +export default { + schema, + settings, +}; diff --git a/src/Campaigns/Blocks/CampaignStats/render.php b/src/Campaigns/Blocks/CampaignStats/render.php new file mode 100644 index 0000000000..82cd23a08a --- /dev/null +++ b/src/Campaigns/Blocks/CampaignStats/render.php @@ -0,0 +1,43 @@ +getById($attributes['campaignId']) +) { + return; +} + +$query = (new CampaignDonationQuery($campaign)) + ->select( + $attributes['statistic'] === 'top-donation' + ? 'MAX(amountMeta.meta_value) as amount' + : 'AVG(amountMeta.meta_value) as amount' + ) + ->joinDonationMeta(DonationMetaKeys::AMOUNT, 'amountMeta'); + +$donationStat = $query->get(); + +$amount = $donationStat && $donationStat->amount + ? Money::fromDecimal($donationStat->amount, give_get_currency()) + : Money::fromDecimal(0, give_get_currency()); + +$title = $attributes['statistic'] === 'top-donation' ? __('Top Donation', 'give') : __('Average Donation', 'give'); +?> + +
+ + formatToLocale()) ?> +
diff --git a/src/Campaigns/Blocks/CampaignStats/styles.scss b/src/Campaigns/Blocks/CampaignStats/styles.scss new file mode 100644 index 0000000000..575b3afbda --- /dev/null +++ b/src/Campaigns/Blocks/CampaignStats/styles.scss @@ -0,0 +1,23 @@ +.givewp-campaign-stats-block { + + span { + display: block; + margin-bottom: 2px; + height: 18px; + font-size: 12px; + font-weight: 600; + line-height: 1.5; + letter-spacing: 0.48px; + text-align: left; + color: var(--givewp-neutral-500); + } + + strong { + height: 32px; + font-size: 20px; + font-weight: 600; + line-height: 1.6; + letter-spacing: normal; + color: var(--givewp-neutral-900); + } +} diff --git a/src/Campaigns/Blocks/DonateButton/render.php b/src/Campaigns/Blocks/DonateButton/render.php index c202651817..63f92ff37d 100644 --- a/src/Campaigns/Blocks/DonateButton/render.php +++ b/src/Campaigns/Blocks/DonateButton/render.php @@ -16,6 +16,11 @@ return; } +$blockInlineStyles = sprintf( + '--givewp-primary-color: %s;', + esc_attr($campaign->primaryColor ?? '#0b72d9') +); + $params = [ 'formId' => $attributes['useDefaultForm'] ? $campaign->defaultFormId @@ -23,5 +28,13 @@ 'openFormButton' => $attributes['buttonText'], 'formFormat' => 'modal', ]; +?> -echo (new BlockRenderController())->render($params); +
'givewp-campaign-donate-button-block'])); ?> + style=""> + render($params); ?> +
diff --git a/src/Campaigns/Blocks/blocks.ts b/src/Campaigns/Blocks/blocks.ts index 59ae9980bc..ada033fe2a 100644 --- a/src/Campaigns/Blocks/blocks.ts +++ b/src/Campaigns/Blocks/blocks.ts @@ -11,10 +11,20 @@ import campaignDonateButton from './DonateButton'; import campaignDonations from './CampaignDonations'; import campaignDonors from './CampaignDonors'; import campaignTitle from './CampaignTitle'; +import campaignGoal from './CampaignGoal'; +import campaignStats from './CampaignStats'; export const getAllBlocks = () => { - return [campaignCover, campaignDonateButton, campaignDonations, campaignDonors, campaignTitle]; -}; + return [ + campaignCover, + campaignDonateButton, + campaignDonations, + campaignDonors, + campaignTitle, + campaignGoal, + campaignStats + ]; +} getAllBlocks().forEach((block) => { if (!getBlockType(block.schema.name)) { diff --git a/src/Campaigns/Blocks/shared/hooks/useCampaign.ts b/src/Campaigns/Blocks/shared/hooks/useCampaign.ts index a7cf2b57ea..3d0ed8f3ad 100644 --- a/src/Campaigns/Blocks/shared/hooks/useCampaign.ts +++ b/src/Campaigns/Blocks/shared/hooks/useCampaign.ts @@ -2,10 +2,12 @@ import {useEntityRecord} from '@wordpress/core-data'; import {Campaign} from '@givewp/campaigns/admin/components/types'; export default function useCampaign(campaignId: number) { - const data = useEntityRecord('givewp', 'campaign', campaignId); + const campaignData = useEntityRecord('givewp', 'campaign', campaignId); return { - campaign: data?.record as Campaign, - hasResolved: data?.hasResolved, + campaign: { + ...campaignData?.record as Campaign + }, + hasResolved: campaignData?.hasResolved, }; } diff --git a/src/Campaigns/CampaignsAdminPage.php b/src/Campaigns/CampaignsAdminPage.php index 9fdf667d23..cf6be15b93 100644 --- a/src/Campaigns/CampaignsAdminPage.php +++ b/src/Campaigns/CampaignsAdminPage.php @@ -39,7 +39,7 @@ public function renderCampaignsPage() wp_die(__('Campaign not found', 'give'), 404); } - give(LoadCampaignDetailsAssets::class)($campaign); + give(LoadCampaignDetailsAssets::class)(); } else { give(LoadCampaignsListTableAssets::class)(); } @@ -52,6 +52,6 @@ public function renderCampaignsPage() */ public static function isShowingDetailsPage(): bool { - return isset($_GET['id']) && isset($_GET['page']) && 'give-campaigns' == isset($_GET['page']); + return isset($_GET['id'], $_GET['page']) && 'give-campaigns' === $_GET['page']; } } diff --git a/src/Campaigns/Controllers/CampaignRequestController.php b/src/Campaigns/Controllers/CampaignRequestController.php index 65dcb05738..836b259a12 100644 --- a/src/Campaigns/Controllers/CampaignRequestController.php +++ b/src/Campaigns/Controllers/CampaignRequestController.php @@ -3,7 +3,6 @@ namespace Give\Campaigns\Controllers; use Exception; -use Give\Campaigns\CampaignDonationQuery; use Give\Campaigns\Models\Campaign; use Give\Campaigns\Repositories\CampaignRepository; use Give\Campaigns\ValueObjects\CampaignGoalType; @@ -34,7 +33,7 @@ public function getCampaign(WP_REST_Request $request) return new WP_REST_Response( array_merge($campaign->toArray(), [ - 'goalProgress' => $campaign->goalProgress(), + 'goalStats' => $campaign->getGoalStats(), 'defaultFormTitle' => $campaign->defaultForm()->title ]) ); @@ -60,7 +59,9 @@ public function getCampaigns(WP_REST_Request $request): WP_REST_Response // todo: remove - temporary solution $campaigns = array_map(function ($campaign) { - return $campaign->toArray(); + return array_merge($campaign->toArray(), [ + 'goalStats' => $campaign->getGoalStats(), + ]); }, $campaigns); $response = rest_ensure_response($campaigns); @@ -186,8 +187,8 @@ public function createCampaign(WP_REST_Request $request): WP_REST_Response 'longDescription' => '', 'logo' => '', 'image' => $request->get_param('image') ?? '', - 'primaryColor' => '', - 'secondaryColor' => '', + 'primaryColor' => '#0b72d9', + 'secondaryColor' => '#27ae60', 'goal' => (int)$request->get_param('goal'), 'goalType' => new CampaignGoalType($request->get_param('goalType')), 'status' => CampaignStatus::DRAFT(), diff --git a/src/Campaigns/Models/Campaign.php b/src/Campaigns/Models/Campaign.php index 23d086d901..f0f8e5ef5c 100644 --- a/src/Campaigns/Models/Campaign.php +++ b/src/Campaigns/Models/Campaign.php @@ -5,7 +5,7 @@ use DateTime; use Exception; use Give\Campaigns\Actions\ConvertQueryDataToCampaign; -use Give\Campaigns\CampaignDonationQuery; +use Give\Campaigns\DataTransferObjects\CampaignGoalData; use Give\Campaigns\Factories\CampaignFactory; use Give\Campaigns\Repositories\CampaignPageRepository; use Give\Campaigns\Repositories\CampaignRepository; @@ -25,6 +25,7 @@ * @unreleased * * @property int $id + * @property int $pageId * @property int $defaultFormId * @property CampaignType $type * @property bool $enableCampaignPage @@ -50,6 +51,7 @@ class Campaign extends Model implements ModelCrud, ModelHasFactory */ protected $properties = [ 'id' => 'int', + 'pageId' => 'int', 'defaultFormId' => 'int', 'type' => CampaignType::class, 'enableCampaignPage' => ['bool', true], @@ -174,10 +176,9 @@ public function merge(Campaign ...$campaignsToMerge): bool return give(CampaignRepository::class)->mergeCampaigns($this, ...$campaignsToMerge); } - public function goalProgress() + public function getGoalStats(): array { - $query = new CampaignDonationQuery($this); - return $query->sumIntendedAmount(); + return (new CampaignGoalData($this))->toArray(); } /** diff --git a/src/Campaigns/Repositories/CampaignRepository.php b/src/Campaigns/Repositories/CampaignRepository.php index b2b814c059..699695932a 100644 --- a/src/Campaigns/Repositories/CampaignRepository.php +++ b/src/Campaigns/Repositories/CampaignRepository.php @@ -143,6 +143,7 @@ public function update(Campaign $campaign): void ->update([ 'campaign_type' => $campaign->type->getValue(), 'enable_campaign_page' => $campaign->enableCampaignPage, + 'campaign_page_id' => $campaign->pageId, 'campaign_title' => $campaign->title, 'short_desc' => $campaign->shortDescription, 'long_desc' => $campaign->longDescription, @@ -355,6 +356,7 @@ public function prepareQuery(): ModelQueryBuilder 'id', ['campaigns.form_id', 'defaultFormId'], // Prefix the `form_id` column to avoid conflicts with the `give_campaign_forms` table. ['campaign_type', 'type'], + ['campaign_page_id', 'pageId'], ['enable_campaign_page', 'enableCampaignPage'], ['campaign_title', 'title'], ['short_desc', 'shortDescription'], diff --git a/src/Campaigns/Routes/RegisterCampaignRoutes.php b/src/Campaigns/Routes/RegisterCampaignRoutes.php index 1628e902db..4a366a40bc 100644 --- a/src/Campaigns/Routes/RegisterCampaignRoutes.php +++ b/src/Campaigns/Routes/RegisterCampaignRoutes.php @@ -252,6 +252,14 @@ public function getSchema(): array 'type' => 'string', 'description' => esc_html__('Campaign short description', 'give'), ], + 'primaryColor' => [ + 'type' => 'string', + 'description' => esc_html__('Primary color for the campaign', 'give'), + ], + 'secondaryColor' => [ + 'type' => 'string', + 'description' => esc_html__('Secondary color for the campaign', 'give'), + ], 'goal' => [ 'type' => 'number', 'minimum' => 1, diff --git a/src/Campaigns/ServiceProvider.php b/src/Campaigns/ServiceProvider.php index e0eb6bca40..4310126a3c 100644 --- a/src/Campaigns/ServiceProvider.php +++ b/src/Campaigns/ServiceProvider.php @@ -3,9 +3,11 @@ namespace Give\Campaigns; use Give\Campaigns\Actions\AddCampaignFormFromRequest; +use Give\Campaigns\Actions\AssociateCampaignPageWithCampaign; use Give\Campaigns\Actions\CreateDefaultCampaignForm; use Give\Campaigns\Actions\DeleteCampaignPage; use Give\Campaigns\Actions\FormInheritsCampaignGoal; +use Give\Campaigns\Actions\LoadCampaignOptions; use Give\Campaigns\Migrations\Donations\AddCampaignId as DonationsAddCampaignId; use Give\Campaigns\Migrations\MigrateFormsToCampaignForms; use Give\Campaigns\Migrations\P2P\SetCampaignType; @@ -51,6 +53,7 @@ public function boot(): void $this->registerCampaignEntity(); $this->registerCampaignBlocks(); $this->setupCampaignForms(); + $this->loadCampaignOptions(); } /** @@ -102,6 +105,7 @@ private function registerActions(): void { Hooks::addAction('givewp_campaign_deleted', DeleteCampaignPage::class); Hooks::addAction('givewp_donation_form_creating', FormInheritsCampaignGoal::class); + Hooks::addAction('givewp_campaign_page_created', AssociateCampaignPageWithCampaign::class); } /** @@ -161,4 +165,12 @@ private function registerCampaignBlocks() Hooks::addAction('rest_api_init', Actions\RegisterCampaignIdRestField::class); Hooks::addAction('init', Actions\RegisterCampaignBlocks::class); } + + /** + * @unreleased + */ + private function loadCampaignOptions() + { + Hooks::addAction('init', LoadCampaignOptions::class); + } } diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/CampaignDetailsPage.module.scss b/src/Campaigns/resources/admin/components/CampaignDetailsPage/CampaignDetailsPage.module.scss index a020eaccef..149aa9dc6f 100644 --- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/CampaignDetailsPage.module.scss +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/CampaignDetailsPage.module.scss @@ -471,6 +471,10 @@ select[name="campaignId"] { width: 1.25rem; } } + + .colorControl { + margin-top: var(--givewp-spacing-3); + } } .loadingContainer { diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/CampaignStats/index.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/CampaignStats/index.tsx index 1de9b3d5b8..101915be04 100644 --- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/CampaignStats/index.tsx +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/CampaignStats/index.tsx @@ -3,36 +3,29 @@ import {useEffect, useState} from "react"; import RevenueChart from "../RevenueChart"; import GoalProgressChart from "../GoalProgressChart"; import apiFetch from '@wordpress/api-fetch'; -import { addQueryArgs } from '@wordpress/url'; +import {addQueryArgs} from '@wordpress/url'; import HeaderText from '../HeaderText'; import HeaderSubText from '../HeaderSubText'; import DefaultFormWidget from "../DefaultForm"; -import {GiveCampaignDetails} from "@givewp/campaigns/admin/components/CampaignDetailsPage/types"; -import {useCampaignEntityRecord} from '@givewp/campaigns/utils'; +import {useCampaignEntityRecord, amountFormatter, getCampaignOptionsWindowData} from '@givewp/campaigns/utils'; import styles from "./styles.module.scss" const campaignId = new URLSearchParams(window.location.search).get('id'); -declare const window: { - GiveCampaignDetails: GiveCampaignDetails; -} & Window; +const {currency} = getCampaignOptionsWindowData(); +const currencyFormatter = amountFormatter(currency); const pluck = (array: any[], property: string) => array.map(element => element[property]) const filterOptions = [ - { label: __('Today', 'give'), value: 1, description: __('from today', 'give') }, - { label: __('Last 7 days', 'give'), value: 7, description: __('from the last 7 days', 'give') }, - { label: __('Last 30 days', 'give'), value: 30, description: __('from the last 30 days', 'give') }, - { label: __('Last 90 days', 'give'), value: 90, description: __('from the last 90 days', 'give') }, - { label: __('All-time', 'give'), value: 0, description: __('total for all-time', 'give') }, + {label: __('Today', 'give'), value: 1, description: __('from today', 'give')}, + {label: __('Last 7 days', 'give'), value: 7, description: __('from the last 7 days', 'give')}, + {label: __('Last 30 days', 'give'), value: 30, description: __('from the last 30 days', 'give')}, + {label: __('Last 90 days', 'give'), value: 90, description: __('from the last 90 days', 'give')}, + {label: __('All-time', 'give'), value: 0, description: __('total for all-time', 'give')}, ] -const currency = new Intl.NumberFormat('en-US', { - style: 'currency', - currency: 'USD', -}) - const CampaignStats = () => { const [dayRange, setDayRange] = useState(null); @@ -46,7 +39,7 @@ const CampaignStats = () => { const onDayRangeChange = async (days: number) => { setDayRange(days) - apiFetch({path: addQueryArgs( '/give-api/v2/campaigns/' + campaignId +'/statistics', {rangeInDays: days} ) } ) + apiFetch({path: addQueryArgs('/give-api/v2/campaigns/' + campaignId + '/statistics', {rangeInDays: days})}) .then(setStats); } @@ -56,10 +49,13 @@ const CampaignStats = () => { <>
- - - - + + + +
@@ -93,9 +89,12 @@ const StatWidget = ({label, values, description, formatter = null}) => {
- {formatter?.format(values[0]) ?? values[0]} + {'undefined' !== typeof values[0] + ? formatter?.format(values[0]) ?? values[0] + :   + } - {!! values[1] && ( + {!!values[1] && ( )}
@@ -150,7 +149,7 @@ const GoalProgressWidget = () => { {__('Goal progress', 'give')} {__('Show your campaign performance', 'give')} - +
) } diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/Icons/CheckIcon.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/Icons/CheckIcon.tsx new file mode 100644 index 0000000000..19b3d353ed --- /dev/null +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/Icons/CheckIcon.tsx @@ -0,0 +1,24 @@ +export default function CheckIcon({refColor}) { + return ( + + + + ); +} + +function getContrastColor(hexColor){ + hexColor = hexColor.replace(/^#/, ''); + + let r = parseInt(hexColor.substring(0, 2), 16); + let g = parseInt(hexColor.substring(2, 4), 16); + let b = parseInt(hexColor.substring(4, 6), 16); + + const luminance = (0.299 * r + 0.587 * g + 0.114 * b) / 255; + + return luminance > 0.5 ? '#000000' : '#FFFFFF'; +} diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/Icons/EditIcon.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/Icons/EditIcon.tsx new file mode 100644 index 0000000000..d254e1cd51 --- /dev/null +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/Icons/EditIcon.tsx @@ -0,0 +1,12 @@ +export default function EditIcon() { + return ( + + + + ); +} diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/index.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/index.tsx new file mode 100644 index 0000000000..4020522af3 --- /dev/null +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/index.tsx @@ -0,0 +1,100 @@ +/** + * External dependencies + */ +import classnames from 'classnames'; +import {useCallback, useState} from 'react'; +import {Controller, useFormContext} from 'react-hook-form'; + +/** + * WordPress dependencies + */ +import {ColorIndicator, ColorPalette, Popover} from '@wordpress/components'; +import {__} from '@wordpress/i18n'; + +/** + * Internal dependencies + */ +import EditIcon from './Icons/EditIcon'; +import CheckIcon from './Icons/CheckIcon'; +import './styles.scss'; + +interface ColorOption { + name: string; + slug: string; + color: string; +} + +const defaultColors: ColorOption[] = [ + {name: 'Blue', slug: 'blue', color: '#0b72d9'}, + {name: 'Green', slug: 'green', color: '#27ae60'}, + {name: 'Purple', slug: 'purple', color: '#19078c'}, + {name: 'Orange', slug: 'orange', color: '#f29718'}, + {name: 'Lavender', slug: 'lavender', color: '#9b51e0'}, + {name: 'Terracotta', slug: 'terracotta', color: '#e26f56'}, + {name: 'Red', slug: 'red', color: '#cc1818'}, +]; + +/** + * @unreleased + */ +function ColorControl({name, disabled = false, className}: { name: string; disabled?: boolean; className?: string }) { + const [popoverIsVisible, setPopoverIsVisible] = useState(false); + const {control} = useFormContext(); + + const toggleVisible = useCallback(() => { + setPopoverIsVisible((prev) => !prev); + }, []); + + return ( + ( +
+
+ + {field.value && } +
+ + {!disabled && ( +
+ + {popoverIsVisible && ( + + + + )} +
+ )} +
+ )} + /> + ); +}; + +export default ColorControl; diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/styles.scss b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/styles.scss new file mode 100644 index 0000000000..d9f49d43f2 --- /dev/null +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/ColorControl/styles.scss @@ -0,0 +1,63 @@ +.givewp-color-control { + align-items: center; + display: flex; + gap: var(--givewp-spacing-4); + + &__indicator { + align-items: center; + display: flex; + position: relative; + + span { + border-radius: 0.5rem; + border: 0; + box-shadow: none; + height: 3rem; + width: 3rem; + } + + svg { + left: 50%; + position: absolute; + top: 50%; + transform: translate(-50%, -50%); + } + } + + &__edit-button { + align-items: center; + background: none; + border-radius: 0.5rem; + border: solid 1px var(--givewp-neutral-300); + color: var(--givewp-neutral-900); + display: flex; + font-size: 1rem; + font-weight: 500; + gap: var(--givewp-spacing-2); + height: 3rem; + line-height: 1.5; + padding: var(--givewp-spacing-3) var(--givewp-spacing-6) var(--givewp-spacing-3) var(--givewp-spacing-4); + + &:hover { + background: var(--givewp-neutral-50); + cursor: pointer; + } + + &--active { + background: var(--givewp-neutral-700); + border: solid 1px var(--givewp-neutral-700); + color: var(--givewp-neutral-25); + + &:hover { + background: var(--givewp-neutral-900); + } + } + } + + &__popover-content { + .components-popover__content { + padding: var(--givewp-spacing-4); + width: auto; + } + } +} diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/DefaultForm/index.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/DefaultForm/index.tsx index 85018e707b..47325399af 100644 --- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/DefaultForm/index.tsx +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/DefaultForm/index.tsx @@ -1,8 +1,8 @@ -import {__} from "@wordpress/i18n"; +import {__} from '@wordpress/i18n'; import HeaderText from '../HeaderText'; import HeaderSubText from '../HeaderSubText'; -import styles from "./styles.module.scss" +import styles from './styles.module.scss'; /** * @unreleased @@ -15,9 +15,6 @@ const DefaultFormWidget = ({defaultForm}: {defaultForm: string}) => { {__('Default campaign form', 'give')} {__('Your campaign page and blocks will collect donations through this form by default.', 'give')}
- - {__('Edit', 'give')} -
{defaultForm} diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/GoalProgressChart/index.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/GoalProgressChart/index.tsx index 7ecd274610..cd4d479788 100644 --- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/GoalProgressChart/index.tsx +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/GoalProgressChart/index.tsx @@ -3,11 +3,10 @@ import Chart from "react-apexcharts"; import React from "react"; import styles from "./styles.module.scss" +import {getCampaignOptionsWindowData, amountFormatter} from '@givewp/campaigns/utils'; -const currency = new Intl.NumberFormat('en-US', { - style: 'currency', - currency: 'USD', -}) +const {currency} = getCampaignOptionsWindowData(); +const currencyFormatter = amountFormatter(currency); const GoalProgressChart = ({ value, goal }) => { const percentage: number = Math.abs((value / goal) * 100); @@ -49,7 +48,7 @@ const GoalProgressChart = ({ value, goal }) => { } }, colors: ['#459948'], - labels: [currency.format(value)], + labels: [currencyFormatter.format(value)], }} series={[percentage]} type="radialBar" @@ -57,7 +56,7 @@ const GoalProgressChart = ({ value, goal }) => {
{__('Goal', 'give')}
-
{currency.format(goal)}
+
{currencyFormatter.format(goal)}
{__('Amount raised', 'give')}
diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx index 9eb9d1b32c..6d45cc4134 100644 --- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx @@ -1,15 +1,15 @@ -import {__} from '@wordpress/i18n'; +import {__, sprintf} from '@wordpress/i18n'; import {useFormContext} from 'react-hook-form'; import {Currency, Editor, Upload} from '../../Inputs'; -import {GiveCampaignDetails} from '../types'; import styles from '../CampaignDetailsPage.module.scss'; import {ToggleControl} from '@wordpress/components'; import campaignPageImage from './images/campaign-page.svg'; import {WarningIcon} from '@givewp/campaigns/admin/components/Icons'; +import {getCampaignOptionsWindowData, amountFormatter} from '@givewp/campaigns/utils'; +import ColorControl from '@givewp/campaigns/admin/components/CampaignDetailsPage/Components/ColorControl'; -declare const window: { - GiveCampaignDetails: GiveCampaignDetails; -} & Window; +const {currency, isRecurringEnabled} = getCampaignOptionsWindowData(); +const currencyFormatter = amountFormatter(currency); /** * @unreleased @@ -151,7 +151,7 @@ export default () => { {/* Campaign Goal */} -
+
{__('Campaign Goal', 'give')}
@@ -168,7 +168,7 @@ export default () => { - {window.GiveCampaignDetails.isRecurringEnabled && ( + {isRecurringEnabled && ( <>
{goalType === 'amount' || goalType === 'amountFromSubscriptions' ? ( - + ) : ( )} @@ -202,6 +202,42 @@ export default () => {
+ + {/* Campaign Theme */} +
+
+
{__('Campaign Theme', 'give')}
+
+ {__('Choose a preferred theme for your campaign.', 'give')} +
+
+ +
+
+
+ {__('Select your preferred primary color', 'give')} +
+
+ {__( + 'This will affect your main cta’s like your donate button, active and focus states of other UI elements.', + 'give' + )} +
+ + +
+
+
+ {__('Select your preferred secondary color', 'give')} +
+
+ {__('This will affect your goal progress indicator, badges, icons, etc', 'give')} +
+ + +
+
+
); }; @@ -209,9 +245,9 @@ export default () => { const goalDescription = (type: string) => { switch (type) { case 'amount': - return __( - 'Your goal progress is measured by the total amount of funds raised eg. $500 of $1,000 raised.', - 'give' + return sprintf(__('Your goal progress is measured by the total amount of funds raised eg. %s of %s raised.', 'give'), + currencyFormatter.format(500), + currencyFormatter.format(1000) ); case 'donations': return __('Your goal progress is measured by the number of donations. eg. 1 of 5 donations.', 'give'); diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx index 123b6b673e..af082b7039 100644 --- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx +++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx @@ -1,10 +1,11 @@ import {__} from '@wordpress/i18n'; import {useEffect, useState} from '@wordpress/element'; import {useDispatch} from '@wordpress/data'; +import {useEntityRecord} from '@wordpress/core-data'; import apiFetch from '@wordpress/api-fetch'; import {JSONSchemaType} from 'ajv'; import {ajvResolver} from '@hookform/resolvers/ajv'; -import {GiveCampaignDetails} from './types'; +import {GiveCampaignOptions} from '@givewp/campaigns/types'; import {Campaign} from '../types'; import {FormProvider, SubmitHandler, useForm} from 'react-hook-form'; import {Spinner as GiveSpinner} from '@givewp/components'; @@ -20,7 +21,7 @@ import {useCampaignEntityRecord} from '@givewp/campaigns/utils'; import styles from './CampaignDetailsPage.module.scss'; declare const window: { - GiveCampaignDetails: GiveCampaignDetails; + GiveCampaignOptions: GiveCampaignOptions; } & Window; interface Show { @@ -28,7 +29,15 @@ interface Show { confirmationModal?: boolean; } -const StatusBadge = ({status}: {status: string}) => { +const getCampaignPageUrl = (campaignPage: { id: number; slug: string; link: string; }) => { + if (!campaignPage.slug) { + return campaignPage.link + '/' + campaignPage.id + } + return campaignPage.link + +} + +const StatusBadge = ({status}: { status: string }) => { const statusMap = { active: __('Active', 'give'), archived: __('Archived', 'give'), @@ -67,7 +76,7 @@ export default function CampaignsDetailsPage({campaignId}) { apiFetch({ path: `/give-api/v2/campaigns/${campaignId}`, method: 'OPTIONS', - }).then(({schema}: {schema: JSONSchemaType}) => { + }).then(({schema}: { schema: JSONSchemaType }) => { setResolver({ resolver: ajvResolver(schema), }); @@ -81,6 +90,10 @@ export default function CampaignsDetailsPage({campaignId}) { edit, } = useCampaignEntityRecord(campaignId); + const {record: campaignPage}: { + record: { id: number, slug: string, link: string } + } = useEntityRecord('postType', 'give_campaign_page', campaign?.pageId); + const methods = useForm({ mode: 'onBlur', ...resolver, @@ -190,7 +203,7 @@ export default function CampaignsDetailsPage({campaignId}) {
{__('Campaigns', 'give')} @@ -207,7 +220,7 @@ export default function CampaignsDetailsPage({campaignId}) { {enableCampaignPage && ( @@ -243,9 +256,9 @@ export default function CampaignsDetailsPage({campaignId}) { {!isSaving && show.contextMenu && (
- {enableCampaignPage && ( + {enableCampaignPage && campaignPage?.id && ( diff --git a/src/Campaigns/resources/admin/components/CampaignFormModal/index.tsx b/src/Campaigns/resources/admin/components/CampaignFormModal/index.tsx index a9cae398b0..5b1abe8368 100644 --- a/src/Campaigns/resources/admin/components/CampaignFormModal/index.tsx +++ b/src/Campaigns/resources/admin/components/CampaignFormModal/index.tsx @@ -1,5 +1,5 @@ import {FormProvider, SubmitHandler, useForm} from 'react-hook-form'; -import {__} from '@wordpress/i18n'; +import {sprintf, __} from '@wordpress/i18n'; import styles from './CampaignFormModal.module.scss'; import FormModal from '../FormModal'; import CampaignsApi from '../api'; @@ -20,8 +20,10 @@ import { SubscriptionsIcon, } from './GoalTypeIcons'; import {getGiveCampaignsListTableWindowData} from '../CampaignsListTable'; +import {amountFormatter} from '@givewp/campaigns/utils'; const {currency, isRecurringEnabled} = getGiveCampaignsListTableWindowData(); +const currencyFormatter = amountFormatter(currency); /** * Get the next sharp hour @@ -160,7 +162,9 @@ export default function CampaignFormModal({isOpen, handleClose, apiSettings, tit amount: { label: __('How much do you want to raise?', 'give'), description: __('Set the target amount your campaign should raise.', 'give'), - placeholder: __('eg. $2,000', 'give'), + placeholder: sprintf(__('eg. %s', 'give'), + currencyFormatter.format(2000), + ), }, donations: { label: __('How many donations do you need?', 'give'), @@ -178,7 +182,9 @@ export default function CampaignFormModal({isOpen, handleClose, apiSettings, tit 'Set the target recurring amount your campaign should raise. One-time donations do not count.', 'give' ), - placeholder: __('eg. $2,000', 'give'), + placeholder: sprintf(__('eg. %s', 'give'), + currencyFormatter.format(2000), + ), }, subscriptions: { label: __('How many recurring donations do you need?', 'give'), @@ -304,9 +310,9 @@ export default function CampaignFormModal({isOpen, handleClose, apiSettings, tit request = $request; $this->listTable = give(DonationFormsListTable::class); - $this->defaultForm = $this->request->get_param('campaignId') - ? Campaign::find((int)$this->request->get_param('campaignId'))->defaultForm()->id - : 0; + $campaignId = (int)($this->request->get_param('campaignId')); + $campaign = $campaignId ? Campaign::find($campaignId) : null; + $defaultCampaignForm = $campaign ? $campaign->defaultForm() : null; + + $this->defaultForm = $defaultCampaignForm->id ?? 0; $forms = $this->getForms(); $totalForms = $this->getTotalFormsCount(); @@ -144,8 +146,6 @@ public function handleRequest(WP_REST_Request $request): WP_REST_Response $this->listTable->items($forms, $this->request->get_param('locale') ?? ''); $items = $this->listTable->getItems(); - $defaultCampaignForm = ($campaignId = $this->request->get_param('campaignId')) ? Campaign::find($campaignId)->defaultForm() : false; - foreach ($items as $i => &$item) { $item['name'] = get_the_title($item['id']); $item['edit'] = get_edit_post_link($item['id'], 'edit'); diff --git a/wordpress-scripts-webpack.config.js b/wordpress-scripts-webpack.config.js index a7b17c7ee7..caadfa550a 100644 --- a/wordpress-scripts-webpack.config.js +++ b/wordpress-scripts-webpack.config.js @@ -66,6 +66,8 @@ module.exports = { campaignBlocks: srcPath('Campaigns/Blocks/blocks.ts'), campaignDonationsBlockApp: srcPath('Campaigns/Blocks/CampaignDonations/app.tsx'), campaignDonorsBlockApp: srcPath('Campaigns/Blocks/CampaignDonors/app.tsx'), + campaignGoalBlockApp: srcPath('Campaigns/Blocks/CampaignGoal/app.tsx'), + campaignStatsBlockApp: srcPath('Campaigns/Blocks/CampaignStats/app.tsx'), campaignPagePostTypeEditor: srcPath('Campaigns/resources/editor/campaign-page-post-type-editor.tsx'), }, };