Merge pull request #989 from influxdata/signin-and-setup-in-component-state

Revise onboarding flow
pull/10616/head
Deniz Kusefoglu 2018-10-10 06:28:47 -07:00 committed by GitHub
commit beed7c7ad7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 407 additions and 256 deletions

View File

@ -19,12 +19,11 @@ import {Links} from 'src/types/v2/links'
interface State { interface State {
loading: RemoteDataState loading: RemoteDataState
isSetupAllowed: boolean isSetupComplete: boolean
} }
interface Props { interface Props {
links: Links links: Links
isSetupComplete: boolean
children: ReactElement<any> children: ReactElement<any>
notify: (message: Notification | NotificationFunc) => void notify: (message: Notification | NotificationFunc) => void
} }
@ -36,27 +35,29 @@ export class Setup extends PureComponent<Props, State> {
this.state = { this.state = {
loading: RemoteDataState.NotStarted, loading: RemoteDataState.NotStarted,
isSetupAllowed: false, isSetupComplete: false,
} }
} }
public async componentDidMount() { public async componentDidMount() {
const {links} = this.props const {links} = this.props
const isSetupAllowed = await getSetupStatus(links.setup) const isSetupAllowed = await getSetupStatus(links.setup)
this.setState({loading: RemoteDataState.Done, isSetupAllowed}) this.setState({
loading: RemoteDataState.Done,
isSetupComplete: !isSetupAllowed,
})
} }
public render() { public render() {
const {isSetupComplete} = this.props const {isSetupComplete} = this.state
const {isSetupAllowed} = this.state
if (this.isLoading) { if (this.isLoading) {
return <div className="page-spinner" /> return <div className="page-spinner" />
} }
if (isSetupAllowed && !isSetupComplete) { if (!isSetupComplete) {
return ( return (
<div className="chronograf-root"> <div className="chronograf-root">
<Notifications inPresentationMode={true} /> <Notifications inPresentationMode={true} />
<OnboardingWizard /> <OnboardingWizard onCompleteSetup={this.handleCompleteSetup} />
</div> </div>
) )
} else { } else {
@ -64,6 +65,10 @@ export class Setup extends PureComponent<Props, State> {
} }
} }
public handleCompleteSetup = () => {
this.setState({isSetupComplete: true})
}
private get isLoading(): boolean { private get isLoading(): boolean {
const {loading} = this.state const {loading} = this.state
return ( return (
@ -73,10 +78,7 @@ export class Setup extends PureComponent<Props, State> {
} }
} }
const mstp = ({links, isSetupComplete}) => ({ const mstp = ({links}) => ({links})
links,
isSetupComplete,
})
const mdtp = { const mdtp = {
notify: notifyAction, notify: notifyAction,

View File

@ -16,7 +16,7 @@ import {Links} from 'src/types/v2/links'
interface State { interface State {
loading: RemoteDataState loading: RemoteDataState
isSourcesAllowed: boolean isUserSignedIn: boolean
} }
interface Props { interface Props {
@ -31,27 +31,28 @@ export class Signin extends PureComponent<Props, State> {
this.state = { this.state = {
loading: RemoteDataState.NotStarted, loading: RemoteDataState.NotStarted,
isSourcesAllowed: false, isUserSignedIn: false,
} }
} }
public async componentDidMount() { public async componentDidMount() {
const {links} = this.props const {links} = this.props
const isSourcesAllowed = await trySources(links.sources) const isSourcesAllowed = await trySources(links.sources)
this.setState({loading: RemoteDataState.Done, isSourcesAllowed}) const isUserSignedIn = isSourcesAllowed
this.setState({loading: RemoteDataState.Done, isUserSignedIn})
} }
public render() { public render() {
const {isSourcesAllowed} = this.state const {isUserSignedIn} = this.state
if (this.isLoading) { if (this.isLoading) {
return <div className="page-spinner" /> return <div className="page-spinner" />
} }
if (!isSourcesAllowed) { if (!isUserSignedIn) {
return ( return (
<div className="chronograf-root"> <div className="chronograf-root">
<Notifications inPresentationMode={true} /> <Notifications inPresentationMode={true} />
<SigninPage /> <SigninPage onSignInUser={this.handleSignInUser} />
</div> </div>
) )
} else { } else {
@ -66,6 +67,10 @@ export class Signin extends PureComponent<Props, State> {
loading === RemoteDataState.NotStarted loading === RemoteDataState.NotStarted
) )
} }
private handleSignInUser = () => {
this.setState({isUserSignedIn: true})
}
} }
const mstp = ({links}) => ({ const mstp = ({links}) => ({

View File

@ -51,38 +51,34 @@ $grid--form-gutter: 6px;
Form Group Addons Form Group Addons
------------------------------------------------------------------------------ ------------------------------------------------------------------------------
*/ */
.form--label { .form--label,
width: 100%; .form--help-text,
.form--element-error {
text-align: left;
display: inline-block; display: inline-block;
font-size: 12px; font-size: 12px;
font-weight: 600; font-weight: 600;
color: $g11-sidewalk;
margin: 0 0 $ix-marg-a 0;
padding: 0 ($form-sm-padding + $ix-border); padding: 0 ($form-sm-padding + $ix-border);
@extend %no-user-select; @extend %no-user-select;
} }
.form--label {
width: 100%;
color: $g11-sidewalk;
margin: 0 0 $ix-marg-a 0;
}
.form--help-text { .form--help-text {
display: inline-block;
font-size: 12px;
font-style: italic; font-style: italic;
line-height: 16px; line-height: 16px;
font-weight: 600;
color: $g11-sidewalk; color: $g11-sidewalk;
margin: $ix-marg-a 0 0 0; margin: $ix-marg-a 0 0 0;
padding: 0 ($form-sm-padding + $ix-border);
@extend %no-user-select;
} }
.form--element-error { .form--element-error {
display: inline-block;
font-size: 12px;
line-height: 16px; line-height: 16px;
font-weight: 600;
color: $c-dreamsicle; color: $c-dreamsicle;
margin: $ix-marg-a 0 0 0; margin: $ix-marg-a 0 0 0;
padding: 0 ($form-sm-padding + $ix-border);
@extend %no-user-select;
} }
/* /*

View File

@ -1,6 +1,7 @@
// Libraries // Libraries
import React, {Component, ComponentClass} from 'react' import React, {Component, ComponentClass} from 'react'
import _ from 'lodash' import _ from 'lodash'
import classnames from 'classnames'
// Components // Components
import FormElement from 'src/clockface/components/form_layout/FormElement' import FormElement from 'src/clockface/components/form_layout/FormElement'
@ -12,6 +13,7 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
interface Props { interface Props {
children: JSX.Element[] children: JSX.Element[]
className?: string
} }
@ErrorHandling @ErrorHandling
@ -39,7 +41,15 @@ class Form extends Component<Props> {
this.validateChildren() this.validateChildren()
return <div className="form--wrapper">{children}</div> return <div className={this.formWrapperClass}>{children}</div>
}
private get formWrapperClass(): string {
const {className} = this.props
return classnames('form--wrapper', {
[`${className}`]: className,
})
} }
private validateChildren = (): void => { private validateChildren = (): void => {

View File

@ -12,38 +12,45 @@
} }
.wizard--progress-icon { .wizard--progress-icon {
height: 22px; position: relative;
width: 22px; font-size: 13px;
border-radius: 12px; height: 26px;
background-color: $g7-graphite; width: 26px;
border-radius: 50%;
background-color: $g2-kevlar;
display: inline-flex; display: inline-flex;
justify-content: center; margin-right: $ix-marg-b;
align-items: center; color: $g7-graphite;
margin-right: 10px;
color: $g3-castle;
transition: background-color .4s; transition: background-color .4s;
}
.wizard--progress-icon.checkmark { > .icon {
background-color: $c-rainforest; position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
} }
.wizard--progress-icon.circle-thick { .wizard--progress-icon.circle-thick {
background-color: $g7-graphite; color: $g7-graphite;
}
.wizard--progress-icon.checkmark {
color: $c-rainforest;
} }
.wizard--progress-icon.current { .wizard--progress-icon.current {
background-color: $c-pool; color: $c-pool;
} }
.wizard--progress-icon.remove { .wizard--progress-icon.remove {
background-color: $c-curacao; color: $c-curacao;
} }
.wizard--progress-title { .wizard--progress-title {
font-weight: 700; font-weight: 700;
color: $g7-graphite; color: $g7-graphite;
font-size: $ix-text-base-2; font-size: 15px;
transition: color .4s; transition: color .4s;
} }
@ -67,7 +74,7 @@
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
cursor: pointer; cursor: pointer;
margin: 0 10px; margin: 0 $ix-marg-a;
} }
.wizard--progress-button:hover { .wizard--progress-button:hover {
@ -77,8 +84,8 @@
} }
.wizard--progress-connector { .wizard--progress-connector {
width: 100px; width: 70px;
margin: 0 10px; margin: 0 $ix-marg-a;
height: 2px; height: 2px;
background-color: $g7-graphite; background-color: $g7-graphite;
position: relative; position: relative;

View File

@ -26,66 +26,69 @@ class ProgressBar extends PureComponent<Props, null> {
handleSetCurrentStep(i) handleSetCurrentStep(i)
} }
private get WizardProgress(): JSX.Element { private get WizardProgress(): JSX.Element[] {
const {stepStatuses, stepTitles, currentStepIndex} = this.props const {stepStatuses, stepTitles, currentStepIndex} = this.props
const lastIndex = stepStatuses.length - 1 const lastIndex = stepStatuses.length - 1
const lastEleIndex = stepStatuses.length - 2 const lastEleIndex = stepStatuses.length - 2
const progressBar = stepStatuses.reduce((acc, stepStatus, i) => { const progressBar: JSX.Element[] = stepStatuses.reduce(
if (i === 0 || i === lastIndex) { (acc, stepStatus, i) => {
return [...acc] if (i === 0 || i === lastIndex) {
} return [...acc]
}
let currentStep = '' let currentStep = ''
// STEP STATUS // STEP STATUS
if (i === currentStepIndex && stepStatus !== StepStatus.Error) { if (i === currentStepIndex && stepStatus !== StepStatus.Error) {
currentStep = 'current' currentStep = 'current'
} }
const stepEle = ( const stepEle = (
<div
key={`stepEle${i}`}
className="wizard--progress-button"
onClick={this.handleSetCurrentStep(i)}
>
<span
className={`wizard--progress-icon ${currentStep || stepStatus}`}
>
<span className={`icon ${stepStatus}`} />
</span>
<div <div
className={`wizard--progress-title ${currentStep || stepStatus}`} key={`stepEle${i}`}
className="wizard--progress-button"
onClick={this.handleSetCurrentStep(i)}
> >
{stepTitles[i]} <span
className={`wizard--progress-icon ${currentStep || stepStatus}`}
>
<span className={`icon ${stepStatus}`} />
</span>
<div
className={`wizard--progress-title ${currentStep || stepStatus}`}
>
{stepTitles[i]}
</div>
</div> </div>
</div> )
)
if (i === lastEleIndex) { if (i === lastEleIndex) {
return [...acc, stepEle] return [...acc, stepEle]
} }
// PROGRESS BAR CONNECTOR // PROGRESS BAR CONNECTOR
let connectorStatus = ConnectorState.None let connectorStatus = ConnectorState.None
if (i === currentStepIndex && stepStatus !== StepStatus.Error) { if (i === currentStepIndex && stepStatus !== StepStatus.Error) {
connectorStatus = ConnectorState.Some connectorStatus = ConnectorState.Some
} }
if (i === lastEleIndex || stepStatus === StepStatus.Complete) { if (i === lastEleIndex || stepStatus === StepStatus.Complete) {
connectorStatus = ConnectorState.Full connectorStatus = ConnectorState.Full
} }
const connectorEle = ( const connectorEle = (
<span <span
key={i} key={i}
className={`wizard--progress-connector wizard--progress-connector--${connectorStatus || className={`wizard--progress-connector wizard--progress-connector--${connectorStatus ||
ConnectorState.None}`} ConnectorState.None}`}
/> />
) )
return [...acc, stepEle, connectorEle] return [...acc, stepEle, connectorEle]
}, []) },
return <span className="wizard--progress-bar">{progressBar}</span> []
)
return progressBar
} }
} }

View File

@ -15,8 +15,11 @@
} }
.wizard--credits { .wizard--credits {
font-size: 13px;
color: $g10-wolf;
display: inline-flex; display: inline-flex;
align-items: center; align-items: center;
@extend %no-user-select;
span { span {
margin: 0 5px; margin: 0 5px;

View File

@ -11,7 +11,7 @@ const WizardFullScreen: SFC<Props> = (props: Props) => {
<div className="wizard--full-screen"> <div className="wizard--full-screen">
{props.children} {props.children}
<div className="wizard--credits"> <div className="wizard--credits">
Made by <span className="icon cubo-uniform" /> InfluxData Powered by <span className="icon cubo-uniform" /> InfluxData
</div> </div>
</div> </div>
<div className="auth-image" /> <div className="auth-image" />

View File

@ -5,10 +5,11 @@
.wizard--progress-header { .wizard--progress-header {
position: relative; position: relative;
background-color: $g3-castle; background-color: $g0-obsidian;
min-width: 100%; width: 80%;
max-width: 1000px;
height: 50px; height: 50px;
padding: $ix-marg-b $ix-marg-c; padding: 0 $ix-marg-c;
display: inline-flex; display: inline-flex;
justify-content: center; justify-content: center;
align-items: center; align-items: center;
@ -19,3 +20,7 @@
position: absolute; position: absolute;
right: $ix-marg-c; right: $ix-marg-c;
} }
.wizard--progress-header.hidden {
visibility: hidden;
}

View File

@ -12,8 +12,10 @@
align-items: center; align-items: center;
text-align: center; text-align: center;
margin: $ix-marg-d; margin: $ix-marg-d;
margin-top: $ix-marg-a;
flex-grow: 1; flex-grow: 1;
width: 100%; width: 80%;
max-width: 1000px;
background-color: $g3-castle; background-color: $g3-castle;
border-radius: $radius; border-radius: $radius;
} }
@ -21,7 +23,7 @@
.wizard-button-bar { .wizard-button-bar {
display: inline-flex; display: inline-flex;
flex-shrink: 0; flex-shrink: 0;
margin: $ix-marg-b auto $ix-marg-c auto; margin: 32px auto;
position: relative; position: relative;
min-width: 100%; min-width: 100%;
justify-content: center; justify-content: center;
@ -50,3 +52,21 @@
.splash-logo.secondary { .splash-logo.secondary {
background-image: url('../../assets/images/cubo_doodle_green.svg'); background-image: url('../../assets/images/cubo_doodle_green.svg');
} }
.wizard-step--title {
font-size: 32px;
font-weight: 300;
color: $g15-platinum;
margin-top: 32px;
margin-bottom: $ix-marg-b;
@include no-user-select();
}
.wizard-step--sub-title {
margin-top: $ix-marg-b;
margin-bottom: 32px;
font-size: 16px;
font-weight: 400;
color: $g11-sidewalk;
@include no-user-select();
}

View File

@ -1,19 +0,0 @@
export type Action = ActionCompleteSetup
export enum ActionTypes {
CompleteSetup = 'COMPLETE_SETUP',
}
export interface ActionCompleteSetup {
type: ActionTypes.CompleteSetup
payload: {isSetupComplete: true}
}
export type CompleteSetup = () => ActionCompleteSetup
export const completeSetup = (): ActionCompleteSetup => {
return {
type: ActionTypes.CompleteSetup,
payload: {isSetupComplete: true},
}
}

View File

@ -9,6 +9,7 @@ import {
ComponentColor, ComponentColor,
ComponentSize, ComponentSize,
Input, Input,
InputType,
Form, Form,
Columns, Columns,
IconFont, IconFont,
@ -52,11 +53,11 @@ class AdminStep extends PureComponent<OnboardingStepProps, State> {
} }
return ( return (
<div className="onboarding-step"> <div className="onboarding-step">
<h3 className="wizard-step-title">Setup Admin User</h3> <h3 className="wizard-step--title">Setup Admin User</h3>
<p> <h5 className="wizard-step--sub-title">
You will be able to create additional Buckets and Organizations later You will be able to create additional Buckets and Organizations later
</p> </h5>
<Form> <Form className="onboarding--admin-user-form">
<Form.Element <Form.Element
label="Admin Username" label="Admin Username"
colsXS={Columns.Six} colsXS={Columns.Six}
@ -80,6 +81,7 @@ class AdminStep extends PureComponent<OnboardingStepProps, State> {
offsetXS={Columns.Three} offsetXS={Columns.Three}
> >
<Input <Input
type={InputType.Password}
value={password} value={password}
onChange={this.handlePassword} onChange={this.handlePassword}
titleText={'Admin Password'} titleText={'Admin Password'}

View File

@ -1,6 +1,5 @@
// Libraries // Libraries
import React, {PureComponent} from 'react' import React, {PureComponent} from 'react'
import {withRouter, WithRouterProps} from 'react-router'
// Components // Components
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
@ -10,15 +9,14 @@ import {Button, ComponentColor, ComponentSize} from 'src/clockface'
import {OnboardingStepProps} from 'src/onboarding/containers/OnboardingWizard' import {OnboardingStepProps} from 'src/onboarding/containers/OnboardingWizard'
@ErrorHandling @ErrorHandling
class CompletionStep extends PureComponent< class CompletionStep extends PureComponent<OnboardingStepProps> {
OnboardingStepProps & WithRouterProps
> {
public render() { public render() {
const {onExit} = this.props
return ( return (
<div className="onboarding-step"> <div className="onboarding-step">
<div className="splash-logo secondary" /> <div className="splash-logo secondary" />
<h3 className="wizard-step-title">Setup Complete! </h3> <h3 className="wizard-step--title">Setup Complete!</h3>
<p>This is completion step</p> <h5 className="wizard-step--sub-title" />
<div className="wizard-button-bar"> <div className="wizard-button-bar">
<Button <Button
color={ComponentColor.Default} color={ComponentColor.Default}
@ -29,23 +27,18 @@ class CompletionStep extends PureComponent<
<Button <Button
color={ComponentColor.Success} color={ComponentColor.Success}
text="Go to status dashboard" text="Go to status dashboard"
size={ComponentSize.Large} size={ComponentSize.Medium}
onClick={this.handleComplete} onClick={onExit}
/> />
</div> </div>
</div> </div>
) )
} }
private handleComplete = () => {
const {router, completeSetup} = this.props
completeSetup()
router.push(`/manage-sources`)
}
private handleDecrement = () => { private handleDecrement = () => {
const {handleSetCurrentStep, currentStepIndex} = this.props const {handleSetCurrentStep, currentStepIndex} = this.props
handleSetCurrentStep(currentStepIndex - 1) handleSetCurrentStep(currentStepIndex - 1)
} }
} }
export default withRouter(CompletionStep) export default CompletionStep

View File

@ -14,8 +14,10 @@ class InitStep extends PureComponent<OnboardingStepProps> {
return ( return (
<div className="onboarding-step"> <div className="onboarding-step">
<div className="splash-logo primary" /> <div className="splash-logo primary" />
<h3 className="wizard-step-title">Welcome to InfluxData </h3> <h3 className="wizard-step--title">Welcome to InfluxData </h3>
<p>"Start using the InfluxData platform in a few easy steps"</p> <h5 className="wizard-step--sub-title">
Start using the InfluxData platform in a few easy steps
</h5>
<div className="wizard-button-bar"> <div className="wizard-button-bar">
<Button <Button
color={ComponentColor.Primary} color={ComponentColor.Primary}

View File

@ -0,0 +1,62 @@
// Libraries
import React, {PureComponent} from 'react'
// Components
import {ErrorHandling} from 'src/shared/decorators/errors'
import {Button, ComponentColor, ComponentSize} from 'src/clockface'
// Types
import {StepStatus} from 'src/clockface/constants/wizard'
import {OnboardingStepProps} from 'src/onboarding/containers/OnboardingWizard'
@ErrorHandling
class OtherStep extends PureComponent<OnboardingStepProps, null> {
constructor(props) {
super(props)
}
public render() {
return (
<div className="onboarding-step">
<h3 className="wizard-step--title">This is Another Step</h3>
<h5 className="wizard-step--sub-title">Import data here</h5>
<div className="wizard-button-bar">
<Button
color={ComponentColor.Default}
text="Back"
size={ComponentSize.Medium}
onClick={this.handlePrevious}
/>
<Button
color={ComponentColor.Primary}
text="Next"
size={ComponentSize.Medium}
onClick={this.handleNext}
titleText={'Next'}
/>
</div>
</div>
)
}
private handleNext = async () => {
const {handleSetStepStatus, currentStepIndex} = this.props
handleSetStepStatus(currentStepIndex, StepStatus.Complete)
this.handleIncrement()
}
private handlePrevious = () => {
this.handleDecrement()
}
private handleIncrement = () => {
const {handleSetCurrentStep, currentStepIndex} = this.props
handleSetCurrentStep(currentStepIndex + 1)
}
private handleDecrement = () => {
const {handleSetCurrentStep, currentStepIndex} = this.props
handleSetCurrentStep(currentStepIndex - 1)
}
}
export default OtherStep

View File

@ -1,11 +1,14 @@
// Libraries // Libraries
import React, {PureComponent} from 'react' import React, {PureComponent} from 'react'
import {withRouter, WithRouterProps} from 'react-router'
import {connect} from 'react-redux' import {connect} from 'react-redux'
import _ from 'lodash' import _ from 'lodash'
// Components // Components
import InitStep from 'src/onboarding/components/InitStep' import InitStep from 'src/onboarding/components/InitStep'
import AdminStep from 'src/onboarding/components/AdminStep' import AdminStep from 'src/onboarding/components/AdminStep'
import OtherStep from 'src/onboarding/components/OtherStep'
import CompletionStep from 'src/onboarding/components/CompletionStep' import CompletionStep from 'src/onboarding/components/CompletionStep'
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
import { import {
@ -16,10 +19,6 @@ import {
// Actions // Actions
import {notify as notifyAction} from 'src/shared/actions/notifications' import {notify as notifyAction} from 'src/shared/actions/notifications'
import {
completeSetup as completeSetupAction,
CompleteSetup,
} from 'src/onboarding/actions'
// Constants // Constants
import {StepStatus} from 'src/clockface/constants/wizard' import {StepStatus} from 'src/clockface/constants/wizard'
@ -39,15 +38,16 @@ export interface OnboardingStepProps {
setupParams: SetupParams setupParams: SetupParams
handleSetSetupParams: (setupParams: SetupParams) => void handleSetSetupParams: (setupParams: SetupParams) => void
notify: (message: Notification | NotificationFunc) => void notify: (message: Notification | NotificationFunc) => void
completeSetup: CompleteSetup onCompleteSetup: () => void
onExit: () => void
} }
interface Props { interface Props extends WithRouterProps {
links: Links links: Links
startStep?: number startStep?: number
stepStatuses?: StepStatus[] stepStatuses?: StepStatus[]
notify: (message: Notification | NotificationFunc) => void notify: (message: Notification | NotificationFunc) => void
completeSetup: CompleteSetup onCompleteSetup: () => void
} }
interface State { interface State {
@ -64,12 +64,13 @@ class OnboardingWizard extends PureComponent<Props, State> {
StepStatus.Incomplete, StepStatus.Incomplete,
StepStatus.Incomplete, StepStatus.Incomplete,
StepStatus.Incomplete, StepStatus.Incomplete,
StepStatus.Incomplete,
], ],
} }
public stepTitles = ['Welcome', 'Setup admin', 'Complete'] public stepTitles = ['Welcome', 'Admin Setup', 'Next Step', 'Complete']
public steps = [InitStep, AdminStep, CompletionStep] public steps = [InitStep, AdminStep, OtherStep, CompletionStep]
public stepSkippable = [false, false, false] public stepSkippable = [false, false, true, false]
constructor(props: Props) { constructor(props: Props) {
super(props) super(props)
@ -81,29 +82,44 @@ class OnboardingWizard extends PureComponent<Props, State> {
} }
public render() { public render() {
const {stepStatuses} = this.props
const {currentStepIndex} = this.state
return ( return (
<WizardFullScreen> <WizardFullScreen>
<WizardProgressHeader {this.progressHeader}
currentStepIndex={currentStepIndex}
stepSkippable={this.stepSkippable}
>
<ProgressBar
currentStepIndex={currentStepIndex}
handleSetCurrentStep={this.onSetCurrentStep}
stepStatuses={stepStatuses}
stepTitles={this.stepTitles}
/>
</WizardProgressHeader>
<div className="wizard-step--container">{this.currentStep}</div> <div className="wizard-step--container">{this.currentStep}</div>
</WizardFullScreen> </WizardFullScreen>
) )
} }
private get progressHeader(): JSX.Element {
const {stepStatuses} = this.props
const {currentStepIndex} = this.state
if (
currentStepIndex === 0 ||
currentStepIndex === stepStatuses.length - 1
) {
return <div className="wizard--progress-header hidden" />
}
return (
<WizardProgressHeader
currentStepIndex={currentStepIndex}
stepSkippable={this.stepSkippable}
onSkip={this.handleExit}
>
<ProgressBar
currentStepIndex={currentStepIndex}
handleSetCurrentStep={this.onSetCurrentStep}
stepStatuses={stepStatuses}
stepTitles={this.stepTitles}
/>
</WizardProgressHeader>
)
}
private get currentStep(): React.ReactElement<OnboardingStepProps> { private get currentStep(): React.ReactElement<OnboardingStepProps> {
const {currentStepIndex, setupParams} = this.state const {currentStepIndex, setupParams} = this.state
const {stepStatuses, links, notify, completeSetup} = this.props const {stepStatuses, links, notify, onCompleteSetup} = this.props
return React.createElement(this.steps[currentStepIndex], { return React.createElement(this.steps[currentStepIndex], {
stepStatuses, stepStatuses,
@ -115,10 +131,17 @@ class OnboardingWizard extends PureComponent<Props, State> {
setupParams, setupParams,
handleSetSetupParams: this.onSetSetupParams, handleSetSetupParams: this.onSetSetupParams,
notify, notify,
completeSetup, onCompleteSetup,
onExit: this.handleExit,
}) })
} }
private handleExit = () => {
const {router, onCompleteSetup} = this.props
onCompleteSetup()
router.push(`/manage-sources`)
}
private onSetSetupParams = (setupParams: SetupParams): void => { private onSetSetupParams = (setupParams: SetupParams): void => {
this.setState({setupParams}) this.setState({setupParams})
} }
@ -140,7 +163,6 @@ const mstp = ({links}) => ({
const mdtp = { const mdtp = {
notify: notifyAction, notify: notifyAction,
completeSetup: completeSetupAction,
} }
export default connect(mstp, mdtp)(OnboardingWizard) export default connect(mstp, mdtp)(withRouter(OnboardingWizard))

View File

@ -5,15 +5,16 @@ import _ from 'lodash'
// Components // Components
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
import SplashPage from 'src/shared/components/splash_page/SplashPage'
import { import {
Button, Button,
ComponentColor, ComponentColor,
ComponentSize, ComponentSize,
Input, Input,
InputType,
Form, Form,
Columns, Columns,
WizardFullScreen,
InputType,
} from 'src/clockface' } from 'src/clockface'
// APIs // APIs
@ -32,6 +33,7 @@ import {Notification, NotificationFunc} from 'src/types'
export interface Props { export interface Props {
links: Links links: Links
notify: (message: Notification | NotificationFunc) => void notify: (message: Notification | NotificationFunc) => void
onSignInUser: () => void
} }
interface State { interface State {
@ -52,47 +54,45 @@ class SigninPage extends PureComponent<Props, State> {
public render() { public render() {
const {username, password} = this.state const {username, password} = this.state
return ( return (
<WizardFullScreen> <SplashPage panelWidthPixels={300}>
<div className="wizard-step--container"> <SplashPage.Panel>
<div className="onboarding-step"> <SplashPage.Logo />
<h3 className="wizard-step-title">Please sign in</h3> <SplashPage.Header title="InfluxData" />
<p>(and remember that you are awesome)</p> <Form>
<Form> <Form.Element
<Form.Element label="Username"
label="Username" colsXS={Columns.Twelve}
colsXS={Columns.Six} errorMessage={''}
offsetXS={Columns.Three} >
errorMessage={''} <Input
> value={username}
<Input onChange={this.handleUsername}
value={username} size={ComponentSize.Medium}
onChange={this.handleUsername} />
size={ComponentSize.Medium} </Form.Element>
/> <Form.Element
</Form.Element> label="Password"
<Form.Element colsXS={Columns.Twelve}
label="Password" errorMessage={''}
colsXS={Columns.Six} >
offsetXS={Columns.Three} <Input
errorMessage={''} value={password}
> onChange={this.handlePassword}
<Input size={ComponentSize.Medium}
value={password} type={InputType.Password}
type={InputType.Password} />
onChange={this.handlePassword} </Form.Element>
size={ComponentSize.Medium} <Form.Footer>
/> <Button
</Form.Element> color={ComponentColor.Primary}
</Form> text="Sign In"
<Button size={ComponentSize.Medium}
color={ComponentColor.Primary} onClick={this.handleSignIn}
text="Sign In" />
size={ComponentSize.Medium} </Form.Footer>
onClick={this.handleSignIn} </Form>
/> </SplashPage.Panel>
</div> </SplashPage>
</div>
</WizardFullScreen>
) )
} }
@ -106,12 +106,11 @@ class SigninPage extends PureComponent<Props, State> {
} }
private handleSignIn = async (): Promise<void> => { private handleSignIn = async (): Promise<void> => {
const {links, notify} = this.props const {links, notify, onSignInUser} = this.props
const {username, password} = this.state const {username, password} = this.state
try { try {
await signin(links.signin, {username, password}) await signin(links.signin, {username, password})
onSignInUser()
window.location.reload(true)
} catch (error) { } catch (error) {
notify(copy.SigninError) notify(copy.SigninError)
} }

View File

@ -1,20 +0,0 @@
import {Action, ActionTypes} from 'src/onboarding/actions'
type State = boolean
const initialisSetupComplete: State = false
const setupReducer = (
state: State = initialisSetupComplete,
action: Action
): State => {
switch (action.type) {
case ActionTypes.CompleteSetup: {
const {isSetupComplete} = action.payload
return isSetupComplete
}
}
return state
}
export default setupReducer

View File

@ -0,0 +1,11 @@
import React, {SFC} from 'react'
interface Props {
title: string
}
const SplashHeader: SFC<Props> = ({title}) => (
<h3 className="auth-heading">{title}</h3>
)
export default SplashHeader

View File

@ -0,0 +1,5 @@
import React, {SFC} from 'react'
const SplashLogo: SFC = () => <div className="auth-logo" />
export default SplashLogo

View File

@ -13,6 +13,7 @@
@include gradient-v($g3-castle, $g0-obsidian); @include gradient-v($g3-castle, $g0-obsidian);
padding: $sidebar--width; padding: $sidebar--width;
} }
.auth-image { .auth-image {
background-image: url('../../assets/images/auth-bg.svg'); background-image: url('../../assets/images/auth-bg.svg');
background-size: cover; background-size: cover;
@ -23,14 +24,15 @@
height: 100%; height: 100%;
top: 0; top: 0;
left: 0; left: 0;
z-index: -1; z-index: 2;
} }
.auth-box { .auth-box {
z-index: 90; z-index: 90;
position: absolute; position: absolute;
top: 43%; top: 43%;
left: 50%; left: 50%;
transform: translate(-50%,-50%); transform: translate(-50%, -50%);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
@ -65,6 +67,7 @@
} }
} }
} }
.auth-logo { .auth-logo {
background-image: url('../../assets/images/auth-logo.svg'); background-image: url('../../assets/images/auth-logo.svg');
background-size: 100% 100%; background-size: 100% 100%;
@ -72,7 +75,9 @@
background-repeat: no-repeat; background-repeat: no-repeat;
width: 100px; width: 100px;
height: 100px; height: 100px;
display: inline-block;
} }
.auth-credits { .auth-credits {
@include no-user-select(); @include no-user-select();
font-weight: 600; font-weight: 600;
@ -169,6 +174,21 @@
color: $g9-mountain; color: $g9-mountain;
@include no-user-select(); @include no-user-select();
} }
.btn.auth--logout { .btn.auth--logout {
margin-top: 30px; margin-top: 30px;
} }
.auth-panel {
border-radius: $radius;
background-color: $g3-castle;
padding: 32px;
text-align: center;
}
.auth-heading {
font-size: 32px;
font-weight: 300;
margin-bottom: 32px;
}

View File

@ -1,20 +1,34 @@
import React, {SFC, ReactElement} from 'react' import React, {Component} from 'react'
import SplashLogo from 'src/shared/components/splash_page/SplashLogo'
import SplashPanel from 'src/shared/components/splash_page/SplashPanel'
import SplashHeader from 'src/shared/components/splash_page/SplashHeader'
interface Props { interface Props {
children: ReactElement<any> children: JSX.Element | JSX.Element[]
panelWidthPixels: number
} }
const SplashPage: SFC<Props> = ({children}) => ( class SplashPage extends Component<Props> {
<div className="auth-page"> public static Logo = SplashLogo
<div className="auth-box"> public static Panel = SplashPanel
<div className="auth-logo" /> public static Header = SplashHeader
{children}
</div> public render() {
<p className="auth-credits"> const {children, panelWidthPixels} = this.props
Made by <span className="icon cubo-uniform" />InfluxData
</p> return (
<div className="auth-image" /> <div className="auth-page">
</div> <div className="auth-box" style={{width: `${panelWidthPixels}px`}}>
) {children}
</div>
<p className="auth-credits">
Powered by <span className="icon cubo-uniform" />InfluxData
</p>
<div className="auth-image" />
</div>
)
}
}
export default SplashPage export default SplashPage

View File

@ -0,0 +1,11 @@
import React, {SFC} from 'react'
interface Props {
children: JSX.Element[] | JSX.Element
}
const SplashPanel: SFC<Props> = ({children}) => (
<div className="auth-panel">{children}</div>
)
export default SplashPanel

View File

@ -9,7 +9,6 @@ import sharedReducers from 'src/shared/reducers'
import persistStateEnhancer from './persistStateEnhancer' import persistStateEnhancer from './persistStateEnhancer'
import scriptReducer from 'src/flux/reducers/script' import scriptReducer from 'src/flux/reducers/script'
import sourceReducer from 'src/sources/reducers/sources' import sourceReducer from 'src/sources/reducers/sources'
import setupReducer from 'src/onboarding/reducers/setup'
// v2 reducers // v2 reducers
import rangesReducer from 'src/dashboards/reducers/v2/ranges' import rangesReducer from 'src/dashboards/reducers/v2/ranges'
@ -28,7 +27,6 @@ const rootReducer = combineReducers({
routing: routerReducer, routing: routerReducer,
script: scriptReducer, script: scriptReducer,
sources: sourceReducer, sources: sourceReducer,
isSetupComplete: setupReducer,
views: viewsReducer, views: viewsReducer,
logs: logsReducer, logs: logsReducer,
}) })