jusrhee 5 лет назад
Родитель
Сommit
38c7a44f58

+ 94 - 0
dashboard/src/main/home/Toolbar.tsx

@@ -0,0 +1,94 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+import ReactModal from 'react-modal';
+
+import { Context } from '../../shared/Context';
+
+import Sidebar from './sidebar/Sidebar';
+import ClusterConfigModal from './modals/ClusterConfigModal';
+
+type PropsType = {
+  logOut: () => void
+};
+
+type StateType = {
+};
+
+export default class Home extends Component<PropsType, StateType> {
+  render() {
+    return (
+      <StyledHome>
+        <ReactModal
+          isOpen={this.context.currentModal === 'ClusterConfigModal'}
+          onRequestClose={() => this.context.setCurrentModal(null)}
+          style={MediumModalStyles}
+          ariaHideApp={false}
+        >
+          <ClusterConfigModal />
+        </ReactModal>
+
+        <Sidebar logOut={this.props.logOut} />
+        <DummyDashboard>
+          🏗️🏗️🏗️🏗️🏗️
+        </DummyDashboard>
+      </StyledHome>
+    );
+  }
+}
+
+Home.contextType = Context;
+
+const MediumModalStyles = {
+  overlay: {
+    backgroundColor: 'rgba(0,0,0,0.6)',
+    zIndex: 2,
+  },
+  content: {
+    borderRadius: '7px',
+    border: 0,
+    width: '760px',
+    maxWidth: '80vw',
+    margin: '0 auto',
+    height: '575px',
+    top: 'calc(50% - 289px)',
+    backgroundColor: '#24272a',
+    animation: 'floatInModal 0.5s 0s',
+    overflow: 'visible',
+  },
+};
+
+const DummyDashboard = styled.div`
+  height: 100%;
+  width: 100vw;
+  font-family: 'Work Sans', sans-serif;
+  overflow-y: auto;
+  display: flex;
+  letter-spacing: 10px;
+  flex: 1;
+  justify-content: center;
+  padding-bottom: 30px;
+  align-items: center;
+  background: ${props => props.theme.bg};
+  position: relative;
+`;
+
+const StyledHome = styled.div`
+  width: 100vw;
+  height: 100vh;
+  position: fixed;
+  top: 0;
+  left: 0;
+  margin: 0;
+  user-select: none;
+  display: flex;
+  justify-content: center;
+
+  @keyframes floatInModal {
+    from {
+      opacity: 0; transform: translateY(30px);
+    }
+    to {
+      opacity: 1; transform: translateY(0px);
+    }
+  }
+`;

+ 302 - 0
dashboard/src/main/home/dashboard/Dashboard.tsx

