Github user robertkowalski commented on a diff in the pull request:

    https://github.com/apache/couchdb-fauxton/pull/495#discussion_r39382801
  
    --- Diff: app/addons/dataimporter/components.react.jsx ---
    @@ -0,0 +1,796 @@
    +// Licensed under the Apache License, Version 2.0 (the "License"); you may 
not
    +// use this file except in compliance with the License. You may obtain a 
copy of
    +// the License at
    +//
    +//   http://www.apache.org/licenses/LICENSE-2.0
    +//
    +// Unless required by applicable law or agreed to in writing, software
    +// distributed under the License is distributed on an "AS IS" BASIS, 
WITHOUT
    +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    +// License for the specific language governing permissions and limitations 
under
    +// the License.
    +
    +define([
    +  'api',
    +  'react',
    +  'addons/dataimporter/stores',
    +  'addons/dataimporter/actions',
    +  'addons/components/react-components.react',
    +  'helpers'
    +], function (FauxtonAPI, React, Stores, Actions, Components, Helpers) {
    +
    +  var dataImporterStore = Stores.dataImporterStore;
    +
    +  var DataImporterController = React.createClass({
    +    getStoreState: function () {
    +      return {
    +        isDataCurrentlyLoading: dataImporterStore.isDataCurrentlyLoading(),
    +        hasDataLoaded: dataImporterStore.hasDataLoaded(),
    +        isBigFile: dataImporterStore.isThisABigFile(),
    +        rowShown: dataImporterStore.getRowsShown(),
    +        rowsTotal: dataImporterStore.getTotalRows(),
    +        data: dataImporterStore.getTheData(),
    +        meta: dataImporterStore.getTheMetadata(),
    +        getPreviewView: dataImporterStore.getPreviewView(),
    +        getSmallPreviewOfData: dataImporterStore.getSmallPreviewOfData(),
    +        getHeaderConfig: dataImporterStore.getConfigSetting('header'),
    +        getDelimiterChosen: 
dataImporterStore.getConfigSetting('delimiter'),
    +        getAllDBs: dataImporterStore.getAllDBs(),
    +        getFileSize: dataImporterStore.getFileSize(),
    +        getTimeSinceLoad: dataImporterStore.getTimeSinceLoad,
    +        getMaxSize: dataImporterStore.getMaxSize(),
    +        showErrorScreen: dataImporterStore.showErrorScreen(),
    +        errorMsg: dataImporterStore.getErrorMsg(),
    +        isLoadingInDBInProgress: dataImporterStore.dbPopulationInProgress()
    +      };
    +    },
    +
    +    getInitialState: function () {
    +      return this.getStoreState();
    +    },
    +
    +    componentDidMount: function () {
    +      dataImporterStore.on('change', this.onChange, this);
    +    },
    +
    +    componentWillUnmount: function () {
    +      dataImporterStore.off('change', this.onChange, this);
    +    },
    +
    +    onChange: function () {
    +      this.setState(this.getStoreState());
    +    },
    +
    +    render: function () {
    +      if (this.state.showErrorScreen) {
    +        return <DataImporterError errorMsg = {this.state.errorMsg} />;
    +      }
    +
    +      if (this.state.hasDataLoaded) {
    +        return (
    +          <DataImporterPreviewData
    +            rowShown = {this.state.rowShown}
    +            rowsTotal = {this.state.rowsTotal}
    +            data = {this.state.data}
    +            isBigFile = {this.state.isBigFile}
    +            meta = {this.state.meta}
    +            getPreviewView = {this.state.getPreviewView}
    +            getSmallPreviewOfData = {this.state.getSmallPreviewOfData}
    +            getHeaderConfig = {this.state.getHeaderConfig}
    +            getDelimiterChosen = {this.state.getDelimiterChosen}
    +            getAllDBs = {this.state.getAllDBs}
    +            filesize = {this.state.getFileSize}
    +            isLoadingInDBInProgress = {this.state.isLoadingInDBInProgress} 
/>
    +        );
    +      }
    +
    +      return (
    +        <DataImporterDropZone
    +          isLoading = {this.state.isDataCurrentlyLoading}
    +          isBigFile = {this.state.isBigFile}
    +          getFileSize = {this.state.getFileSize}
    +          getTimeSinceLoad = {this.state.getTimeSinceLoad}
    +          maxSize = {this.state.getMaxSize} />
    +      );
    +    }
    +  });
    +
    +  var DataImporterDropZone = React.createClass({
    +    getInitialState: function () {
    +      return {
    +        draggingOver: false,
    +        loading: this.props.isLoading,
    +        showLimitInfo: false,
    +        fileSize: this.props.getFileSize,
    +        timeSinceLoad: this.props.getTimeSinceLoad,
    +        fileTooBig: false
    +      };
    +    },
    +
    +    dragOver: function (e) {
    +      e.preventDefault();
    +      this.setState({ draggingOver: true });
    +    },
    +
    +    endDragOver: function (e) {
    +      e.preventDefault();
    +      e.stopPropagation();
    +      this.setState({draggingOver: false});
    +    },
    +
    +    drop: function (e) {
    +      e.preventDefault();
    +      var file = e.nativeEvent.dataTransfer.files[0];
    +      this.checkSize(file);
    +    },
    +
    +    filechosen: function (e) {
    +      e.preventDefault();
    +      var file = e.nativeEvent.target.files[0];
    +      this.checkSize(file);
    +    },
    +
    +    checkSize: function (file) {
    +      if (file.size > this.props.maxSize) {
    +        this.setState({ 
    +          fileTooBig: true,
    +          fileSize: file.size, 
    +          draggingOver: false 
    +        });
    +      } else {
    +        Actions.loadFile(file);
    +        this.setState({ 
    +          loading: true,
    +          fileSize: file.size, 
    +          draggingOver: false 
    +        });
    +        Actions.dataIsCurrentlyLoading();
    +      }
    +    },
    +
    +    uploadButton: function () {
    +      return (
    +        <p>
    +          <span className = "fonticon icon-file-text-alt">
    +            <span className = "file-upload btn">
    +              <span className = "icon icon-search"></span>
    +              Choose File
    +              <input type = "file" className = "upload" onChange = 
{this.filechosen} />
    +            </span>
    +          </span>
    +        </p>
    +      );
    +    },
    +
    +    onFileLimitInfoToggle: function (e) {
    +      e.preventDefault();
    +      var toggle = this.state.showLimitInfo ? false : true;
    +      this.setState({ showLimitInfo : toggle });
    +    },
    +
    +    fileLimitLink: function (msg) {
    +      return (
    +        <div className = "filetype-txt">
    +          <a href = "#"
    +            className = "import-data-limit-info-link"
    +            onClick = {this.onFileLimitInfoToggle}
    +            data-bypass = "true">
    +            {msg}
    +          </a>
    +        </div>
    +      );
    +    },
    +
    +    loadingBoxBigFileMessage: function () {
    +      return (
    +        <div className = "loading-big-file-msg">
    +          <div>This is a large file: 
{Helpers.formatSize(this.state.fileSize)}</div>
    +          <div>Large files may take up to 5 minutes to load</div>
    +          <div>Elapsed time: {this.state.timeSinceLoad()}</div>
    +        </div>
    +      );
    +    },
    +
    +    fileTooBigMsg: function () {
    +      return (
    +        <p className = "file-exceeds-max-msg">
    +          Your file was too big. Please choose another one.
    +        </p>
    +      );
    +    },
    +
    +    defaultBox: function () {
    +      var fileTooBig = this.state.fileTooBig ? this.fileTooBigMsg() : '';
    +
    +      return (
    +        <div className = "dropzone"
    +          onDragOver = {this.dragOver}
    +          onDragLeave = {this.endDragOver}
    +          onDrop = {this.drop} >
    +          <div className = "dropzone-msg default">
    +            {fileTooBig}
    +            {this.uploadButton()}
    +            <p>Or drag a file into box.</p>
    +          </div>
    +          {this.fileLimitLink("File Limitations")}
    +        </div>
    +      );
    +    },
    +
    +    boxIsDraggingOver: function () {
    +      return (
    +        <div className = "dropzone dragging-file-in-background"
    +          onDragOver = {this.dragOver}
    +          onDragLeave = {this.endDragOver}
    +          onDrop = {this.drop}>
    +          <div className = "dropzone-msg draggingover">
    +            <span className = "fonticon icon-file-text-alt">
    +            </span>
    +            <span className = "loading-msg">
    +              Drop your file.
    +            </span>
    +          </div>
    +        </div>
    +      );
    +    },
    +
    +    boxIsLoading: function () {
    +      var loadingBoxBigFileMessage = this.props.isBigFile ?
    +        this.loadingBoxBigFileMessage() : '';
    +
    +      return (
    +        <div className = {"dropzone loading-background"}>
    +          <div className = "dropzone-msg loading">
    +            Loading...
    +            {loadingBoxBigFileMessage}
    +            <Components.LoadLines />
    +          </div>
    +        </div>
    +      );
    +    },
    +
    +    boxShowLimitInfo: function () {
    +      return (
    +         <div className = "dropzone limit-info"
    +          onDragOver = {this.dragOver}
    +          onDragLeave = {this.endDragOver}
    +          onDrop = {this.drop}>
    +          <div className = "dropzone-msg">
    +            <p>150 MB filesize limit</p>
    +            <p>Only .csv files will import correctly</p>
    +            <p>Fine grained import options are only for files under 
200KB</p>
    +          </div>
    +          {this.fileLimitLink("Close")}
    +        </div>
    +      );
    +    },
    +
    +    render: function () {
    +      var box = this.defaultBox();
    +
    +      if (this.state.draggingOver) {
    +        box = this.boxIsDraggingOver();
    +      } else if (this.state.loading) {
    +        box = this.boxIsLoading();
    +      } else if (this.state.showLimitInfo) {
    +        box = this.boxShowLimitInfo();
    +      }
    +      return box;
    +    }
    +  });
    +
    +  var DataImporterPreviewData = React.createClass({
    +    getInitialState: function () {
    +      return {
    +        left: 0
    +      };
    +    },
    +    bigFilePreviewWarning: function () {
    +      var rowShown = this.props.rowShown,
    +          totalRows = this.props.rowsTotal;
    +
    +      return (
    +        <div className = "top-row">
    +          <div className = "big-file-info-message">
    +            <p className = "big-file-preview-limit-info-message">
    +              Because of the size of this file, this preview only shows the
    +              first {rowShown} rows, out of {totalRows} rows total.
    +              However, if you choose to load the data into a database, the
    +              entirety of the file (all {totalRows} rows) will be imported.
    +            </p>
    +          </div>
    +          {this.loadingIntoDB()}
    +        </div>
    +      );
    +    },
    +    componentDidMount: function () {
    +      document.getElementById('preview-page')
    +        .addEventListener('scroll', this.handleScroll);
    +    },
    +    componentWillUnmount: function () {
    +      document.getElementById('preview-page')
    +        .removeEventListener('scroll', this.handleScroll);
    +    },
    +    handleScroll: function (e) {
    +      this.setState({left: 
document.getElementById('preview-page').scrollLeft});
    +    },
    +
    +    loadingIntoDB: function () {
    +      if (this.props.isLoadingInDBInProgress) {
    +        return (
    +          <div className = "dataIsLoading">
    +            <Components.LoadLines />
    +          </div>
    +        );
    +      }
    +      return null;
    +    },
    +
    +    fileMetadataInfo: function () {
    +      var totalRows = this.props.rowsTotal,
    +          previewMsg = totalRows > 500 ? 'This is a 500 row (max) preview 
of the file.' : '';
    +
    +      return (
    +        <div className = "top-row">
    +          <div className = "big-file-info-message">
    +            <p className = "big-file-preview-limit-info-message">
    +              All {totalRows} rows from this file will be imported. 
{previewMsg}
    +            </p>
    +          </div>
    +          {this.loadingIntoDB()}
    +        </div>
    +      );
    +    },
    +    render: function () {
    +
    +      var fileInfoMessage =
    +            this.props.isBigFile ? this.bigFilePreviewWarning() :  
this.fileMetadataInfo(),
    +          style = {'left': this.state.left};
    +
    +      return (
    +        <div id = "preview-page" >
    +          <div id = "data-import-options" style={style}>
    +            {fileInfoMessage}
    +            <OptionsRow 
    +              getDelimiterChosen = {this.props.getDelimiterChosen}
    +              filesize = {this.props.filesize} />
    +          </div>
    +          <div className = "preview-data-space">
    +            <TableView
    +              data = {this.props.data}
    +              isBigFile = {this.props.isBigFile}
    +              meta = {this.props.meta}
    +              getPreviewView = {this.props.getPreviewView}
    +              getSmallPreviewOfData = {this.props.getSmallPreviewOfData}
    +              getHeaderConfig = {this.props.getHeaderConfig} />
    +            <JSONView
    +              data = {this.props.data}
    +              isBigFile = {this.props.isBigFile}
    +              meta = {this.props.meta}
    +              getPreviewView = {this.props.getPreviewView}
    +              getSmallPreviewOfData = {this.props.getSmallPreviewOfData}
    +              getHeaderConfig = {this.props.getHeaderConfig} />
    +          </div>
    +          <Footer getAllDBs = {this.props.getAllDBs} />
    +        </div>
    +      );
    +    }
    +  });
    +
    +  var OptionsRow = React.createClass({
    +    previewToggle: function () {
    +      var config = {
    +        title: 'Preview View',
    +        leftLabel: 'Table',
    +        rightLabel: 'JSON',
    +        defaultLeft: true,
    +        leftClick: function () { Actions.setPreviewView('table'); },
    +        rightClick: function () { Actions.setPreviewView('json'); },
    +        enclosingID: 'preview-toggle-id'
    +      };
    +
    +      return <Components.ToggleState toggleConfig = {config} />;
    +    },
    +
    +    header: function () {
    +      var config = {
    +        title: 'Header',
    +        leftLabel : 'First Line',
    +        rightLabel : 'No Header',
    +        defaultLeft: true,
    +        leftClick: function () {  Actions.setParseConfig('header', true); 
},
    +        rightClick: function () { Actions.setParseConfig('header', false); 
},
    +        enclosingID: 'header-toggle-id'
    +      };
    +
    +      return <Components.ToggleState toggleConfig = {config} />;
    +    },
    +
    +    numbersFormat: function () {
    +      var config = {
    +        title: 'Numbers are',
    +        leftLabel : 'Numbers',
    +        rightLabel : 'Strings',
    +        defaultLeft: true,
    +        leftClick: function () {
    +          Actions.setParseConfig('dynamicTyping', true);
    +        },
    +        rightClick: function () {
    +          Actions.setParseConfig('dynamicTyping', false);
    +        },
    +        enclosingID: 'numbers-toggle-id'
    +      };
    +      return <Components.ToggleState toggleConfig={config} />;
    +
    +    },
    +
    +    delimiter: function () {
    +      var selected = this.props.getDelimiterChosen;
    +      selected = selected === '' ? 'Automatic' : selected;
    +      selected = selected === '\t' ? 'Tab' : selected;
    +
    +      var setup = {
    +        title: 'Delimiter',
    +        id: 'data-importer-delimiter',
    +        selected: selected,
    +        selectOptions: [
    +          {
    +            name: 'Automatic',
    +            onClick: function () {
    +              Actions.setParseConfig('delimiter', '');
    +            }
    +          },
    +          {
    +            name: 'Comma',
    +            onClick: function () {
    +              Actions.setParseConfig('delimiter', ',');
    +            }
    +          },
    +          {
    +            name: 'Tab',
    +            onClick: function () {
    +              Actions.setParseConfig('delimiter', '\t');
    +            }
    +          },
    +          {
    +            name: 'Semicolon',
    +            onClick: function () {
    +              Actions.setParseConfig('delimiter', ';');
    +            }
    +          },
    +          {
    +            name: 'Colon',
    +            onClick: function () {
    +              Actions.setParseConfig('delimiter', ':');
    +            }
    +          },
    +          {
    +            name: 'Hyphen',
    +            onClick: function () {
    +              Actions.setParseConfig('delimiter', '-');
    +            }
    +          },
    +        ]
    +      };
    +      return <Components.SmallDropdown dropdownSetup = {setup}/>;
    +    },
    +
    +    renderControls: function () {
    +      if (this.props.filesize < 200000) {
    +        return (
    +          <span>
    +            {this.header()}
    +            {this.numbersFormat()}
    +            {this.delimiter()}
    +          </span>
    +        );
    +      }
    +
    +      return (
    +        <p className = "no-options-available">
    +          Fine grained import options are disabled for files exceeding 
200KB
    +        </p>
    +      );
    +    },
    +
    +    render: function () {
    +      var controls = this.renderControls();
    +
    +      return (
    +        <div className = "import-options-row">
    +          <div className = "options-row">
    +            {this.previewToggle()}
    +            {controls}
    +          </div>
    +        </div>
    +      );
    +    }
    +  });
    +
    +  var TableView = React.createClass({
    +    eachRow: function () {
    +      var data = this.props.data;
    +
    +      if (this.props.isBigFile) {
    +        data = this.props.getSmallPreviewOfData;
    +      }
    +
    +      return (
    +        data.map(function (dataObj, i) {
    +          if (i < 500) {
    +            return <tr key = {i}>{this.insideEachRow(dataObj)}</tr>;
    +          }
    +        }.bind(this))
    +      );
    +    },
    +
    +    insideEachRow: function (dataObj) {
    +      return _.map(dataObj, function (dataVal, dataKey) {
    +        return <td key = {dataKey} title = {dataVal}>{dataVal}</td>;
    +      });
    +    },
    +
    +    header: function () {
    +      var header = null;
    +
    +      if (this.props.getHeaderConfig) {
    +        header = this.props.meta.fields;
    +        return (
    +          header.map(function (field, i) {
    +            return <th key = {i} title = {field}>{field}</th>;
    +          })
    +        );
    +      } else {
    +        header = this.props.data;
    +        return (
    +          header.map(function (field, i) {
    +           return <th key = {i} title = {i}>{i}</th>;
    +          })
    +        );
    +      }
    +    },
    +
    +    render: function () {
    +      var data = this.eachRow(),
    +          header = this.header();
    +
    +      if (this.props.getPreviewView != 'table') {
    +        return null;
    +      }
    +      return (
    +        <table className = "data-import-table">
    +          <tbody>
    +            <tr>{header}</tr>
    +            {data}
    +          </tbody>
    +        </table>
    +      );
    +    }
    +  });
    +
    +  var JSONView = React.createClass({
    +    objectify: function (array) {
    +      return _.reduce(array, function (obj, val, i) {
    +        obj[i] = val;
    +        return obj;
    +      }, {});
    +    },
    +
    +    rows: function () {
    +      var data = this.props.data;
    +
    +      if (this.props.isBigFile) {
    +        data = this.props.getSmallPreviewOfData;
    +      }
    +
    +      return (
    +        data.map(function (dataObj, i) {
    +          var obj = this.props.getHeaderConfig ? dataObj :
    +            this.objectify(dataObj);
    +          if (i < 500) {
    +            return (
    +              <Components.SimpleDoc
    +                id = {"<UUID>_" + i}
    +                content = {JSON.stringify(obj, null, ' ')}
    +                key = {i} />
    +            );
    +          }
    +        }.bind(this))
    +      );
    +    },
    +
    +    render: function () {
    +      if (this.props.getPreviewView != "json") {
    +        return null;
    +      }
    +      return (
    +        <div id = "doc-list" className = "json-view">
    +          {this.rows()}
    +        </div>
    +      );
    +    }
    +  });
    +
    +  var Footer = React.createClass({
    +    getInitialState: function () {
    +      return {
    +        targetDB: this.props.getAllDBs[0],
    +        selectExistingDB: true
    +      };
    +    },
    +
    +    startOverButton: function () {
    +      return (
    +        <a className = "start-import-over-link footer-button"
    +          onClick = {this.startOver}>
    +          <span className = "fonticon icon-repeat"></span>
    +            Start Over
    +        </a>
    +      );
    +    },
    +
    +    startOver: function () {
    +      Actions.dataImporterInit(true);
    +    },
    +
    +    getExistingDBList: function () {
    +      var allDBs = this.props.getAllDBs,
    +          options = [],
    +          setTargetDB = this.setTargetDB;
    +
    +      _.each(allDBs, function (dbName, i) {
    +        options.push({
    +          name: dbName,
    +          onClick: function () { setTargetDB(dbName); }
    +        });
    +      });
    +
    +      return options;
    +    },
    +
    +    setTargetDB: function (dbName) {
    +      this.setState({ targetDB: dbName });
    +    },
    +
    +    newOrExistingToggle : function () {
    +      var config = {
    +        title: 'Load into',
    +        leftLabel : 'Existing database',
    +        rightLabel : 'New database',
    +        defaultLeft: true,
    +        leftClick: function () {
    +          this.setState({
    +            selectExistingDB: true,
    +            targetDB: this.props.getAllDBs[0]
    +          });
    +        }.bind(this),
    +        rightClick: function () {
    +          this.setState({ selectExistingDB: false });
    +        }.bind(this),
    +        enclosingID: 'choose-database-to-load-data-into'
    +      };
    +
    +      return <Components.ToggleState toggleConfig = {config} />;
    +    },
    +
    +    chooseDatabaseFromDropdown : function () {
    +      var selected = this.state.targetDB;
    +
    +      var setup = {
    +        title: 'Choose a database',
    +        id: 'data-importer-choose-db',
    +        selected: selected,
    +        selectOptions: this.getExistingDBList()
    +      };
    +      return <Components.SmallDropdown dropdownSetup = {setup} />;
    +    },
    +
    +    createNewDB: function () {
    +      return (
    +        <div id = "data-import-name-new-target-database">
    --- End diff --
    
    that's quite unusual formatting and it's in almost every component in this 
PR


---
If your project is set up for it, you can reply to this email and have your
reply appear on GitHub as well. If your project does not have this feature
enabled and wishes so, or if the feature is enabled but not working, please
contact infrastructure at infrastruct...@apache.org or file a JIRA ticket
with INFRA.
---

Reply via email to