UNPKG

d2-ui

Version:
293 lines (261 loc) 10.5 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import log from 'loglevel'; import { Observable } from 'rxjs'; import { config } from 'd2/lib/d2'; import Tabs from 'material-ui/Tabs/Tabs'; import Tab from 'material-ui/Tabs/Tab'; import Paper from 'material-ui/Paper/Paper'; import Divider from 'material-ui/Divider'; import ExpressionDescription from './ExpressionDescription'; import ExpressionOperators from './ExpressionOperators'; import ExpressionFormula from './ExpressionFormula'; import OrganisationUnitGroupSelector from './OrganisationUnitGroupSelector'; import DataElementOperandSelector from './DataElementOperandSelector'; import ReportingRatesSelector from './ReportingRatesSelector'; import ProgramOperandSelector from './ProgramOperandSelector'; import ConstantSelector from './ConstantSelector'; import Heading from '../headings/Heading.component'; import addD2Context from '../component-helpers/addD2Context'; import Action from '../action/Action'; import Row from '../layout/Row.component'; import Column from '../layout/Column.component'; config.i18n.strings.add('description'); config.i18n.strings.add('program_tracked_entity_attributes'); config.i18n.strings.add('program_indicators'); config.i18n.strings.add('program_data_elements'); config.i18n.strings.add('field_is_required'); config.i18n.strings.add('organisation_unit_counts'); // shorten to org_unit_counts in maintenance config.i18n.strings.add('reporting_rates'); config.i18n.strings.add('data_elements'); config.i18n.strings.add('constants'); config.i18n.strings.add('programs'); const styles = { expressionDescription: { padding: '1rem', margin: '1rem 0', }, expressionMessage: { valid: { padding: '1rem', color: '#006400', }, invalid: { padding: '1rem', color: '#8B0000', }, }, list: { width: '100%', outline: 'none', border: 'none', padding: '0rem 1rem', }, expressionFormulaWrap: { padding: '1rem', maxWidth: '650px', marginRight: '1rem', }, expressionValueOptionsWrap: { minHeight: 400, minWidth: 600, }, expressionContentWrap: { minHeight: 360, }, tabItemContainer: { backgroundColor: 'rgb(33, 150, 243)', }, tabs: { color: 'white', }, divider: { padding: '0 1rem 2rem', }, }; class ExpressionManager extends Component { constructor(props, context) { super(props, context); this.state = { formula: this.props.formulaValue, description: this.props.descriptionValue, expressionStatus: { description: '', isValid: false, }, }; this.i18n = this.context.d2.i18n; this.requestExpressionStatusAction = Action.create('requestExpressionStatus'); } componentWillMount() { if (!this.props.expressionStatusStore) { return true; } let first = true; this.disposable = this.props.expressionStatusStore .subscribe((expressionStatus) => { this.setState({ expressionStatus: { description: expressionStatus.description, isValid: expressionStatus.status === 'OK', message: expressionStatus.message, }, }, () => { if (first) { first = false; return; } this.props.expressionChanged({ formula: this.state.formula, description: this.state.description, expressionStatus: this.state.expressionStatus, }); }); }, error => log.error(error)); this.expressionStatusDisposable = this.requestExpressionStatusAction .debounceTime(500) .map(this.props.validateExpression || this.validateExpression) .concatAll() .subscribe( response => this.props.expressionStatusStore.setState(response), error => log.error(error), ); if (this.props.formulaValue.trim()) { this.requestExpressionStatus(); } } componentWillUnmount() { this.disposable && this.disposable.unsubscribe(); this.expressionStatusDisposable && this.expressionStatusDisposable.unsubscribe(); } descriptionChange = (newDescription) => { this.setState({ description: newDescription, }, () => { this.props.expressionChanged({ formula: this.state.formula, description: this.state.description, expressionStatus: this.state.expressionStatus, }); }); } formulaChange = (newFormula) => { this.setState({ formula: newFormula, }, () => { this.requestExpressionStatus(); }); } addOperatorToFormula = (operator) => { this.appendToFormula(operator); } programOperandSelected = (programFormulaPart) => { this.appendToFormula(programFormulaPart); } appendToFormula = (partToAppend) => { this.setState({ formula: [this.state.formula, partToAppend].join(''), }, () => { this.requestExpressionStatus(); }); } dataElementOperandSelected = (dataElementOperandId) => { const dataElementOperandFormula = ['#{', dataElementOperandId, '}'].join(''); this.appendToFormula(dataElementOperandFormula); } requestExpressionStatus = () => { this.requestExpressionStatusAction(this.state.formula); } validateExpression = action => { const formula = action.data; const url = 'expressions/description'; return Observable.fromPromise( this.context.d2.Api.getApi().get(url, {expression: formula}), ); } render() { const isDescriptionValid = () => this.state.description && this.state.description.trim(); return ( <Column> <Heading level={3} text={this.props.titleText} /> <Row> <Paper style={styles.expressionFormulaWrap}> <Column> <ExpressionDescription descriptionValue={this.state.description} descriptionLabel={this.i18n.getTranslation('description')} onDescriptionChange={this.descriptionChange} errorText={!isDescriptionValid() ? this.i18n.getTranslation('field_is_required') : undefined} onBlur={this.requestExpressionStatus} /> <ExpressionFormula onFormulaChange={this.formulaChange} formula={this.state.formula} /> <ExpressionOperators operatorClicked={this.addOperatorToFormula} /> </Column> </Paper> <Paper style={styles.expressionValueOptionsWrap}> <Tabs style={styles.expressionContentWrap} tabItemContainerStyle={styles.tabItemContainer}> <Tab style={styles.tabs} label={this.i18n.getTranslation('data_elements')}> <DataElementOperandSelector listStyle={styles.list} onSelect={this.dataElementOperandSelected} /> </Tab> <Tab style={styles.tabs} label={this.i18n.getTranslation('programs')}> <ProgramOperandSelector onSelect={this.programOperandSelected} /> </Tab> <Tab style={styles.tabs} label={this.i18n.getTranslation('organisation_unit_counts')}> <OrganisationUnitGroupSelector listStyle={styles.list} onSelect={this.appendToFormula} /> </Tab> <Tab style={styles.tabs} label={this.i18n.getTranslation('constants')}> <ConstantSelector listStyle={styles.list} onSelect={this.appendToFormula} /> </Tab> <Tab style={styles.tabs} label={this.i18n.getTranslation('reporting_rates')}> <ReportingRatesSelector listStyle={styles.list} onSelect={this.appendToFormula} /> </Tab> </Tabs> <div style={styles.divider}> <Divider /> </div> </Paper> </Row> <Column> <Paper style={styles.expressionDescription}>{this.state.expressionStatus.description}</Paper> <div style={this.state.expressionStatus.isValid ? styles.expressionMessage.valid : styles.expressionMessage.invalid} > {this.state.expressionStatus.message} </div> </Column> </Column> ); } } ExpressionManager.propTypes = { expressionStatusStore: PropTypes.object.isRequired, expressionChanged: PropTypes.func.isRequired, descriptionValue: PropTypes.string, formulaValue: PropTypes.string, titleText: PropTypes.string, validateExpression: PropTypes.func, }; ExpressionManager.defaultProps = { descriptionValue: '', formulaValue: '', titleText: '', }; export default addD2Context(ExpressionManager);