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/Blocks/CampaignGoal/app.tsx b/src/Campaigns/Blocks/CampaignGoal/app.tsx
new file mode 100644
index 0000000000..968b9b5a6b
--- /dev/null
+++ b/src/Campaigns/Blocks/CampaignGoal/app.tsx
@@ -0,0 +1,26 @@
+import {render} from '@wordpress/element';
+import useCampaign from '../shared/hooks/useCampaign';
+import App from './app/index';
+
+const BlockApp = ({campaignId}: { campaignId: number }) => {
+ 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/blocks.ts b/src/Campaigns/Blocks/blocks.ts
index 39b7fb8ed0..ada033fe2a 100644
--- a/src/Campaigns/Blocks/blocks.ts
+++ b/src/Campaigns/Blocks/blocks.ts
@@ -11,11 +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, campaignStats];
-};
+ 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 b4152cafc2..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)();
}
diff --git a/src/Campaigns/Controllers/CampaignRequestController.php b/src/Campaigns/Controllers/CampaignRequestController.php
index 8aaea9b813..836b259a12 100644
--- a/src/Campaigns/Controllers/CampaignRequestController.php
+++ b/src/Campaigns/Controllers/CampaignRequestController.php
@@ -33,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
])
);
@@ -59,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);
diff --git a/src/Campaigns/Models/Campaign.php b/src/Campaigns/Models/Campaign.php
index 2bd08388a0..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;
@@ -176,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/ServiceProvider.php b/src/Campaigns/ServiceProvider.php
index 639f5fe571..4c627756aa 100644
--- a/src/Campaigns/ServiceProvider.php
+++ b/src/Campaigns/ServiceProvider.php
@@ -7,6 +7,7 @@
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;
@@ -50,6 +51,7 @@ public function boot(): void
$this->registerCampaignEntity();
$this->registerCampaignBlocks();
$this->setupCampaignForms();
+ $this->loadCampaignOptions();
}
/**
@@ -160,4 +162,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/common/getCampaignDetailsWindowData.ts b/src/Campaigns/resources/admin/common/getCampaignDetailsWindowData.ts
deleted file mode 100644
index 6de3dc4aed..0000000000
--- a/src/Campaigns/resources/admin/common/getCampaignDetailsWindowData.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import type {GiveCampaignDetails} from '@givewp/campaigns/admin/components/CampaignDetailsPage/types';
-
-declare const window: {
- GiveCampaignDetails: GiveCampaignDetails;
-} & Window;
-
-export default function getCampaignDetailsWindowData(): GiveCampaignDetails {
- return window.GiveCampaignDetails;
-}
diff --git a/src/Campaigns/resources/admin/common/index.ts b/src/Campaigns/resources/admin/common/index.ts
deleted file mode 100644
index 6e1ea7e07f..0000000000
--- a/src/Campaigns/resources/admin/common/index.ts
+++ /dev/null
@@ -1 +0,0 @@
-export {default as getCampaignDetailsWindowData} from './getCampaignDetailsWindowData';
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 754c192621..101915be04 100644
--- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/CampaignStats/index.tsx
+++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Components/CampaignStats/index.tsx
@@ -7,14 +7,13 @@ import {addQueryArgs} from '@wordpress/url';
import HeaderText from '../HeaderText';
import HeaderSubText from '../HeaderSubText';
import DefaultFormWidget from "../DefaultForm";
-import {useCampaignEntityRecord, amountFormatter} from '@givewp/campaigns/utils';
-import {getCampaignDetailsWindowData} from '@givewp/campaigns/admin/common';
+import {useCampaignEntityRecord, amountFormatter, getCampaignOptionsWindowData} from '@givewp/campaigns/utils';
import styles from "./styles.module.scss"
const campaignId = new URLSearchParams(window.location.search).get('id');
-const {currency} = getCampaignDetailsWindowData();
+const {currency} = getCampaignOptionsWindowData();
const currencyFormatter = amountFormatter(currency);
const pluck = (array: any[], property: string) => array.map(element => element[property])
@@ -150,7 +149,7 @@ const GoalProgressWidget = () => {
{__('Goal progress', 'give')}
{__('Show your campaign performance', 'give')}
-
+
)
}
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 cbe04bba06..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,10 +3,9 @@ import Chart from "react-apexcharts";
import React from "react";
import styles from "./styles.module.scss"
-import {getCampaignDetailsWindowData} from '@givewp/campaigns/admin/common';
-import {amountFormatter} from '@givewp/campaigns/utils';
+import {getCampaignOptionsWindowData, amountFormatter} from '@givewp/campaigns/utils';
-const {currency} = getCampaignDetailsWindowData();
+const {currency} = getCampaignOptionsWindowData();
const currencyFormatter = amountFormatter(currency);
const GoalProgressChart = ({ value, goal }) => {
diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx
index 0b5fc130a2..6d45cc4134 100644
--- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx
+++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/Tabs/Settings.tsx
@@ -5,11 +5,10 @@ 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 {getCampaignDetailsWindowData} from '@givewp/campaigns/admin/common';
-import {amountFormatter} from '@givewp/campaigns/utils';
+import {getCampaignOptionsWindowData, amountFormatter} from '@givewp/campaigns/utils';
import ColorControl from '@givewp/campaigns/admin/components/CampaignDetailsPage/Components/ColorControl';
-const {currency, isRecurringEnabled} = getCampaignDetailsWindowData();
+const {currency, isRecurringEnabled} = getCampaignOptionsWindowData();
const currencyFormatter = amountFormatter(currency);
/**
@@ -152,7 +151,7 @@ export default () => {
{/* Campaign Goal */}
-
+
{__('Campaign Goal', 'give')}
diff --git a/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx b/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx
index 6806ea2c63..af082b7039 100644
--- a/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx
+++ b/src/Campaigns/resources/admin/components/CampaignDetailsPage/index.tsx
@@ -5,7 +5,7 @@ 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';
@@ -21,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 {
@@ -203,7 +203,7 @@ export default function CampaignsDetailsPage({campaignId}) {