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

Merge branch 'beta.3.integration-frontend' into beta.3.provisioning-integration

sunguroku 5 лет назад
Родитель
Сommit
bd0ec472da

BIN
dashboard/src/assets/aws-normal.png


+ 24 - 3
dashboard/src/main/home/Home.tsx

@@ -4,7 +4,7 @@ import ReactModal from 'react-modal';
 
 import { Context } from '../../shared/Context';
 import api from '../../shared/api';
-import { ProjectType } from '../../shared/types';
+import { InfraType } from '../../shared/types';
 
 import Sidebar from './sidebar/Sidebar';
 import Dashboard from './dashboard/Dashboard';
@@ -48,11 +48,29 @@ export default class Home extends Component<PropsType, StateType> {
     let { user, currentProject, projects, setProjects } = this.context;
     api.getProjects('<token>', {}, { id: user.userId }, (err: any, res: any) => {
       if (err) {
-        console.log(err)
+        console.log(err);
       } else if (res.data) {
         setProjects(res.data);
         if (res.data.length > 0 && !currentProject) {
           this.context.setCurrentProject(res.data[0]);
+
+          // Check if current project is provisioning
+          api.getInfra('<token>', {}, { project_id: res.data[0].id }, (err: any, res: any) => {
+            if (err) {
+              console.log(err);
+            } else if (res.data) {
+              
+              // TODO: separately handle non meta-provisioning case
+              res.data.forEach((el: InfraType) => {
+                if (el.status === 'creating') {
+                  this.setState({ currentView: 'provisioner', viewData: {
+                    infra_id: el.id,
+                    kind: el.kind,
+                  }});
+                }
+              });
+            }
+          });
         } else if (res.data.length === 0) {
           this.setState({ currentView: 'new-project' });
         }
@@ -196,7 +214,10 @@ export default class Home extends Component<PropsType, StateType> {
         {this.renderSidebar()}
 
         <ViewWrapper>
-          <Navbar logOut={this.props.logOut} />
+          <Navbar 
+            logOut={this.props.logOut} 
+            currentView={this.state.currentView} // For form feedback
+          />
           {this.renderContents()}
         </ViewWrapper>
       </StyledHome>

+ 8 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -586,6 +586,14 @@ const CloseOverlay = styled.div`
   left: 0;
   width: 100%;
   height: 100%;
+  background: #202227;
+  animation: fadeIn 0.2s 0s;
+  opacity: 0;
+  animation-fill-mode: forwards;
+  @keyframes fadeIn {
+    from { opacity: 0 }
+    to { opacity: 1 }
+  }
 `;
 
 const HeaderWrapper = styled.div`

+ 255 - 0
dashboard/src/main/home/navbar/Feedback.tsx

@@ -0,0 +1,255 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+import axios from 'axios';
+import { Context } from '../../../shared/Context';
+
+type PropsType = {
+  currentView: string,
+};
+
+type StateType = {
+  feedbackSent: boolean,
+  showFeedbackDropdown: boolean,
+  feedbackText: string,
+};
+
+export default class Feedback extends Component<PropsType, StateType> {
+  state = {
+    feedbackSent: false,
+    showFeedbackDropdown: false,
+    feedbackText: '',
+  }
+
+  renderReceipt = () => {
+    if (this.state.feedbackSent) {
+      return (
+        <DropdownAlt dropdownWidth='300px' dropdownMaxHeight='200px'>
+          <ConfirmationMessage>
+            <i className="material-icons-outlined">emoji_food_beverage</i>
+            Thanks for improving Porter.
+          </ConfirmationMessage>
+        </DropdownAlt>
+      );
+    }
+  }
+
+  handleSubmitFeedback = () => {
+    let { user } = this.context;
+    let msg = '👤 ' + user.email + ' 📍 ' + this.props.currentView + ': ' + this.state.feedbackText;
+    axios.post('http://35.190.59.124/feedback', {
+      key: 'uzNP7MVYqDC7hs9Q8YP7ehvsBO4yRO02ZGYQ5rKJ2YngEqgYVBITRsvDww8CfV3q',
+      cid: '794372152769642507',
+      message: msg,
+    }, {
+      headers: {
+        Authorization: `Bearer <>`
+      }
+    })
+    .then(res => {
+      console.log('feedback sent');
+    })
+    .catch(err => {
+      console.log(err);
+    });
+    this.setState({ feedbackSent: true, feedbackText: '' });
+  }
+
+  renderFeedbackDropdown = () => {
+    if (this.state.showFeedbackDropdown) {
+      let disabled = this.state.feedbackText === '';
+      return (
+        <>
+          <CloseOverlay onClick={() => this.setState({ showFeedbackDropdown: false, feedbackSent: false })} />
+          <Dropdown 
+            feedbackSent={this.state.feedbackSent} 
+            dropdownWidth='300px' 
+            dropdownMaxHeight='200px'
+          >
+            <FeedbackInput 
+              autoFocus={true}
+              value={this.state.feedbackText}
+              onChange={(e) => this.setState({ feedbackText: e.target.value })}
+              placeholder='Help us improve this page.' 
+            />
+            <SendButton 
+              disabled={disabled} 
+              onClick={() => !disabled && this.handleSubmitFeedback()}
+            >
+              <i className="material-icons">send</i> Send
+            </SendButton>
+          </Dropdown>
+          {this.renderReceipt()}
+        </>
+      );
+    }
+  }
+
+  render() {
+    return (
+      <FeedbackButton>
+        <Flex onClick={() => this.setState({ showFeedbackDropdown: !this.state.showFeedbackDropdown })}>
+          <i className="material-icons-outlined">
+            campaign
+          </i>
+          Feedback?
+        </Flex>
+        {this.renderFeedbackDropdown()}
+      </FeedbackButton>
+    );
+  }
+}
+
+Feedback.contextType = Context;
+
+const CloseOverlay = styled.div`
+  position: fixed;
+  width: 100vw;
+  height: 100vh;
+  z-index: 100;
+  top: 0;
+  left: 0;
+  cursor: default;
+`;
+
+const ConfirmationMessage = styled.div`
+  width: 100%;
+  height: 100px;
+  display: flex;
+  font-size: 13px;
+  align-items: center;
+  justify-content: center;
+  color: #ffffff55;
+
+  > i {
+    display: flex;
+    font-size: 16px;
+    margin-right: 10px;
+    align-items: center;
+    justify-content: center;
+    color: #ffffff55;
+  }
+`;
+
+const SendButton = styled.div`
+  display: flex;
+  align-items: center;
+  height: 40px;
+  cursor: ${(props: { disabled: boolean }) => props.disabled ? 'not-allowed' : 'pointer'};
+  justify-content: center;
+  margin-top: -3px;
+  font-size: 13px;
+  font-weight: 500;
+  font-family: 'Work Sans', sans-serif;
+  :hover {
+    background: ${(props: { disabled: boolean }) => props.disabled ? '' : '#ffffff11'};
+  }
+
+  > i {
+    background: none;
+    border-radius: 3px;
+    display: flex;
+    font-size: 14px;
+    top: 11px;
+    margin-right: 10px;
+    padding: 1px;
+    align-items: center;
+    justify-content: center;
+    color: #ffffffaa;
+  }
+`;
+
+const FeedbackInput = styled.textarea`
+  resize: none;
+  width: 100%;
+  height: 80px;
+  outline: 0;
+  padding: 14px;
+  color: white;
+  border: 0;
+  font-size: 13px;
+  font-family: 'Work Sans', sans-serif;
+  background: #aaaabb11;
+`;
+
+const Flex = styled.div`
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+`;
+
+const Dropdown = styled.div`
+  position: absolute;
+  right: 0;
+  top: calc(100% + 5px);
+  background: #26282f;
+  width: ${(props: { dropdownWidth: string, dropdownMaxHeight: string, feedbackSent?: boolean }) => props.dropdownWidth};
+  max-height: ${(props: { dropdownWidth: string, dropdownMaxHeight: string, feedbackSent?: boolean }) => props.dropdownMaxHeight ? props.dropdownMaxHeight : '300px'};
+  border-radius: 3px;
+  z-index: 999;
+  overflow-y: auto;
+  margin-bottom: 20px;
+  box-shadow: 0 8px 20px 0px #00000088;
+  animation: ${(props: { dropdownWidth: string, dropdownMaxHeight: string, feedbackSent?: boolean }) => props.feedbackSent ? 'flyOff 0.3s 0.05s' : ''};
+  animation-fill-mode: forwards;
+  @keyframes flyOff {
+    from {
+      opacity: 1; transform: translateX(0px);
+    }
+    to {
+      opacity: 0; transform: translateX(100px);
+    }
+  }
+`;
+
+const DropdownAlt = styled(Dropdown)`
+  animation: fadeIn 0.3s 0.5s;
+  opacity: 0;
+  animation-fill-mode: forwards;
+  @keyframes fadeIn {
+    from { opacity: 0 }
+    to { opacity: 1 }
+  }
+`;
+
+const NavButton = styled.a`
+  display: flex;
+  position: relative;
+  align-items: center;
+  justify-content: center;
+  margin-right: 15px;
+  :hover {
+    > i {
+      color: #ffffff;
+    }
+  }
+  
+  > i {
+    cursor: pointer;
+    color: ${(props: { selected?: boolean }) => props.selected ? '#ffffff' : '#ffffff88'};
+    font-size: 24px;
+  }
+`;
+
+const FeedbackButton = styled(NavButton)`
+  color: ${(props: { selected?: boolean }) => props.selected ? '#ffffff' : '#ffffff88'};
+  font-family: 'Work Sans', sans-serif;
+  font-size: 14px;
+  margin-right: 20px;
+  :hover {
+    color: #ffffff;
+    > div {
+      > i {
+        color: #ffffff;
+      }
+    }
+  }
+
+  > div {
+    > i {
+      color: ${(props: { selected?: boolean }) => props.selected ? '#ffffff' : '#ffffff88'};
+      font-size: 26px;
+      margin-right: 6px;
+    }
+  }
+`;

+ 4 - 109
dashboard/src/main/home/navbar/Navbar.tsx

@@ -4,21 +4,20 @@ import styled from 'styled-components';
 import api from '../../../shared/api';
 import { Context } from '../../../shared/Context';
 
+import Feedback from './Feedback';
+
 type PropsType = {
   logOut: () => void,
+  currentView: string,
 };
 
 type StateType = {
   showDropdown: boolean,
-  showFeedbackDropdown: boolean,
-  feedbackSent: boolean,
 };
 
 export default class Navbar extends Component<PropsType, StateType> {
   state = {
     showDropdown: false,
-    showFeedbackDropdown: false,
-    feedbackSent: false,
   }
 
   handleLogout = (): void => {
@@ -47,48 +46,10 @@ export default class Navbar extends Component<PropsType, StateType> {
     }
   }
 
-  renderConfirmation = () => {
-    if (this.state.feedbackSent) {
-      return (
-        <DropdownAlt dropdownWidth='300px' dropdownMaxHeight='200px'>
-          <ConfirmationMessage>
-            <i className="material-icons-outlined">emoji_food_beverage</i>
-            Thanks for improving Porter.
-          </ConfirmationMessage>
-        </DropdownAlt>
-      );
-    }
-  }
-
-  renderFeedbackDropdown = () => {
-    if (this.state.showFeedbackDropdown) {
-      return (
-        <>
-          <CloseOverlay onClick={() => this.setState({ showFeedbackDropdown: false, feedbackSent: false })} />
-          <Dropdown feedbackSent={this.state.feedbackSent} dropdownWidth='300px' dropdownMaxHeight='200px'>
-            <FeedbackInput placeholder='Help us improve this page.' />
-            <SendButton onClick={() => this.setState({ feedbackSent: true })}>
-              <i className="material-icons">send</i> Send
-            </SendButton>
-          </Dropdown>
-          {this.renderConfirmation()}
-        </>
-      );
-    }
-  }
-
   render() {
     return (
       <StyledNavbar>
-        <FeedbackButton>
-          <Flex onClick={() => this.setState({ showFeedbackDropdown: !this.state.showFeedbackDropdown })}>
-            <i className="material-icons-outlined">
-              campaign
-            </i>
-            Feedback?
-          </Flex>
-          {this.renderFeedbackDropdown()}
-        </FeedbackButton>
+        <Feedback currentView={this.props.currentView} />
         <NavButton selected={this.state.showDropdown}>
           <i 
             className="material-icons-outlined" 
@@ -105,72 +66,6 @@ export default class Navbar extends Component<PropsType, StateType> {
 
 Navbar.contextType = Context;
 
-const ConfirmationMessage = styled.div`
-  width: 100%;
-  height: 100px;
-  display: flex;
-  font-size: 13px;
-  align-items: center;
-  justify-content: center;
-  color: #ffffff55;
-
-  > i {
-    display: flex;
-    font-size: 16px;
-    margin-right: 10px;
-    align-items: center;
-    justify-content: center;
-    color: #ffffff55;
-  }
-`;
-
-const SendButton = styled.div`
-  display: flex;
-  align-items: center;
-  height: 40px;
-  cursor: pointer;
-  justify-content: center;
-  margin-top: -3px;
-  font-size: 13px;
-  font-weight: 500;
-  font-family: 'Work Sans', sans-serif;
-  :hover {
-    background: #ffffff11;
-  }
-
-  > i {
-    background: none;
-    border-radius: 3px;
-    display: flex;
-    font-size: 14px;
-    top: 11px;
-    margin-right: 10px;
-    padding: 1px;
-    align-items: center;
-    justify-content: center;
-    color: #ffffffaa;
-  }
-`;
-
-const FeedbackInput = styled.textarea`
-  resize: none;
-  width: 100%;
-  height: 80px;
-  outline: 0;
-  padding: 14px;
-  color: white;
-  border: 0;
-  font-size: 13px;
-  font-family: 'Work Sans', sans-serif;
-  background: #aaaabb11;
-`;
-
-const Flex = styled.div`
-  display: flex;
-  align-items: center;
-  cursor: pointer;
-`;
-
 const CloseOverlay = styled.div`
   position: fixed;
   width: 100vw;

+ 11 - 2
dashboard/src/main/home/templates/expanded-template/LaunchTemplate.tsx

@@ -61,8 +61,17 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
       _.set(values, key, rawValues[key]);
     }
 
-    _.set(values, "image.repository", this.state.selectedImageUrl)
-    _.set(values, "image.tag", this.state.selectedTag)
+    let imageUrl = this.state.selectedImageUrl;
+    let tag = this.state.selectedTag;
+
+    if (this.state.selectedImageUrl.includes(':')) {
+      let splits = this.state.selectedImageUrl.split(':');
+      imageUrl = splits[0];
+      tag = splits[1];
+    }
+
+    _.set(values, "image.repository", imageUrl)
+    _.set(values, "image.tag", tag)
 
     api.deployTemplate('<token>', {
       templateName: this.props.currentTemplate.name,

+ 8 - 0
dashboard/src/shared/api.tsx

@@ -269,8 +269,16 @@ const getGitRepos = baseApi<{
   return `/api/projects/${pathParams.project_id}/gitrepos`;
 });
 
+const getInfra = baseApi<{
+}, {
+  project_id: number,
+}>('GET', pathParams => {
+  return `/api/projects/${pathParams.project_id}/infra`;
+});
+
 // Bundle export to allow default api import (api.<method> is more readable)
 export default {
+  getInfra,
   linkGithubProject,
   getGitRepos,
   checkAuth,

+ 7 - 0
dashboard/src/shared/types.tsx

@@ -140,4 +140,11 @@ export interface ImageType {
   source: string,
   registryId: number,
   name: string,
+}
+
+export interface InfraType {
+  id: number,
+  project_d: number,
+  kind: string,
+  status: string,
 }