UNPKG

cluedin-widget

Version:

This project contains all the pages needed for browsing entities and searching them. The aim is to replace the CluedIn.Webapp project with this one when all the pages ( including the Admin page ) will be ported to REACT.

569 lines (507 loc) • 19.1 kB
import React, { Component, PropTypes } from 'react'; import radium from 'radium'; import { goToLocation, fetchMostConnectedIfNeeded } from '../../action/core'; import CluedInProgress from '../generics/cluedinprogress.jsx'; import Widget from '../generics/widget.jsx'; import DomainUsers from '../users/DomainUsers.jsx'; import config from '../../config'; import { connect } from 'react-redux'; import { fetchActiveProvidersIfNeeded, skipBusy } from '../../action/provider'; import { fetchUsersIfNeeded, fetchPotentialUsersIfNeeded, removeOnBoarding } from '../../action/user'; import { shouldFetchBoardingIfNeeded, fetchAllInvitations } from '../../action/boarding'; import { FormattedMessage, FormattedHTMLMessage } from 'react-intl'; import RaisedButton from 'material-ui/RaisedButton'; import ArrowUp from 'material-ui/svg-icons/hardware/keyboard-arrow-up'; import ArrowDown from 'material-ui/svg-icons/hardware/keyboard-arrow-down'; import onBoardingStyle from './OnBoarding.style.js'; import theme from '../../theme/index'; export class OnBoarding extends Component { static propTypes = { dispatch: PropTypes.func, }; constructor(props) { super(props); this.state = { isOpen: true, }; } componentWillMount() { const { org, } = this.props; this.props.dispatch(fetchActiveProvidersIfNeeded()); this.props.dispatch(fetchUsersIfNeeded()); this.props.dispatch(fetchMostConnectedIfNeeded()); this.props.dispatch(fetchPotentialUsersIfNeeded()); this.props.dispatch(shouldFetchBoardingIfNeeded(org)); this.props.dispatch(fetchAllInvitations(org)); } componentDidUpdate() { let step = this.getStep(); if ((!this.isBusy() || (step !== 1 && step !== 3)) && this.mainContent && this.state.isOpen) { setTimeout(function () { if (this.mainContent.classList.contains('cluedIn_hide_slide')) { this.mainContent.classList.remove('cluedIn_hide_slide'); this.mainContent.classList.add('cluedIn_show_slide'); } }.bind(this), 3000); } } getTitle(step) { switch (step) { case 1: return ( <span><FormattedMessage id="OnBoarding.Step1.Title"/></span> ); case 2: return ( <span><FormattedMessage id="OnBoarding.Step2.Title"/></span> ); case 3: return ( <span><FormattedMessage id="OnBoarding.Step3.Title"/></span> ); case 4: return ( <span><FormattedMessage id="OnBoarding.Step4.Title"/></span> ); case 5: return ( <span><FormattedMessage id="OnBoarding.Step5.Title"/></span> ); default: return ( <span><FormattedMessage id="OnBoarding.Step0.Title"/></span> ); } } getMenu(step) { let isActiveStep0 = (step === 0) ? 'active' : ''; let isActiveStep1 = (step === 1) ? 'active' : ''; let isActiveStep2 = (step === 2) ? 'active' : ''; let isActiveStep3 = (step === 3) ? 'active' : ''; let isActiveStep4 = (step === 4) ? 'active' : ''; isActiveStep0 = (step > 0) ? 'done' : isActiveStep0; isActiveStep1 = (step > 1) ? 'done' : isActiveStep1; isActiveStep2 = (step > 2) ? 'done' : isActiveStep2; isActiveStep3 = (step > 3) ? 'done' : isActiveStep3; isActiveStep4 = (step > 4) ? 'done' : isActiveStep4; return ( <ul className="cluedIn_progressive_menu cluedIn_m-b cluedIn_m-t"> <li className={isActiveStep0}><a><FormattedMessage id="OnBoarding.Step0.Menu"/></a> </li> <li className={isActiveStep1}><a><FormattedMessage id="OnBoarding.Step1.Menu"/></a> </li> <li className={isActiveStep2}><a><FormattedMessage id="OnBoarding.Step2.Menu"/></a> </li> <li className={isActiveStep3}><a><FormattedMessage id="OnBoarding.Step3.Menu"/></a> </li> <li className={isActiveStep4}><a><FormattedMessage id="OnBoarding.Step4.Menu"/></a> </li> </ul> ) } getSkipTutorial() { return (<div> <RaisedButton primary={true} onClick={this.step5Action.bind(this)} label="Skip Tutorial"></RaisedButton> </div>); } step0Action() { this.props.dispatch(goToLocation('addProvider')); } step2Action() { this.props.dispatch(goToLocation('addProvider')); } step1Action(searchTerm) { this.props.dispatch(goToLocation('goToSearch', searchTerm)); } step3Action(entity) { this.props.dispatch(goToLocation('entity', entity)); } step4Action() { this.props.dispatch(goToLocation('goToInviteUser')); } step5Action() { const { currentUser } = this.props; this.props.dispatch(removeOnBoarding(currentUser.entity.id, currentUser.userProfile)); } progress(step) { var percentage = ((step / 5) * 100); var bigNumber = true; return (<div> <CluedInProgress withBigNumber={bigNumber} percentage={percentage}></CluedInProgress> </div>); } content(step) { const { connectedData, providers, potentialUsers } = this.props; switch (step) { case 1: var step1TranslatedValue = { name: providers[0].Name, searchQuery: connectedData[0].name }; return ( <div> <div className="cluedIn_text_zone cluedIn_highlightText"> <FormattedHTMLMessage id="OnBoarding.Step1.Content" values={step1TranslatedValue}></FormattedHTMLMessage> <a style={[theme.link, theme.inlineBlock]} onClick={this.step1Action.bind(this, connectedData[ 0 ].name)} className="cluedIn_btn-primary clued_In_btn_big cluedIn_pulse-button cluedIn_btn"> <span className="cluedIn_addon"><i className="fa fa-search"></i></span> <span> <FormattedHTMLMessage id="OnBoarding.Step1.ContentAction" values={step1TranslatedValue}/> </span> </a> </div> </div> ); case 2: return ( <div> <div className="cluedIn_text_zone cluedIn_highlightText"> <FormattedHTMLMessage id="OnBoarding.Step2.Content"/> </div> <div className="cluedIn_text_zone cluedIn_highlightText"> In order to benefit from that: </div> <div className="cluedIn_text_zone"> <a style={[theme.link, theme.inlineBlock]} onClick={this.step2Action.bind(this)} className="cluedIn_btn-primary clued_In_btn_big cluedIn_pulse-button cluedIn_btn"> <span className="cluedIn_addon"> <i className="fa fa-plug"></i> </span> <span> <FormattedMessage id="OnBoarding.Step2.ContentAction"/> </span> </a> </div> </div> ); case 3: let step3TranslatedValue = { name: providers[1].Name, connectedData: connectedData[0].name, }; return ( <div> <div className="cluedIn_text_zone cluedIn_highlightText"> <FormattedHTMLMessage id="OnBoarding.Step3.Content" values={step3TranslatedValue}/> </div> <div className="cluedIn_text_zone cluedIn_highlightText"> <a style={[theme.link, theme.inlineBlock]} onClick={this.step3Action.bind(this, connectedData[ 0 ])} className="cluedIn_btn-primary clued_In_btn_big cluedIn_pulse-button cluedIn_btn"> <FormattedHTMLMessage id="OnBoarding.Step3.ContentAction" values={step3TranslatedValue}/> </a> </div> </div> ); case 4: let potentialContent = ''; let invitation = ''; let potentialUsersWithEmail = potentialUsers.filter((domainUser) => { return domainUser.Properties && domainUser.Properties.MetadataDictionary && domainUser.Properties.MetadataDictionary['property-user.email']; }); if (potentialUsersWithEmail && potentialUsersWithEmail.length > 0) { invitation = ( <span><FormattedMessage id="OnBoarding.Step4.Content2"/></span>); potentialContent = ( <div className="cluedIn_line"><DomainUsers inviteClick={this.inviteUserHandler.bind(this)} potentialUsers={potentialUsersWithEmail}></DomainUsers> </div>); } return ( <div> <div className="cluedIn_text_zone cluedIn_highlightText"> <FormattedMessage id="OnBoarding.Step4.Content"/> <br/> <br/> </div> <div className="cluedIn_text_zone cluedIn_highlightText"> {invitation} {potentialContent} </div> <div className="cluedIn_text_zone"> <a style={[theme.link, theme.inlineBlock]} onClick={this.step4Action.bind(this)} className="cluedIn_btn-primary clued_In_btn_big cluedIn_pulse-button cluedIn_btn"> <span className="cluedIn_addon"> <i className="fa fa-user-plus"></i> </span> <span> <FormattedMessage id="OnBoarding.Step4.ContentAction"/> </span> </a> </div> </div> ); case 5: return ( <div className="cluedIn_row"> <div className="cluedIn_col s3"> <img className="cluedIn_img_responsive" src={config.image.party} alt="Congratz!"/> </div> <div className="cluedIn_col s9"> <div className="cluedIn_text_zone"> <FormattedHTMLMessage id="OnBoarding.Step5.Content"/> </div> <div className="cluedIn_text_zone"> <a onClick={this.step5Action.bind(this)} className="cluedIn_btn-primary clued_In_btn_big cluedIn_pulse-button cluedIn_btn"> <span className="cluedIn_addon"> <i className="fa fa-trash-o "></i> </span> <span> <FormattedMessage id="OnBoarding.Step5.ContentAction"/> </span> </a> </div> </div> </div> ); default: return ( <div> <div className="cluedIn_text_zone cluedIn_highlightText"> <FormattedMessage id="OnBoarding.Step0.Content"/> <br/> </div> <div className="cluedIn_text_zone"> <a style={[theme.link, theme.inlineBlock]} onClick={this.step0Action.bind(this)} className="cluedIn_btn-primary clued_In_btn_big cluedIn_pulse-button cluedIn_btn"> <span className="cluedIn_addon"> <i className="fa fa-plug"></i> </span> <span> <FormattedMessage id="OnBoarding.Step0.ContentAction"/> </span> </a> </div> </div> ); } } step(number) { var title = this.getTitle(number); var menu = this.getMenu(number); var content = this.content(number); var progress = this.progress(number); let mainStyle = { display: 'block' }; if (!this.state.isOpen) { mainStyle = { display: 'none' }; } return ( <div> <div className="cluedIn_row"> <div className="cluedIn_col s12"> <div className="cluedIn_title"> <div className="textHeading h2"> {title} </div> </div> </div> </div> <div style={mainStyle} ref={ (ref) => this.mainContent = ref } className="cluedIn_hide_slide cluedIn_m-t cluedIn_onBoarding_main"> <div className="cluedIn_row"> <div className="cluedIn_col s12"> {menu} </div> </div> <div className="cluedIn_row"> <div className="cluedIn_col s8"> <div className="cluedIn_onBoarding_content"> {content} </div> </div> <div className="cluedIn_col s4"> {progress} </div> </div> </div> </div> ); } isLoading() { let { isFetchingProvider, isFetchingUsers, isFetchingPotentialUsers, isFetchingBoardingInfo, isFetchingInvitations, isFetchingConnectedData } = this.props; return (isFetchingProvider || isFetchingUsers || isFetchingPotentialUsers || isFetchingBoardingInfo || isFetchingInvitations || isFetchingConnectedData); } getStep() { let { providers, hasSearched, hasVisitConnectedView, users, allInvitations } = this.props; let step = 0; if (providers && providers.length > 0) { if (hasSearched) { if (providers.length > 1) { if (hasVisitConnectedView) { if (users && users.length > 1 || allInvitations && allInvitations.length > 0) { step = 5; } else { step = 4; } } else { step = 3; } } else { step = 2; } } else { step = 1; } } if (step > 5) { step = 0; } return step; } inviteUserHandler(email, name) { this.props.dispatch(goToLocation('goToInviteUser', { email, name })); } toogle() { this.setState({ isOpen: !this.state.isOpen, }); } skipBusy() { this.props.dispatch(skipBusy()); } isBusy() { const { isBusy, skipBusy } = this.props; return skipBusy ? false : isBusy; } openIntegrationReport(e) { e.preventDefault(); var eventOpenIntegration = new Event('OpenIntegrationPanel'); window.dispatchEvent(eventOpenIntegration); } render() { const { currentUser } = this.props; if (!currentUser || (currentUser.userProfile && currentUser.userProfile.isOnBoardingFinished)) { return (<div></div>); } let content; let isLoading = this.isLoading(); let step = this.getStep(); if (!this.isBusy() && isLoading) { content = (<div> <div className="cluedIn_row"> <div className="cluedIn_col s12"> <div className="cluedIn_title"> <div className="textHeading"> <div className="cluedIn_title"> <div className="textHeading h2"> <div className="bounceball"></div> <div className="bounceball"></div> <div className="bounceball"></div> Loading Tutorial. Let's help you get ready with your CluedIn account. </div> </div> </div> </div> </div> </div> </div>); } if (this.isBusy() && (step === 1 || step === 3)) { content = ( <div> <div className="cluedIn_row"> <div className="cluedIn_col s12"> <div className="cluedIn_title"> <div className="textHeading"> <div className="cluedIn_title"> <div className="textHeading h3"> <div style={ {float: 'left'} }> <div className="bounceball"></div> </div> <div style={ {'paddingRight': '245px', paddingTop: '15px'} }> We are currently crawling your integrations. You can check the status by clicking on the <i style={ {marginLeft: '5px', marginRight: '5px'} } className="fa fa-plug"></i> in the top right of your screen or simply &nbsp;<a href="" onClick={ this.openIntegrationReport.bind(this) }>click here.</a> Once crawling has finished this tutorial will tell you what to do next. </div> </div> <div style={ {marginTop: '10px'} }> <div style={ {float: 'left', marginTop: '8px'} }>Don't want to wait?</div> <div style={ {float: 'left', marginLeft:'15px'} }> <RaisedButton onClick={this.skipBusy.bind(this)} primary={false} label="Continue Tutorial"> </RaisedButton> </div> </div> </div> </div> </div> </div> </div> </div> ) } if ((!this.isBusy() || (step !== 1 && step !== 3)) && !isLoading) { content = this.step(step); } let noScroll = true; let icon = <ArrowUp />; if (!this.state.isOpen) { icon = <ArrowDown />; } let expander = ( <RaisedButton label="" onClick={this.toogle.bind(this)} style={{ position: 'absolute', right: '30px', top: '19px' }} icon={icon} />); let rightPosition = '135px'; if (this.isBusy()) { expander = null; rightPosition = '30px'; } return ( <div> <div style={onBoardingStyle.onBoarding}> <Widget noScroll={noScroll}> <div style={{ position: 'absolute', right: rightPosition, top: '19px' }}> {this.getSkipTutorial()} </div> {expander} {content} </Widget> </div> </div> ); } } function select(state) { return { providers: state.provider.providers, isFetchingProvider: state.provider.isFetching, isFetchingUsers: state.user.isFetching, users: state.user.users, currentUser: state.user.currentUser, hasSearched: (state.boarding.boardingInfo ? state.boarding.boardingInfo.hasSearched : false), hasVisitConnectedView: (state.boarding.boardingInfo ? state.boarding.boardingInfo.hasVisited : false), isFetchingBoardingInfo: state.boarding.isFetching, connectedData: state.core.connectedData, isFetchingConnectedData: state.core.isFetchingConnectedData, org: state.core.org, potentialUsers: state.user.potentialUsers, isFetchingPotentialUsers: state.user.isFetchingPotentialUsers, isFetchingInvitations: state.boarding.isFetchingInvitations, allInvitations: state.boarding.allInvitations, allProviderStats: state.provider.allProviderStats, isBusy: state.provider.isBusy, skipBusy: state.provider.skipBusy, }; } export default connect(select)(radium(OnBoarding));