Procházet zdrojové kódy

- Updates EndpointList
- Renames ConnectionsList to EndpointList
- Fixes Loading on MainList, ProjectList
- Fixes Project Dropdown

George Vrancianu před 9 roky
rodič
revize
518720ee24

+ 0 - 337
src/components/ConnectionsList/ConnectionsList.js

@@ -1,337 +0,0 @@
-/*
-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/>.
-*/
-
-import React, { PropTypes } from 'react';
-import Reflux from 'reflux';
-import withStyles from 'isomorphic-style-loader/lib/withStyles';
-import Location from '../../core/Location';
-import Dropdown from '../NewDropdown';
-import SearchBox from '../SearchBox';
-import Moment from 'react-moment';
-import s from './ConnectionsList.scss';
-import AddCloudConnection from '../AddCloudConnection';
-import Modal from 'react-modal';
-import ConnectionsStore from '../../stores/ConnectionsStore';
-import ConnectionsActions from '../../actions/ConnectionsActions';
-import TextTruncate from 'react-text-truncate';
-import UserIcon from '../UserIcon';
-import FilteredTable from '../FilteredTable';
-import EndpointUsage from '../EndpointUsage';
-import NotificationIcon from '../NotificationIcon';
-import ConfirmationDialog from '../ConfirmationDialog'
-import ProjectsDropdown from '../ProjectsDropdown';
-
-
-const title = 'Cloud Endpoints';
-const connectionTypes = [
-  { label: "All", type: "all" },
-  { label: "Oracle Cloud", type: "opc" },
-  { label: "Oracle VM Server", type: "oracle_vm" },
-  { label: "Openstack", type: "openstack" },
-  { label: "VMware", type: "vmware_vsphere" }
-]
-const connectionActions = [
-  { label: "Delete", value: "delete" }
-]
-
-
-class ConnectionsList extends Reflux.Component {
-  constructor(props) {
-    super(props)
-    this.store = ConnectionsStore
-
-    this.state = {
-      showModal: false,
-      queryText: '',
-      filterType: 'all',
-      selectedAll: false,
-      searchMin: true,
-      connections: null,
-      confirmationDialog: {
-        visible: false,
-        message: "Are you sure?",
-        onConfirm: null,
-        onCancel: null
-      }
-    }
-  }
-
-  static contextTypes = {
-    onSetTitle: PropTypes.func.isRequired,
-  };
-
-  componentWillMount() {
-    super.componentWillMount.call(this)
-
-    this.context.onSetTitle(title);
-    if (this.state.connections == null) {
-      ConnectionsActions.loadConnections()
-    }
-  }
-
-  connectionsSelected() {
-    let count = this.connectionsSelectedCount(),
-        total = 0
-    if (this.state.connections) {
-      total = this.state.connections.length
-    }
-
-    return `${count} of ${total} connection(s) selected`;
-  }
-
-  connectionsSelectedCount() {
-    let count = 0
-    if (this.state.connections) {
-      this.state.connections.forEach((item) => {
-        if (item.selected) count++
-      })
-    }
-    return count
-  }
-
-  connectionDetail(e, item) {
-    Location.push('/cloud-endpoints/' + item.id + "/")
-  }
-
-  checkItem(e, itemRef) {
-    let items = this.state.connections
-    items.forEach((item) => {
-      if (item == itemRef) {
-        item.selected = !item.selected
-      }
-    })
-    this.setState({ connections: items, selectedAll: false  })
-  }
-
-  checkAll() {
-    let items = this.state.connections
-    let selectedAll = this.state.selectedAll
-
-    items.forEach((item) => {
-      item.selected = !selectedAll
-    })
-
-    this.setState({ connections: items, selectedAll: !selectedAll })
-  }
-
-  filterFn(item, queryText, filterType) {
-    return (
-      item.name.toLowerCase().indexOf(queryText.toLowerCase()) != -1 &&
-      (filterType == "all" || filterType == item.type)
-    )
-  }
-
-  searchItem(queryText) {
-    this.setState({ queryText: queryText })
-  }
-
-  filterType(e, type) {
-    this.setState({ filterType: type })
-  }
-
-  closeModal() {
-    this.setState({ showModal: false })
-  }
-
-  bulkActions(action) {
-    switch (action.value) {
-      case "delete":
-        this.setState({
-          confirmationDialog: {
-            visible: true,
-            onConfirm: () => {
-              this.setState({ confirmationDialog: { visible: false }})
-              let selectedConnections = this.state.connections.filter((connection) => connection.selected)
-              selectedConnections.forEach(connection => {
-                ConnectionsActions.deleteConnection(connection)
-              })
-            },
-            onCancel: () => {
-              this.setState({ confirmationDialog: { visible: false }})
-            }
-          }
-        })
-
-        break;
-    }
-  }
-
-  renderSearch(items) {
-    let output = null
-    if (items && items.length) {
-      output = items.map((item, index) => (
-        <div className={"item " + (item.selected ? " selected" : "")} key={"vm_" + index}>
-          <div className="checkbox-container">
-            <input
-              id={"vm_check_" + index}
-              type="checkbox"
-              checked={item.selected}
-              onChange={(e) => this.checkItem(e, item)}
-              className="checkbox-normal"
-            />
-            <label htmlFor={"vm_check_" + index}></label>
-          </div>
-          <span className="cell cell-icon" onClick={(e) => this.connectionDetail(e, item)}>
-            <div className={"icon endpoint"}></div>
-            <span className="details">
-              {/*{item.name ? item.name : "N/A"}*/}
-              <TextTruncate line={1} truncateText="..." text={item.name} />
-              <span className={s.description}>{item.description == "" ? "N/A" : item.description}</span>
-            </span>
-          </span>
-          <span className="cell">
-              <div className={s.cloudImage + " icon small-cloud " + item.type}></div>
-            </span>
-          <span className={"cell " + s.composite}>
-              <span className={s.label}>Created</span>
-              <span className={s.value}>
-                <Moment fromNow ago date={item.created_at}/> ago
-              </span>
-            </span>
-          <span className={"cell " + s.composite}>
-              <span className={s.label}>Usage</span>
-              <span className={s.value}>
-                <EndpointUsage connectionId={item.id} />
-              </span>
-            </span>
-        </div>
-      ), this)
-    }
-    return output
-  }
-  
-  currentInstance(migration) {
-    let instance = "N/A"
-    migration.vms.forEach((item) => {
-      if (item.selected) {
-        instance = item.name
-      }
-    })
-    return instance
-  }
-
-  showNewConnectionModal() {
-    this.setState({ showModal: true })
-  }
-
-  render() {
-    let itemStates = connectionTypes.map((state, index) => (
-        <a
-          className={this.state.filterType == state.type || (this.state.filterType == null && state.type == "all") ?
-            "selected" : ""}
-          onClick={(e) => this.filterType(e, state.type)} key={"status_" + index}
-        >{state.label}</a>
-      ), this)
-
-    let modalStyle = {
-      content: {
-        padding: "0px",
-        borderRadius: "4px",
-        bottom: "auto",
-        width: "576px",
-        height: "auto",
-        left: "50%",
-        top: "70px",
-        marginLeft: "-288px"
-      }
-    }
-
-    return (
-      <div className={s.root}>
-        <div className={s.container}>
-          <div className={s.pageHeader}>
-            <div className={s.top}>
-              <h1>{title}</h1>
-              <div className={s.topActions}>
-                <ProjectsDropdown />
-                <button onClick={(e) => this.showNewConnectionModal(e)}>New</button>
-                <UserIcon />
-                <NotificationIcon />
-              </div>
-            </div>
-            <div className="filters">
-              <div className="checkbox-container">
-                <input
-                  id={"vm_check_all"}
-                  type="checkbox"
-                  checked={this.state.selectedAll}
-                  onChange={(e) => this.checkAll()}
-                  className="checkbox-normal"
-                />
-                <label htmlFor={"vm_check_all"}></label>
-              </div>
-              <div className="category-filter">
-                {itemStates}
-              </div>
-              <div className="name-filter">
-                <SearchBox
-                  placeholder="Search"
-                  value={this.state.queryText}
-                  onChange={(e) => this.searchItem(e)}
-                  minimize={true} // eslint-disable-line react/jsx-boolean-value
-                  onClick={(e) => this.toggleSearch(e)}
-                  className={"searchBox " + (this.state.searchMin ? "minimize" : "")}
-                />
-              </div>
-              <div className={s.bulkActions + (this.connectionsSelectedCount() === 0 ? " invisible": "")}>
-                <div className={s.connectionsCount}>
-                  {this.connectionsSelected()}
-                </div>
-                <Dropdown
-                  options={connectionActions}
-                  placeholder="More Actions"
-                  onChange={(e) => this.bulkActions(e)}
-                />
-              </div>
-            </div>
-          </div>
-          <div className={s.pageContent}>
-            <FilteredTable
-              items={this.state.connections}
-              filterFn={this.filterFn}
-              queryText={this.state.queryText}
-              filterType={this.state.filterType}
-              renderSearch={(e) => this.renderSearch(e)}
-            ></FilteredTable>
-          </div>
-          <div className={s.pageFooter}>
-
-          </div>
-        </div>
-        <Modal
-          isOpen={this.state.showModal}
-          contentLabel="Add new cloud connection"
-          style={modalStyle}
-        >
-          <AddCloudConnection
-            closeHandle={(e) => this.closeModal(e)}
-            addHandle={(e) => this.closeModal(e)}
-          />
-        </Modal>
-        <ConfirmationDialog
-          visible={this.state.confirmationDialog.visible}
-          message={this.state.confirmationDialog.message}
-          onConfirm={(e) => this.state.confirmationDialog.onConfirm(e)}
-          onCancel={(e) => this.state.confirmationDialog.onCancel(e)}
-        />
-      </div>
-    );
-  }
-
-}
-
-export default withStyles(ConnectionsList, s);

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

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

