PluginProbe ʕ •ᴥ•ʔ
GiveWP – Donation Plugin and Fundraising Platform / trunk
GiveWP – Donation Plugin and Fundraising Platform vtrunk
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 11 months ago styles.module.scss 4 years ago
index.js
431 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' || migration.status === 'success') ? (
318 <>
319 {migration.status === 'success' && (
320 <button
321 style={{marginRight: '0.5rem'}}
322 className="button"
323 onClick={() => openMigrationRunModal({
324 action: 'run',
325 ...migration,
326 })}
327 >
328 {__('Re-run Update', 'give')}
329 </button>
330 )}
331 <button
332 className="button"
333 onClick={() => openMigrationRunModal({
334 action: 'rollback',
335 ...migration,
336 })
337 }>
338 {__('Reverse Update', 'give')}
339 </button>
340 </>
341 ) : (
342 <button
343 className="button"
344 onClick={() => openMigrationRunModal({
345 action: 'run',
346 ...migration,
347 })}
348 >
349 {getButtonTextByStatus(migration.status)}
350 </button>
351 )}
352 </>
353 );
354 },
355 details: (value, migration) => {
356 if (!migration.error.length) {
357 return null;
358 }
359
360 return (
361 <Button
362 onClick={(e) => {
363 e.preventDefault();
364 openMigrationModal(migration);
365 }}
366 icon={true}
367 >
368 <span className="dashicons dashicons-visibility" />
369 </Button>
370 );
371 },
372 };
373
374 // Initial load
375 if (!state.initialLoad && isLoading) {
376 return (
377 <Notice>
378 <Spinner />
379 <h2>{__('Loading updates activity', 'give')}</h2>
380 </Notice>
381 );
382 }
383
384 // Is error?
385 if (isError) {
386 return (
387 <Notice>
388 <h2>{__('Something went wrong!', 'give')}</h2>
389 <div>
390 Try to{' '}
391 <a onClick={() => window.location.reload()} href="#">
392 reload
393 </a>{' '}
394 the browser
395 </div>
396 </Notice>
397 );
398 }
399
400 return (
401 <>
402 <Card>
403 <Table
404 columns={columns}
405 data={data}
406 columnFilters={columnFilters}
407 isLoading={isLoading}
408 isSorting={state.isSorting}
409 stripped={false}
410 />
411 </Card>
412
413 <div className={styles.footerRow}>
414 <div className={styles.pagination}>
415 <Pagination
416 currentPage={state.currentPage}
417 setPage={setCurrentPage}
418 totalPages={state.pages}
419 disabled={isLoading}
420 />
421 </div>
422 </div>
423
424 {migrationModal.visible && getMigrationModal()}
425 {migrationRunModal.visible && getMigrationRunModal()}
426 </>
427 );
428 };
429
430 export default Migrations;
431