Kaynağa Gözat

- Adds timeout message to instances, hides other form elements until it loads
- Adds props to LoadingIcon, adds message and padding
- Renames Vms to instances
- Fixes LoginPage for smaller screens

George Vrancianu 8 yıl önce
ebeveyn
işleme
c2e6540d4c

+ 19 - 8
src/components/LoadingIcon/LoadingIcon.js

@@ -46,28 +46,39 @@ class LoadingIcon extends Component {
   static propTypes = {
     width: PropTypes.number,
     height: PropTypes.number,
-    animate: PropTypes.bool
+    animate: PropTypes.bool,
+    padding: PropTypes.number,
+    text: PropTypes.string
   }
 
   static defaultProps = {
     width: 137,
     height: 122,
-    animate: true
+    animate: true,
+    padding: 32,
+    text: "Loading..."
   }
 
   render() {
-    let wrapperStyle = {
+    let loaderStyle = {
       width: this.props.width,
       height: this.props.height,
-      margin: "0 auto"
+      margin: "0 auto",
+    }
+    let wrapperStyle = {
+      paddingTop: this.props.padding,
+      paddingBottom: this.props.padding
     }
-    return <div className={s.root}>
+
+    return (<div className={s.root} style={wrapperStyle}>
       <div
         className={!this.props.animate && "noAnimation"}
-        style={wrapperStyle}
+        style={loaderStyle}
         dangerouslySetInnerHTML={{__html: svgIcon}}
-      ></div>
-    </div>
+      >
+      </div>
+      {this.props.text && (<div className={s.text}>{this.props.text}</div>)}
+    </div>)
   }
 
 }

+ 5 - 0
src/components/LoadingIcon/LoadingIcon.scss

@@ -76,4 +76,9 @@ $base-opacity:          0.3;
       }
     }
   }
+  .text {
+    font-size: 18px;
+    text-align: center;
+    margin-top: 16px;
+  }
 }

+ 8 - 7
src/components/LoginPage/LoginPage.js

