onejgordon/flow-dashboard

View on GitHub
src/js/components/JournalEditor.js

Summary

Maintainability
A
3 hrs
Test Coverage
var PropTypes = require('prop-types');
var React = require('react');
import {Paper, TextField, List, ListItem, Slider} from 'material-ui';
var api = require('utils/api');

export default class JournalEditor extends React.Component {
  static propTypes = {
    questions: PropTypes.array,
    onChange: PropTypes.func,
    form: PropTypes.object
  }

  static defaultProps = {
    questions: [],
  }

  constructor(props) {
    super(props);
    this.state = {
      // Tags
      tags: [],
      tags_loading: false,
      tags_loaded: false,
    }
  }

  componentDidMount() {
    let {tags_loading, tags_loaded} = this.state;
    if (!tags_loading && !tags_loaded) this.fetch_tags();
  }

  changeHanderVal(key, val) {
    let {form} = this.props;
    form[key] = val;
    this.props.onChange(form);
  }

  changeHandler(key, event) {
    this.changeHanderVal(key, event.currentTarget.value);
  }

  changeHandlerSlider(key, event, value) {
    this.changeHanderVal(key, value);
  }

  fetch_tags() {
    api.get("/api/journaltag", {}, (res) => {
      this.setState({tags: res.tags});
    })
  }

  get_params() {
    let {questions} = this.props;
    let {form} = this.props;
    let params = {data: JSON.stringify(form)};
    questions.forEach((q) => {
      if (q.parse_tags) params.tags_from_text = form[q.name];
    });
    return params;
  }

  handle_tag_add(tag, qname) {
    let {form} = this.props;
    let val = form[qname];
    let idx = val.lastIndexOf(tag.type == 1 ? '@' : '#');
    if (idx > -1) {
      form[qname] = form[qname].slice(0, idx) + tag.id + ' ';
    }
    this.setState({form}, () => {
      this.refs[qname].focus();
    });
  }

  filtered_tags(search) {
    let {tags} = this.state;
    let person = search.startsWith('@');
    let stripped = search.slice(1); // Without prefix
    let type = person ? 1 : 2;
    return tags.filter((tag) => {
      return tag.name.toLowerCase().indexOf(stripped.toLowerCase()) > -1 && tag.type == type;
    });
  }

  render_tag_suggest(str, qname) {
    let {tags_loading} = this.state;
    let entering_tag = false;
    let _content, _selector;
    let last_space = str.lastIndexOf(' ');
    let last_word = "";
    if (last_space > -1) {
      last_word = str.slice(last_space + 1);
    } else {
      last_word = str;
    }
    if ((last_word.startsWith('#') || last_word.startsWith('@')) && !last_word.endsWith('.')) {
      entering_tag = true;
    }
    if (entering_tag) {
      if (tags_loading) _content = "Loading";
      else {
        let lis = this.filtered_tags(last_word).map((tag) => {
          return <ListItem primaryText={tag.name} onTouchTap={this.handle_tag_add.bind(this, tag, qname)} />
        })
        if (lis.length == 0) _content = <div className="empty">No suggestions</div>
        else _content = (
          <List>
            { lis }
          </List>
        );
      }
      _selector = (
        <Paper style={{maxHeight: "400px"}}>
          { _content }
        </Paper>
      );

    }
    return (
      <div>
        { _selector }
      </div>
    );
  }

  render_questions() {
    let {questions} = this.props;
    let {form} = this.props;
    return questions.map((q, i) => {
      let _response;
      let _tags;
      let _hint;
      let val = form[q.name];
      if (q.parse_tags && q.response_type == 'text') {
        _tags = this.render_tag_suggest(val || "", q.name);
        _hint = <small>You can @mention and #activity tag</small>
      }
      if (!q.response_type || q.response_type == 'text') _response = <TextField name={q.name} ref={q.name} value={val || ''} multiLine onChange={this.changeHandler.bind(this, q.name)} fullWidth={true} />
      else if (q.response_type == 'slider' || q.response_type == 'number') _response = <Slider name={q.name} value={val} onChange={this.changeHandlerSlider.bind(this, q.name)} max={10} min={1} defaultValue={5} step={1} />
      else if (q.response_type == 'number_oe') _response = <TextField name={q.name} ref={q.name} type='number' value={val || ''} onChange={this.changeHandler.bind(this, q.name)} fullWidth={true} />
      return (
        <div key={i}>
          <p className="lead">{ q.text }</p>
          { _hint }
          { _response }
          { _tags }
        </div>
      )
    });
  }

  render() {
    return (
      <div>
        { this.render_questions() }
      </div>
    )
  }
}