jusrhee пре 5 година
родитељ
комит
d3f0a70478

+ 72 - 49
dashboard/src/main/home/dashboard/expanded-chart/ExpandedChart.tsx

@@ -8,6 +8,7 @@ import { Context } from '../../../../shared/Context';
 import TabSelector from '../../../../components/TabSelector';
 import RevisionSection from './RevisionSection';
 import ValuesYaml from './ValuesYaml';
+import OverviewSection from './OverviewSection';
 
 type PropsType = {
   currentChart: ChartType,
@@ -17,7 +18,8 @@ type PropsType = {
 
 type StateType = {
   showRevisions: boolean,
-  currentTab: string
+  currentTab: string,
+  isExpanded: boolean
 };
 
 const tabOptions = [
@@ -28,7 +30,8 @@ const tabOptions = [
 export default class ExpandedChart extends Component<PropsType, StateType> {
   state = {
     showRevisions: false,
-    currentTab: 'overview'
+    currentTab: 'overview',
+    isExpanded: false,
   }
 
   renderIcon = () => {
@@ -53,9 +56,10 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
 
     if (this.state.currentTab === 'overview') {
       return (
-        <Wrapper>
-          <Placeholder>(Under construction)</Placeholder>
-        </Wrapper>
+        <OverviewSection
+          toggleExpanded={() => this.setState({ isExpanded: !this.state.isExpanded })}
+          isExpanded={this.state.isExpanded}
+        />
       );
     }
 
@@ -67,12 +71,61 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     );
   }
 
-  render() {
+  renderInfo = () => {
     let { currentChart, setCurrentChart, refreshChart } = this.props;
     let chart = currentChart;
 
-    return ( 
-      <StyledExpandedChart>
+    if (!this.state.isExpanded) {
+      return (
+        <HeaderWrapper>
+          <TitleSection>
+            <Title>
+              <IconWrapper>
+                {this.renderIcon()}
+              </IconWrapper>
+              {chart.name}
+            </Title>
+            <InfoWrapper>
+              <StatusIndicator>
+                <StatusColor status={chart.info.status} />
+                {chart.info.status}
+              </StatusIndicator>
+
+              <LastDeployed>
+                <Dot>•</Dot>Last deployed {this.readableDate(chart.info.last_deployed)}
+              </LastDeployed>
+            </InfoWrapper>
+
+            <TagWrapper>
+              Namespace
+            <NamespaceTag>
+                {chart.namespace}
+              </NamespaceTag>
+            </TagWrapper>
+          </TitleSection>
+
+          <CloseButton onClick={() => setCurrentChart(null)}>
+            <CloseButtonImg src={close} />
+          </CloseButton>
+
+          <RevisionSection
+            showRevisions={this.state.showRevisions}
+            toggleShowRevisions={() => this.setState({ showRevisions: !this.state.showRevisions })}
+            chart={chart}
+            refreshChart={refreshChart}
+          />
+
+          <TabSelector
+            options={tabOptions}
+            setCurrentTab={(value: string) => this.setState({ currentTab: value })}
+            tabWidth='120px'
+          />
+        </HeaderWrapper>
+      );
+    }
+
+    return (
+      <HeaderWrapper>
         <TitleSection>
           <Title>
             <IconWrapper>
@@ -80,41 +133,22 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
             </IconWrapper>
             {chart.name}
           </Title>
-          <InfoWrapper>
-            <StatusIndicator>
-              <StatusColor status={chart.info.status} />
-              {chart.info.status}
-            </StatusIndicator>
-
-            <LastDeployed>
-              <Dot>•</Dot>Last deployed {this.readableDate(chart.info.last_deployed)}
-            </LastDeployed>
-          </InfoWrapper>
-
-          <TagWrapper>
-            Namespace
-            <NamespaceTag>
-              {chart.namespace}
-            </NamespaceTag>
-          </TagWrapper>
         </TitleSection>
 
         <CloseButton onClick={() => setCurrentChart(null)}>
           <CloseButtonImg src={close} />
         </CloseButton>
+      </HeaderWrapper>
+    );
+  }
 
-        <RevisionSection
-          showRevisions={this.state.showRevisions}
-          toggleShowRevisions={() => this.setState({ showRevisions: !this.state.showRevisions })}
-          chart={chart}
-          refreshChart={refreshChart}
-        />
+  render() {
+    let { currentChart, setCurrentChart, refreshChart } = this.props;
+    let chart = currentChart;
 
-        <TabSelector
-          options={tabOptions}
-          setCurrentTab={(value: string) => this.setState({ currentTab: value })}
-          tabWidth='120px'
-        />
+    return ( 
+      <StyledExpandedChart>
+        {this.renderInfo()}
         <ContentSection>
           {this.renderTabContents()}
         </ContentSection>
@@ -125,23 +159,12 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
 
 ExpandedChart.contextType = Context;
 
-const Wrapper = styled.div`
-  width: 100%;
-  height: 100%;
-  background: #ffffff11;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-`;
-
-const Placeholder = styled.div`
-  color: #ffffff66;
-  padding-bottom: 30px;
+const HeaderWrapper = styled.div`
+  margin-bottom: 20px;
 `;
 
 const ContentSection = styled.div`
   display: flex;
-  margin-top: 20px;
   border-radius: 5px;
   flex: 1;
   width: 100%;

+ 184 - 0
dashboard/src/main/home/dashboard/expanded-chart/OverviewSection.tsx

@@ -0,0 +1,184 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+import { ResourceType } from '../../../../shared/types';
+
+import ResourceItem from './ResourceItem';
+
+type PropsType = {
+  toggleExpanded: () => void,
+  isExpanded: boolean
+};
+
+type StateType = {
+  viewMode: string,
+  showKindLabels: boolean
+};
+
+const dummyObjects = [
+  {
+    kind: 'deployment', name: 'radical-conspirator', rawYaml: {
+      stuff: {
+        idk: 'test'
+      }
+    }
+  },
+  { kind: 'service', name: 'fawkes-guy', rawYaml: {
+      stuff: {
+        idk: 'test'
+      }
+    }
+  },
+  { kind: 'ingress', name: 'bellion-john', rawYaml: {
+      stuff: {
+        idk: 'test'
+      }
+    }
+  },
+  { kind: 'pod', name: 'tenet-tenet', rawYaml: {
+      stuff: {
+        idk: 'test'
+      }
+    }
+  },
+  { kind: 'statefulset', name: 'brokerage-farm', rawYaml: {
+      stuff: {
+        idk: 'test'
+      }
+    }
+  }
+];
+
+export default class OverviewSection extends Component<PropsType, StateType> {
+  state = {
+    viewMode: 'graph',
+    showKindLabels: true
+  }
+
+  renderResourceList = () => {
+    return dummyObjects.map((resource: ResourceType, i: number) => {
+      return (
+        <ResourceItem
+          key={i}
+          resource={resource}
+          toggleKindLabels={() => this.setState({ showKindLabels: !this.state.showKindLabels })}
+          showKindLabels={this.state.showKindLabels}
+        />
+      );
+    });
+  }
+
+  renderContents = () => {
+    if (this.state.viewMode === 'list') {
+      return (
+        <ResourceList>
+          {this.renderResourceList()}
+        </ResourceList>
+      )
+    }
+  }
+
+  render() {
+    return (
+      <StyledOverviewSection>
+        {this.renderContents()}
+
+        <ButtonSection>
+          <RadioButtons>
+            <RadioOption
+              nudge={true}
+              selected={this.state.viewMode === 'graph'}
+              onClick={() => this.setState({ viewMode: 'graph' })}
+            >
+              <i className="material-icons">device_hub</i> Graph
+            </RadioOption>
+            <RadioOption
+              selected={this.state.viewMode === 'list'}
+              onClick={() => this.setState({ viewMode: 'list' })}
+            >
+              <i className="material-icons">dehaze</i> List
+            </RadioOption>
+          </RadioButtons>
+          <ExpandButton
+            onClick={this.props.toggleExpanded}
+            isExpanded={this.props.isExpanded}
+          >
+            <i className="material-icons">
+              {this.props.isExpanded ? 'close_fullscreen' : 'open_in_full'}
+            </i>
+          </ExpandButton>
+        </ButtonSection>
+      </StyledOverviewSection>
+    );
+  }
+}
+
+const ResourceList = styled.div`
+  width: 100%;
+  overflow-y: auto;
+  padding-bottom: 150px;
+`;
+
+const RadioOption = styled.div`
+  width: 80px;
+  padding-right: 5px;
+  height: 22px;
+  background: ${(props: { selected: boolean, nudge?: boolean }) => props.selected ? '#ffffff44' : '#ffffff11'};
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  justify-content: center;
+  
+  > i {
+    margin-top: ${(props: { nudge?: boolean, selected: boolean }) => props.nudge ? '-1px' : ''};
+    font-size: 15px;
+    margin-right: 8px;
+  }
+`;
+
+const RadioButtons = styled.div`
+  display: flex;
+  align-items: center;
+  border-radius: 3px;
+  border: 1px solid #ffffff44;
+  font-size: 12px;
+  font-family: 'Works Sans', sans-serif;
+  overflow: hidden;
+`;
+
+const ButtonSection = styled.div`
+  position: absolute;
+  top: 17px;
+  right: 15px;
+  display: flex;
+  align-items: center;
+`;
+
+const ExpandButton = styled.div`
+  width: 24px;
+  height: 24px;
+  cursor: pointer;
+  margin-left: 10px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  border-radius: 3px;
+  border: 1px solid #ffffff44;
+  background: ${(props: { isExpanded: boolean }) => props.isExpanded ? '#ffffff44' : ''};
+
+  :hover {
+    background: #ffffff44; 
+  }
+
+  > i {
+    font-size: 14px;
+  }
+`;
+
+const StyledOverviewSection = styled.div`
+  width: 100%;
+  height: 100%;
+  background: #ffffff11;
+  display: flex;
+  position: relative;
+`;

+ 169 - 0
dashboard/src/main/home/dashboard/expanded-chart/ResourceItem.tsx

@@ -0,0 +1,169 @@
+import React, { Component } from 'react';
+import styled from 'styled-components';
+
+import { ResourceType } from '../../../../shared/types';
+import YamlEditor from '../../../../components/YamlEditor';
+
+const kindToIcon: any = {
+  'deployment': 'category',
+  'pod': 'fiber_manual_record',
+  'service': 'alt_route',
+  'ingress': 'sensor_door',
+  'statefulset': 'location_city',
+  'secret': 'vpn_key',
+}
+
+type PropsType = {
+  resource: ResourceType,
+  toggleKindLabels: () => void,
+  showKindLabels: boolean
+};
+
+type StateType = {
+  expanded: boolean,
+  rawYaml: string
+};
+
+// A single resource block in the expanded chart list view
+export default class ResourceItem extends Component<PropsType, StateType> {
+  state = {
+    expanded: false,
+    rawYaml: '# this is placeholder yaml'
+  }
+
+  renderIcon = (kind: string) => {
+
+    let icon = 'tonality';
+    if (Object.keys(kindToIcon).includes(kind)) {
+      icon = kindToIcon[kind]; 
+    }
+    
+    return (
+      <IconWrapper>
+        <i className="material-icons">{icon}</i>
+      </IconWrapper>
+    );
+  }
+
+  renderExpanded = () => {
+    if (this.state.expanded) {
+      return (
+        <ExpandWrapper>
+          <YamlEditor
+            value={this.state.rawYaml}
+            onChange={(e: any) => this.setState({ rawYaml: e })}
+            height='300px'
+          />
+        </ExpandWrapper>
+      );
+    }
+  }
+
+  render() {
+    let { resource, showKindLabels, toggleKindLabels } = this.props;
+    return (
+      <StyledResourceItem>
+        <ResourceHeader
+          expanded={this.state.expanded}
+          onClick={() => this.setState({ expanded: !this.state.expanded })}
+        >
+          <i className="material-icons">arrow_right</i>
+
+          <ClickWrapper onClick={toggleKindLabels}>
+            {this.renderIcon(resource.kind)}
+            {showKindLabels ? `${resource.kind}` : null}
+          </ClickWrapper>
+
+          <ResourceName
+            showKindLabels={showKindLabels}
+          >
+            {resource.name}
+          </ResourceName>
+        </ResourceHeader>
+        {this.renderExpanded()}
+      </StyledResourceItem>
+    );
+  }
+}
+
+const ExpandWrapper = styled.div`
+  padding: 12px;
+  animation: expandResource 0.3s;
+  animation-timing-function: ease-out;
+  overflow: hidden;
+  @keyframes expandResource {
+    from { height: 0px }
+    to { height: 300px }
+  }
+`;
+
+const StyledResourceItem = styled.div`
+  border-bottom: 1px solid #606166;
+`;
+
+const BigPlaceholder = styled.div`
+  height: 200px;
+  width: 100%;
+  background: blue;
+`;
+
+const ClickWrapper = styled.div`
+  display: flex;
+  align-items: center;
+`;
+
+const ResourceName = styled.div`
+  color: #ffffff;
+  margin-left: ${(props: { showKindLabels: boolean }) => props.showKindLabels ? '10px' : ''};
+  text-transform: none;
+`;
+
+const IconWrapper = styled.div`
+  width: 25px;
+  height: 25px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  > i {
+    font-size: 16px;
+    color: #ffffff;
+    margin-right: 14px;
+  }
+`;
+
+const ResourceHeader = styled.div`
+  width: 100%;
+  height: 60px;
+  display: flex;
+  color: #ffffff66;
+  align-items: center;
+  padding: 15px 13px;
+  text-transform: capitalize;
+  cursor: pointer;
+  background: ${(props: { expanded: boolean }) => props.expanded ? '#ffffff11' : ''};
+  :hover {
+    background: #ffffff18;
+
+    > i {
+      background: #ffffff22;
+    }
+  }
+
+  > i {
+    margin-right: 13px;
+    font-size: 20px;
+    color: #ffffff66;
+    cursor: pointer;
+    border-radius: 20px;
+    background: ${(props: { expanded: boolean }) => props.expanded ? '#ffffff18' : ''};
+    transform: ${(props: { expanded: boolean }) => props.expanded ? 'rotate(180deg)' : ''};
+    animation: ${(props: { expanded: boolean }) => props.expanded ? 'quarterTurn 0.3s' : ''};
+    animation-fill-mode: forwards;
+
+    @keyframes quarterTurn {
+      from { transform: rotate(0deg) }
+      to { transform: rotate(90deg) }
+    }
+  }
+`;

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

@@ -30,8 +30,14 @@ export interface ChartType {
   namespace: string
 }
 
+export interface ResourceType {
+  kind: string,
+  name: string,
+  rawYaml: Object
+}
+
 export enum StorageType {
   Secret = 'secret',
   ConfigMap = 'configmap',
   Memory = 'memory'
-}
+}