فهرست منبع

componentize import

mizozobu 6 سال پیش
والد
کامیت
ef09d7bdb2

+ 2 - 2
src/client/js/app.jsx

@@ -39,7 +39,7 @@ import CustomScriptEditor from './components/Admin/CustomScriptEditor';
 import CustomHeaderEditor from './components/Admin/CustomHeaderEditor';
 import AdminRebuildSearch from './components/Admin/AdminRebuildSearch';
 import ExportPage from './components/Admin/Export/ExportPage';
-import GrowiImportForm from './components/Admin/Import/GrowiImportForm';
+import GrowiZipImportSection from './components/Admin/Import/GrowiZipImportSection';
 import GroupDeleteModal from './components/GroupDeleteModal/GroupDeleteModal';
 
 import AppContainer from './services/AppContainer';
@@ -209,7 +209,7 @@ if (growiImportElem != null) {
   ReactDOM.render(
     <Provider inject={[]}>
       <I18nextProvider i18n={i18n}>
-        <GrowiImportForm />
+        <GrowiZipImportSection />
       </I18nextProvider>
     </Provider>,
     growiImportElem,

+ 0 - 188
src/client/js/components/Admin/Import/GrowiImportForm.jsx

@@ -1,188 +0,0 @@
-import React, { Fragment } from 'react';
-import PropTypes from 'prop-types';
-import { withTranslation } from 'react-i18next';
-
-import { createSubscribedElement } from '../../UnstatedUtils';
-import AppContainer from '../../../services/AppContainer';
-// import { toastSuccess, toastError } from '../../../util/apiNotification';
-
-class GrowiImportForm extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.initialState = {
-      meta: {},
-      zipFileName: '',
-      collections: new Set(),
-      fileStats: [],
-      schema: {
-        pages: {},
-        revisions: {},
-      },
-    };
-
-    this.state = this.initialState;
-
-    this.inputRef = React.createRef();
-
-    this.changeFileName = this.changeFileName.bind(this);
-    this.toggleCheckbox = this.toggleCheckbox.bind(this);
-    this.uploadZipFile = this.uploadZipFile.bind(this);
-    this.import = this.import.bind(this);
-    this.validateForm = this.validateForm.bind(this);
-  }
-
-  changeFileName(e) {
-    // to rerender onChange
-    // eslint-disable-next-line react/no-unused-state
-    this.setState({ name: e.target.files[0].name });
-  }
-
-  toggleCheckbox(e) {
-    const { target } = e;
-    const { name, checked } = target;
-
-    this.setState((prevState) => {
-      const collections = new Set(prevState.collections);
-      if (checked) {
-        collections.add(name);
-      }
-      else {
-        collections.delete(name);
-      }
-      return { collections };
-    });
-  }
-
-  async uploadZipFile(e) {
-    e.preventDefault();
-
-    const formData = new FormData();
-    formData.append('_csrf', this.props.appContainer.csrfToken);
-    formData.append('file', this.inputRef.current.files[0]);
-
-    // TODO use appContainer.apiv3.post
-    const { data } = await this.props.appContainer.apiPost('/v3/import/upload', formData);
-    this.setState({ meta: data.meta, zipFileName: data.fileName, fileStats: data.fileStats });
-    // TODO toastSuccess, toastError
-  }
-
-  async import(e) {
-    e.preventDefault();
-
-    // TODO use appContainer.apiv3.post
-    await this.props.appContainer.apiPost('/v3/import', {
-      fileName: this.state.zipFileName,
-      collections: Array.from(this.state.collections),
-      schema: this.state.schema,
-    });
-    // TODO toastSuccess, toastError
-    this.setState(this.initialState);
-  }
-
-  validateForm() {
-    return (
-      this.inputRef.current // null check
-      && this.inputRef.current.files[0] // null check
-      && /\.zip$/.test(this.inputRef.current.files[0].name) // validate extension
-    );
-  }
-
-  render() {
-    const { t } = this.props;
-
-    return (
-      <Fragment>
-        <form className="form-horizontal" onSubmit={this.uploadZipFile}>
-          <fieldset>
-            <legend>Import</legend>
-            <div className="well well-sm small">
-              <ul>
-                <li>Imported pages will overwrite existing pages</li>
-              </ul>
-            </div>
-            <div className="form-group d-flex align-items-center">
-              <label htmlFor="file" className="col-xs-3 control-label">Zip File</label>
-              <div className="col-xs-6">
-                <input
-                  type="file"
-                  name="file"
-                  className="form-control-file"
-                  ref={this.inputRef}
-                  onChange={this.changeFileName}
-                />
-              </div>
-            </div>
-            <div className="form-group">
-              <div className="col-xs-offset-3 col-xs-6">
-                <button type="submit" className="btn btn-primary" disabled={!this.validateForm()}>
-                  Upload
-                </button>
-              </div>
-            </div>
-          </fieldset>
-        </form>
-
-        {/* TODO: move to another component 1 */}
-        {this.state.fileStats.length > 0 && (
-          <Fragment>
-            {/* TODO: move to another component 2 */}
-            <div>{this.state.zipFileName}</div>
-            <div>{JSON.stringify(this.state.meta)}</div>
-            <table className="table table-bordered table-mapping">
-              <thead>
-                <tr>
-                  <th></th>
-                  <th>File</th>
-                  <th>Collection</th>
-                </tr>
-              </thead>
-              <tbody>
-                {this.state.fileStats.map((file) => {
-                  const { fileName, collectionName } = file;
-                  return (
-                    <tr key={fileName}>
-                      <td>
-                        <input
-                          type="checkbox"
-                          id={collectionName}
-                          name={collectionName}
-                          className="form-check-input"
-                          value={collectionName}
-                          checked={this.state.collections.has(collectionName)}
-                          onChange={this.toggleCheckbox}
-                        />
-                      </td>
-                      <td>{fileName}</td>
-                      <td>{collectionName}</td>
-                    </tr>
-                  );
-                })}
-              </tbody>
-            </table>
-            {/* TODO: move to another component 3 */}
-            <button type="submit" className="btn btn-primary" onClick={this.import}>
-              { t('importer_management.import') }
-            </button>
-          </Fragment>
-        )}
-      </Fragment>
-    );
-  }
-
-}
-
-GrowiImportForm.propTypes = {
-  t: PropTypes.func.isRequired, // i18next
-  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-};
-
-/**
- * Wrapper component for using unstated
- */
-const GrowiImportFormWrapper = (props) => {
-  return createSubscribedElement(GrowiImportForm, props, [AppContainer]);
-};
-
-export default withTranslation()(GrowiImportFormWrapper);

