ExportPage.jsx 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import React, { Fragment } from 'react';
  2. import PropTypes from 'prop-types';
  3. import { withTranslation } from 'react-i18next';
  4. import * as toastr from 'toastr';
  5. import { createSubscribedElement } from '../../UnstatedUtils';
  6. import AppContainer from '../../../services/AppContainer';
  7. import WebsocketContainer from '../../../services/WebsocketContainer';
  8. // import { toastSuccess, toastError } from '../../../util/apiNotification';
  9. import ExportZipFormModal from './ExportZipFormModal';
  10. import ZipFileTable from './ZipFileTable';
  11. import ExportingProgressBar from './ExportingProgressBar';
  12. class ExportPage extends React.Component {
  13. constructor(props) {
  14. super(props);
  15. this.state = {
  16. collections: [],
  17. zipFileStats: [],
  18. progressList: [],
  19. isExportModalOpen: false,
  20. isExporting: false,
  21. isExported: false,
  22. };
  23. this.onZipFileStatAdd = this.onZipFileStatAdd.bind(this);
  24. this.onZipFileStatRemove = this.onZipFileStatRemove.bind(this);
  25. this.openExportModal = this.openExportModal.bind(this);
  26. this.closeExportModal = this.closeExportModal.bind(this);
  27. this.exportingRequestedHandler = this.exportingRequestedHandler.bind(this);
  28. }
  29. async componentWillMount() {
  30. // TODO:: use apiv3.get
  31. // eslint-disable-next-line no-unused-vars
  32. const [{ collections }, { status }] = await Promise.all([
  33. this.props.appContainer.apiGet('/v3/mongo/collections', {}),
  34. this.props.appContainer.apiGet('/v3/export/status', {}),
  35. ]);
  36. // TODO: toastSuccess, toastError
  37. const { zipFileStats, isExporting, progressList } = status;
  38. this.setState({
  39. collections: ['pages', 'revisions'],
  40. zipFileStats,
  41. isExporting,
  42. progressList,
  43. }); // FIXME: delete this line and uncomment the line below
  44. // this.setState({ collections, zipFileStats, isExporting });
  45. this.setupWebsocketEventHandler();
  46. }
  47. setupWebsocketEventHandler() {
  48. const socket = this.props.websocketContainer.getWebSocket();
  49. // websocket event
  50. socket.on('admin:onProgressForExport', ({ currentCount, totalCount, progressList }) => {
  51. const isExporting = currentCount !== totalCount;
  52. this.setState({ isExporting, progressList });
  53. });
  54. // websocket event
  55. socket.on('admin:onTerminateForExport', ({ zipFileStats }) => {
  56. this.setState({
  57. isExporting: false,
  58. isExported: true,
  59. zipFileStats,
  60. });
  61. // TODO: toastSuccess, toastError
  62. toastr.success(undefined, 'New Exported Data is added', {
  63. closeButton: true,
  64. progressBar: true,
  65. newestOnTop: false,
  66. showDuration: '100',
  67. hideDuration: '100',
  68. timeOut: '1200',
  69. extendedTimeOut: '150',
  70. });
  71. });
  72. }
  73. onZipFileStatAdd(newStat) {
  74. this.setState((prevState) => {
  75. return {
  76. zipFileStats: [...prevState.zipFileStats, newStat],
  77. };
  78. });
  79. }
  80. async onZipFileStatRemove(fileName) {
  81. try {
  82. await this.props.appContainer.apiDelete(`/v3/export/${fileName}`, {});
  83. this.setState((prevState) => {
  84. return {
  85. zipFileStats: prevState.zipFileStats.filter(stat => stat.fileName !== fileName),
  86. };
  87. });
  88. // TODO: toastSuccess, toastError
  89. toastr.success(undefined, `Deleted ${fileName}`, {
  90. closeButton: true,
  91. progressBar: true,
  92. newestOnTop: false,
  93. showDuration: '100',
  94. hideDuration: '100',
  95. timeOut: '1200',
  96. extendedTimeOut: '150',
  97. });
  98. }
  99. catch (err) {
  100. // TODO: toastSuccess, toastError
  101. toastr.error(err, 'Error', {
  102. closeButton: true,
  103. progressBar: true,
  104. newestOnTop: false,
  105. showDuration: '100',
  106. hideDuration: '100',
  107. timeOut: '3000',
  108. });
  109. }
  110. }
  111. openExportModal() {
  112. this.setState({ isExportModalOpen: true });
  113. }
  114. closeExportModal() {
  115. this.setState({ isExportModalOpen: false });
  116. }
  117. /**
  118. * @params {object} export status data
  119. */
  120. exportingRequestedHandler(status) {
  121. const { zipFileStats, isExporting, progressList } = status;
  122. this.setState({ zipFileStats, isExporting, progressList });
  123. }
  124. renderProgressBars() {
  125. const cols = this.state.progressList.map((progressData) => {
  126. const { collectionName, currentCount, totalCount } = progressData;
  127. return (
  128. <div className="col-md-6" key={collectionName}>
  129. <ExportingProgressBar
  130. collectionName={collectionName}
  131. currentCount={currentCount}
  132. totalCount={totalCount}
  133. />
  134. </div>
  135. );
  136. });
  137. return <div className="row px-3">{cols}</div>;
  138. }
  139. render() {
  140. const { t } = this.props;
  141. const showExportingData = (this.state.isExported || this.state.isExporting) && (this.state.progressList != null);
  142. return (
  143. <Fragment>
  144. <h2>{t('Export Data')}</h2>
  145. <button type="button" className="btn btn-default" onClick={this.openExportModal}>{t('export_management.create_new_exported_data')}</button>
  146. { showExportingData && (
  147. <div className="mt-5">
  148. <h3>{t('export_management.exporting_data_list')}</h3>
  149. { this.renderProgressBars() }
  150. </div>
  151. ) }
  152. <div className="mt-5">
  153. <h3>{t('export_management.exported_data_list')}</h3>
  154. <ZipFileTable
  155. zipFileStats={this.state.zipFileStats}
  156. onZipFileStatRemove={this.onZipFileStatRemove}
  157. />
  158. </div>
  159. <ExportZipFormModal
  160. isOpen={this.state.isExportModalOpen}
  161. onExportingRequested={this.exportingRequestedHandler}
  162. onClose={this.closeExportModal}
  163. collections={this.state.collections}
  164. />
  165. </Fragment>
  166. );
  167. }
  168. }
  169. ExportPage.propTypes = {
  170. t: PropTypes.func.isRequired, // i18next
  171. appContainer: PropTypes.instanceOf(AppContainer).isRequired,
  172. websocketContainer: PropTypes.instanceOf(WebsocketContainer).isRequired,
  173. };
  174. /**
  175. * Wrapper component for using unstated
  176. */
  177. const ExportPageFormWrapper = (props) => {
  178. return createSubscribedElement(ExportPage, props, [AppContainer, WebsocketContainer]);
  179. };
  180. export default withTranslation()(ExportPageFormWrapper);