@@ -1,3 +1,4 @@
+/* eslint-disable max-len */
 /*
 Copyright (C) 2017  Cloudbase Solutions SRL
 
@@ -123,21 +124,21 @@ class LoginPage extends Reflux.Component {
   }
 
   render() {
-    let loginButtonsTemplate = loginButtons.map(btn => {
-      return (<div className="form-group" key={"loginbtn_" + btn.id}>
+    let loginButtonsTemplate = loginButtons.map(btn => (
+      <div className="form-group" key={"loginbtn_" + btn.id}>
         <a className={s.loginBtn + " " + btn.id} href={btn.url}>
           <span className={s.icon}></span> Sign in with {btn.name}
         </a>
-      </div>)
-    })
+      </div>
+    ))
 
     return (
       <div className={s.root}>
         <div className={s.container + " " + (loginButtons.length <= 2 ? s.oneColumn : "")}>
           <div className={s.logo}>
             <div className={s.large}>
-              <LoadingIcon width={224} height={200} animate={this.state.loadingState} />
-              <div className={s.coriolisText} dangerouslySetInnerHTML={{__html: coriolisTextSvg}}></div>
+              <LoadingIcon width={224} height={200} animate={this.state.loadingState} padding={16} text=""/>
+              <div className={s.coriolisText} dangerouslySetInnerHTML={{ __html: coriolisTextSvg }}></div>
             </div>
             <div className={s.small}>
               <div className={"logo coriolis-white"}></div>
@@ -184,7 +185,7 @@ class LoginPage extends Reflux.Component {
         </div>
         <div className={s.footerLogo}>
           <div className={s.text}>Coriolis® is a service offered by</div>
-          <div className={s.coriolisLogo} dangerouslySetInnerHTML={{__html: footerLogoSvg}}></div>
+          <div className={s.coriolisLogo} dangerouslySetInnerHTML={{ __html: footerLogoSvg }}></div>
         </div>
       </div>
     );

+ 2 - 2
src/components/LoginPage/LoginPage.scss

@@ -189,12 +189,12 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 }
 
 
-@media screen and (max-height: 860px) and (min-height: 760px) {
+@media screen and (max-height: 880px) and (min-height: 760px) {
   .container {
     padding-top: 2vh;
   }
   .loginContainer {
-    margin-top: 16px;
+    margin-top: 32px;
   }
   .root .footerLogo {
     bottom: 16px;

+ 2 - 2
src/components/MigrationWizard/MigrationWizard.scss

@@ -59,7 +59,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   .breadcrumbs {
     span {
       display: inline-block;
-      margin-right: 7px;
+      margin-right: 4px;
       color: $gray;
       &:after {
         content: " ";
@@ -67,7 +67,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
         width: 7px;
         height: 13px;
         display: inline-block;
-        margin-left: 5px;
+        margin-left: 3px;
         margin-top: 3px;
         float:right;
       }

+ 3 - 2
src/components/SearchBox/SearchBox.js

@@ -25,7 +25,8 @@ class SearchBox extends Component {
     maxLines: PropTypes.number,
     minimize: PropTypes.bool,
     placeholder: PropTypes.string,
-    onChange: PropTypes.func
+    onChange: PropTypes.func,
+    className: PropTypes.string
   };
 
   static defaultProps = {
@@ -71,7 +72,7 @@ class SearchBox extends Component {
           onChange={(e) => this.onChange(e)}
           onClick={(e) => this.toggleSearch(e)}
           onBlur={(e) => this.onBlurAction(e)}
-          className={s.searchBox + " " + (this.state.isMin ? s.minimize : "") + " searchBox"}
+          className={s.searchBox + " " + (this.state.isMin ? s.minimize : "") + " searchBox " + this.props.className}
         />
       </div>
     );

+ 3 - 2
src/components/Tasks/Tasks.js

@@ -140,7 +140,8 @@ class Tasks extends Component {
           </span>),
           instance: <TextTruncate line={1} text={item.instance} truncateText="..." />,
           latest_message: <TextTruncate line={1} truncateText="..." text={latestMessage} />,
-          timestamp: item.updated_at ? Helper.getTimeObject(moment(item.updated_at)).format("YYYY-MM-DD HH:mm:ss") : "-",
+          timestamp: item.updated_at ? Helper.getTimeObject(moment(item.updated_at)).format("YYYY-MM-DD HH:mm:ss") :
+            "-",
           detailView: taskDetails,
           openState: item.status === 'RUNNING'
         }
@@ -162,7 +163,7 @@ class Tasks extends Component {
               customClassName={s.table}
               show={this.state !== null}
             />
-          </div>) : <LoadingIcon/>
+          </div>) : <LoadingIcon />
         }
       </div>
     );

+ 31 - 37
src/components/WizardVms/WizardVms.js

@@ -19,11 +19,11 @@ import React, { Component, PropTypes } from 'react';
 import withStyles from 'isomorphic-style-loader/lib/withStyles';
 import SearchBox from '../SearchBox';
 import s from './WizardVms.scss';
-import {itemsPerPage} from '../../config';
+import { itemsPerPage } from '../../config';
 import ConnectionsActions from '../../actions/ConnectionsActions';
 import LoadingIcon from '../LoadingIcon';
 
-const title = 'Select VMs to migrate';
+const title = 'Select instances to migrate';
 const vmStatesConst = ["All", "RUNNING", "PAUSED", "STOPPED"]
 const searchTimeout = 1000;
 
@@ -41,7 +41,6 @@ class WizardVms extends Component {
   constructor(props) {
     super(props)
     let valid = false
-    let timeout = null
 
     if (this.props.data.instances) {
       this.props.data.instances.forEach((vm) => {
@@ -60,13 +59,6 @@ class WizardVms extends Component {
     }
   }
 
-  processProps(props) {
-    if (props.data.instances) {
-      this.setState({filteredData: props.data.instances.slice(
-        this.state.page * itemsPerPage, this.state.page * itemsPerPage + itemsPerPage)})
-    }
-  }
-
   componentWillMount() {
     this.context.onSetTitle(title);
     this.props.setWizardState(this.state)
@@ -75,13 +67,20 @@ class WizardVms extends Component {
   componentWillReceiveProps(newProps) {
     this.processProps(newProps)
     if (newProps.data.selectedInstances) {
-      let valid =  newProps.data.selectedInstances.length > 0
+      let valid = newProps.data.selectedInstances.length > 0
       if (this.props.data.valid != valid) {
-        this.props.setWizardState({valid: valid})
+        this.props.setWizardState({ valid: valid })
       }
     }
   }
 
+  processProps(props) {
+    if (props.data.instances) {
+      this.setState({ filteredData: props.data.instances.slice(
+        this.state.page * itemsPerPage, this.state.page * itemsPerPage + itemsPerPage) })
+    }
+  }
+
   checkVm(e, item) {
     let instances = this.props.data.instances
     instances.forEach((vm) => {
@@ -101,7 +100,7 @@ class WizardVms extends Component {
         } else {
           selectedInstances.splice(index, 1)
         }
-        this.props.setWizardState({selectedInstances: selectedInstances})
+        this.props.setWizardState({ selectedInstances: selectedInstances })
       }
     })
 
@@ -114,8 +113,7 @@ class WizardVms extends Component {
     if (this.props.data.instances) {
       this.props.data.instances.forEach((vm) => {
         if (
-          //vm.name.toLowerCase().indexOf(queryText.target.value.toLowerCase()) != -1 &&
-          (this.state.filterStatus == "All" || this.state.filterStatus == vm.status)
+          (this.state.filterStatus === "All" || this.state.filterStatus === vm.status)
         ) {
           queryResult.push(vm)
         }
@@ -127,10 +125,10 @@ class WizardVms extends Component {
         clearTimeout(this.timeout)
       }
       this.timeout = setTimeout(() => {
-        this.setState({page: 0, filteredData: null, queryText: queryText}, () => {
-          this.props.setWizardState({instances: null})
+        this.setState({ page: 0, filteredData: null, queryText: queryText }, () => {
+          this.props.setWizardState({ instances: null })
           ConnectionsActions.loadInstances(
-            {id: this.props.data.sourceCloud.credential.id},
+            { id: this.props.data.sourceCloud.credential.id },
             this.state.page,
             queryText,
             false
@@ -142,7 +140,6 @@ class WizardVms extends Component {
         filteredData: queryResult
       })
     }
-
   }
 
 
@@ -177,26 +174,26 @@ class WizardVms extends Component {
 
   nextPage() {
     if (this.state.filteredData && this.state.filteredData.length == itemsPerPage) {
-      this.setState({page: this.state.page + 1}, () => {
+      this.setState({ page: this.state.page + 1 }, () => {
         ConnectionsActions.loadInstances(
-          {id: this.props.data.sourceCloud.credential.id},
+          { id: this.props.data.sourceCloud.credential.id },
           this.state.page,
           this.state.queryText
         )
-        this.processProps({data: {instances: this.props.data.instances}})
+        this.processProps({ data: { instances: this.props.data.instances } })
       })
     }
   }
 
   previousPage() {
     if (this.state.page > 0) {
-      this.setState({page: this.state.page + -1}, () => {
+      this.setState({ page: this.state.page + -1 }, () => {
         ConnectionsActions.loadInstances(
-          {id: this.props.data.sourceCloud.credential.id},
+          { id: this.props.data.sourceCloud.credential.id },
           this.state.page,
           this.state.queryText
         )
-        this.processProps({data: {instances: this.props.data.instances}})
+        this.processProps({ data: { instances: this.props.data.instances } })
       })
     }
   }
@@ -222,9 +219,6 @@ class WizardVms extends Component {
               {item.instance_name}
             </span>
           </span>
-          {/*<span className="cell">
-            <span className={ item.status + " status-pill" }>{ item.status }</span>
-          </span>*/}
           <span className="cell">{item.num_cpu} vCPU | {item.memory_mb} MB RAM | {item.flavor_name}</span>
         </div>
        )