+ 135 - 0
src/client/js/components/Admin/Import/GrowiZipImportForm.jsx

@@ -0,0 +1,135 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import { createSubscribedElement } from '../../UnstatedUtils';
+import AppContainer from '../../../services/AppContainer';
+// import { toastSuccess, toastError } from '../../../util/apiNotification';
+
+class GrowiImportForm extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.initialState = {
+      collections: new Set(),
+      schema: {
+        pages: {},
+        revisions: {},
+      },
+    };
+
+    this.state = this.initialState;
+
+    this.inputRef = React.createRef();
+
+    this.toggleCheckbox = this.toggleCheckbox.bind(this);
+    this.import = this.import.bind(this);
+  }
+
+  toggleCheckbox(e) {
+    const { target } = e;
+    const { name, checked } = target;
+
+    this.setState((prevState) => {
+      const collections = new Set(prevState.collections);
+      if (checked) {
+        collections.add(name);
+      }
+      else {
+        collections.delete(name);
+      }
+      return { collections };
+    });
+  }
+
+  async import(e) {
+    e.preventDefault();
+
+    // TODO use appContainer.apiv3.post
+    await this.props.appContainer.apiPost('/v3/import', {
+      fileName: this.props.fileName,
+      collections: Array.from(this.state.collections),
+      schema: this.state.schema,
+    });
+    // TODO toastSuccess, toastError
+    this.setState(this.initialState);
+  }
+
+  render() {
+    const { t } = this.props;
+
+    return (
+      <Fragment>
+        {this.props.fileName && (
+          <form className="row">
+            <div className="col-xs-12">
+              <table className="table table-bordered table-mapping">
+                <thead>
+                  <tr>
+                    <th></th>
+                    <th>Extracted File</th>
+                    <th>Collection</th>
+                  </tr>
+                </thead>
+                <tbody>
+                  {this.props.fileStats.map((fileStat) => {
+                  const { fileName, collectionName } = fileStat;
+                  return (
+                    <Fragment key={collectionName}>
+                      <tr>
+                        <td>
+                          <input
+                            type="checkbox"
+                            id={collectionName}
+                            name={collectionName}
+                            className="form-check-input"
+                            value={collectionName}
+                            checked={this.state.collections.has(collectionName)}
+                            onChange={this.toggleCheckbox}
+                          />
+                        </td>
+                        <td>{fileName}</td>
+                        <td className="text-capitalize">{collectionName}</td>
+                      </tr>
+                      {this.state.collections.has(collectionName) && (
+                        <tr>
+                          <td className="text-muted" colSpan="3">
+                            TBD: define how to import {collectionName}
+                          </td>
+                        </tr>
+                      )}
+                    </Fragment>
+                  );
+                })}
+                </tbody>
+              </table>
+            </div>
+            <div className="col-xs-offset-3 col-xs-6">
+              <button type="submit" className="btn btn-primary" onClick={this.import}>
+                { t('importer_management.import') }
+              </button>
+            </div>
+          </form>
+        )}
+      </Fragment>
+    );
+  }
+
+}
+
+GrowiImportForm.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  fileName: PropTypes.string,
+  fileStats: PropTypes.arrayOf(PropTypes.object).isRequired,
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const GrowiImportFormWrapper = (props) => {
+  return createSubscribedElement(GrowiImportForm, props, [AppContainer]);
+};
+
+export default withTranslation()(GrowiImportFormWrapper);

