# containers/Logbook/Tags/TagsManagerContainer.js

## Maintainability

76.04

## Lines of code

251

## Difficulty

36.70

## Estimated Errors

2.02

## Function weight

### By Complexity

### By SLOC

import React, { Component } from 'react' import PropTypes from 'prop-types' import { getTagsByInvestigationId, createTagsByInvestigationId, updateTagsByInvestigationId } from '../../../api/icat/icatPlus'; import axios from 'axios'; import { connect } from 'react-redux'; import TagListPanel from '../../../components/Logbook/Tag/TagListPanel'; import UserMessage from '../../../components/UserMessage'; import { Grid, Row, Col } from 'react-bootstrap'; import { INFO_MESSAGE_TYPE, ERROR_MESSAGE_TYPE } from '../../../constants/EventTypes'; import Loading from '../../../components/Loading'; import TagListMenu from '../../../components/Logbook/Tag/TagListMenu'; import NewOrEditTagPanel from '../../../components/Logbook/Tag/NewOrEditTagPanel'; import _ from 'lodash'; /** * Container which holds the logic to list, create and edit tag. */ export class TagsManagerContainer extends Component { constructor(props) { super(props); this.state = { availableTags: [], errorMessge: null, isEditingOrCreatingTag: false, // false when listing tags. True when editing or creacting tag isFetching: true, isFetched: false, tag: null, // tag being edited, null otherwise userMessage: null // an object which structure is { type: XXX, text:ZZZ } } this.createTag = this.createTag.bind(this); this.getTags = this.getTags.bind(this); this.onEditTagButtonClicked = this.onEditTagButtonClicked.bind(this); this.onNewTagButtonClicked = this.onNewTagButtonClicked.bind(this); this.onSaveButtonClicked = this.onSaveButtonClicked.bind(this); this.requestTags = this.requestTags.bind(this); this.setUserMessage = this.setUserMessage.bind(this); this.updateTag = this.updateTag.bind(this); } render() { if (this.state.isFetching === true) { return <Loading message='Loading tags' /> }; if (this.state.availableTags.length === 0) { return <WrapperWithUserMessage userMessage={this.state.userMessage}> <NoTagGrid /> <TagListMenu onNewTagButtonClicked={this.onNewTagButtonClicked} logbookContext={this.props.logbookContext} /> </WrapperWithUserMessage> }; if (this.state.isEditingOrCreatingTag === true) { return <WrapperWithUserMessage userMessage={this.state.userMessage}> <NewOrEditTagPanel onSaveButtonClicked={this.onSaveButtonClicked} investigationId={this.props.investigationId} panelHeaderText={this.state.tag ? 'Edit tag' : 'New tag'} onCancelButtonClicked={() => this.setState({ isEditingOrCreatingTag: false })} tag={this.state.tag} /> </WrapperWithUserMessage> } return <WrapperWithUserMessage userMessage={this.state.userMessage} > <TagListMenu onNewTagButtonClicked={this.onNewTagButtonClicked} logbookContext={this.props.logbookContext} /> <TagListPanel availableTags={this.state.availableTags} isTagEditionEnabled={true} onEditButtonClicked={this.onEditTagButtonClicked} /> </WrapperWithUserMessage> } componentDidMount() { this.requestTags().then(tags => this.setState({ availableTags: tags })); } /** * Function Triggered when the user clicks the New button in the menu */ onNewTagButtonClicked() { this.setState({ isEditingOrCreatingTag: true, userMessage: null }); } /** * Function triggered when the use clicks on the EDit button for a given tag * @param {*} tag tag to be edited */ onEditTagButtonClicked(tag) { if (tag) { this.setState({ isEditingOrCreatingTag: true, tag: tag, userMessage: null }); }; } /** * Function triggered when the user clicks on the save button during tag creation or edition * @param {object} tag tag to be created or edited */ onSaveButtonClicked(tag) { if (this.state.tag) { // the tag is to be edited this.updateTag(tag) .then(tag => { let availableTags = _.cloneDeep(this.state.availableTags); let tagToBeUpdatedIndex = _.findIndex(availableTags, availableTag => { return _.isEqual(availableTag._id, tag._id) }); availableTags.splice(tagToBeUpdatedIndex, 1, tag); this.setState({ availableTags: availableTags, isEditingOrCreatingTag: false, tag: null }) this.setUserMessage(INFO_MESSAGE_TYPE, 'Tag was successfully updated'); }).catch(() => { this.setUserMessage(ERROR_MESSAGE_TYPE, 'Error. The tag was not edited.'); }); } else { // the tag is to be created this.createTag(tag) .then(tag => this.requestTags()) .then(tags => { this.setState({ availableTags: tags, isEditingOrCreatingTag: false }) this.setUserMessage(INFO_MESSAGE_TYPE, 'Tag was successfully created'); }) .catch(() => { this.setUserMessage(ERROR_MESSAGE_TYPE, 'Error. The tag was not created.'); }); } } /** * Request available tags. This function does not perform the axios call itself. The latter is isolated in the function getTags() */ requestTags() { return new Promise((resolve, reject) => { if (this.props.investigationId && this.props.user.sessionId) { this.setState({ isFetching: true, errorMessage: null }); this.getTags() .then(response => { this.setState({ isFetching: false, isFetched: true }); resolve(response.data.tags) }) .catch(error => { this.setState({ isFetching: false, isFetched: false }); this.setUserMessage(ERROR_MESSAGE_TYPE, 'An error occured while retrieving the tag list.'); reject(error); }); } else { console.log('requestTags() Missing props'); this.setState({ availableTags: [], isFetching: false, isFetched: false }); reject(); } }) } /** * Get available tags * @returns {Promise} */ getTags() { return axios({ method: 'get', url: getTagsByInvestigationId(this.props.user.sessionId, this.props.investigationId), }) }; /** * Set a message which will be displayed to the user * @param {string} type message type * @param {string} text text of the message */ setUserMessage(type, message) { this.setState({ userMessage: { type: type, text: message } }); } /** * Create a tag * @param {object} tag tag to be created * @returns {Promise} */ createTag(tag) { if (tag) { return new Promise((resolve, reject) => { axios({ method: 'post', url: createTagsByInvestigationId(this.props.user.sessionId, this.props.investigationId), data: tag, }) .then(data => { return resolve(data.data); }) .catch(error => { reject(); }); }) } } /** * Update a tag * @param {object} tag tag to be updated * @returns {Promise} */ updateTag(tag) { if (tag) { return new Promise((resolve, reject) => { axios({ method: 'put', url: updateTagsByInvestigationId(this.props.user.sessionId, this.props.investigationId, tag._id), data: tag, }) .then(data => { resolve(data.data) }) .catch(() => { reject(); }) }) } } } /** * Redux related function which is used to map local props with redux state * @param {*} state redux state */ function mapStateToProps(state) { return { logbookContext: state.logbook.context, user: state.user }; } export default connect(mapStateToProps)(TagsManagerContainer); TagsManagerContainer.propTypes = { /** Investigation identifier */ investigationId: PropTypes.string.isRequired, /** */ logbookContext: PropTypes.object } const NoTagGrid = props => { return <Grid fluid={true} > <h3> Tags currently available in this proposal</h3> <Row style={{ marginLeft: '30px', marginRight: '30px', marginTop: '20px' }}> <UserMessage message='There is no tag in this proposal yet.' isTextCentered={true} type={INFO_MESSAGE_TYPE} /> </Row> </Grid > } const TagUserMessage = (props) => { if (props.userMessage) { return <Grid fluid> <Row> <Col xs={12}> <UserMessage type={props.userMessage.type} message={props.userMessage.text} > </UserMessage> </Col> </Row> </Grid> } return null; } const WrapperWithUserMessage = props => { return <React.Fragment> <TagUserMessage userMessage={props.userMessage} /> {props.children} </React.Fragment> }