فهرست منبع

metrics frontend boilerplate

jusrhee 5 سال پیش
والد
کامیت
27b47a9ee2

+ 46 - 0
dashboard/package-lock.json

@@ -435,6 +435,11 @@
       "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.11.tgz",
       "integrity": "sha512-2koNhpWm3DgWRp5tpkiJ8JGc1xTn2q0l+jUNUE7oMKXUf5NpI9AIdC4kbjGNFBdHtcxBD18LAksoudAVhFKCjw=="
     },
+    "@types/d3-array": {
+      "version": "2.9.0",
+      "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-2.9.0.tgz",
+      "integrity": "sha512-sdBMGfNvLUkBypPMEhOcKcblTQfgHbqbYrUqRE31jOwdDHBJBxz4co2MDAq93S4Cp++phk4UiwoEg/1hK3xXAQ=="
+    },
     "@types/d3-color": {
       "version": "1.4.1",
       "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-1.4.1.tgz",
@@ -453,6 +458,11 @@
       "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-1.0.9.tgz",
       "integrity": "sha512-NaIeSIBiFgSC6IGUBjZWcscUJEq7vpVu7KthHN8eieTV9d9MqkSOZLH4chq1PmcKy06PNe3axLeKmRIyxJ+PZQ=="
     },
+    "@types/d3-random": {
+      "version": "1.1.3",
+      "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-1.1.3.tgz",
+      "integrity": "sha512-XXR+ZbFCoOd4peXSMYJzwk0/elP37WWAzS/DG+90eilzVbUSsgKhBcWqylGWe+lA2ubgr7afWAOBaBxRgMUrBQ=="
+    },
     "@types/d3-scale": {
       "version": "3.2.2",
       "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-3.2.2.tgz",
@@ -474,6 +484,11 @@
       "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-1.1.1.tgz",
       "integrity": "sha512-ULX7LoqXTCYtM+tLYOaeAJK7IwCT+4Gxlm2MaH0ErKLi07R5lh8NHCAyWcDkCCmx1AfRcBEV6H9QE9R25uP7jw=="
     },
+    "@types/d3-time-format": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-3.0.0.tgz",
+      "integrity": "sha512-UpLg1mn/8PLyjr+J/JwdQJM/GzysMvv2CS8y+WYAL5K0+wbvXv/pPSLEfdNaprCZsGcXTxPsFMy8QtkYv9ueew=="
+    },
     "@types/glob": {
       "version": "7.1.3",
       "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.3.tgz",
@@ -949,11 +964,32 @@
         "prop-types": "^15.6.2"
       }
     },
+    "@visx/mock-data": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@visx/mock-data/-/mock-data-1.0.0.tgz",
+      "integrity": "sha512-yd4lult1oEpmbj7pxzNb398VW+fRYaZWBFFKcqJEibAxlko4kmBfVqFR2gPZTp/K7I0/5mvD3hhNr1NpH2SIHA==",
+      "requires": {
+        "@types/d3-random": "^1.1.2",
+        "d3-random": "^1.0.3"
+      }
+    },
     "@visx/point": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/@visx/point/-/point-1.0.0.tgz",
       "integrity": "sha512-0L3ILwv6ro0DsQVbA1lo8fo6q3wvIeSTt9C8NarUUkoTNSFZaJtlmvwg2238r8fwwmSv0v9QFBj1hBz4o0bHrg=="
     },
+    "@visx/responsive": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-1.3.0.tgz",
+      "integrity": "sha512-RMfpjdnHKyhB/bb2i6x/vfQeYzfz+pJc3VUK+dP88lXXTkqv1O/NYIkXk2sWk6QhDw5muChHFmnZ1L8TnIOMXg==",
+      "requires": {
+        "@types/lodash": "^4.14.146",
+        "@types/react": "*",
+        "lodash": "^4.17.10",
+        "prop-types": "^15.6.1",
+        "resize-observer-polyfill": "1.5.1"
+      }
+    },
     "@visx/scale": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-1.4.0.tgz",
@@ -2428,6 +2464,11 @@
       "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz",
       "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg=="
     },
+    "d3-random": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz",
+      "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ=="
+    },
     "d3-scale": {
       "version": "3.2.3",
       "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-3.2.3.tgz",
@@ -6266,6 +6307,11 @@
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
       "dev": true
     },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg=="
+    },
     "resolve-cwd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",

+ 6 - 0
dashboard/package.json

