PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.15.3
GiveWP – Donation Plugin and Fundraising Platform v4.15.3
4.16.3 4.16.2 4.16.1 4.16.0 4.15.5 4.15.4 4.15.3 4.15.2 4.15.1 4.15.0 2.3.0 2.3.1 2.3.2 2.30.0 2.31.0 2.31.1 2.32.0 2.33.0 2.33.1 2.33.2 2.33.3 2.33.4 2.33.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.5.0 2.5.1 2.5.10 2.5.11 2.5.12 2.5.13 2.5.2 2.5.3 2.5.4 2.5.5 2.5.6 2.5.7 2.5.8 2.5.9 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.8.0 2.8.1 2.9.0 2.9.1 2.9.2 2.9.3 2.9.4 2.9.5 2.9.6 2.9.7 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.1.0 3.1.1 3.1.2 3.10.0 3.11.0 3.12.0 3.12.1 3.12.2 3.12.3 3.13.0 3.14.0 3.14.1 3.14.2 3.15.0 3.15.1 3.16.0 3.16.1 3.16.2 3.16.3 3.16.4 3.16.5 3.17.0 3.17.1 3.17.2 3.18.0 3.19.0 3.19.1 3.19.2 3.19.3 3.19.4 3.2.0 3.2.1 3.2.2 3.20.0 3.21.0 3.21.1 3.22.0 3.22.1 3.22.2 3.3.0 3.3.1 3.4.0 3.4.1 3.4.2 3.5.0 3.5.1 3.6.0 3.6.1 3.6.2 3.7.0 3.8.0 3.9.0 4.0.0 4.1.0 4.1.1 4.10.0 4.10.1 4.11.0 4.12.0 4.13.0 4.13.1 4.13.2 4.14.0 4.14.1 4.14.2 4.14.3 4.14.4 4.14.5 4.14.6 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.1 4.7.0 4.7.1 4.8.0 4.8.1 4.9.0 trunk 1.9.0 2.0.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.1.0 2.1.1 2.1.2 2.1.3 2.1.4 2.1.5 2.1.6 2.1.7 2.1.8 2.10.0 2.10.1 2.10.2 2.10.3 2.10.4 2.11.0 2.11.1 2.11.2 2.11.3 2.12.0 2.12.1 2.12.2 2.12.3 2.13.0 2.13.1 2.13.2 2.13.3 2.13.4 2.14.0 2.15.0 2.16.0 2.16.1 2.17.0 2.17.1 2.17.3 2.18.0 2.18.1 2.19.1 2.19.2 2.19.3 2.19.4 2.19.5 2.19.6 2.19.7 2.19.8 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.20.0 2.20.1 2.20.2 2.21.0 2.21.1 2.21.2 2.21.3 2.21.4 2.22.0 2.22.1 2.22.2 2.22.3 2.23.0 2.23.1 2.23.2 2.24.0 2.24.1 2.24.2 2.25.0 2.25.1 2.25.2 2.25.3 2.26.0 2.27.0 2.27.1 2.27.2 2.27.3 2.28.0 2.29.0 2.29.1 2.29.2
give / src / Donations / resources / components / DonationsListTable.tsx
give / src / Donations / resources / components Last commit date
DonationRowActions.tsx 8 months ago DonationsListTable.tsx 8 months ago ListTable.module.scss 9 months ago
DonationsListTable.tsx
367 lines
1 import {__} from '@wordpress/i18n';
2 import {ListTablePage} from '@givewp/components';
3 import {DonationRowActions} from './DonationRowActions';
4 import ListTableApi from '@givewp/components/ListTable/api';
5 import tableStyles from '@givewp/components/ListTable/ListTablePage/ListTablePage.module.scss';
6 import styles from './ListTable.module.scss';
7 import {IdBadge} from '@givewp/components/ListTable/TableCell';
8 import {BulkActionsConfig, FilterConfig} from '@givewp/components/ListTable/ListTablePage';
9 import {Interweave} from 'interweave';
10 import BlankSlate from '@givewp/components/ListTable/BlankSlate';
11 import ProductRecommendations from '@givewp/components/ListTable/ProductRecommendations';
12 import {RecommendedProductData} from '@givewp/promotions/hooks/useRecommendations';
13 import { StatConfig } from '@givewp/components/ListTable/ListTableStats/ListTableStats';
14 import filterByOptions from '../constants/filterByOptions';
15
16 declare global {
17 interface Window {
18 GiveDonations: {
19 apiNonce: string;
20 apiRoot: string;
21 adminUrl: string;
22 campaigns: Array<{value: string; text: string}>;
23 table: {columns: Array<object>};
24 paymentMode: boolean;
25 manualDonations: boolean;
26 recurringDonationsEnabled: boolean;
27 pluginUrl: string;
28 dismissedRecommendations: Array<string>;
29 addonsBulkActions: Array<BulkActionsConfig>;
30 donationStatuses: {[statusCode: string]: string};
31 };
32 }
33 }
34
35 const API = new ListTableApi(window.GiveDonations);
36
37 const filters: Array<FilterConfig> = [
38 {
39 name: 'campaignId',
40 type: 'campaignselect',
41 text: __('Select Campaign', 'give'),
42 ariaLabel: __('filter donations by campaign', 'give'),
43 },
44 {
45 name: 'search',
46 type: 'search',
47 inlineSize: '14rem',
48 text: __('Name, Email, or Donation ID', 'give'),
49 ariaLabel: __('search donations', 'give'),
50 },
51 {
52 name: 'toggle',
53 type: 'checkbox',
54 text: __('Test', 'give'),
55 ariaLabel: __('View Test Donations', 'give'),
56 },
57 {
58 name: 'donor',
59 type: 'hidden',
60 },
61 {
62 name: 'filterBy',
63 type: 'filterby',
64 groupedOptions: filterByOptions,
65 },
66 ];
67
68 const bulkActions: Array<BulkActionsConfig> = [
69 {
70 label: __('Delete', 'give'),
71 value: 'delete',
72 type: 'danger',
73 isVisible: (data, parameters) => parameters?.status?.includes('trash'),
74 action: async (selected) => {
75 const response = await API.fetchWithArgs('/delete', {ids: selected.join(',')}, 'DELETE');
76 return response;
77 },
78 confirm: (selected, names) => (
79 <>
80 <p>{__('Really delete the following donations?', 'give')}</p>
81 <ul role="document" tabIndex={0}>
82 {selected.map((donationId, index) => (
83 <li key={donationId}>
84 <IdBadge id={donationId} />{' '}
85 <span>
86 {__('from ', 'give')} <Interweave content={names[index]} />
87 </span>
88 </li>
89 ))}
90 </ul>
91 </>
92 ),
93 },
94 {
95 label: __('Trash', 'give'),
96 value: 'trash',
97 type: 'warning',
98 isVisible: (data, parameters) => !parameters?.status?.includes('trash'),
99 action: async (selected) => {
100 const response = await API.fetchWithArgs('/trash', {ids: selected.join(',')}, 'DELETE');
101 return response;
102 },
103 confirm: (selected, names) => (
104 <>
105 <p>{__('Are you sure you want add to trash the following donations?', 'give')}</p>
106 <ul role="document" tabIndex={0}>
107 {selected.map((donationId, index) => (
108 <li key={donationId}>
109 <IdBadge id={donationId} />{' '}
110 <span>
111 {__('from ', 'give')} <Interweave content={names[index]} />
112 </span>
113 </li>
114 ))}
115 </ul>
116 </>
117 ),
118 },
119 {
120 label: __('Restore', 'give'),
121 value: 'restore',
122 type: 'normal',
123 isVisible: (data, parameters) => parameters?.status?.includes('trash'),
124 action: async (selected) => {
125 const response = await API.fetchWithArgs('/untrash', {ids: selected.join(',')}, 'POST');
126 return response;
127 },
128 confirm: (selected, names) => (
129 <>
130 <p>{__('Are you sure you want remove from trash the following donations?', 'give')}</p>
131 <ul role="document" tabIndex={0}>
132 {selected.map((donationId, index) => (
133 <li key={donationId}>
134 <IdBadge id={donationId} />{' '}
135 <span>
136 {__('from ', 'give')} <Interweave content={names[index]} />
137 </span>
138 </li>
139 ))}
140 </ul>
141 </>
142 ),
143 },
144 ...(() => {
145 const donationStatuses = {
146 publish: __('Set To Completed', 'give'),
147 pending: __('Set To Pending', 'give'),
148 processing: __('Set To Processing', 'give'),
149 refunded: __('Set To Refunded', 'give'),
150 revoked: __('Set To Revoked', 'give'),
151 failed: __('Set To Failed', 'give'),
152 cancelled: __('Set To Cancelled', 'give'),
153 abandoned: __('Set To Abandoned', 'give'),
154 preapproval: __('Set To Preapproval', 'give'),
155 };
156
157 return Object.entries(donationStatuses).map(([value, label]) => {
158 return {
159 label,
160 value,
161 isVisible: (data, parameters) => !parameters?.status?.includes('trash'),
162 action: async (selected) =>
163 await API.fetchWithArgs(
164 '/setStatus',
165 {
166 ids: selected.join(','),
167 status: value,
168 },
169 'POST'
170 ),
171 confirm: (selected, names) => (
172 <>
173 <p>{__('Set status for the following donations?', 'give')}</p>
174 <ul role="document" tabIndex={0}>
175 {selected.map((donationId, index) => (
176 <li key={donationId}>
177 <IdBadge id={donationId} /> <span>{__('from', 'give')}</span>
178 <Interweave content={names[index]} />
179 </li>
180 ))}
181 </ul>
182 </>
183 ),
184 };
185 });
186 })(),
187 {
188 label: __('Resend Email Receipts', 'give'),
189 value: 'resendEmailReceipt',
190 action: async (selected) => await API.fetchWithArgs('/resendEmailReceipt', {ids: selected.join(',')}, 'POST'),
191 confirm: (selected, names) => (
192 <>
193 <p>{__('Resend Email Receipts for following donations?', 'give')}</p>
194 <ul role="document" tabIndex={0}>
195 {selected.map((donationId, index) => (
196 <li key={donationId}>
197 <IdBadge id={donationId} /> <span>{__('from', 'give')}</span>
198 <Interweave content={names[index]} />
199 </li>
200 ))}
201 </ul>
202 </>
203 ),
204 },
205 ];
206
207 /**
208 * Displays a blank slate for the Donations table.
209 * @since 2.27.0
210 */
211 const ListTableBlankSlate = (
212 <BlankSlate
213 imagePath={`${window.GiveDonations.pluginUrl}build/assets/dist/images/list-table/blank-slate-donations-icon.svg`}
214 description={__('No donations found', 'give')}
215 href={'https://docs.givewp.com/donations'}
216 linkText={__('GiveWP Donations.', 'give')}
217 />
218 );
219
220 interface DonationTableRecommendations {
221 recurring: RecommendedProductData;
222 feeRecovery: RecommendedProductData;
223 designatedFunds: RecommendedProductData;
224 }
225
226 /**
227 * @since 2.27.1
228 */
229 const RecommendationConfig: DonationTableRecommendations = {
230 recurring: {
231 enum: 'givewp_donations_recurring_recommendation_dismissed',
232 documentationPage: 'https://docs.givewp.com/recurring-donations-list',
233 message: __('Increase your fundraising revenue by over 30% with recurring giving campaigns.', 'give'),
234 innerHtml: __('Get More Donations', 'give'),
235 },
236 feeRecovery: {
237 enum: 'givewp_donations_fee_recovery_recommendation_dismissed',
238 documentationPage: 'https://docs.givewp.com/feerecovery-donations-list',
239 message: __(
240 'Raise more money per donation by providing donors with the option to help cover the credit card processing fees.',
241 'give'
242 ),
243 innerHtml: __('Get Fee Recovery', 'give'),
244 },
245 designatedFunds: {
246 enum: 'givewp_donations_designated_funds_recommendation_dismissed',
247 documentationPage: 'https://docs.givewp.com/funds-donations-list',
248 message: __(
249 'Elevate your fundraising campaigns with unlimited donation fund designations, and tailored fundraising reports.',
250 'give'
251 ),
252 innerHtml: __('Start creating designated funds', 'give'),
253 },
254 };
255
256 const rotatingRecommendation = (
257 <ProductRecommendations
258 options={[
259 RecommendationConfig.recurring,
260 RecommendationConfig.feeRecovery,
261 RecommendationConfig.designatedFunds,
262 ]}
263 apiSettings={window.GiveDonations}
264 />
265 );
266
267 /**
268 * Configuration for the statistic tiles rendered above the ListTable.
269 *
270 * IMPORTANT: Object keys MUST MATCH the keys returned by the API's `stats` payload.
271 * For example, if the API returns:
272 *
273 * data.stats = {
274 * donationsCount: number;
275 * oneTimeDonationsCount: number;
276 * recurringDonationsCount: number;
277 * }
278 *
279 * then this config must use those same keys: "donationsCount", "oneTimeDonationsCount", "recurringDonationsCount".
280 * Missing or mismatched keys will result in empty/undefined values in the UI.
281 *
282 * @since 4.10.0
283 */
284 const statsConfig: Record<string, StatConfig> = {
285 donationsCount: { label: __('Total Donations', 'give')},
286 oneTimeDonationsCount: { label: __('One-Time Donations', 'give')},
287 recurringDonationsCount: {
288 label: __('Recurring Donations', 'give'),
289 upgrade: !window.GiveDonations.recurringDonationsEnabled && {
290 href: 'https://docs.givewp.com/recurring-stat',
291 tooltip: __('Increase your fundraising revenue by over 30% with recurring giving campaigns.', 'give')
292 }
293 },
294 };
295
296 /**
297 * @since 4.10.0 Update button class names and add aria attributes.
298 * @since 2.24.0
299 */
300 export default function DonationsListTable() {
301 return (
302 <ListTablePage
303 title={__('Donations', 'give')}
304 singleName={__('donation', 'give')}
305 pluralName={__('donations', 'give')}
306 rowActions={DonationRowActions}
307 bulkActions={bulkActions}
308 apiSettings={window.GiveDonations}
309 filterSettings={filters}
310 paymentMode={!!window.GiveDonations.paymentMode}
311 listTableBlankSlate={ListTableBlankSlate}
312 productRecommendation={rotatingRecommendation}
313 statsConfig={statsConfig}
314 >
315 <button
316 className={`button button-tertiary ${tableStyles.secondaryActionButton}`}
317 onClick={showLegacyDonations}
318 aria-label={__('Switch to the legacy donations table view', 'give')}
319 >
320 {__('Switch to Legacy View', 'give')}
321 </button>
322 {window.GiveDonations.manualDonations ? (
323 <a
324 className={`button button-tertiary ${tableStyles.secondaryActionButton}`}
325 href={`${window.GiveDonations.adminUrl}edit.php?post_type=give_forms&page=give-manual-donation`}
326 aria-label={__('Create a new donation record', 'give')}
327 >
328 {__('New donation', 'give')}
329 </a>
330 ) : (
331 <a
332 className={`button button-tertiary ${tableStyles.secondaryActionButton} ${styles.manualDonationsNotice}`}
333 href={'https://docs.givewp.com/enterdonation'}
334 target={'_blank'}
335 aria-label={__('Learn about Manual Donations add-on (opens in new tab)', 'give')}
336 aria-describedby="manual-donations-tooltip"
337 >
338 <span className={styles.manualDonationsAddOn}>{__('ADD-ON', 'give')}</span>
339 {__('Enter donations', 'give')}
340 <span id="manual-donations-tooltip" className={styles.manualDonationsMessage}>
341 <img
342 src={`${window.GiveDonations.pluginUrl}build/assets/dist/images/admin/triangle-tip.svg`}
343 alt={__('Information', 'give')}
344 />{' '}
345 {__(
346 'Need to add in a record for a donation received elsewhere, or reconcile with the payment gateway? Add donation records with the Manual Donations add-on!',
347 'give'
348 )}
349 </span>
350 </a>
351 )}
352 <a
353 className={`button button-primary ${tableStyles.primaryActionButton}`}
354 href={` ${window.GiveDonations.adminUrl}edit.php?post_type=give_forms&page=give-tools&tab=import&importer-type=import_donations`}
355 aria-label={__('Import donations from external source', 'give')}
356 >
357 {__('Import donations', 'give')}
358 </a>
359 </ListTablePage>
360 );
361 }
362
363 const showLegacyDonations = async (event) => {
364 await API.fetchWithArgs('/view', {isLegacy: 1});
365 window.location.reload();
366 };
367