http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx b/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx deleted file mode 100644 index 136d95f..0000000 --- a/contrib/views/storm/src/main/resources/scripts/views/TopologyDetailView.jsx +++ /dev/null @@ -1,1039 +0,0 @@ -/** - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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([ - 'jsx!components/Table', - 'jsx!modules/Table/Pagination', - 'utils/Utils', - 'react', - 'react-dom', - 'collections/BaseCollection', - 'models/VTopology', - 'models/BaseModel', - 'jsx!containers/TopologyConfiguration', - 'jsx!containers/TopologyDetailGraph', - 'jsx!components/Breadcrumbs', - 'jsx!components/SearchLogs', - 'jsx!components/BarChart', - 'jsx!views/RebalanceView', - 'bootbox', - 'x-editable', - 'bootstrap', - 'bootstrap-switch' - ],function(Table, Pagination, Utils, React, ReactDOM, BaseCollection, VTopology, BaseModel, TopologyConfiguration, TopologyDetailGraph, Breadcrumbs, SearchLogs, BarChart, RebalanceView, bootbox, XEditable){ - 'use strict'; - - return React.createClass({ - displayName: 'TopologyDetailView', - propTypes: { - id: React.PropTypes.string.isRequired - }, - getInitialState: function(){ - this.model = new VTopology({'id': this.props.id}); - this.spoutCollection = new BaseCollection(); - this.boltCollection = new BaseCollection(); - this.lagCollection = new BaseCollection(); - this.systemFlag = false; - this.windowSize = ':all-time'; - return { - model: this.model, - graphData: {}, - logLevels: {}, - rebalanceModalOpen: false, - lagData: [], - hideKafkaLagBox: false, - workerHostPort: '' - }; - }, - componentWillMount: function(){ - $('.loader').show(); - this.initializeData(); - }, - componentDidMount: function(){ - $(".boot-switch.systemSum").bootstrapSwitch({ - size: 'small', - onSwitchChange: function(event, state){ - this.systemFlag = state; - this.initializeData(); - }.bind(this) - }); - $("#slideContent").hide(); - $(".boot-switch.debug").bootstrapSwitch({ - size: 'small', - onSwitchChange: function(event, state){ - this.debugAction(state); - }.bind(this) - }); - $("#lag-graph").hide(); - $("#kafkaSpout").bootstrapSwitch({ - onSwitchChange: function() { - $('#lag-graph, #lag-table').slideToggle(); - } - }); - $('[data-rel="tooltip"]').tooltip(); - $('.loader').hide(); - }, - componentWillUpdate: function(){ - $('#collapse-spout').off('hidden.bs.collapse'); - $('#collapse-spout').off('shown.bs.collapse'); - $('#collapse-bolt').off('hidden.bs.collapse'); - $('#collapse-bolt').off('shown.bs.collapse'); - $('#modal-rebalance').off('hidden.bs.modal'); - this.spoutCollection.getFirstPage().fullCollection.reset([]); - this.spouts = this.renderSpouts(); - this.boltCollection.getFirstPage().fullCollection.reset([]); - this.bolts = this.renderBolts(); - }, - componentDidUpdate: function(){ - $('#collapse-spout').on('hidden.bs.collapse', function () { - $("#spout-box").toggleClass("fa-compress fa-expand"); - }).on('shown.bs.collapse', function() { - $("#spout-box").toggleClass("fa-compress fa-expand"); - }); - - $('#collapse-bolt').on('hidden.bs.collapse', function () { - $("#bolt-box").toggleClass("fa-compress fa-expand"); - }).on('shown.bs.collapse', function() { - $("#bolt-box").toggleClass("fa-compress fa-expand"); - }); - $('#modal-rebalance').on('hidden.bs.modal', function (e) { - this.initializeData(); - this.setState({"rebalanceModalOpen":false}); - }.bind(this)); - if(this.state.rebalanceModalOpen){ - $('#modal-rebalance').modal("show"); - } - if(this.refs.barChart){ - ReactDOM.findDOMNode(document.getElementById('lag-graph')).appendChild(this.refs.barChart.legendsEl) - } - }, - initializeData: function(){ - this.model.getData({ - id: this.model.get('id'), - window: this.windowSize, - sys: this.systemFlag, - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.model.set(model); - this.setState({"model": this.model}); - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in fetching topology details."); - } - }); - this.initializeGraphData(); - this.initializeLogConfig(); - this.initializeLagData(); - this.initializeWorkerData(); - }, - initializeGraphData: function(){ - $('#graphLoader').show(); - this.model.getGraphData({ - id: this.model.get('id'), - window: this.windowSize, - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - if(_.isString(model)){ - model = JSON.parse(model); - } - this.setState({graphData: model}); - } - $('#graphLoader').hide(); - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in fetching topology visualization data."); - } - }); - }, - - initializeLogConfig: function() { - this.collection = new BaseCollection(); - this.model.getLogConfig({ - id: this.model.get('id'), - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.resetLogCollection(model); - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in fetching log configuration data."); - } - }); - }, - - initializeLagData: function(){ - $('#kafkaLoader').show(); - this.model.getTopologyLag({ - id: this.model.get('id'), - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - if(model && _.keys(model).length > 0){ - var keys = _.keys(model); - var arr = []; - for(var i = 0; i < keys.length; i++){ - var data = model[keys[i]]; - var topicKeys = _.keys(data.spoutLagResult); - for(var j = 0; j < topicKeys.length; j++){ - var topicName = topicKeys[j]; - var partitionData = data.spoutLagResult[topicName]; - var id = _.keys(partitionData); - for(var k = 0; k < id.length; k++){ - var partitionId = id[k]; - var obj = partitionData[partitionId]; - obj['spoutId'] = data.spoutId; - obj['spoutType'] = data.spoutType; - obj['partition'] = partitionId; - obj['topic'] = topicName; - arr.push(obj); - } - } - } - this.resetLagCollection(arr); - } else { - this.setState({hideKafkaLagBox : true}); - } - } - $('#kafkaLoader').hide(); - }.bind(this) - }) - }, - initializeWorkerData: function(){ - this.model.getWorkerHost({ - id: this.model.get('id'), - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - var workerHostPortArr = model.hostPortList; - var result = ''; - for(var i = 0; i < workerHostPortArr.length; i++){ - result += workerHostPortArr[i].host+':'+workerHostPortArr[i].port - if(i !== workerHostPortArr.length - 1){ - result += ', \n'; - } - } - this.setState({'workerHostPort': result}) - } - }.bind(this) - }) - }, - resetLagCollection: function(model){ - this.lagCollection.reset(model); - this.setState({"lagData": model}); - }, - getLagColums: function(){ - var self = this; - return [ - {name: 'spoutId', title: 'Id', tooltip:'Id'}, - {name: 'topic', title: 'Topic', tooltip:'Topic'}, - {name: 'partition', title: 'Partition', tooltip:'Partition'}, - {name: 'logHeadOffset', title: 'Latest Offset', tooltip:'Latest Offset'}, - {name: 'consumerCommittedOffset', title: 'Spout Committed Offset', tooltip:'Spout Committed Offset'}, - {name: 'lag', title: 'Lag', tooltip:'Lag'}, - ]; - }, - resetLogCollection: function(model) { - this.collection.reset(); - this.setState({logLevels: model.namedLoggerLevels}); - var keys = _.keys(this.state.logLevels); - keys.map(function(key, index) { - var obj = this.state.logLevels[key]; - var model = new BaseModel({ - logger: key, - target_level: obj.target_level, - timeout: obj.timeout, - timeout_epoch: obj.timeout_epoch - }); - this.collection.add(model); - }.bind(this)); - - this.collection.add(new BaseModel({ - logger: 'com.your.organization.LoggerName', - target_level: 'ALL', - timeout: 30, - timeout_epoch: 0, - isAdd: true - })); - }, - - renderAccordion: function(type, header, searchField, searchCb, collection, emptyText, columns, toggleCb){ - return ( - <div className="box"> - <div className="box-header" data-toggle="collapse" data-target={"#collapse-"+type} aria-expanded="false" aria-controls={"collapse-"+type}> - <h4>{header}</h4> - <h4 className="box-control"> - <a href="javascript:void(0);" className="primary"> - <i className="fa fa-compress" id={type+"-box"} onClick={toggleCb}></i> - </a> - </h4> - </div> - <div className="box-body collapse in" id={"collapse-"+type}> - <div className="input-group col-sm-4"> - <input type="text" onKeyUp={searchCb} className="form-control" placeholder={"Search by "+searchField} /> - <span className="input-group-btn"> - <button className="btn btn-primary" type="button"><i className="fa fa-search"></i></button> - </span> - </div> - <Table className="table table-striped" collection={collection} emptyText={emptyText} columns={columns()} /> - <Pagination collection={collection} /> - </div> - </div> - ); - }, - renderSpouts: function(){ - if(this.state.model.has('spouts')){ - Utils.ArrayToCollection(this.state.model.get('spouts'), this.spoutCollection); - this.spoutCollection.searchFields = ['spoutId']; - var searchCb = function(e){ - var value = e.currentTarget.value; - this.spoutCollection.search(value); - }.bind(this); - var toggleCb = function(e){ - $("#collapse-spout").collapse('toggle'); - } - return this.renderAccordion('spout', 'Spouts', 'id', searchCb, this.spoutCollection, 'No spouts found !', this.getSpoutColumns, toggleCb); - } else { - return null; - } - }, - getSpoutColumns: function(){ - var self = this; - return [ - {name: 'spoutId', title: 'Id', tooltip:'The ID assigned to a the Component by the Topology. Click on the name to view the Component\'s page.', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - var topologyId = self.state.model.has('id') ? self.state.model.get('id') : ""; - return ( <a href={"#!/topology/"+topologyId+"/component/"+this.props.model.get('spoutId')}>{this.props.model.get('spoutId')}</a>); - } - })}, - {name: 'executors', title: 'Executors', tooltip:'Executors are threads in a Worker process.'}, - {name: 'tasks', title: 'Tasks', tooltip:'A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors.'}, - {name: 'emitted', title: 'Emitted', tooltip:'The number of Tuples emitted.'}, - {name: 'transferred', title: 'Transferred', tooltip:'The number of Tuples emitted that sent to one or more bolts.'}, - {name: 'completeLatency', title: 'Complete Latency (ms)', tooltip:'The average time a Tuple "tree" takes to be completely processed by the Topology. A value of 0 is expected if no acking is done.'}, - {name: 'acked', title: 'Acked', tooltip:'The number of Tuple "trees" successfully processed. A value of 0 is expected if no acking is done.'}, - {name: 'failed', title: 'Failed', tooltip:'The number of Tuple "trees" that were explicitly failed or timed out before acking was completed. A value of 0 is expected if no acking is done.'}, - {name: 'errorHost', title: 'Error Host:Port', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - return (<span>{this.props.model.has('errorHost') && this.props.model.get('errorHost') !== '' ? this.props.model.get('errorHost')+':'+this.props.model.get('errorPort') : null}</span>); - } - })}, - {name: 'lastError', title: 'Last Error'}, - {name: 'errorTime', title: 'Error Time', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - if(this.props.model.get('errorTime') && this.props.model.get('errorTime') != 0) { - var d = new Date(this.props.model.get('errorTime') * 1000), - date = d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); - return (<span>{date}</span>); - } else return (<span></span>); - } - })} - ]; - }, - renderBolts: function(){ - if(this.state.model.has('bolts')){ - Utils.ArrayToCollection(this.state.model.get('bolts'), this.boltCollection); - this.boltCollection.searchFields = ['boltId']; - var searchCb = function(e){ - var value = e.currentTarget.value; - this.boltCollection.search(value); - }.bind(this); - var toggleCb = function(e){ - $("#collapse-bolt").collapse('toggle'); - } - return this.renderAccordion('bolt', 'Bolts', 'id', searchCb, this.boltCollection, 'No bolts found !', this.getBoltColumns, toggleCb); - } else { - return null; - } - }, - getBoltColumns: function(){ - var self = this; - return [ - {name: 'boltId', title: 'Id', tooltip:'The ID assigned to a the Component by the Topology. Click on the name to view the Component\'s page.', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - var topologyId = self.state.model.has('id') ? self.state.model.get('id') : ""; - return ( <a href={"#!/topology/"+topologyId+"/component/"+this.props.model.get('boltId')}>{this.props.model.get('boltId')}</a>); - } - })}, - {name: 'executors', title: 'Executors', tooltip:'Executors are threads in a Worker process.'}, - {name: 'tasks', title: 'Tasks', tooltip:'A Task is an instance of a Bolt or Spout. The number of Tasks is almost always equal to the number of Executors.'}, - {name: 'emitted', title: 'Emitted', tooltip:'The number of Tuples emitted.'}, - {name: 'transferred', title: 'Transferred', tooltip:'The number of Tuples emitted that sent to one or more bolts.'}, - {name: 'capacity', title: 'Capacity (last 10m)', tooltip:"If this is around 1.0, the corresponding Bolt is running as fast as it can, so you may want to increase the Bolt's parallelism. This is (number executed * average execute latency) / measurement time."}, - {name: 'executeLatency', title: 'Execute Latency (ms)', tooltip:'The average time a Tuple spends in the execute method. The execute method may complete without sending an Ack for the tuple.'}, - {name: 'executed', title: 'Executed', tooltip:'The number of incoming Tuples processed.'}, - {name: 'processLatency', title: 'Process Latency (ms)', tooltip:'The average time it takes to Ack a Tuple after it is first received. Bolts that join, aggregate or batch may not Ack a tuple until a number of other Tuples have been received.'}, - {name: 'acked', title: 'Acked', tooltip:'The number of Tuples acknowledged by this Bolt.'}, - {name: 'failed', title: 'Failed', tooltip:'The number of tuples Failed by this Bolt.'}, - {name: 'errorHost', title: 'Error Host:Port', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - return (<span>{this.props.model.has('errorHost') && this.props.model.get('errorHost') !== '' ? this.props.model.get('errorHost')+':'+this.props.model.get('errorPort') : null}</span>); - } - })}, - {name: 'lastError', title: 'Last Error'}, - {name: 'errorTime', title: 'Error Time', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - if(this.props.model.get('errorTime') && this.props.model.get('errorTime') != 0) { - var d = new Date(this.props.model.get('errorTime') * 1000), - date = d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); - return (<span>{date}</span>); - } else return (<span></span>); - } - })} - ]; - }, - renderWindowOptions: function(){ - if(this.state.model.has('topologyStats')){ - return this.state.model.get('topologyStats').map(function(object, i){ - return ( <option key={i} value={object.window}>{object.windowPretty}</option> ); - }); - } else { - return null; - } - }, - handleWindowChange: function(e){ - this.windowSize = e.currentTarget.value; - this.initializeData(); - }, - getLinks: function() { - var links = [ - {link: '#!/dashboard', title: 'Dashboard'}, - {link: '#!/topology', title: 'Topology Listing'}, - {link: 'javascript:void(0);', title: this.state.model.has('name') ? this.state.model.get('name') : ""} - ]; - return links; - }, - - addLogLevel: function(e) { - var self = this; - var id = e.currentTarget.getAttribute('data-name'); - var namedLoggerLevels = {}; - var targetLevel = $(e.currentTarget).parent().siblings().find('.target-level').val(), - timeout = $(e.currentTarget).parent().siblings().find('.timeout').html(), - logger = $(e.currentTarget).parent().siblings().find('.logger').html(); - - namedLoggerLevels[logger] = { - target_level: targetLevel, - reset_level: 'INFO', - timeout: parseInt(timeout, 10) - }; - - var dataObj = { - namedLoggerLevels: namedLoggerLevels - } - - this.model.saveLogConfig({ - id: this.model.get('id'), - data: JSON.stringify(dataObj), - contentType: "application/json", - success: function(model, response, options){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.resetLogCollection(model); - Utils.notifySuccess("Log configuration added successfully."); - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in saving log configuration data."); - } - - }); - }, - applyLogLevel: function(e) { - var self = this; - var id = e.currentTarget.getAttribute('data-name'); - var namedLoggerLevels = {}; - var targetLevel = $(e.currentTarget).parents('td').siblings().find('.target-level').val(), - timeout = $(e.currentTarget).parents('td').siblings().find('.timeout').html(), - logger = $(e.currentTarget).parents('td').siblings().find('.logger').html(); - - namedLoggerLevels[logger] = { - target_level: targetLevel, - reset_level: 'INFO', - timeout: parseInt(timeout, 10) - }; - - var dataObj = { - namedLoggerLevels: namedLoggerLevels - } - - this.model.saveLogConfig({ - id: this.model.get('id'), - data: JSON.stringify(dataObj), - contentType: "application/json", - success: function(model, response, options){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.resetLogCollection(model); - Utils.notifySuccess("Log configuration applied successfully."); - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in applying log configuration data."); - } - }); - }, - clearLogLevel: function(e) { - var self = this; - var id = e.currentTarget.getAttribute('data-name'); - var namedLoggerLevels = {}; - var logger = $(e.currentTarget).parents('td').siblings().find('.logger').html(); - - namedLoggerLevels[logger] = { - target_level: null, - reset_level: 'INFO', - timeout: 0 - }; - - var dataObj = { - namedLoggerLevels: namedLoggerLevels - } - - this.model.saveLogConfig({ - id: this.model.get('id'), - data: JSON.stringify(dataObj), - contentType: "application/json", - success: function(model, response, options){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.resetLogCollection(model); - Utils.notifySuccess("Log configuration cleared successfully."); - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in clearing log configuration data."); - } - }); - }, - getColumns: function(){ - var self = this; - return [ - {name: 'logger', title: 'Logger', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - if(this.props.model.get('isAdd')) - return (<a href="javascript:void(0)" className="x-editable logger">{this.props.model.get('logger')}</a>); - else return (<a href="javascript:void(0)" className="logger">{this.props.model.get('logger')}</a>); - }, - componentDidMount: function() { - $(".x-editable").editable({ - mode: 'inline' - }); - }}) - }, - {name: 'target_level', title: 'Level', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function() { - return ( - <select className="form-control target-level" defaultValue={this.props.model.get('target_level')}> - <option value="ALL">ALL</option> - <option value="TRACE">TRACE</option> - <option value="DEBUG">DEBUG</option> - <option value="INFO">INFO</option> - <option value="WARN">WARN</option> - <option value="ERROR">ERROR</option> - <option value="FATAL">FATAL</option> - <option value="OFF">OFF</option> - </select> - ); - } - })}, - {name: 'timeout', title: 'Timeout', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - return (<a href="javascript:void(0)" className="x-editable timeout">{this.props.model.get('timeout')}</a>); - }, - componentDidMount: function() { - $(".x-editable").editable({ - mode: 'inline' - }); - }}) - }, - {name: 'timeout_epoch', title: 'Expires At', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - if(this.props.model.get('timeout_epoch') != 0) { - var d = new Date(this.props.model.get('timeout_epoch')), - date = d.toLocaleDateString() + ' ' + d.toLocaleTimeString(); - return (<span>{date}</span>); - } else return (<span></span>); - - } - }) - }, - {name: 'action', title: 'Action', component: React.createClass({ - propTypes: { - model: React.PropTypes.object.isRequired - }, - render: function(){ - if(this.props.model.get('isAdd')) - return( - <a href="javascript:void(0)" data-name={this.props.model.get('logger')} className="btn btn-primary btn-xs" onClick={self.addLogLevel}><i className="fa fa-plus"></i></a> - ) - else - return ( - <span> - <a href="javascript:void(0)" data-name={this.props.model.get('logger')} className="btn btn-success btn-xs" onClick={self.applyLogLevel}><i className="fa fa-check"></i></a> - <a href="javascript:void(0)" data-name={this.props.model.get('logger')} className="btn btn-danger btn-xs" onClick={self.clearLogLevel}><i className="fa fa-times"></i></a> - </span> - ); - } - })} - ]; - }, - toggleSlide: function() { - $("#slideContent").slideToggle(); - }, - - renderStatsRow: function(){ - var statsArr = this.state.model.get('topologyStats'); - if(statsArr){ - return statsArr.map(function(stats, i){ - return ( - <tr key={i}> - <td>{stats.windowPretty}</td> - <td>{stats.emitted}</td> - <td>{stats.transferred}</td> - <td>{stats.completeLatency}</td> - <td>{stats.acked}</td> - <td>{stats.failed}</td> - </tr> - ); - }); - } - }, - render: function() { - var status = this.state.model.has('status') ? this.state.model.get('status') : null; - var workersTotal = this.state.model.has('workersTotal') ? this.state.model.get('workersTotal').toString() : '0'; - if(this.state.model.get('debug')){ - $(".boot-switch.debug").bootstrapSwitch('state', true, true); - } else { - $(".boot-switch.debug").bootstrapSwitch('state', false, true); - } - return ( - <div> - <Breadcrumbs links={this.getLinks()} /> - <SearchLogs id={this.model.get('id')}/> - <div className="row"> - <div className="col-sm-12"> - <div className="box filter"> - <div className="box-body form-horizontal"> - <div className="form-group no-margin"> - <label className="col-sm-1 control-label">Window</label> - <div className="col-sm-2"> - <select className="form-control" onChange={this.handleWindowChange} value={this.windowSize}> - {this.renderWindowOptions()} - </select> - </div> - <label className="col-sm-2 control-label">System Summary</label> - <div className="col-sm-2"> - <input className="boot-switch systemSum" type="checkbox" /> - </div> - <label className="col-sm-1 control-label">Debug</label> - <div className="col-sm-1"> - <input className="boot-switch debug" type="checkbox"/> - </div> - <div className="col-sm-3 text-right"> - <div className="btn-group" role="group"> - <button type="button" className="btn btn-primary" onClick={this.handleTopologyActivation} title="Activate" data-rel="tooltip" disabled={status === 'ACTIVE' ? "disabled" : null}> - <i className="fa fa-play"></i> - </button> - <button type="button" className="btn btn-primary" onClick={this.handleTopologyDeactivation} title="Deactivate" data-rel="tooltip" disabled={status === 'INACTIVE' ? "disabled" : null}> - <i className="fa fa-stop"></i> - </button> - <button type="button" className="btn btn-primary" onClick={this.handleTopologyRebalancing} title="Rebalance" data-rel="tooltip" disabled={status === 'REBALANCING' ? "disabled" : null}> - <i className="fa fa-balance-scale"></i> - </button> - <button type="button" className="btn btn-primary" onClick={this.handleTopologyKilling} title="Kill" data-rel="tooltip" disabled={status === 'KILLED' ? "disabled" : null}> - <i className="fa fa-ban"></i> - </button> - <button type="button" className="btn btn-primary" onClick={this.toggleSlide} title="Change Log Level" data-rel="tooltip"> - <i className="fa fa-file-o"></i> - </button> - </div> - </div> - </div> - <div className="row" id="slideContent"> - <div className="col-sm-12"> - <hr/> - <h4 className="col-sm-offset-5">Change Log Level</h4> - <p>Modify the logger levels for topology. Note that applying a setting restarts the timer in the workers. To configure the root logger, use the name ROOT.</p> - <Table className="table no-margin" collection={this.collection} columns={this.getColumns()}/> - </div> - </div> - </div> - </div> - </div> - </div> - <div className="row"> - <div className="col-sm-5"> - <div className="summary-tile"> - <div className="summary-title">Topology Summary</div> - <div className="summary-body form-horizontal"> - <div className="form-group"> - <label className="col-sm-4 control-label">ID:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('id')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Owner:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('owner')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Status:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('status')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Uptime:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('uptime')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Workers:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('workersTotal')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Executors:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('executorsTotal')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Tasks:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('tasksTotal')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Memory:</label> - <div className="col-sm-8"> - <p className="form-control-static">{this.state.model.get('assignedTotalMem')}</p> - </div> - </div> - <div className="form-group"> - <label className="col-sm-4 control-label">Worker-Host:Port:</label> - <div className="col-sm-8"> - <p className="form-control-static preformatted">{this.state.workerHostPort}</p> - </div> - </div> - - </div> - </div> - </div> - <div className="col-sm-7"> - <div className="stats-tile"> - <div className="stats-title">Topology Stats</div> - <div className="stats-body"> - <table className="table table-enlarge"> - <thead> - <tr> - <th><span data-rel="tooltip" title="The past period of time for which the statistics apply.">Window</span></th> - <th><span data-rel="tooltip" title="The number of Tuples emitted.">Emitted</span></th> - <th><span data-rel="tooltip" title="The number of Tuples emitted that sent to one or more bolts.">Transferred</span></th> - <th><span data-rel="tooltip" title='The average time a Tuple "tree" takes to be completely processed by the Topology. A value of 0 is expected if no acking is done.'>Complete Latency (ms)</span></th> - <th><span data-rel="tooltip" title='The number of Tuple "trees" successfully processed. A value of 0 is expected if no acking is done.'>Acked</span></th> - <th><span data-rel="tooltip" title='The number of Tuple "trees" that were explicitly failed or timed out before acking was completed. A value of 0 is expected if no acking is done.'>Failed</span></th> - </tr> - </thead> - <tbody> - {this.renderStatsRow()} - </tbody> - </table> - </div> - </div> - </div> - </div> - <div className="row"> - <div className="col-sm-12"> - <div className="inner-loader" id="graphLoader" /> - <TopologyDetailGraph model={this.state.model} graphData={this.state.graphData}/> - </div> - </div> - {this.state.hideKafkaLagBox ? null : - <div className="row"> - <div className="col-sm-12"> - <div className="box"> - <div className="box-header"> - <h4>Kafka Spout Lag</h4> - <div className="box-control"> - <input - id="kafkaSpout" - type="checkbox" - data-size="mini" - data-off-color="success" - data-off-text="Table" - data-on-color="info" - data-on-text="Graph" /> - </div> - </div> - <div className="box-body"> - <div className="row"> - <div className="col-sm-12"> - <div className="inner-loader" id="kafkaLoader" /> - <div id="lag-graph"> - {this.lagCollection.length > 0 ? - <BarChart - ref="barChart" - width={window != window.parent ? 1100 : 1300} - height={400} - xAttr="spoutId-partition" - yAttr="count" - data={this.lagCollection.toJSON().map(function(d){ - return { - 'Latest Offset': d.logHeadOffset, - 'Spout Committed Offset': d.consumerCommittedOffset, - 'spoutId-partition': d.spoutId+'-'+d.partition - }; - })} - /> - : null} - </div> - <div id="lag-table"> - <Table - className="table table-striped table-bordered" - collection={this.lagCollection} - emptyText="No Data Found." - columns={this.getLagColums()} - /> - </div> - </div> - </div> - </div> - </div> - </div> - </div> - } - <div className="row"> - <div className="col-sm-12"> - {this.spouts} - </div> - </div> - <div className="row"> - <div className="col-sm-12"> - {this.bolts} - </div> - </div> - <div className="row"> - <div className="col-sm-12"> - <TopologyConfiguration configArr={this.state.model.get('configuration') ? this.state.model.get('configuration') : {}}/> - </div> - </div> - {this.state.rebalanceModalOpen ? <RebalanceView modalId="modal-rebalance" topologyId={this.state.model.get('id')} topologyExecutors={workersTotal} spouts={this.state.model.get('spouts') ? this.state.model.get('spouts') : []} bolts={this.state.model.get('bolts') ? this.state.model.get('bolts') : []}/> : null} - </div> - ); - }, - handleTopologyActivation: function(e){ - if(this.model.get('status') !== 'ACTIVE'){ - var msg = "Do you really want to activate this topology ?"; - var successCb = function(){ - this.model.activateTopology({ - id: this.model.get('id'), - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.initializeData(); - Utils.notifySuccess("Topology activated successfully.") - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in activating topology."); - } - }); - }.bind(this); - Utils.ConfirmDialog(msg, '', successCb); - } - }, - handleTopologyDeactivation: function(e){ - if(this.model.get('status') !== 'INACTIVE'){ - var msg = "Do you really want to deactivate this topology ?"; - var successCb = function(){ - this.model.deactivateTopology({ - id: this.model.get('id'), - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.initializeData(); - Utils.notifySuccess("Topology deactivated successfully.") - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in deactivating topology."); - } - }); - }.bind(this); - Utils.ConfirmDialog(msg, '', successCb); - } - }, - handleTopologyRebalancing: function(e){ - if(this.model.get('status') !== 'REBALANCING'){ - this.setState({"rebalanceModalOpen":true}); - } - }, - handleTopologyKilling: function(e){ - if(this.model.get('status') !== 'KILLED'){ - bootbox.prompt({ - title: 'Are you sure you want to kill this topology ? If yes, please, specify wait time in seconds.', - value: "30", - buttons: { - confirm: { - label: 'Yes', - className: "btn-success", - }, - cancel: { - label: 'No', - className: "btn-default", - } - }, - callback: function(result) { - if(result != null){ - this.model.killTopology({ - id: this.model.get('id'), - waitTime: result, - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.initializeData(); - Utils.notifySuccess("Topology killed successfully.") - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in killing topology."); - } - }); - } - }.bind(this) - }); - } - }, - debugAction: function(toEnableFlag){ - if(toEnableFlag){ - bootbox.prompt({ - title: 'Do you really want to debug this topology ? If yes, please, specify sampling percentage.', - value: this.model.get("samplingPct") ? this.model.get("samplingPct") : '10', - buttons: { - confirm: { - label: 'Yes', - className: "btn-success", - }, - cancel: { - label: 'No', - className: "btn-default", - } - }, - callback: function(result) { - if(result == null) { - $(".boot-switch.debug").bootstrapSwitch('toggleState', true); - } else if(result == "" || isNaN(result) || result < 0) { - Utils.notifyError("Enter valid sampling percentage"); - $(".boot-switch.debug").bootstrapSwitch('toggleState', true); - } else { - this.model.debugTopology({ - id: this.model.get('id'), - debugType: 'enable', - percent: result, - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.initializeData(); - Utils.notifySuccess("Debugging enabled successfully.") - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in enabling debugging."); - } - }); - } - }.bind(this) - }); - } else { - var title = "Do you really want to stop debugging this topology ?"; - var successCb = function(){ - this.model.debugTopology({ - id: this.model.get('id'), - debugType: 'disable', - percent: '0', - success: function(model, response){ - if(response.error || model.error){ - Utils.notifyError(response.error || model.error+'('+model.errorMessage.split('(')[0]+')'); - } else { - this.initializeData(); - Utils.notifySuccess("Debugging disabled successfully.") - } - }.bind(this), - error: function(model, response, options){ - Utils.notifyError("Error occured in disabling debugging."); - } - }); - }.bind(this); - var cancelCb = function(){ - $(".boot-switch.debug").bootstrapSwitch('toggleState', true) - }.bind(this); - Utils.ConfirmDialog(' ', title, successCb, cancelCb); - } - }, - }); -});
http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx b/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx deleted file mode 100644 index 25441fa..0000000 --- a/contrib/views/storm/src/main/resources/scripts/views/TopologyListingView.jsx +++ /dev/null @@ -1,65 +0,0 @@ -/** - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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([ - 'jsx!components/Table', - 'react', - 'react-dom', - 'jsx!containers/TopologyListing', - 'jsx!components/Breadcrumbs' - ],function(Table, React, ReactDOM, TopologyListing, Breadcrumbs){ - 'use strict'; - - return React.createClass({ - displayName: 'TopologyListingView', - getInitialState: function(){ - return null; - }, - componentWillMount: function(){ - $('.loader').show(); - }, - componentDidMount: function(){ - $('.loader').hide(); - }, - componentWillUpdate: function(){ - $('.loader').show(); - }, - componentDidUpdate: function(){ - $('.loader').hide(); - }, - render: function() { - return ( - <div> - <Breadcrumbs links={this.getLinks()} /> - <div className="row"> - <div className="col-sm-12"> - <TopologyListing /> - </div> - </div> - </div> - ); - }, - getLinks: function() { - var links = [ - {link: '#!/dashboard', title: 'Dashboard'}, - {link: 'javascript:void(0);', title: 'Topology Listing'} - ]; - return links; - } - }); -}); \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/styles/style.css ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/styles/style.css b/contrib/views/storm/src/main/resources/styles/style.css deleted file mode 100644 index f6b1685..0000000 --- a/contrib/views/storm/src/main/resources/styles/style.css +++ /dev/null @@ -1,579 +0,0 @@ -/** - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you 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. -*/ -/* - Theme: Apache Storm - Author: Sanket -*/ - -*:focus { - outline: none !important; -} - -body { - font-family: 'Lato', sans-serif; - color: #4b4b4b; -} - -/* Bootstrap Extended */ -.no-margin { - margin: 0px; -} - -.row-margin-bottom { - margin-bottom: 20px; -} - -.table-borderless>tbody>tr>td, -.table-borderless>tbody>tr>th, -.table-borderless>tfoot>tr>td, -.table-borderless>tfoot>tr>th, -.table-borderless>thead>tr>td, -.table-borderless>thead>tr>th { - border-top: none; -} -.table-enlarge > thead > tr > th, -.table-enlarge > tbody > tr > th, -.table-enlarge > tfoot > tr > th, -.table-enlarge > thead > tr > td, -.table-enlarge > tbody > tr > td, -.table-enlarge > tfoot > tr > td { - padding: 11px; -} -#breadcrumb { - margin-top: 15px; - list-style: none; - display: inline-block; - padding: 0px; -} -#breadcrumb .icon { - font-size: 14px; -} -#breadcrumb li { - float: left; -} -#breadcrumb li a { - color: #FFF; - display: block; - background: #27a9e1; - text-decoration: none; - position: relative; - height: 30px; - line-height: 30px; - padding: 0 10px 0 5px; - text-align: center; - margin-right: 23px; -} -#breadcrumb li:nth-child(even) a { - background-color: #1b75bb; -} -#breadcrumb li:nth-child(even) a:before { - border-color: #1b75bb; - border-left-color: transparent; -} -#breadcrumb li:nth-child(even) a:after { - border-left-color: #1b75bb; -} -#breadcrumb li:first-child a { - padding-left: 15px; - -moz-border-radius: 4px 0 0 4px; - -webkit-border-radius: 4px; - border-radius: 4px 0 0 4px; -} -#breadcrumb li:first-child a:before { - border: none; -} -#breadcrumb li:last-child a { - padding-right: 15px; - -moz-border-radius: 0 4px 4px 0; - -webkit-border-radius: 0; - border-radius: 0 4px 4px 0; -} -#breadcrumb li:last-child a:after { - border: none; -} -#breadcrumb li a:before, #breadcrumb li a:after { - content: ""; - position: absolute; - top: 0; - border: 0 solid #27a9e1; - border-width: 15px 10px; - width: 0; - height: 0; -} -#breadcrumb li a:before { - left: -20px; - border-left-color: transparent; -} -#breadcrumb li a:after { - left: 100%; - border-color: transparent; - border-left-color: #27a9e1; -} -#breadcrumb li a:hover { - background-color: #1bbb60; -} -#breadcrumb li a:hover:before { - border-color: #1bbb60; - border-left-color: transparent; -} -#breadcrumb li a:hover:after { - border-left-color: #1bbb60; -} - -.preformatted { - white-space: pre; -} - -/* Boxes */ -.box { - position: relative; - margin-bottom: 15px; - border: 1px #bcbcbc solid; - border-bottom-width: 3px; - border-radius: 5px; -} -.box .box-header:before, -.box .box-header:after { - content: " "; - display: table; -} -.box .box-header:after { - clear: both; -} -.box .box-header { - background-color: #f3f6f9; - padding: 0 10px; - border-bottom: 1px #bcbcbc solid; - border-radius: 4px 4px 0px 0px; -} -.box .box-header h4 { - float: left; - margin: 0px; - font-size: 16px; - font-weight: 700; - line-height: 40px; - letter-spacing: 1px; -} -.box .box-header .box-control { - float: right; -} -.box .box-header .box-control .bootstrap-switch { - margin: 9px 2px; -} -.box .box-header .box-control a { - display: inline-block; - width: 20px; - height: 20px; - font-size: 12px; - line-height: 20px; - text-align: center; - margin: 10px 2px; - border-radius: 50%; - background-color: #4b4b4b; - color: rgba(255,255,255,0.75); -} -/*.box .box-header .box-control a i { - visibility: hidden; -} -.box .box-header .box-control:hover a i { - visibility: visible; -}*/ -.box .box-header .box-control a.primary {background-color: #1b75bb;} -.box .box-header .box-control a.success {background-color: #1bbb60;} -.box .box-header .box-control a.info {background-color: #27a9e1;} -.box .box-header .box-control a.warning {background-color: #fbaf3f;} -.box .box-header .box-control a.danger {background-color: #ff5816;} -.box .box-header .box-control a.secondary {background-color: #df206a;} - -.box .box-body { - padding: 10px; -} -.box .box-body.paddless { - padding: 0px; -} - -.control-search { - position: relative; -} -.control-search input[type="text"] { - width: 250px; - margin-top: 5px; - padding: 4px 24px 4px 4px; - background-color: transparent; - border: none; - border-bottom: 1px #BCBCBC solid; -} -.control-search i.fa-search { - position: absolute; - right: 6px; - top: 12px; - color: #999; -} - -.box.filter { - border-color: #27a9e1; -} -.box.info { - border-color: #27a9e1; -} -.box.info .box-header { - background-color: #E9F8FF; - border-color: #27a9e1; -} -.box.warning { - border-color: #fbaf3f; -} -.box.warning .box-header { - background-color: #FFF6E9; - border-color: #fbaf3f; -} - -/*Tiles*/ -.tile { - display: block; - margin-bottom: 15px; - color: #fff; - border-radius: 2px; -} -.tile .tile-header { - letter-spacing: 2px; - padding: 8px 12px; - line-height: 1.5; - text-transform: uppercase; - font-size: 11px; - border-top-right-radius: 2px; - border-top-left-radius: 2px; -} -.tile .tile-body { - overflow: hidden; - padding: 12px; -} -.tile .tile-body i.fa { - font-size: 30px; - opacity: .4; -} -.tile .tile-body .count { - float: right; - font-size: 30px; - line-height: 30px; - font-weight: 300; -} -.tile.primary { - background: #1b75bb; -} -.tile.primary > .tile-header { - background-color: #085C9D; -} -.tile.warning { - background: #fbaf3f; -} -.tile.warning > .tile-header { - background-color: #ED940E; -} -.tile.success { - background: #1bbb60; -} -.tile.success > .tile-header { - background-color: #00A347; -} -.tile.danger { - background: #ff5816; -} -.tile.danger > .tile-header { - background-color: #D13B00; -} - -.summary-tile { - display: block; - margin-bottom: 15px; - color: #fff; - border-radius: 5px; - background-color: #1B76BB; -} -.summary-tile .summary-title { - letter-spacing: 2px; - padding: 8px 12px; - line-height: 1.5; - text-transform: uppercase; - border-top-right-radius: 5px; - border-top-left-radius: 5px; - background-color: #085C9D; -} -.summary-tile .summary-body { - overflow: hidden; - padding: 12px; -} -.summary-tile .summary-body a{ - color: #fff; - text-decoration: underline; -} -.summary-tile .summary-body strong { - display: inline-block; - width: 120px; - margin-right: 10px; - text-align: right; -} -.summary-tile .summary-body .form-group { - margin-bottom: 0px; -} - -.stats-tile { - display: block; - margin-bottom: 15px; - color: #333; - border-radius: 5px; - background-color: #EEEEEE; -} -.stats-tile .stats-title { - letter-spacing: 2px; - padding: 8px 12px; - line-height: 1.5; - text-transform: uppercase; - border-top-right-radius: 5px; - border-top-left-radius: 5px; - background-color: #C0C0C0; -} -.stats-tile .stats-body { - overflow: hidden; - padding: 12px; -} - -/* Detail Page */ -.graph-bg { - background-image: linear-gradient(to right, rgb(0, 0, 0) -10px, transparent 1px), linear-gradient(rgb(0, 0, 0) -10px, transparent 1px); - background-size: 10px 10px; - min-height: 300px; -} -.summary-panel { - font-size: 90%; - padding: 10px; - background: #f9f9f9; - border: 1px solid #ccc; - border-bottom-width: 2px; - border-left-width: 2px; - box-shadow: -1px 1px 5px rgba(0,0,0,0.25); -} -.circle { - float: left; - margin-right: 15px; -} - -/* -REACT - */ -#supervisorCount, -#slotsCount { - position: 'relative'; - display: 'inline-block' -} -#supervisorCount > svg > g > text, -#slotsCount > svg > g > text { - font-size: 27px; - fill: white; -} -.supervisor-table svg > g > text { - font-size: 12px; - fill: black; -} -#supervisorCount > svg > g > text.percent, -#slotsCount > svg > g > text.percent { - font-size: 18px; -} -#supervisorCount > svg > g > text.graphVal, -#slotsCount > svg > g > text.graphVal { - font-weight: 300; -} -/* D3 */ -.d3-tip { - z-index: 99; - line-height: 1; - font-weight: bold; - padding: 10px; - background: rgba(0, 0, 0, 0.8); - color: #fff; - border-radius: 2px; - font-size: 12px; -} -.d3-tip .summary { - padding: 3px; -} - -/* Creates a small triangle extender for the tooltip */ -.d3-tip:after { - box-sizing: border-box; - display: inline; - font-size: 16px; - width: 100%; - line-height: 1; - color: rgba(0, 0, 0, 0.8); - content: "\25BC"; - position: absolute; - text-align: center; -} -.node ellipse { - stroke: #333; - fill: #fff; - stroke-width: 1px; -} -.edgePath path { - stroke: grey; - fill: none; - cursor: default; - stroke-width: 2; - stroke-dasharray: 5, 5; -} - -/* Style northward tooltips differently */ -.d3-tip.n:after { - margin: -4px 0 0 0; - top: 100%; - left: 0; -} -path.link { - fill: none; - stroke: grey; - cursor: default; -} - -path.link.dragline { - pointer-events: none; -} - -path.link.hidden { - stroke-width: 0; -} - -circle.node { - stroke-width: 1.5px; - cursor: pointer; -} - -circle.node.reflexive { - stroke: #000 !important; - stroke-width: 2.5px; -} -text.id { - text-anchor: middle; - font-weight: bold; -} -.d3-tip ul { - padding:0; - margin:0; - list-style: none; -}⨠-.d3-tip ul li { - font-size: 12px; - line-height: 20px; -} -marker { - fill: grey; -} -.axis path, .axis rect { - fill: none; - stroke: grey; - stroke-width: 1; - shape-rendering: crispEdges; -} - -.axis line { - stroke: grey; - stroke-width: 1; - shape-rendering: crispEdges; -} - -.d3-tip table { - margin-top: 8px; - width : 100%; -} -.d3-tip table tr td { - padding: 3px; - border: 1px red solid; -} -ul.legends { - text-align: center; -} -ul.legends li.legend{ - display: inline-block; - margin-right: 10px; -} -.table-summary { - font-size: 12px; - font-weight: 700; - -webkit-font-smoothing: antialiased; -} -.summary-badge { - font-weight: bold; - padding: 1px 4px; - margin: 1px; - border: 1px solid #d8d8d8; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; - border-radius: 4px; -} -[data-toggle="collapse"]{ - cursor: pointer; -} -#ex1Slider { - margin-left: 13px; - margin-right: 13px; -} -#modal-rebalance .modal-body{ - max-height: 450px; - overflow-y: auto; -} -.loader { - position: fixed; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat center center; - z-index: 9; -} -.inner-loader{ - position: absolute; - top: 0px; - left: 0px; - bottom: 0px; - right: 0px; - background: url('../images/loader.gif') rgba(255,255,255,0.75) no-repeat center center; -} -.searchbar{ - margin-top: 15px; -} -.searchbar .btn-group{ - display: flex !important; -} -.searchbar .dropdown-toggle{ - border-radius: 0; - margin-left: -1px; -} -.searchbar .btn-group .btn { - margin-left: -1px; -} -.searchbar .dropdown-menu{ - padding: 10px 15px 5px; -} -.searchbar .dropdown-menu input[type="checkbox"]{ - vertical-align: top; - margin-right: 5px; -} -.searchbar .open > .dropdown-toggle.btn-default:hover, -.searchbar .open > .dropdown-toggle.btn-default:focus{ - border: 1px solid transparent; -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/.babelrc ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/.babelrc b/contrib/views/storm/src/main/resources/ui/.babelrc new file mode 100644 index 0000000..b533394 --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/.babelrc @@ -0,0 +1,25 @@ +{ + "presets": [ + ["es2015"], + "react", + "stage-0", + "airbnb" + ], + "plugins": [ + "transform-runtime", + "transform-decorators-legacy", + "transform-flow-strip-types", + "transform-es2015-modules-commonjs", + "transform-class-properties", + "react-hot-loader/babel", + "transform-async-to-generator", + ["babel-root-slash-import", { + "rootPathSuffix": "./app/scripts" + }] + ], + "env": { + "production": { + "plugins": ["transform-react-remove-prop-types", "transform-react-constant-elements","transform-async-to-generator"] + } + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/.eslintignore.js ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/.eslintignore.js b/contrib/views/storm/src/main/resources/ui/.eslintignore.js new file mode 100644 index 0000000..2c4e446 --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/.eslintignore.js @@ -0,0 +1,3 @@ +node_modules/* +**/bower_components/* +**/vendor/*.js http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/.eslintrc.js ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/.eslintrc.js b/contrib/views/storm/src/main/resources/ui/.eslintrc.js new file mode 100644 index 0000000..473fe48 --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/.eslintrc.js @@ -0,0 +1,58 @@ +module.exports = { + "parser": "babel-eslint", + "rules": { + "strict": 0 + }, + "env": { + "browser": true, + "es6": true, + "jquery": true + }, + "parserOptions": { + "sourceType": "module" + }, + "plugins": [ + "header", + "react" + ], + "rules": { + "header/header": [2, "block", [ + "*", + " Licensed to the Apache Software Foundation (ASF) under one", + " or more contributor license agreements. See the NOTICE file", + " distributed with this work for additional information", + " regarding copyright ownership. The ASF licenses this file", + " to you 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.", + "*" + ]], + "comma-dangle": [ + "error", + "never" + ], + "indent": [ + "error", + 2 + ], + "linebreak-style": [ + "error", + "unix" + ], + "semi": [ + "error", + "always" + ], + /* Advanced Rules*/ + "no-unexpected-multiline": 2, + "curly": [2,"all"] + } +}; http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/app/scripts/app.js ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/app.js b/contrib/views/storm/src/main/resources/ui/app/scripts/app.js new file mode 100644 index 0000000..41a7fa9 --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/app.js @@ -0,0 +1,40 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 React, {Component} from 'react'; +import routes from './routers/routes'; +import {render} from 'react-dom'; +import {Router, browserHistory, hashHistory} from 'react-router'; +import {getStormVersion} from './utils/Constants'; + +class App extends Component { + constructor() { + super(); + this.fetchVersion(); + } + fetchVersion(){ + getStormVersion().then((res) => { + this.forceUpdate(); + }); + } + render() { + return (<Router ref="router" history={hashHistory} routes={routes}/>); + } +} + +export default App; http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx new file mode 100644 index 0000000..dffd898 --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/BarChart.jsx @@ -0,0 +1,429 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import ReactDOM from 'react-dom'; +import d3 from 'd3'; +import d3Tip from 'd3-tip'; + +export default class BarChart extends Component{ + static propTypes = { + data: PropTypes.array.isRequired, + width: PropTypes.number, + height: PropTypes.number + } + + constructor(props) { + super(props); + } + + componentDidMount(){ + this.setUpSVG(); + this.initToolTip(); + this.setLayout(); + this.initSets(); + this.barTypeTransition = this.transitionGrouped; + this.hiddenLayers = []; + this.drawBars(); + this.drawXAxis(); + this.drawYAxis(); + this.drawTooltip(); + this.drawLegends(); + } + + initSets(){ + this.layers = this.dataMapY(this.props.data); + // this.setMax(); + this.setX(); + this.setY(); + this.colorDomain(); + this.setXAxis(); + this.setYAxis(); + } + + setUpSVG(){ + this.svg = d3.select(ReactDOM.findDOMNode(this)) + .attr('width', this.props.width+"px") + .attr('height', this.props.height+50+"px"); + // .attr("viewBox", "-46 -5 " + (this.props.width+82) + " " + (this.props.height+28) ); + + this.container = this.svg.append("g") + .attr('class', 'svg-container') + .attr("transform", "translate(50,10)"); + + this.tipcontainer = this.svg.append('g').classed('tip-g', true) + .attr("transform", "translate(" + 40 + "," + 0 + ")"); + + this.tipcontainer.append('g').classed('tipLine-g', true).append('line').classed('tipline', true) + .style('stroke', '#aaa') + .style('visibility', 'hidden') + // .style('shape-rendering', 'crispEdges') + .attr('x1', 0).attr('x2', 0).attr('y1', 0).attr('y2', this.props.height); + } + + initToolTip() { + let self = this; + this.tip = d3Tip() + .attr('class', 'd3-tip') + .offset([-10, 0]) + .html(function(d) { + return self.toolTipHtml.call(self, d); + }); + this.svg.call(this.tip); + // const container = document.getElementById('app_container'); + // container.append($('body > .d3-tip')); + } + + setMax() { + this.yGroupMax = d3.max(this.layers, function(layer) { + return d3.max(layer, function(d) { + return d.y; + }); + }); + this.yGroupMin = d3.min(this.layers, function(layer) { + return d3.min(layer, function(d) { + return d.y; + }); + }); + this.yStackMax = d3.max(this.layers, function(layer) { + return d3.max(layer, function(d) { + return d.y0 + d.y; + }); + }); + this.yStackMin = d3.min(this.layers, function(layer) { + return d3.min(layer, function(d) { + return d3.min([d.y0, d.y]); + }); + }); + } + + setX() { + let self = this; + this.x = d3.scale.ordinal() + .domain(self.layers[0].map(function(d) { + return d.x; + })) + .rangeRoundBands([0, this.props.width], 0.08); + } + + setY() { + this.y = d3.scale.linear() + .domain([this.yStackMin, this.yStackMax]) + .range([this.props.height, 0]); + } + + setXAxis() { + this.xAxis = d3.svg.axis().scale(this.x).orient("bottom"); + } + + setYAxis() { + let formatValue = d3.format('.2s'); + this.yAxis = d3.svg + .axis() + .scale(this.y) + .orient("left") + .tickFormat(function(d){return formatValue(d);}); + } + + drawXAxis(xAxis, container, height) { + let xA = xAxis || this.xAxis, + containor = container || this.container, + hght = height || this.props.height; + + this.xAxisGrp = containor['xAxisEl'] = containor.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + hght + ")") + .call(xA) + .selectAll(".tick text") + .call(this.wrap, this.x.rangeBand()); + } + + wrap(text, width) { + text.each(function() { + let text = d3.select(this), + words = text.text().split(/-+/).reverse(), + word, + line = [], + lineNumber = 0, + lineHeight = 1.1, // ems + y = text.attr("y"), + dy = parseFloat(text.attr("dy")), + tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em"); + + //Hack to show hidden div to find getComputedTextLength + // $('#lag-graph').css({visibility: 'hidden', display: 'block', position: 'absolute'}); + + while (word = words.pop()) { + line.push(word); + tspan.text(line.join(" ")); + if (tspan.node().getComputedTextLength() > width) { + line.pop(); + tspan.text(line.join(" ")); + line = [word]; + tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word); + } + } + // $('#lag-graph').css({visibility: '', display: 'none', position: ''}); + }); + } + + drawYAxis(x) { + let yAxis = this.yAxis; + this.yAxisGrp = this.container.append("g") + .attr("class", "y axis"); + this.yAxisGrp.ticks = this.yAxisGrp.call(yAxis); + this.yAxisGrp.append('text') + .text(this.props.yAttr[0].toUpperCase() + this.props.yAttr.substr(1,this.props.yAttr.length)).attr("text-anchor", "end") + .attr("y", 6) + .attr("dy", ".75em") + .attr("transform", "rotate(-90)"); + } + + dataMapY(data) { + let self = this; + let keys = d3.keys(data[0]).filter(function(key) { + return key !== self.props.xAttr; + }); + let layers = this.stack(keys.map(function(yAttr) { + return data.map(function(d) { + return { + x: d[self.props.xAttr], + y: d[yAttr], + type: yAttr + }; + }); + })); + let allLayers = layers.allLayers = []; + layers.forEach(function(d) { + allLayers.push(d); + }); + return layers; + } + + setLayout() { + let self = this; + this.stack = d3.layout.stack(); + } + + colorDomain() { + let self = this; + this.color = d3.scale.ordinal() + .range(["#b9cde5", "#1B76BB"]); + // this.color = d3.scale.category20c(); + // this.color.domain(d3.keys(this.props.data[0]).filter(function(key) { + // return key !== self.props.xAttr; + // })); + } + + drawBars() { + let self = this; + + this.layers_g = this.container.selectAll(".barLayer") + .data(this.layers); + + this.layers_g + .exit() + .remove(); + + this.layers_g + .enter().append("g") + .attr("class", "barLayer") + .style("fill", function(d, i) { + return self.color(d[0].type); + }); + + this.rect = this.layers_g.selectAll("rect") + .data(function(d) { + return d; + }); + + this.rect + .exit() + .remove(); + + this.rect + .enter().append("rect") + .attr("x", function(d) { + return self.x(d.x); + }) + .attr("y", function(d) { + return self.props.height; + }) + .attr("width", function(d) { + return self.x.rangeBand(); + }) + .classed("visible", true) + .attr("height", function(d) { + return 0; + }); + + this.barTypeTransition(); + } + + transitionGrouped() { + let x = this.x, + y = this.y, + height = this.props.height, + n = this.layers.length; + this.setMax(); + let yMin = this.yGroupMin < 0 ? this.yGroupMin : 0; + this.y.domain([yMin, this.yGroupMax]); + + let barWidth = (x.rangeBand() / n > 25) ? 25 : x.rangeBand() / n; + let xArr = new Array(n); + this.layers_g.selectAll('rect.visible') + .attr("x", function(d, i, j) { + if (xArr[i] == undefined) { + xArr[i] = x(d.x) + (x.rangeBand() / 2) - (n / 2 * barWidth); + } else { + xArr[i] += barWidth; + } + return xArr[i]; + }) + .attr("width", barWidth) + .transition().duration(500) + .attr("y", function(d) { + let _y = y(d.y); + if (d.y < 0){ + _y = y(d.y) - (height - y(0)); + } + return _y; + }) + .attr("height", function(d) { + return (height - y(Math.abs(d.y))) - (height - y(0)); + }); + this.container.select(".y.axis").transition().duration(500).call(this.yAxis); + } + + transitionStacked() { + this.stack(this.layers); + let x = this.x, + y = this.y, + height = this.props.height, + self = this, + n = this.layers.length; + this.setMax(); + this.y.domain([this.yStackMin, this.yStackMax]); + + let barWidth = (x.rangeBand() / n > 25) ? 25 : x.rangeBand() / n; + let xArr = new Array(n); + this.layers_g.selectAll('rect.visible').transition().duration(500) + .attr("y", function(d) { + let _y = y(d.y0 + d.y); + if (d.y < 0){ + _y = y(d.y) - Math.abs(y(d.y0) - y(d.y0 + d.y)); + } + return _y; + }) + .attr("height", function(d) { + return Math.abs(y(d.y0) - y(d.y0 + d.y)); + }) + .attr("x", function(d, i, j) { + xArr[i] = x(d.x) + (x.rangeBand() / 2) - (barWidth / 2); + return xArr[i]; + }) + .attr("width", barWidth); + this.container.select(".y.axis").transition().duration(500).call(this.yAxis); + } + + drawTooltip() { + let self = this; + let x = this.x.rangeBand ? this.x : d3.scale.ordinal() + .domain(self.data.map(function(d) { + return d[self.props.xAttr]; + })) + .rangeRoundBands([0, this.props.width]); + + let tipline = this.tipcontainer.select('.tipline'); + + this.tipcontainer.append('g').classed('tipRect-g', true).selectAll(".tipRect") + .data(this.props.data) + .enter().append("rect") + .attr("class", "tipRect") + .style('opacity', '0') + .attr("x", function(d) { + return self.x(d[self.props.xAttr]); + }) + .attr("width", function() { + return x.rangeBand(); + }) + .attr("y", function(d) { + return 0; + }) + .attr("height", function(d) { + return self.props.height; + }) + .on('mouseover', function(d) { + let x1 = parseInt(d3.select(this).attr('x')) + parseInt((x.rangeBand() / 2)); + tipline.attr('x1', x1).attr('x2', x1); + tipline.style('visibility', 'visible'); + return self.tip.show(d,this); + }) + .on('mouseout', function(d) { + tipline.style('visibility', 'hidden'); + return self.tip.hide(d,this); + }); + } + + toolTipHtml(d) { + let self = this; + let html = d[self.props.xAttr] + '<table><tbody>'; + _.each(d, function(val, key) { + if (key != self.props.xAttr){ + html += '<tr><td>' + key + ' </td><td> ' + val + '</td></tr>'; + } + }); + html += '</tbody></table>'; + return html; + } + + drawLegends() { + let self = this; + let legends = this.legendsEl = document.createElement('ul'); + legends = d3.select(legends) + .attr('class', 'legends') + .style('list-style', 'none'); + + let legend = legends.selectAll('.legend') + .data(this.color.domain()) + .enter() + .append('li') + .attr('class', 'legend'); + + legend.append('div') + .style('width', '10px') + .style('height', '10px') + .style('display', 'inline-block') + .style('background-color', function(d) { + return self.color(d); + }); + + legend.append('span') + .style('padding', '4px 0 4px 4px') + .text(function(d) { + return d; + }); + } + + render() { + return ( + <svg></svg> + ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx new file mode 100644 index 0000000..e4926ab --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/Breadcrumbs.jsx @@ -0,0 +1,45 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import ReactDom from 'react-dom'; + +export default class Breadcrumbs extends Component{ + static propTypes = { + links: PropTypes.array.isRequired + } + render() { + return ( + <ol id="breadcrumb"> + {this.renderLinks()} + </ol> + ); + } + renderLinks() { + var links = []; + for(var i = 0; i < this.props.links.length; i++){ + var object = this.props.links[i]; + if(object.link === '#/'){ + object.title = <i className="fa fa-home"></i>; + } + links.push(<li key={i}><a href={object.link}>{object.title}</a></li>); + } + return links; + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx new file mode 100644 index 0000000..558d2a2 --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonExpanded.jsx @@ -0,0 +1,30 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 React, {Component} from 'react'; + +const CommonExpanded = (props) => { + const {expandFlag} = props; + return ( + <div className="box-control pull-right" style={{marginLeft : '17px',marginTop : '-2px'}}> + <span className="primary"><i className={`fa ${expandFlag ? 'fa-compress' : 'fa-expand'}`}></i></span> + </div> + ); +}; + +export default CommonExpanded; http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx new file mode 100644 index 0000000..34e402c --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonNotification.jsx @@ -0,0 +1,69 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 React, {Component} from 'react'; +import PropTypes from 'prop-types'; +import {notifyTextLimit} from '../utils/Constants'; + +class CommonNotification extends Component { + constructor(props) { + super(props); + this.state = { + data: false, + text: "Read more" + }; + } + showMore = () => { + if (this.state.text === "Read more") { + this.setState({text: "Hide", data: true}); + } else { + this.setState({text: "Read more", data: false}); + } + } + + render() { + /* flag value error, info, sucess */ + const {text, data} = this.state; + const {flag, content} = this.props; + const initial = content.substr(0, notifyTextLimit); + const moreText = content.substr(notifyTextLimit); + const readMoreTag = <a href="javascript:void(0)" onClick={this.showMore}>{text}</a>; + return ( + <div> + {initial} + {(data) + ? moreText + : null +} + <div> + {(flag === 'error' && moreText.length > 0) + ? readMoreTag + : null +} + </div> + </div> + ); + } +} + +export default CommonNotification; + +CommonNotification.propTypes = { + flag: PropTypes.string.isRequired, + content: PropTypes.string +}; http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx new file mode 100644 index 0000000..5128a09 --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonPagination.jsx @@ -0,0 +1,56 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 React, {Component} from 'react'; +import _ from 'lodash'; +import {Pagination} from 'react-bootstrap'; + +export default class CommonPagination extends Component{ + constructor(props){ + super(props); + } + + handleSelect = (eventKey) => { + this.props.callBackFunction(eventKey,this.props.tableName); + } + + render(){ + const {activePage,pageSize,filteredEntities} = this.props; + const totalPages = Math.ceil(filteredEntities.length / pageSize); + + return( + <div className="pagination-wrapper"> + <div className="pull-left"> + <span>{`Showing ${activePage > 1 ? (activePage-1)*pageSize : activePage } to ${activePage*pageSize > filteredEntities.length ? filteredEntities.length : (activePage*pageSize)} of ${filteredEntities.length} entries`}</span> + </div> + <Pagination + className={`${filteredEntities.length === 0? 'hidden':'shown pull-right'}`} + prev={false} + next={false} + first + last + ellipsis + items={totalPages} + maxButtons={5} + activePage={activePage} + onSelect={this.handleSelect}> + </Pagination> + </div> + ); + } +} http://git-wip-us.apache.org/repos/asf/ambari/blob/e3931cc2/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx ---------------------------------------------------------------------- diff --git a/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx new file mode 100644 index 0000000..804f51e --- /dev/null +++ b/contrib/views/storm/src/main/resources/ui/app/scripts/components/CommonSwitchComponent.jsx @@ -0,0 +1,41 @@ +/** + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you 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 React, {Component} from 'react'; + +export default class CommonSwitchComponent extends Component { + render(){ + const {switchCallBack,checked,textON,textOFF,KYC} = this.props; + let switchId = "switch-"+((Math.random())*100).toFixed(0); + return ( + <div className={`switchWrapper ${!!KYC ? 'lagSwitchSetting pull-right' : ''}`}> + <span className={`switchSlider ${checked ? 'onSlider' : 'offSlider'}`} onClick={switchCallBack}> + <span className={`switchItemOn sliderText ${!!KYC ? 'graphSwitchOn' : ''}`}>{textON}</span> + <span className="switchItemMid"></span> + <span className={`switchItemOff sliderText ${!!KYC ? 'graphSwitchOff' : ''}`}>{textOFF}</span> + </span> + </div> + + ); + } +} + +CommonSwitchComponent.defaultProps = { + textON : "ON", + textOFF : "OFF" +};