@@ -5,6 +5,8 @@
   "dependencies": {
     "@fullstory/browser": "^1.4.5",
     "@material-ui/core": "^4.11.3",
+    "@types/d3-array": "^2.9.0",
+    "@types/d3-time-format": "^3.0.0",
     "@types/js-yaml": "^3.12.5",
     "@types/lodash": "^4.14.165",
     "@types/markdown-to-jsx": "^6.11.3",
@@ -15,11 +17,15 @@
     "@visx/event": "^1.3.0",
     "@visx/gradient": "^1.0.0",
     "@visx/grid": "^1.4.0",
+    "@visx/mock-data": "^1.0.0",
+    "@visx/responsive": "^1.3.0",
     "@visx/scale": "^1.4.0",
     "@visx/shape": "^1.4.0",
     "@visx/tooltip": "^1.3.0",
     "ace-builds": "^1.4.12",
     "axios": "^0.20.0",
+    "d3-array": "^2.11.0",
+    "d3-time-format": "^3.0.0",
     "dotenv": "^8.2.0",
     "ini": ">=1.3.6",
     "js-base64": "^3.6.0",

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

@@ -368,7 +368,7 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     // Append universal tabs
     tabOptions.push(
       { label: "Status", value: "status" },
-      // { label: "Metrics", value: "metrics" },
+      { label: "Metrics", value: "metrics" },
       { label: "Chart Overview", value: "graph" }
     );
 

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/RevisionSection.tsx

@@ -222,7 +222,7 @@ export default class RevisionSection extends Component<PropsType, StateType> {
             ? `Current Revision`
             : `Previewing Revision (Not Deployed)`}{" "}
           - <Revision>No. {this.props.chart.version}</Revision>
-          <i className="material-icons">expand_more</i>
+          <i className="material-icons">arrow_drop_down</i>
         </RevisionHeader>
 
         <RevisionList>{this.renderExpanded()}</RevisionList>

+ 18 - 30
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/AreaChart.tsx

@@ -1,4 +1,3 @@
-/*
 import React, { useMemo, useCallback } from 'react';
 import { AreaClosed, Line, Bar } from '@visx/shape';
 import appleStock, { AppleStock } from '@visx/mock-data/lib/mocks/appleStock';
@@ -12,13 +11,18 @@ import { LinearGradient } from '@visx/gradient';
 import { max, extent, bisector } from 'd3-array';
 import { timeFormat } from 'd3-time-format';
 
+/*
+export const accentColor = '#f5cb42';
+export const accentColorDark = '#949eff';
+*/
+
 type TooltipData = AppleStock;
 
 const stock = appleStock.slice(800);
-export const background = '#3b6978';
-export const background2 = '#204051';
-export const accentColor = '#edffea';
-export const accentColorDark = '#75daad';
+export const background = '#3b697800';
+export const background2 = '#20405100';
+export const accentColor = '#949eff';
+export const accentColorDark = '#949eff';
 const tooltipStyles = {
   ...defaultStyles,
   background,
@@ -55,7 +59,7 @@ export default withTooltip<AreaProps, TooltipData>(
 
     // bounds
     const innerWidth = width - margin.left - margin.right;
-    const innerHeight = height - margin.top - margin.bottom;
+    const innerHeight = height - margin.top - margin.bottom - 20;
 
     // scales
     const dateScale = useMemo(
@@ -109,25 +113,7 @@ export default withTooltip<AreaProps, TooltipData>(
             rx={14}
           />
           <LinearGradient id="area-background-gradient" from={background} to={background2} />
-          <LinearGradient id="area-gradient" from={accentColor} to={accentColor} toOpacity={0.1} />
-          <GridRows
-            left={margin.left}
-            scale={stockValueScale}
-            width={innerWidth}
-            strokeDasharray="1,3"
-            stroke={accentColor}
-            strokeOpacity={0}
-            pointerEvents="none"
-          />
-          <GridColumns
-            top={margin.top}
-            scale={dateScale}
-            height={innerHeight}
-            strokeDasharray="1,3"
-            stroke={accentColor}
-            strokeOpacity={0.2}
-            pointerEvents="none"
-          />
+          <LinearGradient id="area-gradient" from={accentColor} to={accentColor} toOpacity={0} />
           <AreaClosed<AppleStock>
             data={stock}
             x={d => dateScale(getDate(d)) ?? 0}
@@ -194,13 +180,16 @@ export default withTooltip<AreaProps, TooltipData>(
               {`$${getStockValue(tooltipData)}`}
             </TooltipWithBounds>
             <Tooltip
-              top={innerHeight + margin.top - 14}
+              top={-10}
               left={tooltipLeft}
               style={{
                 ...defaultStyles,
-                minWidth: 72,
+                background: '#26272f',
+                color: '#aaaabb',
+                width: 100,
+                paddingTop: 35,
                 textAlign: 'center',
-                transform: 'translateX(-50%)',
+                transform: 'translateX(-60px)',
               }}
             >
               {formatDate(getDate(tooltipData))}
@@ -210,5 +199,4 @@ export default withTooltip<AreaProps, TooltipData>(
       </div>
     );
   },
-);
-*/
+);

+ 153 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/metrics/MetricsSection.tsx

@@ -1,25 +1,95 @@
 import React, { Component } from "react";
 import styled from "styled-components";
+import ParentSize from '@visx/responsive/lib/components/ParentSize';
 
 import { Context } from "shared/Context";
 import { ChartType } from "shared/types";
 
-import Loading from "components/Loading";
+import TabSelector from "components/TabSelector";
+import AreaChart from "./AreaChart";
 
 type PropsType = {
   currentChart: ChartType;
 };
 
 type StateType = {
+  selectedRange: string,
+  selectedMetricLabel: string,
+  dropdownExpanded: boolean,
 };
 
 export default class ListSection extends Component<PropsType, StateType> {
   state = {
+    selectedRange: '1H',
+    selectedMetricLabel: 'CPU Utilization',
+    dropdownExpanded: false,
   }
 
+  renderDropdown = () => {
+    if (this.state.dropdownExpanded) {
+      return (
+        <>
+          <DropdownOverlay onClick={() => this.setState({ dropdownExpanded: false })} />
+          <Dropdown
+            dropdownWidth='200px'
+            dropdownMaxHeight='200px'
+            onClick={() => this.setState({ dropdownExpanded: false })}
+          >
+            {this.renderOptionList()}
+          </Dropdown>
+        </>
+      );
+    }
+  };
+
+  renderOptionList = () => {
+    let metricOptions = [
+      { value: 'cpu', label: 'CPU Utilization' },
+      { value: 'ram', label: 'RAM Utilization' },
+    ];
+    return metricOptions.map(
+      (option: { value: string; label: string }, i: number) => {
+        return (
+          <Option
+            key={i}
+            selected={option.label === this.state.selectedMetricLabel}
+            onClick={() => this.setState({ selectedMetricLabel: option.label })}
+            lastItem={i === metricOptions.length - 1}
+          >
+            {option.label}
+          </Option>
+        );
+      }
+    );
+  };
+
   render() {
     return (
       <StyledMetricsSection>
+        <ParentSize>
+          {({ width, height }) => <AreaChart width={width} height={height} />}
+        </ParentSize>
+        <MetricSelector 
+          onClick={() => this.setState({ dropdownExpanded: !this.state.dropdownExpanded })}
+        >
+          {this.state.selectedMetricLabel}
+          <i className="material-icons">arrow_drop_down</i>
+          {this.renderDropdown()}
+        </MetricSelector>
+        <RangeWrapper>
+          <TabSelector
+            options={[
+              { value: '1H', label: '1H' }, 
+              { value: '1D', label: '1D' },
+              { value: '1M', label: '1M' }, 
+              { value: '3M', label: '3M' },
+              { value: '1Y', label: '1Y' }, 
+              { value: 'ALL', label: 'ALL' },
+            ]}
+            currentTab={this.state.selectedRange}
+            setCurrentTab={(x: string) => this.setState({ selectedRange: x })}
+          />
+        </RangeWrapper>
       </StyledMetricsSection>
     );
   }
@@ -27,6 +97,88 @@ export default class ListSection extends Component<PropsType, StateType> {
 
 ListSection.contextType = Context;
 
+const DropdownOverlay = styled.div`
+  position: fixed;
+  width: 100%;
+  height: 100%;
+  z-index: 10;
+  left: 0px;
+  top: 0px;
+  cursor: default;
+`;
+
+const Option = styled.div`
+  width: 100%;
+  border-top: 1px solid #00000000;
+  border-bottom: 1px solid
+    ${(props: { selected: boolean; lastItem: boolean }) =>
+      props.lastItem ? "#ffffff00" : "#ffffff15"};
+  height: 37px;
+  font-size: 13px;
+  padding-top: 9px;
+  align-items: center;
+  padding-left: 15px;
+  cursor: pointer;
+  padding-right: 10px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  background: ${(props: { selected: boolean; lastItem: boolean }) =>
+    props.selected ? "#ffffff11" : ""};
+
+  :hover {
+    background: #ffffff22;
+  }
+`;
+
+const Dropdown = styled.div`
+  position: absolute;
+  left: 0;
+  top: calc(100% + 10px);
+  background: #26282f;
+  width: ${(props: { dropdownWidth: string; dropdownMaxHeight: string }) =>
+    props.dropdownWidth};
+  max-height: ${(props: { dropdownWidth: string; dropdownMaxHeight: string }) =>
+    props.dropdownMaxHeight || "300px"};
+  border-radius: 3px;
+  z-index: 999;
+  overflow-y: auto;
+  margin-bottom: 20px;
+  box-shadow: 0 4px 8px 0px #00000088;
+`;
+
+const RangeWrapper = styled.div`
+  position: absolute;
+  bottom: 10px;
+  font-weight: bold;
+  left: 0;
+  width: 100%;
+`;
+
+const MetricSelector = styled.div`
+  font-size: 16px;
+  font-weight: 500;
+  color: #ffffff;
+  position: absolute;
+  top: 0;
+  left: 5px;
+  display: flex;
+  align-items: center;
+  cursor: pointer;
+  border-radius: 5px;
+  :hover {
+    > i {
+      background: #ffffff22;
+    }
+  }
+
+  > i {
+    border-radius: 20px;
+    font-size: 20px;
+    margin-left: 10px;
+  }
+`;
+
 const StyledMetricsSection = styled.div`
   width: 100%;
   height: 100%;