| /** |
| * 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 from 'react'; |
| import TreeView from 'react-treeview'; |
| import assign from 'object-assign'; |
| import { Link } from 'react-router'; |
| import 'react-treeview/react-treeview.css'; |
| import ClassNames from 'classnames'; |
| |
| import DatabaseStore from '../stores/DatabaseStore'; |
| import CubeStore from '../stores/CubeStore'; |
| import AdhocQueryActions from '../actions/AdhocQueryActions'; |
| import UserStore from '../stores/UserStore'; |
| import Loader from '../components/LoaderComponent'; |
| import '../styles/css/tree.css'; |
| |
| function getCubeData () { |
| return { |
| cubes: CubeStore.getCubes(UserStore.currentDatabase()) |
| }; |
| } |
| |
| class CubeTree extends React.Component { |
| constructor (props) { |
| super(props); |
| |
| // setting the initial state, as getInitialState only |
| // comes with React.createClass, using constructor is the new |
| // idiomatic way |
| // https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html |
| this.state = {cubes: CubeStore.getCubes(UserStore.currentDatabase()), loading: false, isCollapsed: false }; |
| |
| // no autobinding with ES6 so doing it manually, see link below |
| // https://facebook.github.io/react/blog/2015/01/27/react-v0.13.0-beta-1.html#autobinding |
| this._onChange = this._onChange.bind(this); |
| this.toggle = this.toggle.bind(this); |
| if (!this.state.cubes) { |
| this.state.cubes = this.state.cubes || []; |
| this.state.loading = true; |
| AdhocQueryActions.getCubes(UserStore.getUserDetails().secretToken, UserStore.currentDatabase()); |
| } |
| } |
| |
| componentDidMount () { |
| CubeStore.addChangeListener(this._onChange); |
| } |
| |
| componentWillUnmount () { |
| CubeStore.removeChangeListener(this._onChange); |
| } |
| |
| render () { |
| // cube tree structure sample |
| // [{ |
| // name: 'Cube-1', |
| // measures: [{name: 'Measure-1'}, {name: 'Measure-1'}], // optional |
| // dimensions: [{name: 'Dimension-1'}, {name: 'Dimension-1'}] //optional |
| // }, ...] |
| |
| var cubeHash = assign({}, this.state.cubes); |
| var cubeTree = Object.keys(this.state.cubes).map((cubeName, i) => { |
| let cube = cubeHash[cubeName]; |
| |
| let label = <Link to='cubeschema' params={{databaseName: this.state.database, cubeName: cubeName}}> |
| <span className='node'>{cube.name}</span> |
| </Link>; |
| |
| let measureLabel = <Link to='cubeschema' params={{databaseName: this.state.database, cubeName: cubeName}} |
| query={{type: 'measures'}}> |
| <span className='quiet'>Measures</span> |
| </Link>; |
| |
| let dimensionLabel = <Link to='cubeschema' params={{databaseName: this.state.database, cubeName: cubeName}} |
| query={{type: 'dimensions'}}> |
| <span className='quiet'>Dim-Attributes</span> |
| </Link>; |
| return ( |
| <TreeView key={cube.name + '|' + i} nodeLabel={label} |
| defaultCollapsed={!cube.isLoaded} onClick={this.getDetails.bind(this, cube.name)}> |
| |
| <TreeView key={cube.name + '|measures'} nodeLabel={measureLabel} |
| defaultCollapsed={!cube.isLoaded}> |
| { cube.measures ? cube.measures.map(measure => { |
| return ( |
| <div key={measure.name} className='treeNode measureNode'> |
| {measure.name} ({measure.default_aggr}) |
| </div> |
| ); |
| }) : null } |
| </TreeView > |
| |
| <TreeView key={cube.name + '|dim attributes'} nodeLabel={dimensionLabel} |
| defaultCollapsed={!cube.isLoaded}> |
| { cube.dimensions ? cube.dimensions.map(dimension => { |
| return ( |
| <div className='treeNode dimensionNode' key={dimension.name}> |
| {dimension.name} |
| </div> |
| ); |
| }) : null } |
| </TreeView > |
| |
| </TreeView > |
| ); |
| }); |
| |
| if (this.state.loading) { |
| cubeTree = <Loader size='4px' margin='2px'/>; |
| } else if (!Object.keys(this.state.cubes).length) { |
| cubeTree = (<div className='alert-danger' style={{padding: '8px 5px'}}> |
| <strong>Sorry, we couldn't find any cubes.</strong> |
| </div>); |
| } |
| |
| let collapseClass = ClassNames({ |
| 'pull-right': true, |
| 'glyphicon': true, |
| 'glyphicon-chevron-up': !this.state.isCollapsed, |
| 'glyphicon-chevron-down': this.state.isCollapsed |
| }); |
| |
| let panelBodyClassName = ClassNames({ |
| 'panel-body': true, |
| 'hide': this.state.isCollapsed |
| }); |
| |
| return ( |
| <div className='panel panel-default'> |
| <div className='panel-heading'> |
| <h3 className='panel-title'> |
| Cubes |
| <span className={collapseClass} onClick={this.toggle}></span> |
| </h3> |
| </div> |
| <div className={panelBodyClassName} style={{maxHeight: '350px', overflowY: 'auto'}}> |
| {cubeTree} |
| </div> |
| </div> |
| ); |
| } |
| |
| _onChange () { |
| let state = getCubeData(); |
| state.loading = false; |
| this.setState(state); |
| } |
| |
| // TODO: check its binding in the onClick method |
| // needs to be investigated |
| // https://facebook.github.io/react/tips/communicate-between-components.html |
| getDetails (cubeName) { |
| if (this.state.cubes[cubeName].isLoaded) return; |
| |
| AdhocQueryActions |
| .getCubeDetails(UserStore.getUserDetails().secretToken, cubeName); |
| } |
| |
| toggle () { |
| this.setState({isCollapsed: !this.state.isCollapsed}); |
| } |
| } |
| |
| export default CubeTree; |