Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- //React & Redux
- import React, { Component } from 'react';
- import { connect } from 'react-redux';
- import { firebaseConnect, dataToJS } from 'react-redux-firebase';
- import { replace } from 'react-router-redux';
- //Components from Packages
- import { Form } from 'react-form';
- import Dropzone from 'react-dropzone';
- //Tools
- import _ from 'lodash';
- import moment from 'moment';
- //React Toolbox
- import { Input, Dialog, Autocomplete, } from 'react-toolbox/lib';
- import Button from 'react-toolbox/lib/button';
- import DropDown from "react-toolbox/lib/dropdown";
- import FontIcon from 'react-toolbox/lib/font_icon';
- import ProgressBar from 'react-toolbox/lib/progress_bar';
- //Actions
- import { setReloadSearch } from 'store/actions';
- import { updateFile, getFileMetaData, updateFileInPath } from 'store/actions/files';
- //Other Components
- import InformationBlock from 'components/Layouts/InformationBlock/InformationBlock';
- import { FormInputStyled, HeaderTitle, DivFloatRight, ButtonsWrapper, PFull, SectionFull, LinkStyled } from './styled';
- const mapStateToProps = (state) => {
- return {
- organizations: dataToJS(state.firebase, 'organizations'),
- deals: dataToJS(state.firebase, 'deals'),
- contacts: dataToJS(state.firebase, 'contacts'),
- tasks: dataToJS(state.firebase, 'tasks'),
- currentUser: state.users.currentUser,
- };
- };
- const mapDispatchToProps = (dispatch) => {
- return {
- setReloadSearch: (state) => {
- dispatch(setReloadSearch(state));
- },
- updateFile: async (key, newState) => {
- return await dispatch(updateFile(key, newState));
- },
- getFileMetaData: async (path) => {
- return await dispatch(getFileMetaData(path));
- },
- updateFileInPath: async(fileDbPath, newState) => {
- return await dispatch(updateFileInPath(fileDbPath, newState));
- },
- replacePath: (path) => {
- dispatch(replace(path));
- }
- }
- };
- @firebaseConnect([ 'organizations', 'deals', 'contacts', 'tasks', ])
- @connect(mapStateToProps, mapDispatchToProps)
- class DocumentUploadForm extends Component{
- state = {
- step: 1,
- uploading: false,
- valuesStep2: {},
- fileOnBrowser: null,
- organizations: {},
- deals: {},
- contacts: {},
- tasks: {},
- errorDialog: false,
- errorDialogMessage: '',
- };
- //Check when the data is available (either on Will Mount or later with Receive Props)
- componentWillMount(){
- let { organizations, deals, contacts, tasks } = this.props;
- if(!_.isEmpty(organizations)){
- this.mapStateValues(this.props, 'organizations');
- }
- if(!_.isEmpty(deals)){
- this.mapStateValues(this.props, 'deals');
- }
- if(!_.isEmpty(contacts)){
- this.mapStateValues(this.props, 'contacts');
- }
- if(!_.isEmpty(tasks)){
- this.mapStateValues(this.props, 'tasks');
- }
- }
- componentWillReceiveProps(nextProps){
- if(!_.isEqual(this.props.organizations, nextProps.organizations)){
- this.mapStateValues(nextProps, 'organizations');
- }
- if(!_.isEqual(this.props.deals, nextProps.deals)){
- this.mapStateValues(nextProps, 'deals');
- }
- if(!_.isEqual(this.props.contacts, nextProps.contacts)){
- this.mapStateValues(nextProps, 'contacts');
- }
- if(!_.isEqual(this.props.tasks, nextProps.tasks)){
- this.mapStateValues(nextProps, 'tasks');
- }
- }
- /**
- * Map the component props to state values
- * @param {Object} props
- * @param {string} type
- */
- mapStateValues = (props, type) => {
- switch (type){
- case 'organizations':
- let organizations = _.mapValues(props.organizations, organization => organization.details.name);
- this.setState({organizations});
- break;
- case 'deals':
- let deals = _.pickBy(_.mapValues(props.deals, deal => {if(!deal.organization.id) return deal.organization.details.name; else return null;}));
- this.setState({deals});
- break;
- case 'contacts':
- let contacts = _.mapValues(props.contacts, contact => `${contact.details.name.first} ${contact.details.name.last}`);
- this.setState({contacts});
- break;
- case 'tasks':
- let tasks = _.mapValues(props.tasks, task => task.title);
- this.setState({tasks});
- break;
- default: break;
- }
- };
- /**
- * Save the file to the state of the component
- * @param {array} files
- */
- saveStep1 = files => {
- if(!_.isEmpty(files)){
- let file = _.first(files);
- let fileName = file.name;
- let fileExtension = '';
- if(fileName.indexOf('.') > 0){
- fileName = fileName.substring(0, fileName.lastIndexOf('.'));
- fileExtension = file.name.substring(file.name.lastIndexOf('.')+1);
- }
- this.setState({fileOnBrowser: file, step: 2, valuesStep2: {...this.state.valuesStep2, name: fileName, extension: fileExtension}});
- }
- };
- /**
- * Uploads the file and saves the document meta data
- * @param {Object} values
- * @returns {Promise.<void>}
- */
- saveFile = async (values) => {
- try {
- this.setState({uploading: true});
- let file = this.state.fileOnBrowser;
- //Rename the file to the value set by the user
- Object.defineProperty(file, 'name', {
- writable: true,
- value: values.name + (values.extension?`.${values.extension}`:'')
- });
- //Select where the file would be associated with in the CRM
- let filePath, dbPath, typeKey;
- let type = values.type;
- switch(type){
- case 'contact':
- typeKey = values.contact;
- break;
- case 'organization':
- typeKey = values.organization;
- break;
- case 'deal':
- typeKey = values.deal;
- break;
- case 'task':
- typeKey = values.task;
- break;
- default:
- break;
- }
- filePath = `/files/${type}s/${typeKey}`;
- dbPath = `/${type}s/${typeKey}/files`;
- //Upload the document to Fireabase Storage
- let result = await this.props.firebase.uploadFiles(filePath, [file], dbPath);
- let firstResult = _.first(result);
- //Get the metadata from Firebase Storage
- if(!_.isEmpty(firstResult)){
- let fileUploaded = _.clone(firstResult.File);
- let fileMetaData = await this.props.getFileMetaData(fileUploaded.fullPath);
- fileUploaded = _.merge(fileUploaded, fileMetaData);
- fileUploaded[values.type] = typeKey;
- fileUploaded.owner = this.props.currentUser.uid;
- fileUploaded = _.pickBy(fileUploaded);
- //Update meta data in Firebase
- await this.props.updateFile(firstResult.key, fileUploaded);
- await this.props.updateFileInPath(`${dbPath}/${firstResult.key}`, fileUploaded);
- }
- //When done, reload the search results and go back to the documents listing
- setTimeout(() => this.props.setReloadSearch(true), 500);
- this.props.replacePath('/documents');
- } catch(error) {
- this.setState({loading: false, errorDialog: true, errorDialogMessage: error.message});
- }
- };
- render(){
- if(this.state.uploading){
- return <ProgressBar type="circular" mode="indeterminate"/>;
- }
- if(this.state.step === 1) {
- return (
- <div>
- <InformationBlock>
- <HeaderTitle><h5>Upload File</h5></HeaderTitle>
- <SectionFull>
- <div>
- <Dropzone
- className='dropzone'
- activeClassName='dropzone-active'
- onDrop={this.saveStep1}
- multiple={false}
- style={{ width: '100%', height: '200px'}}
- >
- <FontIcon value='cloud_upload'/><p style={{textAlign: 'center'}}>Drop a file</p>
- </Dropzone>
- </div>
- </SectionFull>
- </InformationBlock>
- <ButtonsWrapper>
- {this.state.fileOnBrowser?
- <DivFloatRight><Button className="btn--primary" icon={'navigate_next'} label={'Next'} onClick={() => this.setState({step: 2})} /></DivFloatRight> : null
- }
- </ButtonsWrapper>
- </div>
- );
- } else if(this.state.step === 2) {
- let types = [{value: 'contact', label: 'Contact'}, {value: 'organization', label: 'Organization'}, {value: 'deal', label: 'Deal'}, {value: 'task', label: 'Task'},];
- return (
- <div>
- <InformationBlock>
- <HeaderTitle><h5>File Information</h5></HeaderTitle>
- <PFull><strong>File Name:</strong> {this.state.fileOnBrowser.name}</PFull>
- <PFull><strong>Last Modified:</strong> {moment(this.state.fileOnBrowser.lastModified, 'x').format('MMMM Do YYYY, h:mm:ss a')}</PFull>
- <PFull><strong>Type:</strong> {this.state.fileOnBrowser.type}</PFull>
- <PFull><strong>Preview:</strong> <LinkStyled target={'_blank'} label={'File'} href={this.state.fileOnBrowser.preview} /></PFull>
- </InformationBlock>
- <Form
- onSubmit={this.saveFile}
- defaultValues={this.state.valuesStep2}
- validate={({type, contact, organization, deal, task}) => {
- return {
- type: !type? 'Type is required' : undefined,
- contact: (type === 'contact' && !contact)?'Contact is required' : undefined,
- organization: (type === 'organization' && !organization)?'Organization is required' : undefined,
- deal: (type === 'deal' && !deal)?'Deal is required' : undefined,
- task: (type === 'task' && !task)?'Task is required' : undefined,
- };
- }}
- onChange={({values}) => {
- this.setState({valuesStep2: values});
- }}
- >
- {({submitForm, values}) => {
- return(
- <form onSubmit={submitForm}>
- <InformationBlock>
- <HeaderTitle><h5>Change File Name</h5></HeaderTitle>
- <FormInputStyled field="name">
- {({ setValue, getValue }) => {
- return <Input label='File Name' value={getValue()?getValue():''} onChange={val => {setValue(val);}} />;
- }}
- </FormInputStyled>
- <FormInputStyled field="extension">
- {({ getValue }) => {
- return <Input label='File Extension' disabled={true} value={getValue()?getValue():''} />;
- }}
- </FormInputStyled>
- </InformationBlock>
- <InformationBlock>
- <HeaderTitle><h5>Attach To</h5></HeaderTitle>
- <FormInputStyled field="type">
- {({setValue, getValue}) => {
- return <DropDown label={'Attach to'} required={true} source={types} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />;
- }}
- </FormInputStyled>
- {values && values.type === 'contact'?
- <FormInputStyled field='contact'>
- {({setValue, getValue}) => {
- return <Autocomplete label='Contact' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.contacts} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
- }}
- </FormInputStyled> : null
- }
- {values && values.type === 'organization' ?
- <FormInputStyled field='organization'>
- {({setValue, getValue}) => {
- return <Autocomplete label='Organization' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.organizations} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
- }}
- </FormInputStyled> : null
- }
- {values && values.type === 'deal' ?
- <FormInputStyled field='deal'>
- {({setValue, getValue}) => {
- return <Autocomplete label='Deal' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.deals} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
- }}
- </FormInputStyled> : null
- }
- {values && values.type === 'task' ?
- <FormInputStyled field='task'>
- {({setValue, getValue}) => {
- return <Autocomplete label='Task' showSuggestionsWhenValueIsSet={true} required={true} multiple={false} source={this.state.tasks} value={getValue()?getValue():''} onChange={val => {setValue(val);}} />
- }}
- </FormInputStyled> : null
- }
- </InformationBlock>
- <ButtonsWrapper>
- <Button className="btn--secondary" icon={'keyboard_arrow_left'} label={'Previous'} onClick={() => this.setState({step: 1})} />
- <DivFloatRight><Button label={'Save & Upload File'} type={'submit'} className="btn--primary" icon={'file_upload'} /></DivFloatRight>
- </ButtonsWrapper>
- </form>
- );
- }}
- </Form>
- <Dialog
- active={this.state.errorDialog}
- type='small'
- actions={[{label: 'Ok', onClick: () => this.setState({errorDialog: false})}]}
- title='Error'
- >
- <p>{this.state.errorDialogMessage}</p>
- </Dialog>
- </div>
- );
- } else {
- return null;
- }
- }
- }
- export default DocumentUploadForm;
Add Comment
Please, Sign In to add comment