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

- Adds project detail view, route
- Adds notification upon project switch

George Vrancianu 9 лет назад
Родитель
Сommit
ff0dbfac8e

+ 121 - 0
src/components/Project/Project.js

@@ -0,0 +1,121 @@
+/*
+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 s from './Project.scss';
+import UserStore from '../../stores/UserStore';
+import Header from '../Header';
+import ConfirmationDialog from '../ConfirmationDialog';
+import LoadingIcon from '../LoadingIcon';
+import Location from '../../core/Location';
+
+class Project extends Reflux.Component {
+  title = ""
+  constructor(props) {
+    super(props)
+    this.store = UserStore
+
+    this.state = {
+      connection: {
+        name: null,
+        cloudName: null,
+        id: null
+      }
+    }
+  }
+
+  static propTypes = {
+    type: PropTypes.string,
+
+  }
+
+  static contextTypes = {
+    onSetTitle: PropTypes.func.isRequired,
+  };
+
+  componentWillMount() {
+    super.componentWillMount.call(this)
+  }
+
+  componentDidMount() {
+    this.context.onSetTitle(this.title);
+  }
+
+  deleteProject() {
+
+  }
+
+  goBack() {
+    Location.push("/projects")
+  }
+
+  render() {
+    let item = null
+    if (this.state.currentUser && this.state.currentUser.projects) {
+      this.state.currentUser.projects.forEach(project => {
+        if (project.id == this.props.projectId) {
+          item = project
+        }
+      })
+    }
+    if (item) {
+      return (
+        <div className={s.root}>
+          <Header linkUrl="/projects"/>
+          <div className={s.projectHead + " detailViewHead"}>
+            <div className={s.container}>
+              <div className="backBtn" onClick={(e) => this.goBack(e)}></div>
+              <div className={s.connectionTypeImg + " icon project-large"}></div>
+              <div className={s.connectionInfo}>
+                <h2>{item.name}</h2>
+                <p>{item.description}</p>
+              </div>
+            </div>
+          </div>
+          <div className={s.container}>
+            <div className={s.sidebar}>
+
+            </div>
+            <div className={s.content}>
+              {React.cloneElement(this.props.children, {
+                projects: this.state.currentUser ? this.state.currentUser.projects : null,
+                projectId: this.props.projectId })}
+            </div>
+          </div>
+          {/*<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>
+      );
+    } else {
+      return (
+        <div className={s.root}>
+          <LoadingIcon />
+        </div>
+      );
+    }
+
+  }
+
+}
+
+export default withStyles(Project, s);

+ 111 - 0
src/components/Project/Project.scss

@@ -0,0 +1,111 @@
+/*
+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 '../variables.scss';
+
+.projectHead {
+  background-color: #D9DCE3;
+  margin-top: 64px;
+  padding: 16px;
+  .connectionTypeImg {
+    margin-right: 64px;
+    float: left;
+  }
+  .connectionInfo {
+    float: left;
+    .connectionStatus {
+      width: 80px;
+      border-radius: 4px;
+      color: #FFF;
+      line-height: 16px;
+      display: inline-block;
+      font-size: 10px;
+      text-align: center;
+      &:global(.PAUSED) {
+        background-color: $gray;
+      }
+    }
+    .connectionType {
+      width: 80px;
+      background-color: #FFF;
+      border-radius: 4px;
+      line-height: 14px;
+      font-size: 10px;
+      text-align: center;
+      display: inline-block;
+      text-transform: uppercase;
+      border: 1px solid $blue;
+      color: $blue;
+      margin-right: 16px;
+      &:global(.replica) {
+        border: 1px solid $color-replica;
+        color: $color-replica;
+      }
+    }
+  }
+  .connectionActions {
+    float: right;
+    margin-top: 16px;
+    button {
+      margin-left: 16px;
+      max-width: 96px;
+    }
+    :global(.Dropdown-root) {
+      width: 160px;
+      float: left;
+      margin-right: 16px;
+    }
+  }
+  &:after {
+    clear: both;
+    content: " ";
+    display: block;
+    height: 0;
+  }
+}
+
+.container {
+  margin: 0 auto;
+  padding: 0;
+  max-width: $narrow-content-width;
+}
+.sidebar {
+  padding-top: 32px;
+  width: 128px;
+  float: left;
+  a {
+    display: block;
+    /* Tasks: */
+    font-weight: $weight-regular;
+    font-size: 16px;
+    color: $gray;
+    text-decoration: none;
+    margin-bottom: 16px;
+    &:global(.active) {
+      color: $blue;
+    }
+  }
+}
+.content {
+  padding-top: 32px;
+  float: left;
+  width: 800px;
+  padding-bottom: 32px;
+  .connectionTypeImg {
+    margin-bottom: 16px;
+  }
+}

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

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