+ 70 - 0
src/client/js/components/Admin/Import/GrowiZipImportSection.jsx

@@ -0,0 +1,70 @@
+import React, { Fragment } from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import GrowiZipUploadForm from './GrowiZipUploadForm';
+import GrowiZipImportForm from './GrowiZipImportForm';
+import { createSubscribedElement } from '../../UnstatedUtils';
+import AppContainer from '../../../services/AppContainer';
+// import { toastSuccess, toastError } from '../../../util/apiNotification';
+
+class GrowiZipImportSection extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.initialState = {
+      meta: {},
+      zipFileName: '',
+      fileStats: [],
+    };
+
+    this.state = this.initialState;
+
+    this.inputRef = React.createRef();
+
+    this.handleUpload = this.handleUpload.bind(this);
+  }
+
+  handleUpload({ meta, fileName, fileStats }) {
+    this.setState({ fileName, fileStats });
+  }
+
+  render() {
+    // eslint-disable-next-line no-unused-vars
+    const { t } = this.props;
+
+    return (
+      <Fragment>
+        <legend>Import from GROWI</legend>
+        <div className="well well-sm small">
+          <ul>
+            <li>Imported documents will overwrite existing pages</li>
+          </ul>
+        </div>
+        <GrowiZipUploadForm
+          onUpload={this.handleUpload}
+        />
+        <GrowiZipImportForm
+          fileName={this.state.fileName}
+          fileStats={this.state.fileStats}
+        />
+      </Fragment>
+    );
+  }
+
+}
+
+GrowiZipImportSection.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const GrowiZipImportSectionWrapper = (props) => {
+  return createSubscribedElement(GrowiZipImportSection, props, [AppContainer]);
+};
+
+export default withTranslation()(GrowiZipImportSectionWrapper);

+ 94 - 0
src/client/js/components/Admin/Import/GrowiZipUploadForm.jsx

@@ -0,0 +1,94 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { withTranslation } from 'react-i18next';
+
+import { createSubscribedElement } from '../../UnstatedUtils';
+import AppContainer from '../../../services/AppContainer';
+// import { toastSuccess, toastError } from '../../../util/apiNotification';
+
+class GrowiZipUploadForm extends React.Component {
+
+  constructor(props) {
+    super(props);
+
+    this.inputRef = React.createRef();
+
+    this.changeFileName = this.changeFileName.bind(this);
+    this.uploadZipFile = this.uploadZipFile.bind(this);
+    this.validateForm = this.validateForm.bind(this);
+  }
+
+  changeFileName(e) {
+    // to trigger rerender at onChange event
+    // eslint-disable-next-line react/no-unused-state
+    this.setState({ name: e.target.files[0].name });
+  }
+
+  async uploadZipFile(e) {
+    e.preventDefault();
+
+    const formData = new FormData();
+    formData.append('_csrf', this.props.appContainer.csrfToken);
+    formData.append('file', this.inputRef.current.files[0]);
+
+    // TODO use appContainer.apiv3.post
+    const { data } = await this.props.appContainer.apiPost('/v3/import/upload', formData);
+    this.props.onUpload(data);
+    // TODO toastSuccess, toastError
+  }
+
+  validateForm() {
+    return (
+      this.inputRef.current // null check
+      && this.inputRef.current.files[0] // null check
+      && /\.zip$/.test(this.inputRef.current.files[0].name) // validate extension
+    );
+  }
+
+  render() {
+    // eslint-disable-next-line no-unused-vars
+    const { t } = this.props;
+
+    return (
+      <form className="form-horizontal" onSubmit={this.uploadZipFile}>
+        <fieldset>
+          <div className="form-group d-flex align-items-center">
+            <label htmlFor="file" className="col-xs-3 control-label">Zip File</label>
+            <div className="col-xs-6">
+              <input
+                type="file"
+                name="file"
+                className="form-control-file"
+                ref={this.inputRef}
+                onChange={this.changeFileName}
+              />
+            </div>
+          </div>
+          <div className="form-group">
+            <div className="col-xs-offset-3 col-xs-6">
+              <button type="submit" className="btn btn-primary" disabled={!this.validateForm()}>
+                Upload
+              </button>
+            </div>
+          </div>
+        </fieldset>
+      </form>
+    );
+  }
+
+}
+
+GrowiZipUploadForm.propTypes = {
+  t: PropTypes.func.isRequired, // i18next
+  appContainer: PropTypes.instanceOf(AppContainer).isRequired,
+  onUpload: PropTypes.func.isRequired,
+};
+
+/**
+ * Wrapper component for using unstated
+ */
+const GrowiZipUploadFormWrapper = (props) => {
+  return createSubscribedElement(GrowiZipUploadForm, props, [AppContainer]);
+};
+
+export default withTranslation()(GrowiZipUploadFormWrapper);