@@ -0,0 +1,302 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+import Grad from '../../assets/grad.jpg';
+import ServiceFunction from './ServiceFunction';
+import StatusSummary from './StatusSummary';
+import Analytics from './Analytics';
+import { Context } from '../../Context';
+
+import Loading from '../../Loading';
+
+class Dashboard extends Component {
+  state = {
+    demo: false,
+  }
+
+  renderStatusSummary = () => {
+    return (
+        <StatusSummary 
+          resources={this.context.activeProject.resources}
+          namespace={this.context.activeProject.namespace}
+        />          
+    );
+  }
+
+  render() {
+    if (!this.context.activeProject || !this.context.activeProject.namespace) {
+      return (
+        <StyledDashboard>
+          <DashboardWrapper>
+            <Loading fixed={true} />
+          </DashboardWrapper>
+        </StyledDashboard>
+      );
+    }
+    return ( 
+      <StyledDashboard>
+        <DashboardWrapper>
+        <TitleSection demo={this.state.demo}>
+          <ProjectIcon>
+            <ProjectImage src={Grad} />
+            <Overlay>{this.context.activeProject && this.context.activeProject.name[0].toUpperCase()}</Overlay>
+          </ProjectIcon>
+          <Title>{this.context.activeProject && this.context.activeProject.name}</Title>
+          <i 
+            className="material-icons" 
+            onClick={() => { if (!this.state.demo) { this.context.setCurrentModal('UpdateProjectModal') }}}
+          >
+            more_vert
+          </i>
+        </TitleSection>
+
+        <InfoSection>
+          <TopRow>
+            <InfoLabel onClick={()=>this.setState({ info: !this.state.info })}>
+              <i className="material-icons">info</i> Info
+            </InfoLabel>
+            {this.renderStatusSummary()}
+          </TopRow>
+          <Description>{this.context.activeProject && this.context.activeProject.description}</Description>
+        </InfoSection>
+
+        <LineBreak />
+
+        <ServiceSection>
+          <ButtonWrap>
+            <Button onClick={() => {this.context.setCurrentModal('CreateService')}}>
+              <i className="material-icons">add</i>
+              Add a Container
+            </Button>
+            <ConfigButtonAlt onClick={() => {this.context.setCurrentModal('UpdateConfig')}}>
+              <i className="material-icons">add</i>
+              Update ConfigMaps
+            </ConfigButtonAlt>
+          </ButtonWrap>
+
+          <ServiceFunction 
+            namespace={this.context.activeProject.namespace}
+            app={this.context}
+          />
+        </ServiceSection>
+
+        </DashboardWrapper>
+        <Banner>You are currently on your 7 day free trial.</Banner>
+      </StyledDashboard>
+    );
+  }
+}
+
+Dashboard.contextType = Context;
+export default Dashboard;
+
+const Banner = styled.div`
+  position: fixed;
+  bottom: 0;
+  display: flex;
+  left: 0;
+  padding-left: 200px;
+  width: calc(100%);
+  align-items: center;
+  justify-content: center;
+  height: 30px;
+  background: #616FEEcc;
+  color: white;
+  z-index: 1;
+`;
+
+const TopRow = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const Description = styled.div`
+  color: ${props => props.theme.font};
+  margin-top: 13px;
+  font-size: 13px;
+`;
+
+const InfoLabel = styled.div`
+  width: 72px;
+  height: 20px;
+  display: flex;
+  align-items: center;
+  color: #7A838F;
+  font-size: 13px;
+  > i {
+    color: #8B949F;
+    font-size: 18px;
+    margin-right: 5px;
+  }
+`;
+
+const InfoSection = styled.div`
+  margin-top: 20px;
+  font-family: 'Work Sans', sans-serif;
+  margin-left: 7px;
+  margin-bottom: 35px;
+`;
+
+const ButtonWrap = styled.div`
+  display: flex;
+  align-items: center;
+  font-size: 18px;
+  margin-top: 2px;
+  margin-bottom: 25px;
+  color: #00000020;
+`;
+
+const Button = styled.div`
+  min-width: 145px;
+  max-width: 145px;
+  display: flex;
+  flex: 1;
+  flex-direction: row;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 13px;
+  cursor: pointer;
+  font-family: 'Work Sans', sans-serif;
+  margin-left: 5px;
+  border-radius: 20px;
+  color: white;
+  padding: 6px 8px;
+  margin-right: 10px;
+  padding-right: 13px;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+
+  background: #616FEEcc;
+  :hover {
+    background: #505edddd;
+  }
+
+  > i {
+    color: white;
+    width: 18px;
+    height: 18px;
+    font-size: 12px;
+    border-radius: 20px;
+    display: flex;
+    align-items: center;
+    margin-top: -1px;
+    justify-content: center;
+  }
+`;
+
+const ButtonStack = styled(Button)`
+  min-width: 119px;
+  max-width: 119px;
+  background: #616FEEcc;
+  :hover {
+    background: #505edddd;
+  }
+`;
+
+const ButtonAlt = styled(Button)`
+  min-width: 150px;
+  max-width: 150px;
+  background: #7A838Fdd;
+
+  :hover {
+    background: #69727eee;
+  }
+`;
+
+const ConfigButtonAlt = styled(ButtonAlt)`
+  min-width: 166px;
+  max-width: 166px;
+`;
+
+const LineBreak = styled.div`
+  width: calc(100% - 180px);
+  height: 2px;
+  background: ${props => props.theme.lineBreak};
+  margin: 10px 80px 35px;
+`;
+
+const ServiceSection = styled.div`
+  padding-bottom: 150px;
+`;
+
+const Overlay = styled.div`
+  height: 100%;
+  width: 100%;
+  position: absolute;
+  background: #00000011;
+  top: 0;
+  left: 0;
+  border-radius: 5px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 24px;
+  font-weight: 500;
+  font-family: 'Work Sans', sans-serif;
+  color: white;
+`;
+
+const ProjectImage = styled.img`
+  height: 45px;
+  width: 45px;
+  border-radius: 5px;
+`;
+
+const ProjectIcon = styled.div`
+  position: relative;
+  height: 45px;
+  width: 45px;
+  border-radius: 5px;
+`;
+
+const Title = styled.div`
+  font-size: 20px;
+  font-weight: 500;
+  font-family: 'Work Sans', sans-serif;
+  margin-left: 20px;
+  color: ${props => props.theme.dashboardTitle};
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+`;
+
+const TitleSection = styled.div`
+  height: 80px;
+  margin-bottom: 10px;
+  display: flex;
+  flex-direction: row;
+  align-items: center;
+  padding-left: 17px;
+
+  > i {
+    margin-left: 8px;
+    cursor: ${props => props.demo ? 'not-allowed' : 'pointer'};
+    font-size 18px;
+    color: #858FAAaa;
+    padding: 5px;
+    border-radius: 100px;
+    :hover {
+      background: ${props => props.theme.shade};
+    }
+    margin-bottom: -3px;
+  }
+`;
+
+const StyledDashboard = styled.div`
+  height: 100%;
+  width: 100vw;
+  padding-top: 80px;
+  overflow-y: auto;
+  display: flex;
+  flex: 1;
+  justify-content: center;
+  background: ${props => props.theme.bg};
+  position: relative;
+`;
+
+const DashboardWrapper = styled.div`
+  width: 80%;
+  min-width: 300px;
+  padding-bottom: 120px;
+`;

