Guest User

Untitled

a guest
Nov 22nd, 2017
114
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.27 KB | None | 0 0
  1. //React & Redux
  2. import React, { Component } from 'react';
  3. import { connect } from 'react-redux';
  4. import { firebaseConnect, dataToJS } from 'react-redux-firebase';
  5. import { replace } from 'react-router-redux';
  6. //Components from Packages
  7. import { Form } from 'react-form';
  8. import Dropzone from 'react-dropzone';
  9. //Tools
  10. import _ from 'lodash';
  11. import moment from 'moment';
  12. //React Toolbox
  13. import { Input, Dialog, Autocomplete, } from 'react-toolbox/lib';
  14. import Button from 'react-toolbox/lib/button';
  15. import DropDown from "react-toolbox/lib/dropdown";
  16. import FontIcon from 'react-toolbox/lib/font_icon';
  17. import ProgressBar from 'react-toolbox/lib/progress_bar';
  18. //Actions
  19. import { setReloadSearch } from 'store/actions';
  20. import { updateFile, getFileMetaData, updateFileInPath } from 'store/actions/files';
  21. //Other Components
  22. import InformationBlock from 'components/Layouts/InformationBlock/InformationBlock';
  23. import { FormInputStyled, HeaderTitle, DivFloatRight, ButtonsWrapper, PFull, SectionFull, LinkStyled } from './styled';
  24.  
  25. const mapStateToProps = (state) => {
  26. return {
  27. organizations: dataToJS(state.firebase, 'organizations'),
  28. deals: dataToJS(state.firebase, 'deals'),
  29. contacts: dataToJS(state.firebase, 'contacts'),
  30. tasks: dataToJS(state.firebase, 'tasks'),
  31. currentUser: state.users.currentUser,
  32. };
  33. };
  34. const mapDispatchToProps = (dispatch) => {
  35. return {
  36. setReloadSearch: (state) => {
  37. dispatch(setReloadSearch(state));
  38. },
  39. updateFile: async (key, newState) => {
  40. return await dispatch(updateFile(key, newState));
  41. },
  42. getFileMetaData: async (path) => {
  43. return await dispatch(getFileMetaData(path));
  44. },
  45. updateFileInPath: async(fileDbPath, newState) => {
  46. return await dispatch(updateFileInPath(fileDbPath, newState));
  47. },
  48. replacePath: (path) => {
  49. dispatch(replace(path));
  50. }
  51. }
  52. };
  53.  
  54. @firebaseConnect([ 'organizations', 'deals', 'contacts', 'tasks', ])
  55. @connect(mapStateToProps, mapDispatchToProps)
  56. class DocumentUploadForm extends Component{
  57. state = {
  58. step: 1,
  59. uploading: false,
  60. valuesStep2: {},
  61. fileOnBrowser: null,
  62. organizations: {},
  63. deals: {},
  64. contacts: {},
  65. tasks: {},
  66. errorDialog: false,
  67. errorDialogMessage: '',
  68. };
  69. //Check when the data is available (either on Will Mount or later with Receive Props)
  70. componentWillMount(){
  71. let { organizations, deals, contacts, tasks } = this.props;
  72. if(!_.isEmpty(organizations)){
  73. this.mapStateValues(this.props, 'organizations');
  74. }
  75. if(!_.isEmpty(deals)){
  76. this.mapStateValues(this.props, 'deals');
  77. }
  78. if(!_.isEmpty(contacts)){
  79. this.mapStateValues(this.props, 'contacts');
  80. }
  81. if(!_.isEmpty(tasks)){
  82. this.mapStateValues(this.props, 'tasks');
  83. }
  84. }
  85. componentWillReceiveProps(nextProps){
  86. if(!_.isEqual(this.props.organizations, nextProps.organizations)){
  87. this.mapStateValues(nextProps, 'organizations');
  88. }
  89. if(!_.isEqual(this.props.deals, nextProps.deals)){
  90. this.mapStateValues(nextProps, 'deals');
  91. }
  92. if(!_.isEqual(this.props.contacts, nextProps.contacts)){
  93. this.mapStateValues(nextProps, 'contacts');
  94. }
  95. if(!_.isEqual(this.props.tasks, nextProps.tasks)){
  96. this.mapStateValues(nextProps, 'tasks');
  97. }
  98. }
  99.  
  100. /**
  101. * Map the component props to state values
  102. * @param {Object} props
  103. * @param {string} type
  104. */
  105. mapStateValues = (props, type) => {
  106. switch (type){
  107. case 'organizations':
  108. let organizations = _.mapValues(props.organizations, organization => organization.details.name);
  109. this.setState({organizations});
  110. break;
  111. case 'deals':
  112. let deals = _.pickBy(_.mapValues(props.deals, deal => {if(!deal.organization.id) return deal.organization.details.name; else return null;}));
  113. this.setState({deals});
  114. break;
  115. case 'contacts':
  116. let contacts = _.mapValues(props.contacts, contact => `${contact.details.name.first} ${contact.details.name.last}`);
  117. this.setState({contacts});
  118. break;
  119. case 'tasks':
  120. let tasks = _.mapValues(props.tasks, task => task.title);
  121. this.setState({tasks});
  122. break;
  123. default: break;
  124. }
  125. };
  126. /**
  127. * Save the file to the state of the component
  128. * @param {array} files
  129. */
  130. saveStep1 = files => {
  131. if(!_.isEmpty(files)){
  132. let file = _.first(files);
  133. let fileName = file.name;
  134. let fileExtension = '';
  135. if(fileName.indexOf('.') > 0){
  136. fileName = fileName.substring(0, fileName.lastIndexOf('.'));
  137. fileExtension = file.name.substring(file.name.lastIndexOf('.')+1);
  138. }
  139. this.setState({fileOnBrowser: file, step: 2, valuesStep2: {...this.state.valuesStep2, name: fileName, extension: fileExtension}});
  140. }
  141. };
  142. /**
  143. * Uploads the file and saves the document meta data
  144. * @param {Object} values
  145. * @returns {Promise.<void>}
  146. */
  147. saveFile = async (values) => {
  148. try {
  149. this.setState({uploading: true});
  150. let file = this.state.fileOnBrowser;
  151. //Rename the file to the value set by the user
  152. Object.defineProperty(file, 'name', {
  153. writable: true,
  154. value: values.name + (values.extension?`.${values.extension}`:'')
  155. });
  156.  
  157. //Select where the file would be associated with in the CRM
  158. let filePath, dbPath, typeKey;
  159. let type = values.type;
  160. switch(type){
  161. case 'contact':
  162. typeKey = values.contact;
  163. break;
  164. case 'organization':
  165. typeKey = values.organization;
  166. break;
  167. case 'deal':
  168. typeKey = values.deal;
  169. break;
  170. case 'task':
  171. typeKey = values.task;
  172. break;
  173. default:
  174. break;
  175. }
  176. filePath = `/files/${type}s/${typeKey}`;
  177. dbPath = `/${type}s/${typeKey}/files`;
  178.  
  179. //Upload the document to Fireabase Storage
  180. let result = await this.props.firebase.uploadFiles(filePath, [file], dbPath);
  181. let firstResult = _.first(result);
  182.  
  183. //Get the metadata from Firebase Storage
  184. if(!_.isEmpty(firstResult)){
  185. let fileUploaded = _.clone(firstResult.File);
  186. let fileMetaData = await this.props.getFileMetaData(fileUploaded.fullPath);
  187. fileUploaded = _.merge(fileUploaded, fileMetaData);
  188. fileUploaded[values.type] = typeKey;
  189. fileUploaded.owner = this.props.currentUser.uid;
  190. fileUploaded = _.pickBy(fileUploaded);
  191.  
  192. //Update meta data in Firebase
  193. await this.props.updateFile(firstResult.key, fileUploaded);
  194. await this.props.updateFileInPath(`${dbPath}/${firstResult.key}`, fileUploaded);
  195. }
  196.  
  197. //When done, reload the search results and go back to the documents listing
  198. setTimeout(() => this.props.setReloadSearch(true), 500);
  199. this.props.replacePath('/documents');
  200. } catch(error) {
  201. this.setState({loading: false, errorDialog: true, errorDialogMessage: error.message});
  202. }
  203. };
  204. render(){
  205. if(this.state.uploading){
  206. return <ProgressBar type="circular" mode="indeterminate"/>;
  207. }
  208. if(this.state.step === 1) {
  209. return (
  210. <div>
  211. <InformationBlock>
  212. <HeaderTitle><h5>Upload File</h5></HeaderTitle>
  213. <SectionFull>
  214. <div>
  215. <Dropzone
  216. className='dropzone'
  217. activeClassName='dropzone-active'
  218. onDrop={this.saveStep1}
  219. multiple={false}
  220. style={{ width: '100%', height: '200px'}}
  221. >
  222. <FontIcon value='cloud_upload'/><p style={{textAlign: 'center'}}>Drop a file</p>
  223. </Dropzone>
  224. </div>
  225. </SectionFull>
  226. </InformationBlock>
  227. <ButtonsWrapper>
  228. {this.state.fileOnBrowser?
  229. <DivFloatRight><Button className="btn--primary" icon={'navigate_next'} label={'Next'} onClick={() => this.setState({step: 2})} /></DivFloatRight> : null
  230. }
  231. </ButtonsWrapper>
  232. </div>
  233. );
  234. } else if(this.state.step === 2) {
  235. let types = [{value: 'contact', label: 'Contact'}, {value: 'organization', label: 'Organization'}, {value: 'deal', label: 'Deal'}, {value: 'task', label: 'Task'},];
  236. return (
  237. <div>
  238. <InformationBlock>
  239. <HeaderTitle><h5>File Information</h5></HeaderTitle>
  240. <PFull><strong>File Name:</strong> {this.state.fileOnBrowser.name}</PFull>
  241. <PFull><strong>Last Modified:</strong> {moment(this.state.fileOnBrowser.lastModified, 'x').format('MMMM Do YYYY, h:mm:ss a')}</PFull>
  242. <PFull><strong>Type:</strong> {this.state.fileOnBrowser.type}</PFull>
  243. <PFull><strong>Preview:</strong> <LinkStyled target={'_blank'} label={'File'} href={this.state.fileOnBrowser.preview} /></PFull>
  244. </InformationBlock>
  245. <Form
  246. onSubmit={this.saveFile}
  247. defaultValues={this.state.valuesStep2}
  248. validate={({type, contact, organization, deal, task}) => {
  249. return {
  250. type: !type? 'Type is required' : undefined,
  251. contact: (type === 'contact' && !contact)?'Contact is required' : undefined,
  252. organization: (type === 'organization' && !organization)?'Organization is required' : undefined,
  253. deal: (type === 'deal' && !deal)?'Deal is required' : undefined,
  254. task: (type === 'task' && !task)?'Task is required' : undefined,
  255. };
  256. }}
  257. onChange={({values}) => {
  258. this.setState({valuesStep2: values});
  259. }}
  260. >
  261. {({submitForm, values}) => {
  262. return(
  263. <form onSubmit={submitForm}>
  264. <InformationBlock>
  265. <HeaderTitle><h5>Change File Name</h5></HeaderTitle>
  266. <FormInputStyled field="name">
  267. {({ setValue, getValue }) => {
  268. return <Input label='File Name' value={getValue()?getValue():''} onChange={val => {setValue(val);}} />;
  269. }}
  270. </FormInputStyled>
  271. <FormInputStyled field="extension">
  272. {({ getValue }) => {
  273. return <Input label='File Extension' disabled={true} value={getValue()?getValue():''} />;
  274. }}
  275. </FormInputStyled>
  276. </InformationBlock>
  277. <InformationBlock>
  278. <HeaderTitle><h5>Attach To</h5></HeaderTitle>
  279. <FormInputStyled field="type">
  280. {({setValue, getValue}) => {
  281. return <DropDown label={'Attach to'} required={true} source={types} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />;
  282. }}
  283. </FormInputStyled>
  284. {values && values.type === 'contact'?
  285. <FormInputStyled field='contact'>
  286. {({setValue, getValue}) => {
  287. return <Autocomplete label='Contact' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.contacts} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
  288. }}
  289. </FormInputStyled> : null
  290. }
  291. {values && values.type === 'organization' ?
  292. <FormInputStyled field='organization'>
  293. {({setValue, getValue}) => {
  294. return <Autocomplete label='Organization' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.organizations} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
  295. }}
  296. </FormInputStyled> : null
  297. }
  298. {values && values.type === 'deal' ?
  299. <FormInputStyled field='deal'>
  300. {({setValue, getValue}) => {
  301. return <Autocomplete label='Deal' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.deals} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
  302. }}
  303. </FormInputStyled> : null
  304. }
  305. {values && values.type === 'task' ?
  306. <FormInputStyled field='task'>
  307. {({setValue, getValue}) => {
  308. return <Autocomplete label='Task' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.tasks} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
  309. }}
  310. </FormInputStyled> : null
  311. }
  312. </InformationBlock>
  313. <ButtonsWrapper>
  314. <Button className="btn--secondary" icon={'keyboard_arrow_left'} label={'Previous'} onClick={() => this.setState({step: 1})} />
  315. <DivFloatRight><Button label={'Save & Upload File'} type={'submit'} className="btn--primary" icon={'file_upload'} /></DivFloatRight>
  316. </ButtonsWrapper>
  317. </form>
  318. );
  319. }}
  320. </Form>
  321. <Dialog
  322. active={this.state.errorDialog}
  323. type='small'
  324. actions={[{label: 'Ok', onClick: () => this.setState({errorDialog: false})}]}
  325. title='Error'
  326. >
  327. <p>{this.state.errorDialogMessage}</p>
  328. </Dialog>
  329. </div>
  330. );
  331. } else {
  332. return null;
  333. }
  334. }
  335. }
  336.  
  337. export default DocumentUploadForm;
Add Comment
Please, Sign In to add comment