PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / 4.5.0
GiveWP – Donation Plugin and Fundraising Platform v4.5.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 / MigrationLog / Admin / Migrations / index.js
give / src / MigrationLog / Admin / Migrations Last commit date
api.js 4 years ago index.js 1 year ago styles.module.scss 4 years ago
index.js
417 lines
1 import {useState} from 'react';
2 import classNames from 'classnames';
3 import {Button, Card, Label, Modal, Notice, Pagination, Spinner, Table} from '@givewp/components';
4 import API, {getEndpoint, useMigrationFetcher} from './api';
5
6 import styles from './styles.module.scss';
7
8 import {__} from '@wordpress/i18n';
9
10 const Migrations = () => {
11 const [state, setState] = useState({
12 initialLoad: false,
13 currentPage: 1,
14 sortColumn: 'run_order',
15 sortDirection: 'asc',
16 pages: 0,
17 showOptions: false,
18 isSorting: false,
19 });
20
21 const [migrationModal, setMigrationModal] = useState({
22 visible: false,
23 });
24
25 const [migrationRunModal, setMigrationRunModal] = useState({
26 visible: false,
27 });
28
29 const parameters = {
30 page: state.currentPage,
31 sort: state.sortColumn,
32 direction: state.sortDirection,
33 };
34
35 const {data, isLoading, isError, mutate} = useMigrationFetcher(getEndpoint('/get-migrations', parameters), {
36 onSuccess: ({response}) => {
37 setState((previousState) => {
38 return {
39 ...previousState,
40 initialLoad: true,
41 pages: response.pages,
42 currentPage: state.currentPage > response.pages ? 1 : state.currentPage,
43 showOptions: response.showOptions,
44 isSorting: false,
45 };
46 });
47 },
48 });
49
50 const runMigration = () => {
51 setMigrationRunModal((previousState) => {
52 return {
53 ...previousState,
54 visible: true,
55 running: true,
56 };
57 });
58
59 let url;
60
61 if (migrationRunModal.action === 'run' && migrationRunModal.isBatchMigration) {
62 url = migrationRunModal.status === 'incomplete'
63 ? '/reschedule-failed-actions'
64 : '/run-batch-migration';
65 } else if (migrationRunModal.action === 'rollback') {
66 url = '/rollback-migration';
67 } else {
68 url = '/run-migration';
69 }
70
71 API.post(url, {id: migrationRunModal.id})
72 .then((response) => {
73 if (response.data.status) {
74 closeMigrationRunModal();
75 } else {
76 setMigrationRunModal((previousState) => {
77 return {
78 ...previousState,
79 type: 'error',
80 error: true,
81 errorMessage: response.data.message,
82 };
83 });
84 }
85 // Invalidate the cache
86 mutate(getEndpoint('/get-migrations', parameters));
87 })
88 .catch(() => {
89 setMigrationRunModal((previousState) => {
90 return {
91 ...previousState,
92 type: 'error',
93 error: true,
94 };
95 });
96 });
97 };
98
99 const openMigrationModal = (migration) => {
100 setMigrationModal({
101 visible: true,
102 id: migration.id,
103 status: migration.status,
104 error: migration.error,
105 last_run: migration.last_run,
106 });
107 };
108
109 const closeMigrationModal = () => {
110 setMigrationModal({visible: false});
111 };
112
113 const openMigrationRunModal = (migration) => {
114 setMigrationRunModal({
115 id: migration.id,
116 visible: true,
117 type: 'warning',
118 isBatchMigration: migration.isBatchMigration,
119 action: migration.action,
120 status: migration.status,
121 });
122 };
123
124 const closeMigrationRunModal = () => {
125 setMigrationRunModal({visible: false});
126 };
127
128 const setSortDirectionForColumn = (column, direction) => {
129 setState((previousState) => {
130 return {
131 ...previousState,
132 sortColumn: column,
133 sortDirection: direction,
134 isSorting: true,
135 };
136 });
137 };
138
139 const setCurrentPage = (currentPage) => {
140 setState((previousState) => {
141 return {
142 ...previousState,
143 currentPage,
144 };
145 });
146 };
147
148 const getMigrationModal = () => {
149 return (
150 <Modal visible={migrationModal.visible} type="error" handleClose={closeMigrationModal}>
151 <Modal.Title>
152 <strong>{__('Migration Failed', 'give')}</strong>
153
154 <Modal.CloseIcon onClick={closeMigrationModal} />
155 </Modal.Title>
156
157 <Modal.Section title={__('Migration ID', 'give')} content={migrationModal.id} />
158 <Modal.Section title={__('Last run', 'give')} content={migrationModal.last_run ?? __('n/a', 'give')} />
159
160 <Modal.AdditionalContext type={migrationModal.status} context={migrationModal.error} />
161 </Modal>
162 );
163 };
164
165 const getMigrationRunModal = () => {
166 return (
167 <Modal
168 visible={migrationRunModal.visible}
169 type={migrationRunModal.type}
170 handleClose={closeMigrationRunModal}
171 >
172 {migrationRunModal.running ? (
173 <Modal.Content align="center">
174 {migrationRunModal.error ? (
175 <>
176 <Modal.CloseIcon onClick={closeMigrationRunModal} />
177 <h2>{__('Database update failed!', 'give')}</h2>
178 {migrationRunModal.errorMessage && (
179 <Modal.Content align="center">{migrationRunModal.errorMessage}</Modal.Content>
180 )}
181 <Modal.Content align="center">
182 {__('Check migration details for more information', 'give')}
183 </Modal.Content>
184 </>
185 ) : (
186 <>
187 <Spinner />
188 <div style={{marginTop: 20}}>{__('Running Update', 'give')}</div>
189 </>
190 )}
191 </Modal.Content>
192 ) : (
193 <>
194 <Modal.Title>
195 <span className={classNames(styles.titleIcon, styles.warning)}>
196 <span className="dashicons dashicons-warning" />
197 </span>
198 {__('Create a Backup Before Running Database Update', 'give')}
199 </Modal.Title>
200
201 <Modal.Content>
202 <strong style={{marginRight: 5}}>{__('Notice', 'give')}:</strong>
203 {__(
204 'We strongly recommend you create a complete backup of your WordPress files and database prior to performing an update. We are not responsible for any misuse, deletions, white screens, fatal errors, or any other issue resulting from the use of this plugin.',
205 'give',
206 )}
207 </Modal.Content>
208
209 <Modal.Content>
210 <button style={{marginRight: 20}} className="button button-primary" onClick={runMigration}>
211 {__('Confirm', 'give')}
212 </button>
213 <button className="button" onClick={closeMigrationRunModal}>
214 {__('Cancel', 'give')}
215 </button>
216 </Modal.Content>
217 </>
218 )}
219 </Modal>
220 );
221 };
222
223 const getButtonTextByStatus = status => {
224 switch (status) {
225 case 'incomplete':
226 return __('Continue Update', 'give');
227 default:
228 return __('Re-run Update', 'give');
229
230 }
231 };
232
233 const columns = [
234 {
235 key: 'status',
236 label: __('Status', 'give'),
237 sort: true,
238 sortCallback: (direction) => setSortDirectionForColumn('status', direction),
239 styles: {
240 maxWidth: 120,
241 },
242 },
243 {
244 key: 'title',
245 label: __('Migration Title', 'give'),
246 sort: true,
247 sortCallback: (direction) => setSortDirectionForColumn('title', direction),
248 styles: {
249 overflowWrap: 'break-word',
250 wordWrap: 'break-word',
251 wordBreak: 'break-all',
252 },
253 },
254 {
255 key: 'last_run',
256 label: __('Last run', 'give'),
257 sort: true,
258 sortCallback: (direction) => setSortDirectionForColumn('last_run', direction),
259 styles: {
260 maxWidth: 180,
261 },
262 },
263 {
264 key: 'source',
265 label: __('Source', 'give'),
266 sort: true,
267 sortCallback: (direction) => setSortDirectionForColumn('source', direction),
268 styles: {
269 maxWidth: 150,
270 },
271 },
272 {
273 key: 'run_order',
274 label: __('Run Order', 'give'),
275 sort: true,
276 sortCallback: (direction) => setSortDirectionForColumn('run_order', direction),
277 styles: {
278 maxWidth: 100,
279 },
280 },
281 {
282 key: 'actions',
283 label: __('Actions', 'give'),
284 append: true,
285 styles: {
286 maxWidth: 200,
287 },
288 },
289 {
290 key: 'details',
291 label: __('Details', 'give'),
292 append: true,
293 styles: {
294 maxWidth: 100,
295 textAlign: 'center',
296 justifyContent: 'center',
297 },
298 },
299 ];
300
301 const columnFilters = {
302 status: (type, migration) => {
303 const text = migration.status === 'reversed' ? __('Pending', 'give') : null;
304 return <Label type={type} text={text} />;
305 },
306 actions: (type, migration) => {
307 if (!state.showOptions) {
308 return null;
309 }
310
311 if (migration.status === 'running') {
312 return null;
313 }
314
315 return (
316 <>
317 {migration.isReversible && migration.status === 'failed' ? (
318 <button
319 className="button"
320 onClick={() => openMigrationRunModal({
321 action: 'rollback',
322 ...migration,
323 })
324 }>
325 {__('Reverse Update', 'give')}
326 </button>
327 ) : (
328 <button
329 className="button"
330 onClick={() => openMigrationRunModal({
331 action: 'run',
332 ...migration,
333 })}
334 >
335 {getButtonTextByStatus(migration.status)}
336 </button>
337 )}
338 </>
339 );
340 },
341 details: (value, migration) => {
342 if (!migration.error.length) {
343 return null;
344 }
345
346 return (
347 <Button
348 onClick={(e) => {
349 e.preventDefault();
350 openMigrationModal(migration);
351 }}
352 icon={true}
353 >
354 <span className="dashicons dashicons-visibility" />
355 </Button>
356 );
357 },
358 };
359
360 // Initial load
361 if (!state.initialLoad && isLoading) {
362 return (
363 <Notice>
364 <Spinner />
365 <h2>{__('Loading updates activity', 'give')}</h2>
366 </Notice>
367 );
368 }
369
370 // Is error?
371 if (isError) {
372 return (
373 <Notice>
374 <h2>{__('Something went wrong!', 'give')}</h2>
375 <div>
376 Try to{' '}
377 <a onClick={() => window.location.reload()} href="#">
378 reload
379 </a>{' '}
380 the browser
381 </div>
382 </Notice>
383 );
384 }
385
386 return (
387 <>
388 <Card>
389 <Table
390 columns={columns}
391 data={data}
392 columnFilters={columnFilters}
393 isLoading={isLoading}
394 isSorting={state.isSorting}
395 stripped={false}
396 />
397 </Card>
398
399 <div className={styles.footerRow}>
400 <div className={styles.pagination}>
401 <Pagination
402 currentPage={state.currentPage}
403 setPage={setCurrentPage}
404 totalPages={state.pages}
405 disabled={isLoading}
406 />
407 </div>
408 </div>
409
410 {migrationModal.visible && getMigrationModal()}
411 {migrationRunModal.visible && getMigrationRunModal()}
412 </>
413 );
414 };
415
416 export default Migrations;
417