Просмотр исходного кода

Added migration from replica options modal popup CORWEB-77

The options are dynamically rendered from a static list.
Sergiu Miclea 8 лет назад
Родитель
Сommit
83f92a016a

+ 5 - 4
src/actions/MigrationActions/MigrationActions.js

@@ -331,14 +331,15 @@ MigrationActions.addMigration.listen((migration, callback = null, errorCallback
     .catch(MigrationActions.addMigration.failed);
 })
 
-MigrationActions.createMigrationFromReplica.listen((replica) => {
+MigrationActions.createMigrationFromReplica.listen((replica, options) => {
   let payload = {
     migration: {
-      replica_id: replica.id,
-      force: false,
-      clone_disks: true
+      replica_id: replica.id
     }
   }
+  options.forEach(o => {
+    payload.migration[o.field] = o.value || false
+  })
 
   let projectId = Reflux.GlobalState.userStore.currentUser.project.id
 

+ 115 - 0
src/components/MigrationFromReplicaOptions/MigrationFromReplicaOptions.js

@@ -0,0 +1,115 @@
+/*
+ Copyright (C) 2017  Cloudbase Solutions SRL
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation, either version 3 of the
+ License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+/* eslint-disable dot-notation */
+
+import React, { PropTypes } from 'react';
+import Reflux from 'reflux';
+import withStyles from 'isomorphic-style-loader/lib/withStyles';
+import s from './MigrationFromReplicaOptions.scss';
+import Dropdown from '../NewDropdown';
+import Helper from "../Helper";
+
+class MigrationFromReplicaOptions extends Reflux.Component {
+  static propTypes = {
+    onCancel: PropTypes.func.isRequired,
+    onMigrate: PropTypes.func.isRequired
+  }
+
+  constructor(props) {
+    super(props)
+
+    this.state = {
+      fields: [
+        {
+          field: 'clone_disks',
+          type: 'boolean'
+        },
+        {
+          field: 'force',
+          type: 'boolean'
+        },
+        {
+          field: 'skip_os_morphing',
+          type: 'boolean'
+        }
+      ]
+    }
+  }
+
+  componentWillMount() {
+    super.componentWillMount.call(this)
+  }
+
+  componentWillUnmount() {
+    super.componentWillMount.call(this)
+  }
+
+  fieldChange(e, field) {
+    let fields = this.state.fields.concat([]);
+    fields.find(f => f.field === field).value = e.value === 'true'
+    this.setState({ fields: fields })
+  }
+
+  render() {
+    let booleanOptions = [{ label: 'Yes', value: 'true' }, { label: 'No', value: 'false' }]
+
+    let fields = this.state.fields.map((f, i) => {
+      if (f.type !== 'boolean') {
+        return null
+      }
+
+      return (
+        <div className="form-group" key={i}>
+          <label>{Helper.convertCloudFieldLabel(f.field)}</label>
+          <Dropdown
+            options={booleanOptions}
+            onChange={(e) => { this.fieldChange(e, f.field) }}
+            placeholder="Choose a value"
+            value={f.value ? booleanOptions[0] : booleanOptions[1]}
+          />
+        </div>
+      )
+    })
+    let modalBody = (
+      <div className={s.container}>
+        <div className={s.formContainer}>
+          {fields}
+        </div>
+        <div className={s.buttons}>
+          <button className="gray" onClick={() => this.props.onCancel()}>Cancel</button>
+          <button onClick={() => this.props.onMigrate(this.state.fields)}>Migrate</button>
+        </div>
+      </div>
+    )
+
+    return (
+      <div className={s.root}>
+        <div className={s.header}>
+          <h3>Create Migration from Replica</h3>
+        </div>
+        <div className={s.images}>
+          <div className={s.replicaImage} />
+          <div className={s.arrowImage} />
+          <div className={s.migrationImage} />
+        </div>
+        {modalBody}
+      </div>
+    )
+  }
+}
+
+export default withStyles(MigrationFromReplicaOptions, s)

Разница между файлами не показана из-за своего большого размера
+ 79 - 0
src/components/MigrationFromReplicaOptions/MigrationFromReplicaOptions.scss


+ 6 - 0
src/components/MigrationFromReplicaOptions/package.json

@@ -0,0 +1,6 @@
+{
+  "name": "MigrationFromReplicaOptions",
+  "version": "0.0.0",
+  "private": true,
+  "main": "./MigrationFromReplicaOptions.js"
+}

+ 47 - 3
src/components/ReplicaDetail/ReplicaDetail.js

@@ -19,6 +19,7 @@ import React, { Component, PropTypes } from 'react';
 import withStyles from 'isomorphic-style-loader/lib/withStyles';
 import s from './ReplicaDetail.scss';
 import Moment from 'react-moment';
+import Modal from 'react-modal';
 import Helper from "../Helper";
 import NotificationActions from '../../actions/NotificationActions'
 import Location from '../../core/Location';
@@ -26,6 +27,7 @@ import EndpointLink from '../EndpointLink';
 import ConfirmationDialog from '../ConfirmationDialog'
 import MigrationActions from '../../actions/MigrationActions';
 import MigrationNetworks from '../MigrationNetworks';
+import MigrationFromReplicaOptions from '../MigrationFromReplicaOptions'
 
 const title = 'Migration details';
 
@@ -42,6 +44,7 @@ class MigrationDetail extends Component {
   constructor(props) {
     super(props)
     this.state = {
+      showModal: false,
       confirmationDialog: {
         visible: false,
         message: "Are you sure?",
@@ -55,8 +58,17 @@ class MigrationDetail extends Component {
     this.context.onSetTitle(title);
   }
 
-  createMigrationFromReplica(e, replica) {
-    MigrationActions.createMigrationFromReplica(replica)
+  showModal() {
+    this.setState({ showModal: true })
+  }
+
+  closeModal() {
+    this.setState({ showModal: false })
+  }
+
+  migrate(options) {
+    this.closeModal()
+    MigrationActions.createMigrationFromReplica(this.props.replica, options)
   }
 
   deleteReplica(e, replica) {
@@ -85,6 +97,28 @@ class MigrationDetail extends Component {
   }
 
   render() {
+    let modalStyle = {
+      overlay: {
+        position: "fixed",
+        top: 0,
+        left: 0,
+        right: 0,
+        bottom: 0,
+        backgroundColor: "rgba(164, 170, 181, 0.69)"
+      },
+      content: {
+        padding: "0px",
+        borderRadius: "4px",
+        border: "none",
+        bottom: "auto",
+        width: "576px",
+        height: "auto",
+        left: "50%",
+        top: "120px",
+        marginLeft: "-288px"
+      }
+    }
+
     let item = this.props.replica
     let output = null
     if (item) {
@@ -164,7 +198,7 @@ class MigrationDetail extends Component {
           <MigrationNetworks className={s.migrationNetworks} migration={item} />
           <div className={s.container + " " + s.buttons}>
             { item.type == "replica" && <button
-              onClick={(e) => this.createMigrationFromReplica(e, item)}
+              onClick={() => this.showModal()}
               disabled={disabled} className={disabled ? "disabled" : ""}
             >
               Create Migration
@@ -177,6 +211,16 @@ class MigrationDetail extends Component {
             onConfirm={(e) => this.state.confirmationDialog.onConfirm(e)}
             onCancel={(e) => this.state.confirmationDialog.onCancel(e)}
           />
+          <Modal
+            isOpen={this.state.showModal}
+            style={modalStyle}
+            onRequestClose={this.closeModal.bind(this)}
+          >
+            <MigrationFromReplicaOptions
+              onCancel={this.closeModal.bind(this)}
+              onMigrate={this.migrate.bind(this)}
+            />
+          </Modal>
         </div>
       )
     }

+ 4 - 1
src/constants/CloudLabels.js

@@ -71,7 +71,10 @@ export const defaultLabels = {
   region: "Region",
   access_key_id: "Access Key ID",
   secret_access_key: "Secret Access Key",
-  session_token: "Session Token"
+  session_token: "Session Token",
+  clone_disks: "Clone Disks",
+  force: "Force",
+  skip_os_morphing: "Skip OS Morphing"
 }
 
 export const cloudLabels = {

Некоторые файлы не были показаны из-за большого количества измененных файлов