grommet/grommet-ferret

View on GitHub
src/js/components/dashboard/Dashboard.js

Summary

Maintainability
D
2 days
Test Coverage
// (C) Copyright 2014-2016 Hewlett Packard Enterprise Development LP

import React, { Component, PropTypes } from 'react';
import ReactDOM from 'react-dom';
import { connect } from 'react-redux';
import { loadDashboard, unloadDashboard, searchSuggestions, navActivate,
  selectIndex, showUtilization } from '../../actions/actions';
import Article from 'grommet/components/Article';
import Header from 'grommet/components/Header';
import Heading from 'grommet/components/Heading';
import Section from 'grommet/components/Section';
import Search from 'grommet/components/Search';
import Tiles from 'grommet/components/Tiles';
import Tile from 'grommet/components/Tile';
import AnnotatedMeter from 'grommet-addons/components/AnnotatedMeter';
import Box from 'grommet/components/Box';
import NavControl from '../NavControl';
import Notifications from '../Notifications';
import DashboardTasks from './DashboardTasks';
import GraphicSizer from '../../utils/GraphicSizer';

const CATEGORY_LABELS = {
  alerts: 'Alert',
  images: 'Image',
  networks: 'Network',
  'os-types': 'OS Type',
  tasks: 'Task',
  'virtual-machines': 'Virtual Machine',
  'virtual-machine-sizes': 'Size'
};

class Suggestion extends Component {
  render () {
    return (
      <Box direction="row" justify="between">
        <span>{this.props.name}</span>
        <label className="secondary">
          {CATEGORY_LABELS[this.props.category] || this.props.category}
        </label>
      </Box>
    );
  }
}

Suggestion.propTypes = {
  name: PropTypes.string.isRequired,
  category: PropTypes.string.isRequired
};

class Dashboard extends Component {

  constructor (props) {
    super(props);

    this._onClickTitle = this._onClickTitle.bind(this);
    this._onCloseNav = this._onCloseNav.bind(this);
    this._onSearchChange = this._onSearchChange.bind(this);
    this._onSearchSelect = this._onSearchSelect.bind(this);
    this._onClickUtilization = this._onClickUtilization.bind(this);
    this._onGraphicSize = this._onGraphicSize.bind(this);

    this._setDocumentTitle(props);
    this.state = { graphicSize: 'small',
      cpuIndex: 0, memoryIndex: 0, storageIndex: 0 };
  }

  componentDidMount () {
    this.props.dispatch(loadDashboard());
    const container = ReactDOM.findDOMNode(this.refs.utilizationMeters);
    this._graphicSizer = new GraphicSizer(container, this._onGraphicSize);
  }

  componentWillReceiveProps(nextProps) {
    this._setDocumentTitle(nextProps);
    // if (this.props.nav.active !== nextProps.nav.active) {
    //   // The NavSidebar is changing, relayout when it finishes animating.
    //   // TODO: Convert to an animation event listener.
    //   this._graphicSizer.layout();
    // }
    const suggestions = nextProps.search.suggestions.map((suggestion) => {
      return {
        suggestion: suggestion,
        label: (
          <Suggestion name={suggestion.name} category={suggestion.category} />
        )
      };
    });
    this.setState({ suggestions: suggestions });
  }

  componentWillUnmount () {
    this.props.dispatch(unloadDashboard());
    this._graphicSizer.stop();
  }

  _setDocumentTitle (props) {
    document.title = `Dashboard - ${props.instanceName || ''}`;
  }

  _onClickTitle () {
    this.props.dispatch(navActivate(true));
  }

  _onCloseNav () {
    this.props.dispatch(navActivate(false));
  }

  _onSearchChange (event) {
    this.props.dispatch(searchSuggestions(event.target.value));
  }

  _onSearchSelect (pseudoEvent) {
    const suggestion = pseudoEvent.suggestion.suggestion;
    this.props.dispatch(selectIndex('/' + suggestion.category, suggestion.uri));
  }

  _onClickUtilization (attribute) {
    this.props.dispatch(showUtilization(attribute));
  }

  _onGraphicSize (size) {
    this.setState({ graphicSize: size });
  }

  render () {
    const { dashboard, search } = this.props;

    let tasks;
    if (dashboard.tasks.count > 0) {
      tasks = (
        <Section pad="medium" full="horizontal">
          <Heading tag="h2">Running Tasks</Heading>
          <DashboardTasks tasks={dashboard.tasks} />
        </Section>
      );
    }

    let cpuData = [
      {value: 58, label: 'In use', colorIndex: 'accent-1',
        onClick: this._onClickUtilization.bind(this, 'memoryUsed')},
      {value: 42, label: 'Available', colorIndex: 'unset'}
    ];

    let memoryData = [
      {value: 127, label: 'In use', colorIndex: 'accent-1',
        onClick: this._onClickUtilization.bind(this, 'memoryUsed')},
      {value: 73, label: 'Available', colorIndex: 'unset'}
    ];

    let storageData = [
      {value: 427, label: 'In use', colorIndex: 'accent-1',
        onClick: this._onClickUtilization.bind(this, 'diskUsed')},
      {value: 573, label: 'Available', colorIndex: 'unset'}
    ];

    return (
      <Article ref="content" pad="none">
        <Header direction="row" justify="between" size="large"
          pad={{ horizontal: 'medium', between: 'small' }}>
          <NavControl />
          <Search ref="search" inline={true} responsive={false} fill={true}
            size="medium" placeHolder="Search"
            defaultValue={search.text} onDOMChange={this._onSearchChange}
            onSelect={this._onSearchSelect}
            suggestions={this.state.suggestions} />
        </Header>
        <Notifications context={{global: true}} includeResource={true}
          includeCategories="alerts" />
        <Section key="utilization" pad="medium" full="horizontal">
          <Header justify="between">
            <Heading tag="h2" margin="none">Utilization</Heading>
          </Header>
          <Tiles ref="utilizationMeters" fill={true}>
            <Tile pad="medium">
              <Header size="small" justify="center">
                <Heading tag="h3">CPU</Heading>
              </Header>
              <AnnotatedMeter type="circle" legend={true} units="GHz"
                size={this.state.graphicSize} series={cpuData} />
            </Tile>
            <Tile pad="medium">
              <Header size="small" justify="center">
                <Heading tag="h3">Memory</Heading>
              </Header>
              <AnnotatedMeter type="circle" legend={true} units="GB"
                size={this.state.graphicSize} series={memoryData} />
            </Tile>
            <Tile pad="medium">
              <Header size="small" justify="center">
                <Heading tag="h3">Storage</Heading>
              </Header>
              <AnnotatedMeter type="circle" legend={true} units="TB"
                size={this.state.graphicSize} series={storageData} />
            </Tile>
          </Tiles>
        </Section>
        {tasks}
      </Article>
    );
  }

}

Dashboard.propTypes = {
  dashboard: PropTypes.object,
  search: PropTypes.object
};

let select = (state, props) => ({
  dashboard: state.dashboard,
  search: state.search
});

export default connect(select)(Dashboard);