summaryrefslogtreecommitdiff
path: root/ui/app/components/wizard
diff options
context:
space:
mode:
Diffstat (limited to 'ui/app/components/wizard')
-rw-r--r--ui/app/components/wizard/add_provider_modal.js94
-rw-r--r--ui/app/components/wizard/index.js38
-rw-r--r--ui/app/components/wizard/provider_select_stage.js86
-rw-r--r--ui/app/components/wizard/stage_layout.js37
-rw-r--r--ui/app/components/wizard/wizard.less44
5 files changed, 299 insertions, 0 deletions
diff --git a/ui/app/components/wizard/add_provider_modal.js b/ui/app/components/wizard/add_provider_modal.js
new file mode 100644
index 00000000..bc5e0236
--- /dev/null
+++ b/ui/app/components/wizard/add_provider_modal.js
@@ -0,0 +1,94 @@
+//
+// A modal popup to add a new provider.
+//
+
+import React from 'react'
+import { FormGroup, ControlLabel, FormControl, HelpBlock, Button, Modal } from 'react-bootstrap'
+import Spinner from '../spinner'
+import Validate from '../../lib/validate'
+import App from '../../app'
+
+class AddProviderModal extends React.Component {
+
+ static get defaultProps() {return{
+ title: 'Add a provider',
+ onClose: null
+ }}
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ validationState: null,
+ errorMsg: null,
+ domain: ""
+ }
+ this.accept = this.accept.bind(this)
+ this.cancel = this.cancel.bind(this)
+ this.changed = this.changed.bind(this)
+ }
+
+ accept() {
+ if (this.state.domain) {
+ App.providers.add(this.state.domain)
+ }
+ this.props.onClose()
+ }
+
+ cancel() {
+ this.props.onClose()
+ }
+
+ changed(e) {
+ let domain = e.target.value
+ let newState = null
+ let newMsg = null
+
+ if (domain.length > 0) {
+ let error = Validate.domain(domain)
+ newState = error ? 'error' : 'success'
+ newMsg = error
+ }
+ this.setState({
+ domain: domain,
+ validationState: newState,
+ errorMsg: newMsg
+ })
+ }
+
+ render() {
+ let help = null
+ if (this.state.errorMsg) {
+ help = <HelpBlock>{this.state.errorMsg}</HelpBlock>
+ } else {
+ help = <HelpBlock>&nbsp;</HelpBlock>
+ }
+ let form = <form onSubmit={this.accept} autoComplete="off">
+ <FormGroup controlId="addprovider" validationState={this.state.validationState}>
+ <ControlLabel>Domain</ControlLabel>
+ <FormControl
+ type="text"
+ ref="domain"
+ autoFocus
+ value={this.state.domain}
+ onChange={this.changed}
+ onBlur={this.changed} />
+ <FormControl.Feedback/>
+ {help}
+ </FormGroup>
+ <Button onClick={this.accept}>Add</Button>
+ </form>
+
+ return(
+ <Modal show={true} onHide={this.cancel}>
+ <Modal.Header closeButton>
+ <Modal.Title>{this.props.title}</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ {form}
+ </Modal.Body>
+ </Modal>
+ )
+ }
+}
+
+export default AddProviderModal \ No newline at end of file
diff --git a/ui/app/components/wizard/index.js b/ui/app/components/wizard/index.js
new file mode 100644
index 00000000..613b88fd
--- /dev/null
+++ b/ui/app/components/wizard/index.js
@@ -0,0 +1,38 @@
+//
+// The provider setup wizard
+//
+
+import React from 'react'
+import App from 'app'
+
+import ProviderSelectStage from './provider_select_stage'
+import './wizard.less'
+
+export default class Wizard extends React.Component {
+
+ constructor(props) {
+ super(props)
+ this.state = {
+ stage: 'provider'
+ }
+ }
+
+ setStage(stage) {
+ this.setState({stage: stage})
+ }
+
+ render() {
+ let stage = null
+ switch(this.state.stage) {
+ case 'provider':
+ stage = <ProviderSelectStage />
+ break
+ }
+ return(
+ <div className="wizard">
+ {stage}
+ </div>
+ )
+ }
+
+}
diff --git a/ui/app/components/wizard/provider_select_stage.js b/ui/app/components/wizard/provider_select_stage.js
new file mode 100644
index 00000000..20674be1
--- /dev/null
+++ b/ui/app/components/wizard/provider_select_stage.js
@@ -0,0 +1,86 @@
+import React from 'react'
+import {Button, ButtonGroup, ButtonToolbar, Glyphicon} from 'react-bootstrap'
+
+import App from 'app'
+import ListEdit from 'components/list_edit'
+import StageLayout from './stage_layout'
+import AddProviderModal from './add_provider_modal'
+
+export default class ProviderSelectStage extends React.Component {
+
+ static get defaultProps() {return{
+ title: "Choose a provider",
+ subtitle: "This doesn't work yet"
+ }}
+
+ constructor(props) {
+ super(props)
+ let domains = this.currentDomains()
+ this.state = {
+ domains: domains,
+ showModal: false
+ }
+ this.add = this.add.bind(this)
+ this.remove = this.remove.bind(this)
+ this.close = this.close.bind(this)
+ this.previous = this.previous.bind(this)
+ }
+
+ currentDomains() {
+ // return(App.providers.domains().slice() || [])
+ return ['domain1', 'domain2', 'domain3']
+ }
+
+ add() {
+ this.setState({showModal: true})
+ }
+
+ remove(provider) {
+ // App.providers.remove(provider)
+ this.setState({domains: this.currentDomains()})
+ }
+
+ close() {
+ let domains = this.currentDomains()
+ if (domains.length != this.state.domains.length) {
+ // this is ugly, but i could not get selection working
+ // by passing it as a property
+ this.refs.list.setSelected(0)
+ }
+ this.setState({
+ domains: domains,
+ showModal: false
+ })
+ }
+
+ previous() {
+ App.start()
+ }
+
+ render() {
+ let modal = null
+ if (this.state.showModal) {
+ modal = <AddProviderModal onClose={this.close} />
+ }
+ let buttons = (
+ <ButtonToolbar className="pull-right">
+ <Button onClick={this.previous}>
+ <Glyphicon glyph="chevron-left" />
+ Previous
+ </Button>
+ <Button>
+ Next
+ <Glyphicon glyph="chevron-right" />
+ </Button>
+ </ButtonToolbar>
+ )
+ let select = <ListEdit ref="list" items={this.state.domains}
+ onRemove={this.remove} onAdd={this.add} />
+ return(
+ <StageLayout title={this.props.title} subtitle={this.props.subtitle} buttons={buttons}>
+ {select}
+ {modal}
+ </StageLayout>
+ )
+ }
+}
diff --git a/ui/app/components/wizard/stage_layout.js b/ui/app/components/wizard/stage_layout.js
new file mode 100644
index 00000000..31540221
--- /dev/null
+++ b/ui/app/components/wizard/stage_layout.js
@@ -0,0 +1,37 @@
+import React from 'react'
+
+class StageLayout extends React.Component {
+
+ static get defaultProps() {return{
+ title: 'untitled',
+ subtitle: null,
+ buttons: null
+ }}
+
+ constructor(props) {
+ super(props)
+ }
+
+ render() {
+ let subtitle = null
+ if (this.props.subtitle) {
+ subtitle = <span>{this.props.subtitle}</span>
+ }
+ return(
+ <div className="stage">
+ <div className="header">
+ {this.props.title}
+ {subtitle}
+ </div>
+ <div className="body">
+ {this.props.children}
+ </div>
+ <div className="footer">
+ {this.props.buttons}
+ </div>
+ </div>
+ )
+ }
+}
+
+export default StageLayout \ No newline at end of file
diff --git a/ui/app/components/wizard/wizard.less b/ui/app/components/wizard/wizard.less
new file mode 100644
index 00000000..29efc20e
--- /dev/null
+++ b/ui/app/components/wizard/wizard.less
@@ -0,0 +1,44 @@
+.wizard .stage {
+ position: absolute;
+ height: 100%;
+ width: 100%;
+
+ display: -webkit-flex;
+ display: flex;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+ -webkit-flex: 1;
+ flex: 1;
+}
+
+.wizard .stage .footer {
+ -webkit-flex: 0 0 auto;
+ flex: 0 0 auto;
+ background-color: #ddd;
+ padding: 20px;
+ text-align: right;
+}
+
+.wizard .stage .header {
+ -webkit-flex: 0 0 auto;
+ flex: 0 0 auto;
+ padding: 20px;
+ background-color: #333;
+ color: white;
+ font-size: 2em;
+ span {
+ margin-left: 10px;
+ font-size: 0.5em;
+ }
+}
+
+.wizard .stage .body {
+ -webkit-flex: 1 1 auto;
+ flex: 1 1 auto;
+ padding: 20px;
+ overflow: auto;
+ display: -webkit-flex;
+ display: flex;
+ -webkit-flex-direction: column;
+ flex-direction: column;
+} \ No newline at end of file