@@ -232,7 +226,7 @@ class WizardVms extends Component {
     } else if (this.props.data.instances && this.props.data.instances.length == 0) {
       return <div className="no-results">Your search returned no results</div>
     } else {
-      return <LoadingIcon/>
+      return <LoadingIcon padding={64} text="Loading instances.." />
     }
   }
 
@@ -253,25 +247,26 @@ class WizardVms extends Component {
             placeholder="Search VMs"
             value={this.state.queryText}
             onChange={(e) => this.searchVm(e)}
-            className="searchBox"
+            className={"searchBox" + (this.state.filteredData.length == 0 ? " hidden" : " ")}
           />
-          <div className="category-filter">
+          <div className={"category-filter" + (this.state.filteredData.length == 0 ? " hidden" : " ")}>
             {vmStates}
           </div>
           <div className="items-list instances">
             {this.renderSearch()}
           </div>
-          <div className={s.selectionCount}>
-            {this.instancesSelected()} VMs selected
+          <div className={s.selectionCount + (this.state.filteredData.length == 0 ? " hidden" : " ")}>
+            {this.instancesSelected()} instances selected
           </div>
-          <div className={s.pagination}>
+          <div className={s.pagination + (this.state.filteredData.length == 0 ? " hidden" : " ")}>
             <span
-              className={(this.state.page == 0 ? "disabled " : "")+ s.prev}
+              className={(this.state.page == 0 ? "disabled " : "") + s.prev}
               onClick={(e) => this.previousPage(e)}
             ></span>
             <span className={s.currentPage}>{this.state.page + 1}</span>
             <span
-              className={(this.state.filteredData && this.state.filteredData.length == itemsPerPage ? " " : "disabled ") + s.next}
+              className={(this.state.filteredData && this.state.filteredData.length == itemsPerPage ?
+                " " : "disabled ") + s.next}
               onClick={(e) => this.nextPage(e)}
             ></span>
           </div>
@@ -279,7 +274,6 @@ class WizardVms extends Component {
       </div>
     );
   }
