Jelajahi Sumber

impl progress bar

Yuki Takei 6 tahun lalu
induk
melakukan
442f6cedb6

+ 34 - 8
src/client/js/components/Admin/ImportData/GrowiZipImportForm.jsx

@@ -33,7 +33,8 @@ class GrowiImportForm extends React.Component {
     this.initialState = {
       isImporting: false,
       isImported: false,
-      progressList: [],
+      progressMap: [],
+      errorsMap: [],
 
       collectionNameToFileNameMap: {},
       selectedCollections: new Set(),
@@ -75,18 +76,26 @@ class GrowiImportForm extends React.Component {
 
     // websocket event
     // eslint-disable-next-line object-curly-newline
-    socket.on('admin:onProgressForImport', ({ currentCount, totalCount, progressList, appendedErrors }) => {
-      console.log(progressList);
-      console.log(appendedErrors);
+    socket.on('admin:onProgressForImport', ({ collectionName, collectionProgress, appendedErrors }) => {
+      console.log('onProgressForImport');
+
+      const { progressMap, errorsMap } = this.state;
+      progressMap[collectionName] = collectionProgress;
+
+      const errors = errorsMap[collectionName] || [];
+      errorsMap[collectionName] = errors.concat(appendedErrors);
 
       this.setState({
         isImporting: true,
-        progressList,
+        progressMap,
+        errorsMap,
       });
     });
 
     // websocket event
     socket.on('admin:onTerminateForImport', () => {
+      console.log('onTerminateForImport');
+
       this.setState({
         isImporting: false,
         isImported: true,
@@ -297,7 +306,7 @@ class GrowiImportForm extends React.Component {
             </ul>
           </div>
         ) }
-        { this.renderCheckboxes(collectionNames) }
+        { this.renderImportItems(collectionNames) }
         { this.renderWarnForGroups(errors, `warnFor${groupName}`) }
       </div>
     );
@@ -311,15 +320,32 @@ class GrowiImportForm extends React.Component {
     return this.renderGroups(collectionNames, 'Other', this.state.errorsForOtherGroups);
   }
 
-  renderCheckboxes(collectionNames) {
-    const { selectedCollections, optionsMap } = this.state;
+  renderImportItems(collectionNames) {
+    const {
+      isImporting,
+      isImported,
+      progressMap,
+      errorsMap,
+
+      selectedCollections,
+      optionsMap,
+    } = this.state;
 
     return (
       <div className="row">
         {collectionNames.map((collectionName) => {
+          const collectionProgress = progressMap[collectionName] || {};
+          const errors = errorsMap[collectionName] || [];
+
           return (
             <div className="col-xs-6 my-1" key={collectionName}>
               <GrowiZipImportItem
+                isImporting={isImporting}
+                isImported={isImported}
+                insertedCount={collectionProgress.insertedCount}
+                modifiedCount={collectionProgress.modifiedCount}
+                errorsCount={errors.length}
+
                 collectionName={collectionName}
                 isSelected={selectedCollections.has(collectionName)}
                 option={optionsMap[collectionName]}

+ 92 - 53
src/client/js/components/Admin/ImportData/GrowiZipImportItem.jsx

@@ -1,10 +1,10 @@
 import React from 'react';
 import PropTypes from 'prop-types';
 
-
 // eslint-disable-next-line no-unused-vars
 import { withTranslation } from 'react-i18next';
 
+import ProgressBar from 'react-bootstrap/es/ProgressBar';
 
 import GrowiZipImportOption from '../../../models/GrowiZipImportOption';
 
@@ -53,60 +53,81 @@ export default class GrowiZipImportItem extends React.Component {
     return <span className={className}><i className={attrMap.icon}></i> {attrMap.label}</span>;
   }
 
-  renderControls() {
+  renderCheckbox() {
     const {
-      collectionName, option, isSelected,
+      collectionName, isSelected,
+    } = this.props;
+
+    return (
+      <div className="checkbox checkbox-info my-0">
+        <input
+          type="checkbox"
+          id={collectionName}
+          name={collectionName}
+          className="form-check-input"
+          value={collectionName}
+          checked={isSelected}
+          onChange={this.changeHandler}
+        />
+        <label className="text-capitalize form-check-label" htmlFor={collectionName}>
+          {collectionName}
+        </label>
+      </div>
+    );
+  }
+
+  renderModeSelector() {
+    const {
+      option,
     } = this.props;
 
     const attrMap = MODE_ATTR_MAP[option.mode];
     const btnColor = `btn-${attrMap.color}`;
 
     return (
-      <div className="d-flex justify-content-between align-items-center">
-        {/* left */}
-        <div className="checkbox checkbox-info my-0">
-          <input
-            type="checkbox"
-            id={collectionName}
-            name={collectionName}
-            className="form-check-input"
-            value={collectionName}
-            checked={isSelected}
-            onChange={this.changeHandler}
-          />
-          <label className="text-capitalize form-check-label" htmlFor={collectionName}>
-            {collectionName}
-          </label>
+      <span className="d-inline-flex align-items-center">
+        Mode:&nbsp;
+        <div className="dropdown d-inline-block">
+          <button
+            className={`btn ${btnColor} btn-xs dropdown-toggle`}
+            type="button"
+            id="ddmMode"
+            data-toggle="dropdown"
+            aria-haspopup="true"
+            aria-expanded="true"
+          >
+            {this.renderModeLabel(option.mode)}
+            <span className="caret ml-2"></span>
+          </button>
+          <ul className="dropdown-menu" aria-labelledby="ddmMode">
+            { ['insert', 'upsert', 'flushAndInsert'].map((mode) => {
+              return (
+                <li key={`buttonMode_${mode}`}>
+                  <a href="#" onClick={() => this.modeSelectedHandler(mode)}>
+                    {this.renderModeLabel(mode, true)}
+                  </a>
+                </li>
+              );
+            }) }
+          </ul>
         </div>
-        {/* right */}
-        <span className="d-inline-flex align-items-center">
-          Mode:&nbsp;
-          <div className="dropdown d-inline-block">
-            <button
-              className={`btn ${btnColor} btn-xs dropdown-toggle`}
-              type="button"
-              id="ddmMode"
-              data-toggle="dropdown"
-              aria-haspopup="true"
-              aria-expanded="true"
-            >
-              {this.renderModeLabel(option.mode)}
-              <span className="caret ml-2"></span>
-            </button>
-            <ul className="dropdown-menu" aria-labelledby="ddmMode">
-              { ['insert', 'upsert', 'flushAndInsert'].map((mode) => {
-                return (
-                  <li key={`buttonMode_${mode}`}>
-                    <a href="#" onClick={() => this.modeSelectedHandler(mode)}>
-                      {this.renderModeLabel(mode, true)}
-                    </a>
-                  </li>
-                );
-              }) }
-            </ul>
-          </div>
-        </span>
-      </div>
+      </span>
+    );
+  }
+
+  renderProgressBar() {
+    const {
+      isImporting, insertedCount, modifiedCount, errorsCount,
+    } = this.props;
+
+    const total = insertedCount + modifiedCount + errorsCount;
+
+    return (
+      <ProgressBar className="mb-0">
+        <ProgressBar max={total} striped={isImporting} active={isImporting} now={insertedCount} bsStyle="info" />
+        <ProgressBar max={total} striped={isImporting} active={isImporting} now={modifiedCount} bsStyle="success" />
+        <ProgressBar max={total} striped={isImporting} active={isImporting} now={errorsCount} bsStyle="danger" />
+      </ProgressBar>
     );
   }
 
@@ -115,17 +136,23 @@ export default class GrowiZipImportItem extends React.Component {
       isSelected,
     } = this.props;
 
-    const cotrols = this.renderControls();
-
     return (
       <div className="panel panel-default">
         <div className="panel-heading">
-          {cotrols}
+          <div className="d-flex justify-content-between align-items-center">
+            {/* left */}
+            {this.renderCheckbox()}
+            {/* right */}
+            {this.renderModeSelector()}
+          </div>
         </div>
         { isSelected && (
-          <div className="panel-body">
-            Ready
-          </div>
+          <>
+            {this.renderProgressBar()}
+            <div className="panel-body">
+              Ready
+            </div>
+          </>
         ) }
       </div>
     );
@@ -138,6 +165,18 @@ GrowiZipImportItem.propTypes = {
   isSelected: PropTypes.bool.isRequired,
   option: PropTypes.instanceOf(GrowiZipImportOption).isRequired,
 
+  isImporting: PropTypes.bool.isRequired,
+  isImported: PropTypes.bool.isRequired,
+  insertedCount: PropTypes.number,
+  modifiedCount: PropTypes.number,
+  errorsCount: PropTypes.number,
+
   onChange: PropTypes.func,
   onOptionChange: PropTypes.func,
 };
+
+GrowiZipImportItem.defaultProps = {
+  insertedCount: 0,
+  modifiedCount: 0,
+  errorsCount: 0,
+};

+ 2 - 0
src/server/models/vo/collection-progress.js

@@ -3,6 +3,8 @@ class CollectionProgress {
   constructor(collectionName, totalCount) {
     this.collectionName = collectionName;
     this.currentCount = 0;
+    this.insertedCount = 0;
+    this.modifiedCount = 0;
     this.totalCount = totalCount;
   }
 

+ 9 - 12
src/server/service/import.js

@@ -199,9 +199,14 @@ class ImportService {
         // exec
         const { insertedCount, modifiedCount, errors } = await execUnorderedBulkOpSafely(unorderedBulkOp);
         logger.debug(`Importing ${collectionName}. Inserted: ${insertedCount}. Modified: ${modifiedCount}. Failed: ${errors.length}.`);
-        collectionProgress.currentCount += insertedCount + modifiedCount;
 
-        emitProgressEvent(errors);
+        const increment = insertedCount + modifiedCount + errors.length;
+        collectionProgress.currentCount += increment;
+        collectionProgress.totalCount += increment;
+        collectionProgress.insertedCount += insertedCount;
+        collectionProgress.modifiedCount += modifiedCount;
+
+        emitProgressEvent(collectionName, collectionProgress, errors);
 
         callback();
       },
@@ -250,17 +255,9 @@ class ImportService {
    * emit progress event
    * @param {object} appendedErrors key: collection name, value: array of error object
    */
-  emitProgressEvent(appendedErrors) {
-    const { currentCount, totalCount, progressList } = this.currentProgressingStatus;
-    const data = {
-      currentCount,
-      totalCount,
-      progressList,
-      appendedErrors,
-    };
-
+  emitProgressEvent(collectionName, collectionProgress, appendedErrors) {
     // send event (in progress in global)
-    this.adminEvent.emit('onProgressForImport', data);
+    this.adminEvent.emit('onProgressForImport', { collectionName, collectionProgress, appendedErrors });
   }
 
   /**