# src/containers/TagContainer.js

## Maintainability

69.55

## Lines of code

379

## Difficulty

53.51

## Estimated Errors

2.69

## Function weight

### By Complexity

### By SLOC

import React from 'react' import PropTypes from 'prop-types'; import EventMessage from '../components/Event/EventMessage'; import axios from 'axios'; import { connect } from 'react-redux'; import TagList from '../components/Event/Tag/TagList'; import TagEditor from '../components/Event/Tag/TagEditor'; import { URL } from '../api/icat/icatPlus'; import _ from 'lodash'; import { INFO_MESSAGE_TYPE, ERROR_MESSAGE_TYPE, TAG_MANAGER_CONTEXT, BASIC_EVENT_CONTEXT, TAG_EDITOR_CONTEXT, TAG_CREATOR_CONTEXT, NEW_EVENT_CONTEXT, DETAILED_EVENT_CONTEXT } from '../constants/EventTypes'; import TagActionBar from '../components/Event/Tag/TagActionBar'; const TAGS_FETCHED_STATUS = "tagFetchedStatus"; const TAGS_FETCHING_STATUS = "fetchingTagsStatus"; /** * The event tag container in charge of managing event tags. */ class TagContainer extends React.Component { constructor(props) { super(props); this.state = { availableTags: null, // all the tag objects for this investigation context: this.props.context, message: null, selectedTags: [], // contains currently selected tags for a given event when tagContainer is used in the context of an event. Contains the edited tag when used with tag editor. tagsReceptionStatus: TAGS_FETCHING_STATUS, //'idle', 'fetching', 'fetched' tagsUploadStatus: null } if (props.setTagContainer) { props.setTagContainer(this); } this.addTagToSelection = this.addTagToSelection.bind(this); this.createNewTag = this.createNewTag.bind(this); this.getTagsByInvestigationId = this.getTagsByInvestigationId.bind(this); this.setMessage = this.setMessage.bind(this); this.removeTagFromSelection = this.removeTagFromSelection.bind(this); this.updateTags = this.updateTags.bind(this); this.editTag = this.editTag.bind(this); this.showAvailableTags = this.showAvailableTags.bind(this); this.showNewTag = this.showNewTag.bind(this); } render() { let { event, investigationId } = this.props; let errorMessage = null; if (this.state.message) { errorMessage = (<EventMessage type={this.state.message.type} message={this.state.message.text} > </EventMessage>); } if (this.state.tagsReceptionStatus === TAGS_FETCHED_STATUS) { if (this.state.context === BASIC_EVENT_CONTEXT) { return (<div> {errorMessage} <TagList selectedTags={this.state.selectedTags} context={BASIC_EVENT_CONTEXT} /> </div>) } if (this.state.context === DETAILED_EVENT_CONTEXT || this.state.context === NEW_EVENT_CONTEXT) { return (<div> {errorMessage} <TagList availableTags={this.state.availableTags} addTagToSelection={this.addTagToSelection} context={DETAILED_EVENT_CONTEXT} createNewTag={this.createNewTag} investigationId={investigationId} removeTagFromSelection={this.removeTagFromSelection} selectedTags={this.state.selectedTags} /> </div>) } // no event is provided. if (this.state.context === TAG_MANAGER_CONTEXT) { return (<div> {errorMessage} <TagActionBar showNewTag={this.showNewTag} /> <TagList availableTags={this.state.availableTags} context={TAG_MANAGER_CONTEXT} editTag={this.editTag} investigationId={investigationId} updateTags={this.updateTags} /> </div>) } if (this.state.context === TAG_EDITOR_CONTEXT || this.state.context === TAG_CREATOR_CONTEXT) { // if (this.state.selectedTags && this.state.selectedTags.length != 0) { return (<div> {errorMessage} <TagEditor showAvailableTags={this.showAvailableTags} tag={this.state.selectedTags.length !== 0 ? this.state.selectedTags[0] : null} createNewTag={this.createNewTag} updateTags={this.updateTags} /> </div>) // } else { // return (<div> // {errorMessage} // <TagList // availableTags={this.state.availableTags} // context={TAG_MANAGER_CONTEXT} // investigationId={investigationId} // updateTags={this.updateTags} // /> // </div>) // } } } if (this.state.tagsReceptionStatus === TAGS_FETCHING_STATUS) { return <p> Please wait... </p> } } componentDidMount() { let onSuccess = (data) => { // then set selected tags if (data.data.tags) { let availableTags = data.data.tags; if (this.props.event) { //tag container is used to display tag in an event let selectedTags = this.props.event.tag.map(tagId => { return _.find(availableTags, (tag) => (tag._id === tagId)) }); this.setState({ availableTags: data.data.tags, selectedTags: selectedTags, tagsReceptionStatus: TAGS_FETCHED_STATUS }); return } else { //tag container is used to edit tag (not in an event) this.setState({ availableTags: data.data.tags, tagsReceptionStatus: TAGS_FETCHED_STATUS }) return } } this.setMessage(ERROR_MESSAGE_TYPE, 'Tag display error') }; let onError = (err) => { this.setMessage(ERROR_MESSAGE_TYPE, 'Error during tag reception') } // first get available Tags by investigationId this.getTagsByInvestigationId(onSuccess, onError) } // /** // * Request tags associated to the given investigationId // * @param {integer} investigationId the investigation indentifier // * @param {String} sessionId the session identifier // * @param {func} onError the callback function executed when an error occured during the request. // */ getTagsByInvestigationId(onSuccess, onError) { let { investigationId } = this.props; let sessionId = this.props.user.sessionId; if (investigationId && sessionId) { axios({ method: 'get', url: URL.getTagsByInvestigationId(sessionId, investigationId), }) .then(data => onSuccess(data)) .catch((error) => { console.log("[ERROR] An error occured while retrieving the tag list. The error is " + error); onError("An error occured while retrieving the tag list.") }) } else { console.log("[ERROR] Missing parameters in the request handled by getEventsByInvestigationId()") onError("An error occured while retrieving the tag list.") } } /** callBack function triggered when an error occurs. * @param {string} type type of error * @param {string} text text of the message * @param {string} context the context in which the container should enter (if provided) */ setMessage(type, message, context) { if (context) { this.setState({ message: { type: type, text: message, context: context, } }) } else { this.setState({ message: { type: type, text: message } }) } } /** * Creates a new tag on the server side * @param {Object} newTag tag to create */ createNewTag(newTag) { let _this = this; let nextContext = (this.props.context === TAG_CREATOR_CONTEXT) ? TAG_MANAGER_CONTEXT : this.props.context // POST the new tag to the server if (newTag) { axios({ method: 'post', url: URL.createTagsByInvestigationId(this.props.user.sessionId, this.props.investigationId), data: newTag, }) .then((data) => { let newlyCreatedTag = data.data; console.log("The new tag was created successfully on ICAT+ server."); let selectedTags = []; if (this.props.context === DETAILED_EVENT_CONTEXT) { // ADD the new tag in the currently selected tags. selectedTags = _.map(_this.state.selectedTags, _.clone); selectedTags.push(newlyCreatedTag); } // GET the available tags and finally update the compoenent state let onSuccess = (data) => { if (data.data.tags) { this.setState({ availableTags: data.data.tags, context: nextContext, selectedTags: selectedTags, tagsReceptionStatus: TAGS_FETCHED_STATUS }); this.setMessage(INFO_MESSAGE_TYPE, "Tag successfully created"); return } this.setMessage(ERROR_MESSAGE_TYPE, 'Error in server response', nextContext) } let onError = () => { this.setMessage(ERROR_MESSAGE_TYPE, 'Error while retrieving tags', nextContext) } this.getTagsByInvestigationId(onSuccess, onError) }) .catch((error) => { console.log("[ERROR] the tag could not be created. The error is : " + error); this.setMessage(ERROR_MESSAGE_TYPE, "[ERROR] Tag not created.", nextContext); }); } } /** * Update tags on the server side * @param {Array} tags the tags to update on the server */ updateTags(tags) { if (tags) { let promises = []; tags.forEach(tag => { promises.push( axios({ method: 'put', url: URL.updateTagsByInvestigationId(this.props.user.sessionId, this.props.investigationId, tag._id), data: tag, }) ) }); Promise.all(promises) .then((results) => { results.forEach(function (data) { console.log("The tag " + data.name + " was successfully updated on ICAT+ server."); }) this.setMessage(INFO_MESSAGE_TYPE, "Tags was successfully updated"); this.setState({ context: TAG_MANAGER_CONTEXT }) }) .catch((error) => { console.log("[ERROR] At least one tag could not be updated. The error is : " + error); this.setMessage(ERROR_MESSAGE_TYPE, "[ERROR] All tags could not be updated."); }) } } /** Add a tag to the selected Tags */ addTagToSelection(tag) { let selectedTags = _.map(this.state.selectedTags, _.clone); selectedTags.push(tag); // enable the save button in detailed event view this.props.canEnableSaveButton({ hasText: true, currentTextEqualsOriginal: false }) this.setState({ message: null, selectedTags: selectedTags }) } /** * Remove the given tag from the current tags * @param {*} tag */ removeTagFromSelection(tag) { if (tag) { // enable the save button in detailed event view this.props.canEnableSaveButton({ hasText: true, currentTextEqualsOriginal: false }) this.setState({ message: null, selectedTags: _.filter(this.state.selectedTags, (value) => (_.isEqual(value, tag) === false)), }) } } /** * Edit a given tag * @param {*} tag the tag to be edited */ editTag(tag) { if (tag) { this.setState({ context: TAG_EDITOR_CONTEXT, message: null, selectedTags: [tag], }); } } showNewTag() { this.setState({ context: TAG_CREATOR_CONTEXT, message: null, }); } /** * Shows available tags in the tag manager */ showAvailableTags() { this.setState({ context: TAG_MANAGER_CONTEXT, }); } } function mapStateToProps(state) { return { user: state.user, }; } TagContainer.propTypes = { /* the event to display the tags of. No events means we use the TagManager */ event: PropTypes.object, /* the investigationId to display the event from */ investigationId: PropTypes.string.isRequired, /* Callback function used to enable the save button for a detailed event */ canEnableSaveButton: PropTypes.func, setTagContainer: PropTypes.func, } export default connect( mapStateToProps, )(TagContainer);