-
 }
 
 export default withStyles(WizardVms, s);

+ 2 - 2
src/config.sample.js

@@ -77,8 +77,8 @@ export const migrationSteps = [
     component: "WizardTarget"
   },
   {
-    name: "Migrate VMs",
-    title: "Select VMs",
+    name: "Migrate instances",
+    title: "Select instances",
     component: "WizardVms"
   },
   {

+ 8 - 0
src/stores/NotificationsStore/NotificationsStore.js

@@ -201,6 +201,14 @@ class NotificationsStore extends Reflux.Store
     this.setState({ notifications: notifications })
   }
 
+  onLoadInstancesFailed() {
+    let notifications = [{
+      message: "Could not load instances.",
+      type: 'error'
+    }]
+    this.setState({ notifications: notifications })
+  }
+
   onNotify(message, type = "info", title = null, callback = null) {
     this.setState({notifications: [
       {

+ 5 - 4
src/stores/UserStore/UserStore.js

@@ -22,6 +22,7 @@ import ConnectionsActions from '../../actions/ConnectionsActions'
 import Location from '../../core/Location';
 import Api from '../../components/ApiCaller';
 import cookie from 'react-cookie';
+import moment from 'moment';
 import { servicesUrl } from '../../config'
 
 class UserStore extends Reflux.Store
@@ -65,7 +66,7 @@ class UserStore extends Reflux.Store
   onLoginSuccess(response) {
     let token = response.headers['X-Subject-Token']
     Api.setDefaultHeader('X-Auth-Token', token)
-    cookie.save('unscopedToken', token, { path: "/" })
+    cookie.save('unscopedToken', token, { path: "/", expires: moment().add(1, 'hour').toDate() })
 
     UserActions.getScopedProjects(response => {
       if (response.data.projects) {
@@ -89,8 +90,8 @@ class UserStore extends Reflux.Store
     currentUser.token = response.headers['X-Subject-Token']
     currentUser.project = response.data.token.project
 
-    cookie.save('token', currentUser.token, { path: "/" })
-    cookie.save('projectId', currentUser.project.id, { path: "/" })
+    cookie.save('token', currentUser.token, { path: "/", expires: moment().add(1, 'hour').toDate() })
+    cookie.save('projectId', currentUser.project.id, { path: "/", expires: moment().add(1, 'months').toDate() })
     Api.setDefaultHeader('X-Auth-Token', currentUser.token)
 
     this.setState({currentUser: currentUser})
@@ -172,7 +173,7 @@ class UserStore extends Reflux.Store
 
   onFederateToken(token) {
     Api.setDefaultHeader('X-Auth-Token', token)
-    cookie.save('unscopedToken', token, { path: "/" })
+    cookie.save('unscopedToken', token, { path: "/", expires: moment().add(1, 'hour').toDate() })
     UserActions.getScopedProjects(response => {
       if (response.data.projects) {
         UserActions.loginScope(token, response.data.projects[0].id)

+ 4 - 0
src/stores/WizardStore/WizardStore.js

@@ -104,6 +104,10 @@ class UsersStore extends Reflux.Store
     this.setState({ instances: instances })
   }
 
+  onLoadInstancesFailed() {
+    this.setState({ instances: [] })
+  }
+
   onNewState() {
     this.setState(this.blankState)
   }