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
JSX
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
<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));