| // 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. |
| |
| import app from "../../app"; |
| import React from "react"; |
| import ReactDOM from "react-dom"; |
| import Stores from "./stores"; |
| import Resources from "./resources"; |
| import Actions from "./actions"; |
| import Components from "../components/react-components"; |
| |
| const {TabElement, TabElementWrapper, Polling} = Components; |
| |
| const activeTasksStore = Stores.activeTasksStore; |
| |
| export const ActiveTasksController = React.createClass({ |
| |
| getStoreState () { |
| return { |
| collection: activeTasksStore.getCollection(), |
| searchTerm: activeTasksStore.getSearchTerm(), |
| selectedRadio: activeTasksStore.getSelectedRadio(), |
| |
| sortByHeader: activeTasksStore.getSortByHeader(), |
| headerIsAscending: activeTasksStore.getHeaderIsAscending() |
| |
| }; |
| }, |
| |
| getInitialState () { |
| return this.getStoreState(); |
| }, |
| |
| componentDidMount () { |
| Actions.init(new Resources.AllTasks()); |
| activeTasksStore.on('change', this.onChange, this); |
| }, |
| |
| componentWillUnmount () { |
| activeTasksStore.off('change', this.onChange, this); |
| }, |
| |
| onChange () { |
| this.setState(this.getStoreState()); |
| }, |
| |
| setNewSearchTerm (searchTerm) { |
| Actions.setSearchTerm(searchTerm); |
| }, |
| |
| switchTab (newRadioButton) { //tabs buttons |
| Actions.switchTab(newRadioButton); |
| }, |
| |
| tableHeaderOnClick (headerClicked) { |
| Actions.sortByColumnHeader(headerClicked); |
| }, |
| |
| render () { |
| const {collection, searchTerm, selectedRadio, sortByHeader, headerIsAscending} = this.state; |
| |
| const setSearchTerm = this.setNewSearchTerm; |
| const onTableHeaderClick = this.tableHeaderOnClick; |
| |
| return ( |
| <div id="active-tasks-page" className="scrollable"> |
| <div className="inner"> |
| <ActiveTasksFilterTabs |
| searchTerm={searchTerm} |
| selectedRadio={selectedRadio} |
| onSearch={setSearchTerm} |
| onRadioClick={this.switchTab}/> |
| <ActiveTaskTable |
| collection={collection} |
| searchTerm={searchTerm} |
| selectedRadio={selectedRadio} |
| onTableHeaderClick={onTableHeaderClick} |
| sortByHeader={sortByHeader} |
| headerIsAscending={headerIsAscending} /> |
| </div> |
| </div> |
| ); |
| } |
| }); |
| |
| var ActiveTasksFilterTabs = React.createClass({ |
| getDefaultProps () { |
| return { |
| radioNames : [ |
| 'All Tasks', |
| 'Replication', |
| 'Database Compaction', |
| 'Indexer', |
| 'View Compaction' |
| ] |
| }; |
| }, |
| |
| checked (radioName) { |
| return this.props.selectedRadio === radioName; |
| }, |
| |
| onRadioClick (e) { |
| var radioName = e.target.value; |
| this.props.onRadioClick(radioName); |
| }, |
| |
| createFilterTabs () { |
| return ( |
| this.props.radioNames.map((radioName, i) => { |
| const checked = this.checked(radioName); |
| |
| return ( |
| <TabElement |
| key={i} |
| selected={checked} |
| text={radioName} |
| onChange={this.onRadioClick} /> |
| ); |
| }) |
| ); |
| }, |
| |
| searchTermChange (e) { |
| var searchTerm = e.target.value; |
| this.props.onSearch(searchTerm); |
| }, |
| |
| render () { |
| const filterTabs = this.createFilterTabs(); |
| return ( |
| <TabElementWrapper> |
| {filterTabs} |
| <li className="component-tab-list-element"> |
| <input |
| id="active-tasks-search-box" |
| className="searchbox" |
| type="text" |
| name="search" |
| placeholder="Search for databases..." |
| value={this.props.searchTerm} |
| onChange={this.searchTermChange} /> |
| </li> |
| </TabElementWrapper> |
| ); |
| } |
| }); |
| |
| var ActiveTaskTable = React.createClass({ |
| render () { |
| var collection = this.props.collection; |
| var selectedRadio = this.props.selectedRadio; |
| var searchTerm = this.props.searchTerm; |
| var sortByHeader = this.props.sortByHeader; |
| var onTableHeaderClick = this.props.onTableHeaderClick; |
| var headerIsAscending = this.props.headerIsAscending; |
| |
| return ( |
| <div id="dashboard-lower-content"> |
| <table id="active-tasks-table" className="table table-bordered table-striped active-tasks"> |
| <ActiveTasksTableHeader |
| onTableHeaderClick={onTableHeaderClick} |
| sortByHeader={sortByHeader} |
| headerIsAscending={headerIsAscending}/> |
| <ActiveTasksTableBody |
| collection={collection} |
| selectedRadio={selectedRadio} |
| searchTerm={searchTerm}/> |
| </table> |
| </div> |
| ); |
| } |
| }); |
| |
| var ActiveTasksTableHeader = React.createClass({ |
| getDefaultProps () { |
| return { |
| headerNames : [ |
| ['type', 'Type'], |
| ['database', 'Database'], |
| ['started-on', 'Started on'], |
| ['updated-on', 'Updated on'], |
| ['pid', 'PID'], |
| ['progress', 'Status'] |
| ] |
| }; |
| }, |
| |
| createTableHeadingFields () { |
| var onTableHeaderClick = this.props.onTableHeaderClick; |
| var sortByHeader = this.props.sortByHeader; |
| var headerIsAscending = this.props.headerIsAscending; |
| return this.props.headerNames.map(function (header) { |
| return <TableHeader |
| headerName={header[0]} |
| displayName={header[1]} |
| key={header[0]} |
| onTableHeaderClick={onTableHeaderClick} |
| sortByHeader={sortByHeader} |
| headerIsAscending={headerIsAscending} />; |
| }); |
| }, |
| |
| render () { |
| return ( |
| <thead> |
| <tr>{this.createTableHeadingFields()}</tr> |
| </thead> |
| ); |
| } |
| }); |
| |
| var TableHeader = React.createClass({ |
| arrow () { |
| var sortBy = this.props.sortByHeader; |
| var currentName = this.props.headerName; |
| var headerIsAscending = this.props.headerIsAscending; |
| var arrow = headerIsAscending ? 'icon icon-caret-up' : 'icon icon-caret-down'; |
| |
| if (sortBy === currentName) { |
| return <i className={arrow}></i>; |
| } |
| }, |
| |
| onTableHeaderClick (e) { |
| var headerSelected = e.target.value; |
| this.props.onTableHeaderClick(headerSelected); |
| }, |
| |
| render () { |
| var arrow = this.arrow(); |
| var th_class = 'header-field ' + this.props.headerName; |
| |
| return ( |
| <td className={th_class + " tableheader"} value={this.props.headerName}> |
| <input |
| type="radio" |
| name="header-field" |
| id={this.props.headerName} |
| value={this.props.headerName} |
| className="header-field radio" |
| onChange={this.onTableHeaderClick} /> |
| <label |
| className="header-field label-text active-tasks-header noselect" |
| htmlFor={this.props.headerName}> |
| {this.props.displayName} {arrow} |
| </label> |
| </td> |
| ); |
| } |
| }); |
| |
| var ActiveTasksTableBody = React.createClass({ |
| |
| getStoreState () { |
| return { |
| filteredTable: activeTasksStore.getFilteredTable(this.props.collection) |
| }; |
| }, |
| |
| getInitialState () { |
| return this.getStoreState(); |
| }, |
| |
| componentWillReceiveProps () { |
| this.setState({ |
| filteredTable: activeTasksStore.getFilteredTable(this.props.collection) |
| }); |
| }, |
| |
| createRows () { |
| var isThereASearchTerm = this.props.searchTerm.trim() === ""; |
| |
| if (this.state.filteredTable.length === 0) { |
| return isThereASearchTerm ? this.noActiveTasks() : this.noActiveTasksMatchFilter(); |
| } |
| |
| return _.map(this.state.filteredTable, function (item, key) { |
| return <ActiveTaskTableBodyContents key={key} item={item} />; |
| }); |
| }, |
| |
| noActiveTasks () { |
| var type = this.props.selectedRadio; |
| if (type === "All Tasks") { |
| type = ""; |
| } |
| |
| return ( |
| <tr className="no-matching-database-on-search"> |
| <td colSpan="6">No active {type} tasks.</td> |
| </tr> |
| ); |
| }, |
| |
| noActiveTasksMatchFilter () { |
| var type = this.props.selectedRadio; |
| if (type === "All Tasks") { |
| type = ""; |
| } |
| |
| return ( |
| <tr className="no-matching-database-on-search"> |
| <td colSpan="6">No active {type} tasks match with filter: "{this.props.searchTerm}"</td> |
| </tr> |
| ); |
| }, |
| |
| render () { |
| return ( |
| <tbody className="js-tasks-go-here"> |
| {this.createRows()} |
| </tbody> |
| ); |
| } |
| }); |
| |
| var ActiveTaskTableBodyContents = React.createClass({ |
| getInfo (item) { |
| return { |
| type : item.type, |
| objectField: activeTasksHelpers.getDatabaseFieldMessage(item), |
| started_on: activeTasksHelpers.getTimeInfo(item.started_on), |
| updated_on: activeTasksHelpers.getTimeInfo(item.updated_on), |
| pid: item.pid.replace(/[<>]/g, ''), |
| progress: activeTasksHelpers.getProgressMessage(item), |
| }; |
| }, |
| |
| multilineMessage (messageArray, optionalClassName) { |
| |
| if (!optionalClassName) { |
| optionalClassName = ''; |
| } |
| var cssClasses = 'multiline-active-tasks-message ' + optionalClassName; |
| |
| return messageArray.map(function (msgLine, iterator) { |
| return <p key={iterator} className={cssClasses}>{msgLine}</p>; |
| }); |
| }, |
| |
| render () { |
| var rowData = this.getInfo(this.props.item); |
| var objectFieldMsg = this.multilineMessage(rowData.objectField, 'to-from-database'); |
| var startedOnMsg = this.multilineMessage(rowData.started_on, 'time'); |
| var updatedOnMsg = this.multilineMessage(rowData.updated_on, 'time'); |
| var progressMsg = this.multilineMessage(rowData.progress); |
| |
| return ( |
| <tr> |
| <td>{rowData.type}</td> |
| <td>{objectFieldMsg}</td> |
| <td>{startedOnMsg}</td> |
| <td>{updatedOnMsg}</td> |
| <td>{rowData.pid}</td> |
| <td>{progressMsg}</td> |
| </tr> |
| ); |
| } |
| }); |
| |
| export const ActiveTasksPollingWidgetController = React.createClass({ |
| |
| getStoreState () { |
| return { |
| collection: activeTasksStore.getBackboneCollection() |
| }; |
| }, |
| |
| getInitialState () { |
| return this.getStoreState(); |
| }, |
| |
| componentDidMount () { |
| activeTasksStore.on('change', this.onChange, this); |
| }, |
| |
| onChange () { |
| this.setState(this.getStoreState()); |
| }, |
| |
| runPollingUpdate () { |
| Actions.runPollingUpdate(this.state.collection); |
| }, |
| |
| getPluralForLabel () { |
| return this.state.pollingInterval === "1" ? '' : 's'; |
| }, |
| |
| render () { |
| return ( |
| <div className="active-tasks__polling-wrapper"> |
| <Polling |
| min={1} |
| max={30} |
| stepSize={1} |
| startValue={15} |
| valueUnits={"second"} |
| onPoll={this.runPollingUpdate} |
| /> |
| </div> |
| ); |
| } |
| }); |
| |
| var activeTasksHelpers = { |
| getTimeInfo (timeStamp) { |
| var timeMessage = [ |
| app.helpers.formatDate(timeStamp), |
| app.helpers.getDateFromNow(timeStamp) |
| ]; |
| return timeMessage; |
| }, |
| |
| getDatabaseFieldMessage (item) { |
| var type = item.type; |
| var databaseFieldMessage = []; |
| |
| if (type === 'replication') { |
| databaseFieldMessage.push('From: ' + item.source); |
| databaseFieldMessage.push('To: ' + item.target); |
| } else if (type === 'indexer') { |
| databaseFieldMessage.push(item.database); |
| databaseFieldMessage.push('(View: ' + item.design_document + ')'); |
| } else { |
| databaseFieldMessage.push(item.database); |
| } |
| |
| return databaseFieldMessage; |
| }, |
| |
| getProgressMessage (item) { |
| var progressMessage = []; |
| var type = item.type; |
| |
| if (_.has(item, 'progress')) { |
| progressMessage.push('Progress: ' + item.progress + '%'); |
| } |
| |
| if (type === 'indexer') { |
| progressMessage.push( |
| 'Processed ' + item.changes_done + ' of ' + item.total_changes + ' changes.' |
| ); |
| } else if (type === 'replication') { |
| progressMessage.push(item.docs_written + ' docs written.'); |
| |
| if (_.has(item, 'changes_pending')) { |
| progressMessage.push(item.changes_pending + ' pending changes.'); |
| } |
| } |
| |
| if (_.has(item, 'changes_done')) { |
| progressMessage.push(item.changes_done + ' Changes done.'); |
| } |
| |
| return progressMessage; |
| }, |
| |
| getSourceSequence (item) { |
| return item.source_seq; |
| } |
| |
| }; |
| |
| export default { |
| ActiveTasksController: ActiveTasksController, |
| ActiveTasksFilterTabs: ActiveTasksFilterTabs, |
| |
| ActiveTaskTable: ActiveTaskTable, |
| ActiveTasksTableHeader: ActiveTasksTableHeader, |
| TableHeader: TableHeader, |
| ActiveTasksTableBody: ActiveTasksTableBody, |
| ActiveTaskTableBodyContents: ActiveTaskTableBodyContents, |
| |
| ActiveTasksPollingWidgetController: ActiveTasksPollingWidgetController |
| }; |