+ 68 - 0
dashboard/src/main/home/dashboard/StatusSummary.tsx

@@ -0,0 +1,68 @@
+import React, { Component, useState, useContext } from 'react';
+import styled from 'styled-components';
+import { useQuery, useSubscription } from '@apollo/client'; 
+import helpers from '../../helpers';
+
+import { SUBSCRIBE_TO_RESOURCE_UPDATES } from '../../queries';
+
+function StatusSummary(props) {
+    let namespace = props.namespace;
+    let resources = props.resources;
+
+    const { ...resourcesSub } = useSubscription(SUBSCRIBE_TO_RESOURCE_UPDATES, {
+        variables: { namespace: namespace }
+    });
+    
+    let cpuUsed = helpers.cpuParser(resources.used['limits.cpu']);
+    let cpuHard = helpers.cpuParser(resources.hard['limits.cpu']);
+    
+    let memUsed = `${helpers.memoryParser(resources.used['limits.memory']) / (1024 ** 2)}Mi`;
+    let memHard = `${helpers.memoryParser(resources.hard['limits.memory']) / (1024 ** 2)}Mi`;
+    
+    if (resourcesSub.data) {
+      cpuUsed = parseFloat(cpuUsed) + parseFloat(resourcesSub.data.resourceUpdates.cpu);
+      memUsed = `${(helpers.memoryParser(resources.used['limits.memory']) + parseFloat(resourcesSub.data.resourceUpdates.memory)) / (1024 ** 2)}Mi`;
+    }
+
+    cpuUsed = Number((cpuUsed).toFixed(2))
+
+    return (
+        <StatusSection>
+        <Indicator /> 
+        <Status> 
+        {`${resources.used.pods ? resources.used.pods : '?'}/${resources.hard.pods ? resources.hard.pods : '?'}`} containers created 
+        </Status>
+        <Indicator /> 
+        <Status> 
+        {`${memUsed}/${memHard}`} RAM reserved 
+        </Status>
+        <Indicator /> 
+        <Status> 
+        {`${cpuUsed}/${cpuHard}`} vCPUs reserved 
+        </Status>    
+        </StatusSection>
+    )
+}
+
+export default StatusSummary;
+
+const StatusSection = styled.div`
+  font-size: 13px;
+  color: ${props => props.theme.statusSummary};
+  display: flex;
+  margin-left: 10px;
+  align-items: center;
+`;
+
+const Indicator = styled.div`
+  min-width: 9px;
+  min-height: 9px;
+  background: ${props => props.theme.indicator};
+  border-radius: 10px;
+  margin-right: 10px;
+`;
+
+const Status = styled.span`
+  font-size: 13px;
+  margin-right: 24px; 
+`

+ 14 - 5
dashboard/src/main/home/sidebar/Sidebar.tsx

@@ -117,9 +117,14 @@ export default class Sidebar extends Component<PropsType, StateType> {
             releaseDrawer={() => this.setState({ forceCloseDrawer: false })}
           />
 
-          <LogOutButton onClick={this.handleLogout}>
-            Log Out <i className="material-icons">keyboard_return</i>
-          </LogOutButton>
+          <BottomSection>
+            <LogOutButton onClick={this.handleLogout}>
+              <i className="material-icons">keyboard_return</i> DevOps Mode
+            </LogOutButton>
+            <LogOutButton onClick={this.handleLogout}>
+              Log Out <i className="material-icons">keyboard_return</i>
+            </LogOutButton>
+          </BottomSection>
         </StyledSidebar>
       </div>
     );
@@ -160,10 +165,14 @@ const NavButton = styled.div`
   }
 `;
 
-const LogOutButton = styled(NavButton)`
+const BottomSection = styled.div`
   position: absolute;
-  width: calc(100% - 55px); 
+  width: 100%;
   bottom: 12px;
+`;
+
+const LogOutButton = styled(NavButton)`
+  width: calc(100% - 55px); 
   border-top-right-radius: 3px;
   border-bottom-right-radius: 3px;
   margin-left: -1px;

+ 4 - 0
dashboard/src/shared/Context.tsx

@@ -41,6 +41,10 @@ class ContextProvider extends Component {
     setUserId: (userId: number): void => {
       this.setState({ userId });
     },
+    devOpsMode: true,
+    setDevOpsMode: (devOpsMode: boolean): void => {
+      this.setState({ devOpsMode });
+    }
   };
 
   componentDidMount() {