+ 187 - 0
src/components/EndpointList/EndpointList.js

@@ -0,0 +1,187 @@
+/*
+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/>.
+*/
+
+import React, { PropTypes } from 'react';
+import Reflux from 'reflux';
+import withStyles from 'isomorphic-style-loader/lib/withStyles';
+import Location from '../../core/Location';
+import Moment from 'react-moment';
+import s from './EndpointList.scss';
+import AddCloudConnection from '../AddCloudConnection';
+import Modal from 'react-modal';
+import ConnectionsStore from '../../stores/ConnectionsStore';
+import ConnectionsActions from '../../actions/ConnectionsActions';
+import TextTruncate from 'react-text-truncate';
+import UserIcon from '../UserIcon';
+import EndpointUsage from '../EndpointUsage';
+import NotificationIcon from '../NotificationIcon';
+import ProjectsDropdown from '../ProjectsDropdown';
+import MainList from '../MainList';
+
+
+const title = 'Cloud Endpoints';
+const connectionActions = {
+  delete_action: {
+    label: "Delete",
+    action: (item) => {
+      ConnectionsActions.deleteConnection(item)
+    },
+    confirm: true
+  }
+}
+
+const filters = [
+  {
+    field: "type",
+    options: [
+      { value: null, label: "All"},
+      { value: "opc", label: "Oracle Cloud"},
+      { value: "oracle_vm", label: "Oracle VM Server"},
+      { value: "openstack", label: "Openstack"},
+      { value: "vmware_vsphere", label: "VMware"}
+    ]
+  }
+]
+
+class EndpointList extends Reflux.Component {
+
+
+
+  constructor(props) {
+    super(props)
+    this.store = ConnectionsStore
+
+    this.state = {
+      showModal: false,
+      connections: null
+    }
+
+    this.renderItem = this.renderItem.bind(this)
+  }
+
+  static contextTypes = {
+    onSetTitle: PropTypes.func.isRequired,
+  };
+
+  componentWillMount() {
+    super.componentWillMount.call(this)
+
+    this.context.onSetTitle(title);
+    if (this.state.connections == null) {
+      ConnectionsActions.loadConnections()
+    }
+  }
+
+  connectionDetail(e, item) {
+    Location.push('/cloud-endpoints/' + item.id + "/")
+  }
+
+  refresh() {
+    ConnectionsActions.loadConnections()
+  }
+
+  showNewConnectionModal() {
+    this.setState({ showModal: true })
+  }
+
+  closeModal() {
+    this.setState({ showModal: false })
+  }
+
+  renderItem(item) {
+    return (
+      <div className={"item " + (item.selected ? " selected" : "")} key={"vm_" + item.id}>
+        <span className="cell cell-icon" onClick={(e) => this.connectionDetail(e, item)}>
+          <div className={"icon endpoint"}></div>
+          <span className="details">
+            <TextTruncate line={1} truncateText="..." text={item.name} />
+            <span className={s.description}>{item.description == "" ? "N/A" : item.description}</span>
+          </span>
+        </span>
+        <span className="cell">
+          <div className={s.cloudImage + " icon small-cloud " + item.type}></div>
+        </span>
+        <span className={"cell " + s.composite}>
+          <span className={s.label}>Created</span>
+          <span className={s.value}>
+            <Moment fromNow ago date={item.created_at}/> ago
+          </span>
+        </span>
+        <span className={"cell " + s.composite}>
+          <span className={s.label}>Usage</span>
+          <span className={s.value}>
+            <EndpointUsage connectionId={item.id} />
+          </span>
+        </span>
+      </div>
+    )
+  }
+
+  render() {
+    let modalStyle = {
+      content: {
+        padding: "0px",
+        borderRadius: "4px",
+        bottom: "auto",
+        width: "576px",
+        height: "auto",
+        left: "50%",
+        top: "70px",
+        marginLeft: "-288px"
+      }
+    }
+
+    return (
+      <div className={s.root}>
+        <div className={s.container}>
+          <div className={s.pageHeader}>
+            <div className={s.top}>
+              <h1>{title}</h1>
+              <div className={s.topActions}>
+                <ProjectsDropdown />
+                <button onClick={(e) => this.showNewConnectionModal(e)}>New</button>
+                <UserIcon />
+                <NotificationIcon />
+              </div>
+            </div>
+          </div>
+          <MainList
+            items={this.state.connections}
+            actions={connectionActions}
+            itemName="connection"
+            renderItem={this.renderItem}
+            filters={filters}
+            refresh={this.refresh}
+          />
+        </div>
+        <Modal
+          isOpen={this.state.showModal}
+          contentLabel="Add new cloud connection"
+          style={modalStyle}
+        >
+          <AddCloudConnection
+            closeHandle={(e) => this.closeModal(e)}
+            addHandle={(e) => this.closeModal(e)}
+          />
+        </Modal>
+      </div>
+    );
+  }
+
+}
+
+export default withStyles(EndpointList, s);