+ 122 - 0
src/components/ProjectDetail/ProjectDetail.js

@@ -0,0 +1,122 @@
+/*
+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, { Component, PropTypes } from 'react';
+import withStyles from 'isomorphic-style-loader/lib/withStyles';
+import s from './ProjectDetail.scss';
+import Moment from 'react-moment';
+import TextTruncate from 'react-text-truncate';
+import LoadingIcon from '../LoadingIcon';
+
+const title = 'connection details';
+
+class CloudConnectionDetail extends Component {
+  static propTypes = {
+    projects: PropTypes.array,
+    projectId: PropTypes.string
+  }
+
+  static contextTypes = {
+    onSetTitle: PropTypes.func.isRequired,
+  };
+
+  constructor(props) {
+    super(props)
+    this.state = {
+      project: null
+    }
+  }
+
+  componentWillReceiveProps(props) {
+    let project = null
+    if (props.projects) {
+      props.projects.forEach(item => {
+        if (item.id == props.projectId) {
+          project = item
+        }
+      })
+    }
+
+    this.setState({ project: project })
+  }
+
+  componentWillMount() {
+    this.context.onSetTitle(title);
+    this.componentWillReceiveProps(this.props)
+  }
+
+  render() {
+    let item = this.state.project
+    if (item) {
+      return (
+        <div className={s.root}>
+          <div className={s.container}>
+            <div className={s.formGroup}>
+              <div className={s.title}>
+                Name
+              </div>
+              <div className={s.value}>
+                {item.name}
+              </div>
+            </div>
+            <div className={s.formGroup}>
+              <div className={s.title}>
+                Description
+              </div>
+              <div className={s.value}>
+                {item.description == "" ? "-" : item.description}
+              </div>
+            </div>
+            <div className={s.formGroup}>
+              <div className={s.title}>
+                Enabled
+              </div>
+              <div className={s.value}>
+                {item.enabled ? "Yes" : "No"}
+              </div>
+            </div>
+            <div className={s.formGroup}>
+              <div className={s.title}>
+                Is Domain
+              </div>
+              <div className={s.value}>
+                {item.is_domain ? "Yes" : "No"}
+              </div>
+            </div>
+          </div>
+          <div className={s.buttons}>
+            <div className={s.leftSide}>
+              <button onClick={(e) => this.showEditConnectionModal(e)} className="gray" disabled>Edit Project</button>
+            </div>
+            <div className={s.rightSide}>
+              <button onClick={(e) => this.deleteProject(e)} className="wire">Delete</button>
+            </div>
+          </div>
+        </div>
+      )
+    } else {
+      return (<div className={s.root}>
+        <div className={s.container}>
+          <LoadingIcon />
+        </div>
+      </div>)
+    }
+  }
+
+}
+
+export default withStyles(CloudConnectionDetail, s);

+ 79 - 0
src/components/ProjectDetail/ProjectDetail.scss

@@ -0,0 +1,79 @@
+/*
+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 '../variables.scss';
+
+.root {
+  :global(.arrow) {
+    width: 36px;
+    height: 28px;
+    margin-top: 30px;
+    margin-left: 130px;
+    position: absolute;
+  }
+}
+
+.container {
+  margin: 0 auto;
+  &:after {
+    clear: both;
+    height: 0;
+    display: block;
+    content: ' ';
+  }
+}
+.columnLeft, .columnRight {
+  width: 50%;
+  float: left;
+}
+.formGroup {
+  margin-bottom: 32px;
+  width: 50%;
+  float: left;
+  .title {
+    font-weight: $weight-semibold;
+    font-size: 10px;
+    color: $gray-dark;
+    text-transform: uppercase;
+    margin-bottom: 8px;
+  }
+  .titleIp {
+    font-weight: $weight-semibold;
+    font-size: 14px;
+    color: $gray;
+  }
+  .value {
+    font-weight: $weight-regular;
+    font-size: 14px;
+    color: $black;
+    a {
+      color: $blue;
+    }
+  }
+}
+
+.buttons {
+  button {
+    margin-bottom: 16px;
+  }
+  .leftSide {
+    float: left;
+  }
+  .rightSide {
+    float: right;
+  }
+}

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

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

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

@@ -96,7 +96,7 @@ class ProjectList extends Reflux.Component {
   }
   }
 
 
   projectDetail(e, item) {
   projectDetail(e, item) {
-    //Location.push('/project/' + item.id + "/")
+    Location.push('/project/details/' + item.id + "/")
   }
   }
 
 
   checkItem(e, itemRef) {
   checkItem(e, itemRef) {

+ 5 - 5
src/routes.js

@@ -32,6 +32,8 @@ import CloudConnectionsView from './components/CloudConnectionsView';
 import CloudConnectionDetail from './components/CloudConnectionDetail';
 import CloudConnectionDetail from './components/CloudConnectionDetail';
 import CloudConnectionAuth from './components/CloudConnectionAuth';
 import CloudConnectionAuth from './components/CloudConnectionAuth';
 import ConnectionsList from './components/ConnectionsList';
 import ConnectionsList from './components/ConnectionsList';
+import Project from './components/Project';
+import ProjectDetail from './components/ProjectDetail';
 import ProjectList from './components/ProjectList';
 import ProjectList from './components/ProjectList';
 import ReplicaExecutions from './components/ReplicaExecutions';
 import ReplicaExecutions from './components/ReplicaExecutions';
 import UserView from './components/UserView';
 import UserView from './components/UserView';
@@ -109,11 +111,9 @@ const router = new Router(on => {
   )
   )
 
 
   on('/project/details/:projectId/', async (params) =>
   on('/project/details/:projectId/', async (params) =>
-    <CloudConnection connectionId={params.params.projectId}>
-      <CloudConnectionsView type="auth">
-        <CloudConnectionAuth />
-      </CloudConnectionsView>
-    </CloudConnection>
+    <Project projectId={params.params.projectId}>
+      <ProjectDetail />
+    </Project>
   )
   )
 
 
   on('/user/profile/', async () => <UserView type="profile"><UserOverview /></UserView>)
   on('/user/profile/', async () => <UserView type="profile"><UserOverview /></UserView>)

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

@@ -36,7 +36,7 @@ class NotificationsStore extends Reflux.Store
     }
     }
   }
   }
 
 
-  onLoginSuccess(response) {
+  onLoginScopeSuccess(response) {
     let notifications = [{
     let notifications = [{
       message: "Signed in",
       message: "Signed in",
       type: 'success'
       type: 'success'
@@ -218,6 +218,13 @@ class NotificationsStore extends Reflux.Store
     this.setState({notifications: []})
     this.setState({notifications: []})
   }
   }
 
 
+  onSwitchProject() {
+    let notifications = [{
+      message: "Switching project",
+      type: 'info'
+    }]
+    this.setState({notifications: notifications})
+  }
 
 
 }
 }