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

Reorganized components and implemented delete app resource functionality

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

+ 4 - 325
dashboard/src/main/home/cluster-dashboard/stacks/launch/Overview.tsx

@@ -1,19 +1,14 @@
 import React, { useContext, useEffect, useMemo, useRef, useState } from "react";
-import semver from "semver";
 import { StacksLaunchContext } from "./Store";
 import InputRow from "components/form-components/InputRow";
 import Selector from "components/Selector";
 import api from "shared/api";
 import { Context } from "shared/Context";
-import { ClusterType, PorterTemplate } from "shared/types";
 import useAuth from "shared/auth/useAuth";
-import DynamicLink from "components/DynamicLink";
-import styled from "styled-components";
-import { useOutsideAlerter } from "shared/hooks/useOutsideAlerter";
-import { capitalize } from "shared/string_utils";
-import SaveButton from "components/SaveButton";
 import { useRouting } from "shared/routing";
-import Loading from "components/Loading";
+import { CardGrid, SubmitButton } from "./components/styles";
+import { AppCard } from "./components/AppCard";
+import { AddResourceButton } from "./components/AddResourceButton";
 
 const Overview = () => {
   const {
@@ -125,7 +120,7 @@ const Overview = () => {
       <br />
       <CardGrid>
         {newStack.app_resources.map((app) => (
-          <Card key={app.name}>{app.name}</Card>
+          <AppCard key={app.name} app={app} />
         ))}
 
         <AddResourceButton />
@@ -146,319 +141,3 @@ const Overview = () => {
 };
 
 export default Overview;
-
-const AddResourceButton = () => {
-  const [templates, setTemplates] = useState<PorterTemplate[]>([]);
-  const [currentTemplate, setCurrentTemplate] = useState<PorterTemplate>();
-  const [currentVersion, setCurrentVersion] = useState("");
-
-  const getTemplates = async () => {
-    try {
-      const res = await api.getTemplates<PorterTemplate[]>(
-        "<token>",
-        {
-          repo_url: process.env.APPLICATION_CHART_REPO_URL,
-        },
-        {}
-      );
-      let sortedVersionData = res.data
-        .map((template: PorterTemplate) => {
-          let versions = template.versions.reverse();
-
-          versions = template.versions.sort(semver.rcompare);
-
-          return {
-            ...template,
-            versions,
-            currentVersion: versions[0],
-          };
-        })
-        .sort((a, b) => {
-          if (a.name < b.name) {
-            return -1;
-          }
-          if (a.name > b.name) {
-            return 1;
-          }
-          return 0;
-        });
-
-      return sortedVersionData;
-    } catch (err) {}
-  };
-
-  useEffect(() => {
-    getTemplates().then((templates) => {
-      setTemplates(templates);
-      setCurrentTemplate(templates[0]);
-      setCurrentVersion(templates[0].currentVersion);
-    });
-  }, []);
-
-  return (
-    <AddResourceButtonStyles.Wrapper>
-      <AddResourceButtonStyles.Flex>
-        Add a new{" "}
-        <TemplateSelector
-          options={templates}
-          value={currentTemplate}
-          onChange={(template) => {
-            setCurrentTemplate(template);
-            setCurrentVersion(template.currentVersion);
-          }}
-        />
-        <VersionSelector
-          options={currentTemplate?.versions || []}
-          value={currentVersion}
-          onChange={setCurrentVersion}
-        />
-      </AddResourceButtonStyles.Flex>
-
-      <DynamicLink
-        to={`/stacks/launch/new-app/${currentTemplate?.name}/${currentVersion}`}
-      >
-        Create
-      </DynamicLink>
-    </AddResourceButtonStyles.Wrapper>
-  );
-};
-
-const TemplateSelector = ({
-  value,
-  options,
-  onChange,
-}: {
-  value: PorterTemplate;
-  options: PorterTemplate[];
-  onChange: (newValue: PorterTemplate) => void;
-}) => {
-  const wrapperRef = useRef();
-
-  const [isExpanded, setIsExpanded] = useState(false);
-
-  useOutsideAlerter(wrapperRef, () => setIsExpanded(false));
-
-  const getName = (template: PorterTemplate) => {
-    if (template?.name === "web") {
-      return "Web Application";
-    }
-    return capitalize(template?.name || "");
-  };
-
-  if (!Array.isArray(options) || options.length === 0) {
-    return (
-      <SelectorStyles.Wrapper>
-        <SelectorStyles.Button expanded={false}>
-          <Loading />
-        </SelectorStyles.Button>
-      </SelectorStyles.Wrapper>
-    );
-  }
-
-  return (
-    <>
-      <SelectorStyles.Wrapper ref={wrapperRef}>
-        <SelectorStyles.Button
-          expanded={isExpanded}
-          onClick={() => setIsExpanded((prev) => !prev)}
-        >
-          {getName(value)}
-          <i className="material-icons">arrow_drop_down</i>
-        </SelectorStyles.Button>
-
-        {isExpanded ? (
-          <SelectorStyles.Dropdown>
-            {options.map((template) => (
-              <SelectorStyles.Option
-                className={template.name === value.name ? "active" : ""}
-                onClick={() => {
-                  onChange(template);
-                  setIsExpanded(false);
-                }}
-              >
-                <SelectorStyles.OptionText>
-                  {getName(template)}
-                </SelectorStyles.OptionText>
-              </SelectorStyles.Option>
-            ))}
-          </SelectorStyles.Dropdown>
-        ) : null}
-      </SelectorStyles.Wrapper>
-    </>
-  );
-};
-
-const VersionSelector = ({
-  value,
-  options,
-  onChange,
-}: {
-  value: string;
-  options: string[];
-  onChange: (newValue: string) => void;
-}) => {
-  const wrapperRef = useRef();
-
-  const [isExpanded, setIsExpanded] = useState(false);
-
-  useOutsideAlerter(wrapperRef, () => setIsExpanded(false));
-
-  if (!Array.isArray(options) || options.length === 0) {
-    return (
-      <SelectorStyles.Wrapper>
-        <SelectorStyles.Button expanded={false}>
-          <Loading />
-        </SelectorStyles.Button>
-      </SelectorStyles.Wrapper>
-    );
-  }
-
-  return (
-    <>
-      <SelectorStyles.Wrapper ref={wrapperRef}>
-        <SelectorStyles.Button
-          expanded={isExpanded}
-          onClick={() => setIsExpanded((prev) => !prev)}
-        >
-          {capitalize(value)}
-          <i className="material-icons">arrow_drop_down</i>
-        </SelectorStyles.Button>
-
-        {isExpanded ? (
-          <SelectorStyles.Dropdown>
-            {options.map((version) => (
-              <SelectorStyles.Option
-                className={version === value ? "active" : ""}
-                onClick={() => {
-                  onChange(version);
-                  setIsExpanded(false);
-                }}
-              >
-                {capitalize(version)}
-              </SelectorStyles.Option>
-            ))}
-          </SelectorStyles.Dropdown>
-        ) : null}
-      </SelectorStyles.Wrapper>
-    </>
-  );
-};
-
-const CardGrid = styled.div`
-  margin-top: 32px;
-  margin-bottom: 32px;
-  display: grid;
-  grid-row-gap: 25px;
-`;
-
-const Card = styled.div`
-  display: flex;
-  color: #ffffff;
-  background: #2b2e3699;
-  justify-content: space-between;
-  border-radius: 5px;
-  cursor: pointer;
-  height: 75px;
-  padding: 12px;
-  padding-left: 14px;
-  border: 1px solid #ffffff0f;
-
-  :hover {
-    border: 1px solid #ffffff3c;
-  }
-  animation: fadeIn 0.5s;
-  @keyframes fadeIn {
-    from {
-      opacity: 0;
-    }
-    to {
-      opacity: 1;
-    }
-  }
-`;
-
-const SubmitButton = styled(SaveButton)`
-  width: 100%;
-  display: flex;
-  justify-content: flex-end;
-`;
-
-const AddResourceButtonStyles = {
-  Wrapper: styled(Card)`
-    align-items: center;
-  `,
-  Text: styled.span`
-    font-size: 20px;
-  `,
-  Flex: styled.div`
-    display: flex;
-    align-items: center;
-  `,
-};
-
-const SelectorStyles = {
-  Wrapper: styled.div`
-    max-width: 200px;
-    position: relative;
-    font-size: 13px;
-
-    margin-left: 10px;
-  `,
-  Button: styled.div`
-    background-color: #ffffff11;
-    border: 1px solid #ffffff22;
-    border-radius: 5px;
-    min-width: 115px;
-    min-height: 30px;
-    padding: 0 15px;
-
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-
-    white-space: nowrap;
-    overflow-y: hidden;
-    text-overflow: ellipsis;
-    cursor: pointer;
-
-    > i {
-      font-size: 20px;
-      transform: ${(props: { expanded: boolean }) =>
-        props.expanded ? "rotate(180deg)" : ""};
-    }
-  `,
-  Dropdown: styled.div`
-    position: absolute;
-    background-color: #26282f;
-    width: 100%;
-    max-height: 200px;
-    overflow-y: auto;
-  `,
-  Option: styled.div`
-    min-height: 35px;
-    padding: 0 15px;
-
-    display: flex;
-    align-items: center;
-
-    cursor: pointer;
-
-    &.active {
-      background-color: #32343c;
-    }
-
-    :hover {
-      background-color: #32343c;
-    }
-
-    :not(:last-child) {
-      border-bottom: 1px solid #ffffff15;
-    }
-  `,
-  OptionText: styled.span`
-    max-width: 115px;
-    overflow-x: hidden;
-    white-space: nowrap;
-    text-overflow: ellipsis;
-  `,
-};

+ 1 - 9
dashboard/src/main/home/cluster-dashboard/stacks/launch/SelectSource.tsx

@@ -3,8 +3,7 @@ import React, { useContext, useState } from "react";
 import { StacksLaunchContext } from "./Store";
 import { CreateStackBody } from "../types";
 import { useRouting } from "shared/routing";
-import styled from "styled-components";
-import SaveButton from "components/SaveButton";
+import { SubmitButton } from "./components/styles";
 
 const SelectSource = () => {
   const { addSourceConfig } = useContext(StacksLaunchContext);
@@ -49,10 +48,3 @@ const SelectSource = () => {
 };
 
 export default SelectSource;
-
-const SubmitButton = styled(SaveButton)`
-  width: 100%;
-  display: flex;
-  justify-content: flex-end;
-  margin-top: 15px;
-`;

+ 16 - 0
dashboard/src/main/home/cluster-dashboard/stacks/launch/Store.tsx

@@ -17,6 +17,8 @@ export type StacksLaunchContextType = {
 
   addAppResource: (appResource: CreateStackBody["app_resources"][0]) => void;
 
+  removeAppResource: (appResource: CreateStackBody["app_resources"][0]) => void;
+
   submit: () => Promise<void>;
 };
 
@@ -38,6 +40,8 @@ const defaultValues: StacksLaunchContextType = {
 
   addAppResource: (appResource: CreateStackBody["app_resources"][0]) => {},
 
+  removeAppResource: (appResource: CreateStackBody["app_resources"][0]) => {},
+
   submit: async () => {},
 };
 
@@ -96,6 +100,17 @@ const StacksLaunchContextProvider: React.FC<{}> = ({ children }) => {
     }));
   };
 
+  const removeAppResource: StacksLaunchContextType["removeAppResource"] = (
+    appResource
+  ) => {
+    setNewStack((prev) => ({
+      ...prev,
+      app_resources: prev.app_resources.filter(
+        (ar) => ar.name !== appResource.name
+      ),
+    }));
+  };
+
   const submit: StacksLaunchContextType["submit"] = async () => {
     try {
       await api.createStack("<token>", newStack, {
@@ -118,6 +133,7 @@ const StacksLaunchContextProvider: React.FC<{}> = ({ children }) => {
         setStackNamespace,
         addSourceConfig,
         addAppResource,
+        removeAppResource,
         submit,
       }}
     >

+ 84 - 0
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/AddResourceButton.tsx

@@ -0,0 +1,84 @@
+import React, { useEffect, useState } from "react";
+import api from "shared/api";
+import { PorterTemplate } from "shared/types";
+import semver from "semver";
+import { AddResourceButtonStyles } from "./styles";
+import { TemplateSelector } from "./TemplateSelector";
+import { VersionSelector } from "./VersionSelector";
+import DynamicLink from "components/DynamicLink";
+
+export const AddResourceButton = () => {
+  const [templates, setTemplates] = useState<PorterTemplate[]>([]);
+  const [currentTemplate, setCurrentTemplate] = useState<PorterTemplate>();
+  const [currentVersion, setCurrentVersion] = useState("");
+
+  const getTemplates = async () => {
+    try {
+      const res = await api.getTemplates<PorterTemplate[]>(
+        "<token>",
+        {
+          repo_url: process.env.APPLICATION_CHART_REPO_URL,
+        },
+        {}
+      );
+      let sortedVersionData = res.data
+        .map((template: PorterTemplate) => {
+          let versions = template.versions.reverse();
+
+          versions = template.versions.sort(semver.rcompare);
+
+          return {
+            ...template,
+            versions,
+            currentVersion: versions[0],
+          };
+        })
+        .sort((a, b) => {
+          if (a.name < b.name) {
+            return -1;
+          }
+          if (a.name > b.name) {
+            return 1;
+          }
+          return 0;
+        });
+
+      return sortedVersionData;
+    } catch (err) {}
+  };
+
+  useEffect(() => {
+    getTemplates().then((templates) => {
+      setTemplates(templates);
+      setCurrentTemplate(templates[0]);
+      setCurrentVersion(templates[0].currentVersion);
+    });
+  }, []);
+
+  return (
+    <AddResourceButtonStyles.Wrapper>
+      <AddResourceButtonStyles.Flex>
+        Add a new{" "}
+        <TemplateSelector
+          options={templates}
+          value={currentTemplate}
+          onChange={(template) => {
+            setCurrentTemplate(template);
+            setCurrentVersion(template.currentVersion);
+          }}
+        />
+        <VersionSelector
+          options={currentTemplate?.versions || []}
+          value={currentVersion}
+          onChange={setCurrentVersion}
+        />
+      </AddResourceButtonStyles.Flex>
+
+      <DynamicLink
+        to={`/stacks/launch/new-app/${currentTemplate?.name}/${currentVersion}`}
+      >
+        Create
+      </DynamicLink>
+    </AddResourceButtonStyles.Wrapper>
+  );
+};

+ 26 - 0
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/AppCard.tsx

@@ -0,0 +1,26 @@
+import React, { useContext } from "react";
+import { StacksLaunchContext, StacksLaunchContextType } from "../Store";
+import { ButtonWithIcon, Card } from "./styles";
+
+const DeleteButton = ButtonWithIcon;
+
+export const AppCard = ({
+  app,
+}: {
+  app: StacksLaunchContextType["newStack"]["app_resources"][0];
+}) => {
+  const { removeAppResource } = useContext(StacksLaunchContext);
+
+  const handleDelete = () => {
+    removeAppResource(app);
+  };
+
+  return (
+    <Card>
+      {app.name}
+      <DeleteButton onClick={handleDelete}>
+        <i className="material-icons-outlined">delete</i>
+      </DeleteButton>
+    </Card>
+  );
+};

+ 71 - 0
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/TemplateSelector.tsx

@@ -0,0 +1,71 @@
+import Loading from "components/Loading";
+import React, { useRef, useState } from "react";
+import { useOutsideAlerter } from "shared/hooks/useOutsideAlerter";
+import { capitalize } from "shared/string_utils";
+import { PorterTemplate } from "shared/types";
+import { SelectorStyles } from "./styles";
+
+export const TemplateSelector = ({
+  value,
+  options,
+  onChange,
+}: {
+  value: PorterTemplate;
+  options: PorterTemplate[];
+  onChange: (newValue: PorterTemplate) => void;
+}) => {
+  const wrapperRef = useRef();
+
+  const [isExpanded, setIsExpanded] = useState(false);
+
+  useOutsideAlerter(wrapperRef, () => setIsExpanded(false));
+
+  const getName = (template: PorterTemplate) => {
+    if (template?.name === "web") {
+      return "Web Application";
+    }
+    return capitalize(template?.name || "");
+  };
+
+  if (!Array.isArray(options) || options.length === 0) {
+    return (
+      <SelectorStyles.Wrapper>
+        <SelectorStyles.Button expanded={false}>
+          <Loading />
+        </SelectorStyles.Button>
+      </SelectorStyles.Wrapper>
+    );
+  }
+
+  return (
+    <>
+      <SelectorStyles.Wrapper ref={wrapperRef}>
+        <SelectorStyles.Button
+          expanded={isExpanded}
+          onClick={() => setIsExpanded((prev) => !prev)}
+        >
+          {getName(value)}
+          <i className="material-icons">arrow_drop_down</i>
+        </SelectorStyles.Button>
+
+        {isExpanded ? (
+          <SelectorStyles.Dropdown>
+            {options.map((template) => (
+              <SelectorStyles.Option
+                className={template.name === value.name ? "active" : ""}
+                onClick={() => {
+                  onChange(template);
+                  setIsExpanded(false);
+                }}
+              >
+                <SelectorStyles.OptionText>
+                  {getName(template)}
+                </SelectorStyles.OptionText>
+              </SelectorStyles.Option>
+            ))}
+          </SelectorStyles.Dropdown>
+        ) : null}
+      </SelectorStyles.Wrapper>
+    </>
+  );
+};

+ 61 - 0
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/VersionSelector.tsx

@@ -0,0 +1,61 @@
+import Loading from "components/Loading";
+import React, { useRef, useState } from "react";
+import { useOutsideAlerter } from "shared/hooks/useOutsideAlerter";
+import { capitalize } from "shared/string_utils";
+import { SelectorStyles } from "./styles";
+
+export const VersionSelector = ({
+  value,
+  options,
+  onChange,
+}: {
+  value: string;
+  options: string[];
+  onChange: (newValue: string) => void;
+}) => {
+  const wrapperRef = useRef();
+
+  const [isExpanded, setIsExpanded] = useState(false);
+
+  useOutsideAlerter(wrapperRef, () => setIsExpanded(false));
+
+  if (!Array.isArray(options) || options.length === 0) {
+    return (
+      <SelectorStyles.Wrapper>
+        <SelectorStyles.Button expanded={false}>
+          <Loading />
+        </SelectorStyles.Button>
+      </SelectorStyles.Wrapper>
+    );
+  }
+
+  return (
+    <>
+      <SelectorStyles.Wrapper ref={wrapperRef}>
+        <SelectorStyles.Button
+          expanded={isExpanded}
+          onClick={() => setIsExpanded((prev) => !prev)}
+        >
+          {capitalize(value)}
+          <i className="material-icons">arrow_drop_down</i>
+        </SelectorStyles.Button>
+
+        {isExpanded ? (
+          <SelectorStyles.Dropdown>
+            {options.map((version) => (
+              <SelectorStyles.Option
+                className={version === value ? "active" : ""}
+                onClick={() => {
+                  onChange(version);
+                  setIsExpanded(false);
+                }}
+              >
+                {capitalize(version)}
+              </SelectorStyles.Option>
+            ))}
+          </SelectorStyles.Dropdown>
+        ) : null}
+      </SelectorStyles.Wrapper>
+    </>
+  );
+};

+ 142 - 0
dashboard/src/main/home/cluster-dashboard/stacks/launch/components/styles.tsx

@@ -0,0 +1,142 @@
+import SaveButton from "components/SaveButton";
+import styled from "styled-components";
+
+export const CardGrid = styled.div`
+  margin-top: 32px;
+  margin-bottom: 32px;
+  display: grid;
+  grid-row-gap: 25px;
+`;
+
+export const Card = styled.div`
+  display: flex;
+  color: #ffffff;
+  background: #2b2e3699;
+  justify-content: space-between;
+  border-radius: 5px;
+  cursor: pointer;
+  height: 75px;
+  padding: 12px;
+  padding-left: 14px;
+  border: 1px solid #ffffff0f;
+  align-items: center;
+
+  :hover {
+    border: 1px solid #ffffff3c;
+  }
+  animation: fadeIn 0.5s;
+  @keyframes fadeIn {
+    from {
+      opacity: 0;
+    }
+    to {
+      opacity: 1;
+    }
+  }
+`;
+
+export const SubmitButton = styled(SaveButton)`
+  width: 100%;
+  display: flex;
+  justify-content: flex-end;
+`;
+
+export const AddResourceButtonStyles = {
+  Wrapper: styled(Card)`
+    align-items: center;
+  `,
+  Text: styled.span`
+    font-size: 20px;
+  `,
+  Flex: styled.div`
+    display: flex;
+    align-items: center;
+  `,
+};
+
+export const SelectorStyles = {
+  Wrapper: styled.div`
+    max-width: 200px;
+    position: relative;
+    font-size: 13px;
+
+    margin-left: 10px;
+  `,
+  Button: styled.div`
+    background-color: #ffffff11;
+    border: 1px solid #ffffff22;
+    border-radius: 5px;
+    min-width: 115px;
+    min-height: 30px;
+    padding: 0 15px;
+
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+
+    white-space: nowrap;
+    overflow-y: hidden;
+    text-overflow: ellipsis;
+    cursor: pointer;
+
+    > i {
+      font-size: 20px;
+      transform: ${(props: { expanded: boolean }) =>
+        props.expanded ? "rotate(180deg)" : ""};
+    }
+  `,
+  Dropdown: styled.div`
+    position: absolute;
+    background-color: #26282f;
+    width: 100%;
+    max-height: 200px;
+    overflow-y: auto;
+  `,
+  Option: styled.div`
+    min-height: 35px;
+    padding: 0 15px;
+
+    display: flex;
+    align-items: center;
+
+    cursor: pointer;
+
+    &.active {
+      background-color: #32343c;
+    }
+
+    :hover {
+      background-color: #32343c;
+    }
+
+    :not(:last-child) {
+      border-bottom: 1px solid #ffffff15;
+    }
+  `,
+  OptionText: styled.span`
+    max-width: 115px;
+    overflow-x: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+  `,
+};
+
+export const ButtonWithIcon = styled.div`
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 30px;
+  height: 30px;
+  border-radius: 50%;
+  background-color: #ffffff11;
+  border: 1px solid #ffffff22;
+  cursor: pointer;
+
+  &:hover {
+    background-color: #ffffff3c;
+  }
+
+  > i {
+    font-size: 18px;
+  }
+`;