2
0
Эх сурвалжийг харах

new add-on route w listed templates

Justin Rhee 3 жил өмнө
parent
commit
8a35bd2bd4

+ 6 - 0
dashboard/src/main/home/Home.tsx

@@ -32,6 +32,7 @@ import { NewProjectFC } from "./new-project/NewProject";
 import InfrastructureRouter from "./infrastructure/InfrastructureRouter";
 import { overrideInfraTabEnabled } from "utils/infrastructure";
 import NoClusterPlaceHolder from "components/NoClusterPlaceHolder";
+import NewAddOnFlow from "./add-on-dashboard/NewAddOnFlow";
 
 // Guarded components
 const GuardedProjectSettings = fakeGuardedRoute("settings", "", [
@@ -390,6 +391,11 @@ const Home: React.FC<Props> = (props) => {
             >
               <AppDashboard />
             </Route>
+            <Route
+              path="/addons/new"
+            >
+              <NewAddOnFlow />
+            </Route>
             <Route
               path="/addons"
             >

+ 36 - 9
dashboard/src/main/home/add-on-dashboard/AddOnDashboard.tsx

@@ -1,4 +1,10 @@
-import React, { useEffect, useState, useContext, useMemo } from "react";
+import React, { 
+  useEffect, 
+  useState, 
+  useContext, 
+  useMemo, 
+  useCallback 
+} from "react";
 import styled from "styled-components";
 import _ from "lodash";
 
@@ -14,6 +20,7 @@ import { search } from "shared/search";
 import api from "shared/api";
 
 import DashboardHeader from "../cluster-dashboard/DashboardHeader";
+
 import Container from "components/porter/Container";
 import Button from "components/porter/Button";
 import Spacer from "components/porter/Spacer";
@@ -22,6 +29,7 @@ import SearchBar from "components/porter/SearchBar";
 import Toggle from "components/porter/Toggle";
 import { readableDate } from "shared/string_utils";
 import Loading from "components/Loading";
+import { Link } from "react-router-dom";
 
 type Props = {
 };
@@ -70,7 +78,6 @@ const AppDashboard: React.FC<Props> = ({
   }, [addOns, searchValue]);
 
   const getAddOns = async () => {
-
     try {
       setIsLoading(true);
       const res = await api.getCharts(
@@ -107,6 +114,23 @@ const AppDashboard: React.FC<Props> = ({
     getAddOns();
   }, [currentCluster, currentProject]);
 
+  const getExpandedChartLinkURL = useCallback((x: any) => {
+    const params = new Proxy(new URLSearchParams(window.location.search), {
+      get: (searchParams, prop: string) => searchParams.get(prop),
+    });
+    const cluster = currentCluster?.name;
+    const route = `/applications/${cluster}/${x.namespace}/${x.name}`;
+    const newParams = {
+      // @ts-ignore
+      project_id: params.project_id,
+      closeChartRedirectUrl: '/addons',
+    };
+    const newURLSearchParams = new URLSearchParams(
+      _.omitBy(newParams, _.isNil)
+    );
+    return `${route}?${newURLSearchParams.toString()}`;
+  }, [currentCluster]);
+
   return (
     <StyledAppDashboard>
       <DashboardHeader
@@ -133,9 +157,11 @@ const AppDashboard: React.FC<Props> = ({
           setActive={setView}
         />
         <Spacer inline x={2} />
-        <Button onClick={() => console.log("cool")} height="30px" width="130px">
-          <I className="material-icons">add</I> New add-on
-        </Button>
+        <Link to="/addons/new">
+          <Button onClick={() => {}} height="30px" width="130px">
+            <I className="material-icons">add</I> New add-on
+          </Button>
+        </Link>
       </Container>
       <Spacer y={1} />
       {isLoading ? <Loading offset="-150px" /> : view === "grid" ? (
@@ -146,7 +172,7 @@ const AppDashboard: React.FC<Props> = ({
               !templateBlacklist.includes(app.chart.metadata.name)
             ) {
               return (
-                <Block>
+                <Block to={getExpandedChartLinkURL(app)}>
                   <Text size={14}>
                     <Icon src={app.chart.metadata.icon} />
                     {app.name}
@@ -169,7 +195,7 @@ const AppDashboard: React.FC<Props> = ({
               !templateBlacklist.includes(app.chart.metadata.name)
             ) {
               return (
-                <Row>
+                <Row to={getExpandedChartLinkURL(app)}>
                   <Text size={14}>
                     <MidIcon src={app.chart.metadata.icon} />
                     {app.name}
@@ -194,8 +220,9 @@ const AppDashboard: React.FC<Props> = ({
 
 export default AppDashboard;
 
-const Row = styled.div<{ isAtBottom?: boolean }>`
+const Row = styled(Link)<{ isAtBottom?: boolean }>`
   cursor: pointer;
+  display: block;
   padding: 15px;
   border-bottom: ${props => props.isAtBottom ? "none" : "1px solid #494b4f"};
   background: ${props => props.theme.clickable.bg};
@@ -240,7 +267,7 @@ const SmallIcon = styled.img<{ opacity?: string }>`
   margin-right: 10px;
 `;
 
-const Block = styled.div`
+const Block = styled(Link)`
   height: 110px;
   flex-direction: column;
   display: flex;

+ 142 - 0
dashboard/src/main/home/add-on-dashboard/NewAddOnFlow.tsx

@@ -0,0 +1,142 @@
+import React, { useEffect, useState, useContext, useMemo } from "react";
+import styled from "styled-components";
+import DashboardHeader from "../cluster-dashboard/DashboardHeader";
+import semver from "semver";
+import _ from "lodash";
+
+import addOn from "assets/add-ons.png";
+
+import { Context } from "shared/Context";
+import api from "shared/api";
+import { search } from "shared/search";
+
+import Link from "components/porter/Link";
+import TemplateList from "../launch/TemplateList";
+import SearchBar from "components/porter/SearchBar";
+import Spacer from "components/porter/Spacer";
+import Loading from "components/Loading";
+
+type Props = {
+};
+
+const HIDDEN_CHARTS = ["porter-agent", "loki"];
+
+const NewAddOnFlow: React.FC<Props> = ({
+}) => {
+  const { capabilities, currentProject } = useContext(Context);
+  const [isLoading, setIsLoading] = useState<boolean>(true);
+  const [searchValue, setSearchValue] = useState("");
+  const [addOnTemplates, setAddOnTemplates] = useState<any[]>([]);
+  const [currentTemplate, setCurrentTemplate] = useState<any>(null);
+
+  const filteredTemplates = useMemo(() => {
+    const filteredBySearch = search(
+      addOnTemplates ?? [],
+      searchValue,
+      {
+        keys: ["name", "chart.metadata.name"],
+        isCaseSensitive: false,
+      }
+    );
+
+    return _.sortBy(filteredBySearch);
+  }, [addOnTemplates, searchValue]);
+  
+  const getTemplates = async () => {
+    setIsLoading(true);
+    const default_addon_helm_repo_url = capabilities?.default_addon_helm_repo_url;
+    try {
+      const res = await api.getTemplates(
+        "<token>",
+        {
+          repo_url: default_addon_helm_repo_url,
+        },
+        {
+          project_id: currentProject.id,
+        }
+      );
+      setIsLoading(false);
+      var sortedVersionData = res.data.map((template: any) => {
+        let versions = template.versions.reverse();
+        versions = template.versions.sort(semver.rcompare);
+        return {
+          ...template,
+          versions,
+          currentVersion: versions[0],
+        };
+      });
+      sortedVersionData.sort((a: any, b: any) => (a.name > b.name ? 1 : -1));
+      sortedVersionData = sortedVersionData.filter(
+        (template: any) => !HIDDEN_CHARTS.includes(template?.name)
+      );
+      setAddOnTemplates(sortedVersionData);
+    } catch (error) {
+      setIsLoading(false);
+    }
+  };
+
+  useEffect(() => {
+    getTemplates();
+  }, []);
+
+  return (
+    <StyledTemplateComponent>
+      <DashboardHeader
+        prefix={(
+          <Link to="/addons">
+            <I className="material-icons">keyboard_backspace</I>
+          </Link>
+        )}
+        image={addOn}
+        title="Deploy a new add-on"
+        capitalize={false}
+        description="Create a new add-ons for this project."
+        disableLineBreak
+      />
+      <SearchBar 
+        value={searchValue}
+        setValue={setSearchValue}
+        placeholder="Search available add-ons . . ."
+        width="100%"
+      />
+      <Spacer y={1} />
+
+      {/* Temporary space reducer for legacy template list */}
+      {isLoading ? <Loading offset="-150px" /> : (
+        <>
+          <DarkMatter />
+          <TemplateList
+            templates={addOnTemplates}
+            setCurrentTemplate={(x) => setCurrentTemplate(x)}
+          />
+        </>
+      )}
+    </StyledTemplateComponent>
+  );
+};
+
+export default NewAddOnFlow;
+
+const DarkMatter = styled.div`
+  width: 100%;
+  margin-top: -35px;
+`;
+
+const I = styled.i`
+  font-size: 16px;
+  padding: 4px;
+  cursor: pointer;
+  border-radius: 50%;
+  margin-right: 15px;
+  background: ${props => props.theme.fg};
+  color: ${props => props.theme.text.primary};
+  border: 1px solid ${props => props.theme.border};
+  :hover {
+    filter: brightness(150%);
+  }
+`;
+
+const StyledTemplateComponent = styled.div`
+  width: 100%;
+  height: 100%;
+`;

+ 1 - 1
dashboard/src/main/home/app-dashboard/AppDashboard.tsx

@@ -109,7 +109,7 @@ const AppDashboard: React.FC<Props> = ({
           setActive={setView}
         />
         <Spacer inline x={2} />
-        <Button onClick={() => console.log("cool")} height="30px" width="160px">
+        <Button onClick={() => {}} height="30px" width="160px">
           <I className="material-icons">add</I> New application
         </Button>
       </Container>

+ 14 - 9
dashboard/src/main/home/cluster-dashboard/DashboardHeader.tsx

@@ -6,6 +6,7 @@ import { Context } from "shared/Context";
 import TitleSection from "components/TitleSection";
 import Spacer from "components/porter/Spacer";
 import Tooltip from "components/porter/Tooltip";
+import Container from "components/porter/Container";
 
 type PropsType = {
   image?: any;
@@ -14,6 +15,7 @@ type PropsType = {
   materialIconClass?: string;
   disableLineBreak?: boolean;
   capitalize?: boolean;
+  prefix?: any;
 };
 
 type StateType = {};
@@ -22,15 +24,18 @@ export default class DashboardHeader extends Component<PropsType, StateType> {
   render() {
     return (
       <>
-        <TitleSection
-          capitalize={
-            this.props.capitalize === undefined || this.props.capitalize
-          }
-          icon={this.props.image}
-          materialIconClass={this.props.materialIconClass}
-        >
-          {this.props.title}
-        </TitleSection>
+        <Container row>
+          {this.props.prefix}
+          <TitleSection
+            capitalize={
+              this.props.capitalize === undefined || this.props.capitalize
+            }
+            icon={this.props.image}
+            materialIconClass={this.props.materialIconClass}
+          >
+            {this.props.title}
+          </TitleSection>
+        </Container>
 
         {this.props.description && (
           <>

+ 1 - 0
dashboard/src/shared/themes/midnight.ts

@@ -1,6 +1,7 @@
 const theme = {
   bg: "#121212",
   fg: "#171B21",
+  border: "#494b4f",
   clickable: {
     bg: "linear-gradient(180deg, #171B21, #121212)",
   },