+ 4 - 4
src/components/ConnectionsList/ConnectionsList.scss → src/components/EndpointList/EndpointList.scss

@@ -112,17 +112,17 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
   :global(.items-list) {
     :global(.item) {
       position: relative;
-      span:global(.cell):nth-child(2) {
+      span:global(.cell):nth-child(1) {
         flex: 3;
         cursor: pointer;
       }
-      span:global(.cell):nth-child(3) {
+      span:global(.cell):nth-child(2) {
         flex: 2;
       }
-      span:global(.cell):nth-child(4) {
+      span:global(.cell):nth-child(3) {
         flex: 2;
       }
-      span:global(.cell):nth-child(5) {
+      span:global(.cell):nth-child(4) {
         flex: 3;
       }
       :global(.checkbox-container) {

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

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

+ 1 - 1
src/components/FilteredTable/FilteredTable.js

@@ -24,7 +24,7 @@ import LoadingIcon from "../LoadingIcon/LoadingIcon";
 class FilteredTable extends Component {
 
   static defaultProps = {
-    items: [],
+    items: null,
     filterFn: null,
     renderSearchItem: null,
     customClassName: null

+ 24 - 11
src/components/MainList/MainList.js

@@ -43,7 +43,7 @@ class MainList extends Component {
 
   static defaultProps = {
     itemName: "items",
-    items: [],
+    items: null,
     filters: [],
     actions: null,
     refresh: false
@@ -192,17 +192,30 @@ class MainList extends Component {
   }
 
   onActionChange(option) {
-    let items = this.state.items.forEach((item) => {
-      if (item.selected) {
-        return item
-      } else {
-        return null
-      }
-    })
-    if (this.props.actions[option.value].action) {
-      items.forEach((item) => {
-        this.props.actions[option.value].action(item)
+    let items = this.state.items.filter(item => item.selected)
+    if (this.props.actions[option.value].confirm) {
+      this.setState({
+        confirmationDialog: {
+          visible: true,
+          onConfirm: () => {
+            this.setState({ confirmationDialog: { visible: false }})
+            if (this.props.actions[option.value].action) {
+              items.forEach((item) => {
+                this.props.actions[option.value].action(item)
+              })
+            }
+          },
+          onCancel: () => {
+            this.setState({ confirmationDialog: { visible: false }})
+          }
+        }
       })
+    } else {
+      if (this.props.actions[option.value].action) {
+        items.forEach((item) => {
+          this.props.actions[option.value].action(item)
+        })
+      }
     }
   }
 

+ 3 - 1
src/components/ProjectList/ProjectList.js

@@ -67,7 +67,9 @@ class ProjectList extends Reflux.Component {
     this.store = UserStore
 
     this.state = {
-      projects: null
+      currentUser: {
+        projects: null
+      }
     }
 
     this.renderItem = this.renderItem.bind(this)

+ 1 - 1
src/components/ProjectList/ProjectList.scss

@@ -106,7 +106,7 @@ along with this program.  If not, see <http://www.gnu.org/licenses/>.
 .container {
   margin: 0 auto;
   padding: 0;
-  max-width: $max-content-width;
+  max-width: $max-content-widt9h;
   height: 100%;
   display: flex;
   flex-direction: column;

+ 11 - 9
src/components/ProjectsDropdown/ProjectsDropdown.js

@@ -40,18 +40,20 @@ class ProjectsDropdown extends Reflux.Component {
   }
 
   render() {
-    let projects = this.state.currentUser.projects.map(project => {
-      return { label: project.name, value: project.id }
-    })
-
+    let projects = []
     let currentProject = null
-    if (Reflux.GlobalState.userStore.currentUser.project) {
-      currentProject = {
-        label: Reflux.GlobalState.userStore.currentUser.project.name,
-        value: Reflux.GlobalState.userStore.currentUser.project.id
+    if (this.state.currentUser.projects) {
+      projects = this.state.currentUser.projects.map(project => {
+        return {label: project.name, value: project.id}
+      })
+
+      if (Reflux.GlobalState.userStore.currentUser.project) {
+        currentProject = {
+          label: Reflux.GlobalState.userStore.currentUser.project.name,
+          value: Reflux.GlobalState.userStore.currentUser.project.id
+        }
       }
     }
-
     return <div className={s.root}>
       <Dropdown
         options={projects}