PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.15.0
GiveWP – Donation Plugin and Fundraising Platform v4.15.0
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 / DonationForms / V2 / resources / components / DonationFormsListTable.tsx
give / src / DonationForms / V2 / resources / components Last commit date
AddCampaignFormModal 1 year ago Migration 1 year ago Onboarding 4 months ago DonationFormsListTable.tsx 4 months ago DonationFormsRowActions.tsx 1 year ago
DonationFormsListTable.tsx
392 lines
1 import {useState} from 'react';
2 import {__} from '@wordpress/i18n';
3 import {ListTableApi, ListTablePage} from '@givewp/components';
4 import {DonationFormsRowActions} from './DonationFormsRowActions';
5 import Onboarding, {OnboardingContext, OnboardingStateProps} from './Onboarding';
6 import styles from '@givewp/components/ListTable/ListTablePage/ListTablePage.module.scss';
7 import {BulkActionsConfig, ColumnFilterConfig, FilterConfig} from '@givewp/components/ListTable/ListTablePage';
8 import Select from '@givewp/components/ListTable/Select';
9 import {Interweave} from 'interweave';
10 import InterweaveSSR from '@givewp/components/ListTable/InterweaveSSR';
11 import BlankSlate from '@givewp/components/ListTable/BlankSlate';
12 import {CubeIcon} from '@givewp/components/AdminUI/Icons';
13 import AddCampaignFormModal from './AddCampaignFormModal';
14 import DefaultFormNotice from '@givewp/campaigns/admin/components/CampaignDetailsPage/Components/Notices/DefaultFormNotice';
15 import apiFetch from '@wordpress/api-fetch';
16 import {CampaignEntity} from '@givewp/campaigns/admin/components/types';
17
18 declare global {
19 interface Window {
20 GiveDonationForms: {
21 apiNonce: string;
22 bannerActionUrl: string;
23 tooltipActionUrl: string;
24 migrationApiRoot: string;
25 defaultFormActionUrl: string;
26 apiRoot: string;
27 authors: Array<{id: string | number; name: string}>;
28 table: {columns: Array<object>};
29 pluginUrl: string;
30 showUpgradedTooltip: boolean;
31 isMigrated: boolean;
32 migratedFormUrl: string;
33 supportedAddons: Array<string>;
34 supportedGateways: Array<string>;
35 isOptionBasedFormEditorEnabled: boolean;
36 locale: string;
37 showDefaultFormTooltip: boolean;
38 campaignUrl: string;
39 };
40
41 GiveNextGen?: {
42 newFormUrl: string;
43 };
44 }
45 }
46
47 const API = new ListTableApi(window.GiveDonationForms);
48
49 const donationStatus = [
50 {
51 value: 'any',
52 text: __('All Status', 'give'),
53 },
54 {
55 value: 'publish',
56 text: __('Published', 'give'),
57 },
58 {
59 value: 'pending',
60 text: __('Pending', 'give'),
61 },
62 {
63 value: 'draft',
64 text: __('Draft', 'give'),
65 },
66 {
67 value: 'trash',
68 text: __('Trash', 'give'),
69 },
70 {
71 value: 'upgraded',
72 text: __('Upgraded', 'give'),
73 },
74 ];
75
76 const urlParams = new URLSearchParams(window.location.search);
77
78 const isCampaignDetailsPage = urlParams.get('id') && 'give-campaigns' === urlParams.get('page');
79 const campaignId = urlParams.get('id');
80
81 const donationFormsFilters: Array<FilterConfig> = [
82 {
83 name: 'status',
84 type: 'select',
85 text: __('status', 'give'),
86 ariaLabel: __('Filter donation forms by status', 'give'),
87 options: donationStatus,
88 },
89 {
90 name: 'search',
91 type: 'search',
92 text: __('Search by name or ID', 'give'),
93 ariaLabel: __('Search donation forms', 'give'),
94 },
95 ];
96
97 if (isCampaignDetailsPage) {
98 donationFormsFilters.push({
99 name: 'campaignId',
100 type: 'hidden',
101 options: [
102 {
103 value: campaignId,
104 text: __('All Campaign Forms', 'give'),
105 },
106 ],
107 });
108 }
109
110 /**
111 * @since 4.6.0 add option based form editor enabled check.
112 */
113 const columnFilters: Array<ColumnFilterConfig> = [
114 {
115 column: 'title',
116 filter: (item) => {
117 const CubeTooltip = () => (
118 <div className={styles.tooltipContainer}>
119 <CubeIcon />
120 <div className={styles.tooltip}>{__('Uses the Visual Form Builder', 'give')}</div>
121 </div>
122 );
123
124 return (
125 <>
126 <div className={styles.titleContainer}>
127 <div className={styles.migratedForm}>
128 {item?.v3form && window?.GiveDonationForms?.isOptionBasedFormEditorEnabled && <CubeTooltip />}
129 <Interweave attributes={{className: 'interweave'}} content={item?.title} />
130 </div>
131 {item?.isDefaultCampaignForm && (
132 <div className={`${styles.defaultFormPill} givewp-default-form-pill`}>{__('Default')}</div>
133 )}
134 </div>
135 </>
136 );
137 },
138 },
139 {
140 column: 'status',
141 filter: (item, column) => {
142 if (window.GiveDonationForms.showUpgradedTooltip && item?.status_raw === 'upgraded') {
143 return (
144 <div className={styles.upgradedForm}>
145 <div className={styles.tooltipContainer}>
146 <div className={styles.tooltip}>
147 {__(
148 'The name of this form is already associated with an upgraded form. You can safely delete this form',
149 'give'
150 )}
151 .
152 <div
153 className={styles.link}
154 onClick={(e) => {
155 e.currentTarget.parentElement.remove();
156 fetch(window.GiveDonationForms.tooltipActionUrl, {method: 'POST'});
157 }}
158 >
159 {__('Got it', 'give')}
160 </div>
161 </div>
162 </div>
163 <InterweaveSSR column={column} item={item} />
164 </div>
165 );
166 }
167
168 return <InterweaveSSR column={column} item={item} />;
169 },
170 },
171 ];
172
173 const donationFormsBulkActions: Array<BulkActionsConfig> = [
174 {
175 label: __('Edit', 'give'),
176 value: 'edit',
177 action: async (selected) => {
178 const authorSelect = document.getElementById('giveDonationFormsTableSetAuthor') as HTMLSelectElement;
179 const author = authorSelect.value;
180 const statusSelect = document.getElementById('giveDonationFormsTableSetStatus') as HTMLSelectElement;
181 const status = statusSelect.value;
182 if (!(author || status)) {
183 return {errors: [], successes: []};
184 }
185 const editParams = {
186 ids: selected.join(','),
187 author,
188 status,
189 };
190 return await API.fetchWithArgs('/edit', editParams, 'UPDATE');
191 },
192 confirm: (selected, names) => (
193 <>
194 <p>{__(' Below are the donation forms to be edited.', 'give')}</p>
195 <ul role="document" tabIndex={0}>
196 {selected.map((id, index) => (
197 <li key={id}>
198 <Interweave content={names[index]} />
199 </li>
200 ))}
201 </ul>
202 <div className={styles.flexRow}>
203 <label htmlFor="giveDonationFormsTableSetAuthor">{__('Set form author', 'give')}</label>
204 <Select id="giveDonationFormsTableSetAuthor" style={{paddingInlineEnd: '2rem'}}>
205 <option value="">{__('Keep current author', 'give')}</option>
206 {window.GiveDonationForms.authors.map((author) => (
207 <option key={author.id} value={author.id}>
208 {author.name}
209 </option>
210 ))}
211 </Select>
212 </div>
213 <div className={styles.flexRow}>
214 <label htmlFor="giveDonationFormsTableSetStatus">{__('Set form status', 'give')}</label>
215 <Select id="giveDonationFormsTableSetStatus" style={{paddingInlineEnd: '2rem'}}>
216 <option value="">{__('Keep current status')}</option>
217 <option value="publish">{__('Published', 'give')}</option>
218 <option value="private">{__('Private', 'give')}</option>
219 <option value="pending">{__('Pending Review', 'give')}</option>
220 <option value="draft">{__('Draft', 'give')}</option>
221 </Select>
222 </div>
223 </>
224 ),
225 },
226 {
227 label: __('Delete', 'give'),
228 value: 'delete',
229 type: 'danger',
230 isVisible: (data, parameters) => parameters.status === 'trash' || !data?.trash,
231 action: async (selected) => {
232 try {
233 return await API.fetchWithArgs('/delete', {ids: selected.join(',')}, 'DELETE');
234 } catch (error) {
235 const errorData = JSON.parse(error.message.replace('Error: ', ''));
236 alert(errorData.message);
237 }
238 },
239 confirm: (selected, names) => (
240 <div>
241 <p>{__('Really delete the following donation forms?', 'give')}</p>
242 <ul role="document" tabIndex={0}>
243 {selected.map((id, index) => (
244 <li key={id}>
245 <Interweave content={names[index]} />
246 </li>
247 ))}
248 </ul>
249 </div>
250 ),
251 },
252 {
253 label: __('Trash', 'give'),
254 value: 'trash',
255 type: 'danger',
256 isVisible: (data, parameters) => parameters.status !== 'trash' && data?.trash,
257 isIdSelectable: (id, data) => !(typeof data?.defaultForm === 'number') || data.defaultForm !== Number(id),
258 action: async (selected) => {
259 try {
260 return await API.fetchWithArgs('/trash', {ids: selected.join(',')}, 'DELETE');
261 } catch (error) {
262 const errorData = JSON.parse(error.message.replace('Error: ', ''));
263 alert(errorData.message);
264 }
265 },
266 confirm: (selected, names) => (
267 <div>
268 <p>{__('Are you sure you want to trash the following donation forms?', 'give')}</p>
269 <ul role="document" tabIndex={0}>
270 {selected.map((id, index) => (
271 <li key={id}>
272 <Interweave content={names[index]} />
273 </li>
274 ))}
275 </ul>
276 </div>
277 ),
278 },
279 ];
280
281 /**
282 * Displays a blank slate for the Forms table.
283 * @since 2.27.0
284 */
285 const ListTableBlankSlate = (
286 <BlankSlate
287 imagePath={`${window.GiveDonationForms.pluginUrl}build/assets/dist/images/list-table/blank-slate-donation-forms-icon.svg`}
288 description={__('No donation forms', 'give')}
289 href={'https://docs.givewp.com/forms'}
290 linkText={__('GiveWP Forms', 'give')}
291 />
292 );
293
294 export default function DonationFormsListTable({entity}: {entity?: CampaignEntity}) {
295 const [state, setState] = useState<OnboardingStateProps>({
296 showFeatureNoticeDialog: false,
297 showDefaultFormTooltip: window.GiveDonationForms.showDefaultFormTooltip,
298 });
299
300 const handleDefaultFormTooltipDismiss = () => {
301 apiFetch({
302 url: window.GiveDonationForms.defaultFormActionUrl,
303 method: 'POST',
304 }).then(() => {
305 setState((prevState) => {
306 return {
307 ...prevState,
308 showDefaultFormTooltip: false,
309 };
310 });
311 });
312 };
313
314 const [isOpen, setOpen] = useState<boolean>(false);
315 const openModal = () => setOpen(true);
316 const closeModal = () => setOpen(false);
317
318 return (
319 <OnboardingContext.Provider value={[state, setState]}>
320 <ListTablePage
321 title={__('Donation Forms', 'give')}
322 singleName={__('donation form', 'give')}
323 pluralName={__('donation forms', 'give')}
324 rowActions={({data, item, removeRow, addRow, setUpdateErrors, parameters}) => {
325 return DonationFormsRowActions({
326 data,
327 item,
328 removeRow,
329 addRow,
330 setUpdateErrors,
331 parameters,
332 entity,
333 });
334 }}
335 bulkActions={donationFormsBulkActions}
336 apiSettings={window.GiveDonationForms}
337 filterSettings={donationFormsFilters}
338 listTableBlankSlate={ListTableBlankSlate}
339 columnFilters={columnFilters}
340 banner={Onboarding}
341 contentMode={isCampaignDetailsPage}
342 >
343 {isCampaignDetailsPage ? (
344 <div className={`${styles.flexRow} ${styles.justifyContentEnd}`}>
345 {window.GiveDonationForms.isOptionBasedFormEditorEnabled ? (
346 <>
347 <button className={styles.addCampaignFormButton} onClick={openModal}>
348 {__('Add campaign form', 'give')}
349 </button>
350 <AddCampaignFormModal
351 isOpen={isOpen}
352 handleClose={closeModal}
353 title={__('Choose how you want to edit your campaign form', 'give')}
354 campaignId={campaignId}
355 />
356 </>
357 ) : (
358 <>
359 <a
360 href={`edit.php?post_type=give_forms&page=givewp-form-builder&donationFormID=new&locale=${window.GiveDonationForms.locale}&campaignId=${campaignId}`}
361 className={styles.addCampaignFormButton}
362 >
363 {__('Add campaign form', 'give')}
364 </a>
365 </>
366 )}
367 </div>
368 ) : (
369 <>
370 {window.GiveDonationForms.isOptionBasedFormEditorEnabled && (
371 <button
372 className={`button button-secondary ${styles.button} ${styles.buttonSecondary}`}
373 onClick={showLegacyDonationForms}
374 >
375 {__('Switch to Legacy View', 'give')}
376 </button>
377 )}
378 </>
379 )}
380 {state.showDefaultFormTooltip && isCampaignDetailsPage && (
381 <DefaultFormNotice handleClick={handleDefaultFormTooltipDismiss} />
382 )}
383 </ListTablePage>
384 </OnboardingContext.Provider>
385 );
386 }
387
388 const showLegacyDonationForms = async (event) => {
389 await API.fetchWithArgs('/view', {isLegacy: 1});
390 window.location.href = '/wp-admin/edit.php?post_type=give_forms';
391 };
392