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

Implemented source config view

jnfrati 3 лет назад
Родитель
Сommit
cc8c9e2709

+ 52 - 25
dashboard/src/components/image-selector/ImageList.tsx

@@ -9,17 +9,31 @@ import { ImageType } from "shared/types";
 import Loading from "../Loading";
 import TagList from "./TagList";
 
-type PropsType = {
-  selectedImageUrl: string | null;
-  selectedTag: string | null;
-  clickedImage: ImageType | null;
-  registry?: any;
-  noTagSelection?: boolean;
-  setSelectedImageUrl: (x: string) => void;
-  setSelectedTag: (x: string) => void;
-  setClickedImage: (x: ImageType) => void;
-  disableImageSelect?: boolean;
-};
+type PropsType =
+  | {
+      selectedImageUrl: string | null;
+      selectedTag: string | null;
+      clickedImage: ImageType | null;
+      registry?: any;
+      noTagSelection?: boolean;
+      setSelectedImageUrl: (x: string) => void;
+      setSelectedTag: (x: string) => void;
+      setClickedImage: (x: ImageType) => void;
+      disableImageSelect?: boolean;
+      readOnly?: boolean;
+    }
+  | {
+      selectedImageUrl: string | null;
+      selectedTag: string | null;
+      clickedImage: ImageType | null;
+      registry?: any;
+      noTagSelection?: boolean;
+      setSelectedImageUrl?: (x: string) => void;
+      setSelectedTag?: (x: string) => void;
+      setClickedImage?: (x: ImageType) => void;
+      disableImageSelect?: boolean;
+      readOnly: true;
+    };
 
 type StateType = {
   loading: boolean;
@@ -222,28 +236,41 @@ export default class ImageList extends Component<PropsType, StateType> {
   renderExpanded = () => {
     let { selectedTag, selectedImageUrl, setSelectedTag } = this.props;
 
-    if (!this.props.clickedImage || this.props.noTagSelection) {
+    if (this.props.readOnly && this.props.clickedImage) {
       return (
-        <div>
-          <ExpandedWrapper>{this.renderImageList()}</ExpandedWrapper>
-          {this.renderBackButton()}
-        </div>
+        <ExpandedWrapper>
+          <TagList
+            selectedTag={selectedTag}
+            selectedImageUrl={selectedImageUrl}
+            registryId={this.props.clickedImage.registryId}
+            readOnly
+          />
+        </ExpandedWrapper>
       );
-    } else {
+    }
+
+    if (!this.props.clickedImage || this.props.noTagSelection) {
       return (
         <div>
-          <ExpandedWrapper>
-            <TagList
-              selectedTag={selectedTag}
-              selectedImageUrl={selectedImageUrl}
-              setSelectedTag={setSelectedTag}
-              registryId={this.props.clickedImage.registryId}
-            />
-          </ExpandedWrapper>
+          <ExpandedWrapper>{this.renderImageList()}</ExpandedWrapper>
           {this.renderBackButton()}
         </div>
       );
     }
+
+    return (
+      <div>
+        <ExpandedWrapper>
+          <TagList
+            selectedTag={selectedTag}
+            selectedImageUrl={selectedImageUrl}
+            setSelectedTag={setSelectedTag}
+            registryId={this.props.clickedImage.registryId}
+          />
+        </ExpandedWrapper>
+        {this.renderBackButton()}
+      </div>
+    );
   };
 
   render() {

+ 51 - 10
dashboard/src/components/image-selector/ImageSelector.tsx

@@ -10,15 +10,27 @@ import { ImageType } from "shared/types";
 import Loading from "../Loading";
 import ImageList from "./ImageList";
 
-type PropsType = {
-  forceExpanded?: boolean;
-  selectedImageUrl: string | null;
-  selectedTag: string | null;
-  setSelectedImageUrl: (x: string) => void;
-  setSelectedTag: (x: string) => void;
-  noTagSelection?: boolean;
-  disableImageSelect?: boolean;
-};
+type PropsType =
+  | {
+      forceExpanded?: boolean;
+      selectedImageUrl: string | null;
+      selectedTag: string | null;
+      setSelectedImageUrl: (x: string) => void;
+      setSelectedTag: (x: string) => void;
+      noTagSelection?: boolean;
+      disableImageSelect?: boolean;
+      readOnly?: boolean;
+    }
+  | {
+      forceExpanded?: boolean;
+      selectedImageUrl: string | null;
+      selectedTag: string | null;
+      setSelectedImageUrl?: (x: string) => void;
+      setSelectedTag?: (x: string) => void;
+      noTagSelection?: boolean;
+      disableImageSelect?: boolean;
+      readOnly: true;
+    };
 
 type StateType = {
   isExpanded: boolean;
@@ -94,7 +106,7 @@ export default class ImageSelector extends Component<PropsType, StateType> {
       <Label>
         <img src={icon} />
         <Input
-          disabled={this.props.disableImageSelect}
+          disabled={this.props.readOnly || this.props.disableImageSelect}
           onClick={(e: any) => e.stopPropagation()}
           value={selectedImageUrl}
           onChange={(e: any) => {
@@ -118,6 +130,35 @@ export default class ImageSelector extends Component<PropsType, StateType> {
   };
 
   render() {
+    if (this.props.readOnly) {
+      return (
+        <>
+          <StyledImageSelector isExpanded={true} forceExpanded={true}>
+            {this.renderSelected()}
+            {this.props.forceExpanded ? null : (
+              <i className="material-icons">
+                {this.state.isExpanded ? "close" : "build"}
+              </i>
+            )}
+          </StyledImageSelector>
+
+          <ImageList
+            disableImageSelect={true}
+            selectedImageUrl={this.props.selectedImageUrl}
+            selectedTag={this.props.selectedTag}
+            clickedImage={this.state.clickedImage}
+            noTagSelection={this.props.noTagSelection}
+            setSelectedImageUrl={this.props.setSelectedImageUrl}
+            setSelectedTag={this.props.setSelectedTag}
+            setClickedImage={(x: ImageType) =>
+              this.setState({ clickedImage: x })
+            }
+            readOnly
+          />
+        </>
+      );
+    }
+
     return (
       <div>
         <StyledImageSelector

+ 17 - 7
dashboard/src/components/image-selector/TagList.tsx

@@ -10,12 +10,21 @@ import Loading from "../Loading";
 
 var ecrRepoRegex = /(^[a-zA-Z0-9][a-zA-Z0-9-_]*)\.dkr\.ecr(\-fips)?\.([a-zA-Z0-9][a-zA-Z0-9-_]*)\.amazonaws\.com(\.cn)?/gim;
 
-type PropsType = {
-  setSelectedTag: (x: string) => void;
-  selectedTag: string;
-  selectedImageUrl: string;
-  registryId: number;
-};
+type PropsType =
+  | {
+      setSelectedTag: (x: string) => void;
+      selectedTag: string;
+      selectedImageUrl: string;
+      registryId: number;
+      readOnly?: boolean;
+    }
+  | {
+      setSelectedTag?: (x: string) => void;
+      selectedTag: string;
+      selectedImageUrl: string;
+      registryId: number;
+      readOnly: true;
+    };
 
 type StateType = {
   loading: boolean;
@@ -123,7 +132,8 @@ export default class TagList extends Component<PropsType, StateType> {
       <>
         <TagNameAlt>
           <Label>
-            <img src={info} /> Select Image Tag
+            <img src={info} />
+            {this.props.readOnly ? "Current image tag" : "Select Image Tag"}
           </Label>
           <Refresh onClick={this.refreshTagList}>
             <i className="material-icons">autorenew</i> Refresh

+ 8 - 3
dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/ExpandedStack.tsx

@@ -20,8 +20,9 @@ import {
   Text,
 } from "../components/styles";
 import { getStackStatus, getStackStatusMessage } from "../shared";
-import { Stack, StackRevision } from "../types";
+import { FullStackRevision, Stack, StackRevision } from "../types";
 import RevisionList from "./_RevisionList";
+import SourceConfig from "./_SourceConfig";
 
 const ExpandedStack = () => {
   const { namespace, stack_id } = useParams<{
@@ -35,7 +36,7 @@ const ExpandedStack = () => {
   const [isLoading, setIsLoading] = useState(true);
   const [currentTab, setCurrentTab] = useState("apps");
 
-  const [currentRevision, setCurrentRevision] = useState<StackRevision>();
+  const [currentRevision, setCurrentRevision] = useState<FullStackRevision>();
 
   useEffect(() => {
     console.log(stack_id);
@@ -163,7 +164,11 @@ const ExpandedStack = () => {
           {
             label: "Source Config",
             value: "source_config",
-            component: <>Sourrrce configuration</>,
+            component: (
+              <>
+                <SourceConfig revision={currentRevision}></SourceConfig>
+              </>
+            ),
           },
         ]}
         setCurrentTab={(tab) => {

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/_RevisionList.tsx

@@ -12,7 +12,7 @@ type RevisionListProps = {
   latestRevision: StackRevision;
   stackNamespace: string;
   stackId: string;
-  onRevisionClick: (revision: StackRevision) => void;
+  onRevisionClick: (revision: FullStackRevision) => void;
 };
 
 const _RevisionList = ({

+ 99 - 0
dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/_SourceConfig.tsx

@@ -0,0 +1,99 @@
+import { Tooltip } from "@material-ui/core";
+import ImageSelector from "components/image-selector/ImageSelector";
+import React from "react";
+import styled from "styled-components";
+import { AppResource, FullStackRevision, SourceConfig } from "../types";
+
+const _SourceConfig = ({ revision }: { revision: FullStackRevision }) => {
+  return (
+    <SourceConfigStyles.Wrapper>
+      {revision.source_configs.map((sourceConfig) => {
+        const apps = getAppsFromSourceConfig(revision.resources, sourceConfig);
+
+        const appList = formatAppList(apps, 2);
+        console.log({ appList });
+        return (
+          <SourceConfigStyles.ItemContainer>
+            <Tooltip
+              title={
+                <>
+                  {appList.hiddenApps.map((appName) => (
+                    <SourceConfigStyles.TooltipItem>
+                      {appName}
+                    </SourceConfigStyles.TooltipItem>
+                  ))}
+                </>
+              }
+              placement={"bottom-end"}
+            >
+              <SourceConfigStyles.ItemTitle>
+                Used by {appList.value}
+              </SourceConfigStyles.ItemTitle>
+            </Tooltip>
+            <ImageSelector
+              selectedImageUrl={sourceConfig.image_repo_uri}
+              selectedTag={sourceConfig.image_tag}
+              forceExpanded
+              readOnly
+            />
+          </SourceConfigStyles.ItemContainer>
+        );
+      })}
+    </SourceConfigStyles.Wrapper>
+  );
+};
+
+export default _SourceConfig;
+
+const getAppsFromSourceConfig = (
+  apps: AppResource[],
+  sourceConfig: SourceConfig
+) => {
+  return apps.filter((app) => {
+    return app.stack_source_config.id === sourceConfig.id;
+  });
+};
+
+const formatAppList = (apps: AppResource[], limit: number = 3) => {
+  if (apps.length <= limit) {
+    const formatter = new Intl.ListFormat("en", {
+      style: "long",
+      type: "conjunction",
+    });
+    return {
+      value: formatter.format(apps.map((app) => app.name)),
+      hiddenApps: [],
+    };
+  }
+
+  const hiddenApps = [...apps]
+    .splice(limit, apps.length)
+    .map((app) => app.name);
+
+  return {
+    value: apps
+      .map((app) => app.name)
+      .splice(0, limit)
+      .join(", ")
+      .concat(` and ${apps.length - limit} more`),
+    hiddenApps,
+  };
+};
+
+const SourceConfigStyles = {
+  Wrapper: styled.div`
+    margin-top: 30px;
+  `,
+  ItemContainer: styled.div`
+    background: #ffffff11;
+    border-radius: 15px;
+    padding: 20px 15px;
+  `,
+  ItemTitle: styled.div`
+    font-size: 16px;
+    width: fit-content;
+  `,
+  TooltipItem: styled.div`
+    font-size: 14px;
+  `,
+};