Ver Fonte

fully implemented buildpack selection with mock data

jnfrati há 4 anos atrás
pai
commit
b5e7bad84b

+ 2 - 1
dashboard/src/components/Selector.tsx

@@ -16,6 +16,7 @@ type PropsType = {
   closeOverlay?: boolean;
   placeholder?: string;
   scrollBuffer?: boolean;
+  disableTooltip?: boolean;
 };
 
 type StateType = {};
@@ -185,7 +186,7 @@ export default class Selector extends Component<PropsType, StateType> {
           </Flex>
           <i className="material-icons">arrow_drop_down</i>
         </MainSelector>
-        {this.state.showTooltip && (
+        {!this.props.disableTooltip && this.state.showTooltip && (
           <Tooltip>
             {activeValue
               ? activeValue === ""

+ 2 - 0
dashboard/src/components/repo-selector/ActionConfEditor.tsx

@@ -24,6 +24,7 @@ type Props = {
   setFolderPath: (x: string) => void;
   setSelectedRegistry: (x: any) => void;
   selectedRegistry: any;
+  setBuildConfig: (x: any) => void;
 };
 
 const defaultActionConfig: ActionConfigType = {
@@ -121,6 +122,7 @@ const ActionConfEditor: React.FC<Props> = (props) => {
       folderPath={props.folderPath}
       setSelectedRegistry={props.setSelectedRegistry}
       selectedRegistry={props.selectedRegistry}
+      setBuildConfig={props.setBuildConfig}
     />
   );
 };

+ 225 - 69
dashboard/src/components/repo-selector/ActionDetails.tsx

@@ -16,6 +16,7 @@ import InputRow from "../form-components/InputRow";
 import Selector from "components/Selector";
 import Heading from "components/form-components/Heading";
 import Helper from "components/form-components/Helper";
+import SelectRow from "components/form-components/SelectRow";
 
 type PropsType = {
   actionConfig: ActionConfigType | null;
@@ -30,6 +31,7 @@ type PropsType = {
   selectedRegistry: any;
   setDockerfilePath: (x: string) => void;
   setFolderPath: (x: string) => void;
+  setBuildConfig: (x: any) => void;
 };
 
 type Buildpack = {
@@ -63,17 +65,13 @@ const ActionDetails: React.FC<PropsType> = (props) => {
     setProcfilePath,
     setProcfileProcess,
     setSelectedRegistry,
+    setBuildConfig,
   } = props;
 
   const { currentProject } = useContext(Context);
   const [registries, setRegistries] = useState<any[]>(null);
   const [loading, setLoading] = useState(true);
-  const [buildersOptions, setBuildersOptions] = useState<[]>(null);
-  const [currentBuilder, setCurrentBuilder] = useState(null);
-  const [availableBuildpacks, setAvailableBuildpacks] = useState(null);
-  const [availableStacks, setAvailableStacks] = useState(null);
-  const [selectedStack, setSelectedStack] = useState(null);
-  const [selectedBuildpacks, setSelectedBuildpacks] = useState(null);
+  const [showBuildpacksConfig, setShowBuildpacksConfig] = useState(false);
 
   useEffect(() => {
     const project_id = currentProject.id;
@@ -173,12 +171,24 @@ const ActionDetails: React.FC<PropsType> = (props) => {
       />
       {renderRegistrySection()}
       {!dockerfilePath && (
-        <BuildpackSelection
-          actionConfig={actionConfig}
-          branch={branch}
-          folderPath={folderPath}
-          onChange={(config) => {}}
-        />
+        <>
+          <HighlightTrigger
+            onClick={() => setShowBuildpacksConfig((prev) => !prev)}
+          >
+            {showBuildpacksConfig
+              ? "Hide buildpacks config"
+              : "Show buildpacks config"}
+          </HighlightTrigger>
+          <BuildpackSelection
+            actionConfig={actionConfig}
+            branch={branch}
+            folderPath={folderPath}
+            onChange={(config) => {
+              setBuildConfig(config);
+            }}
+            hide={!showBuildpacksConfig}
+          />
+        </>
       )}
       <Br />
 
@@ -215,7 +225,7 @@ export default ActionDetails;
 
 const DEFAULT_BUILDER_NAME = "paketo";
 const DEFAULT_PAKETO_STACK = "paketobuildpacks/builder:full";
-const DEFAULT_HEROKU_STACK = "heroku:20";
+const DEFAULT_HEROKU_STACK = "heroku/buildpacks:20";
 
 type BuildConfig = {
   builder: string;
@@ -225,12 +235,13 @@ type BuildConfig = {
   };
 };
 
-const BuildpackSelection: React.FC<{
+export const BuildpackSelection: React.FC<{
   actionConfig: ActionConfigType;
   folderPath: string;
   branch: string;
+  hide: boolean;
   onChange: (config: BuildConfig) => void;
-}> = ({ actionConfig, folderPath, branch }) => {
+}> = ({ actionConfig, folderPath, branch, hide, onChange }) => {
   const { currentProject } = useContext(Context);
 
   const [builders, setBuilders] = useState<DetectedBuildpack[]>(null);
@@ -247,21 +258,34 @@ const BuildpackSelection: React.FC<{
   );
 
   useEffect(() => {
-    api
-      .detectBuildpack<DetectBuildpackResponse>(
-        "<token>",
-        {
-          dir: folderPath || ".",
-        },
-        {
-          project_id: currentProject.id,
-          git_repo_id: actionConfig.git_repo_id,
-          kind: "github",
-          owner: actionConfig.git_repo.split("/")[0],
-          name: actionConfig.git_repo.split("/")[1],
-          branch: branch,
-        }
-      )
+    let buildConfig: BuildConfig = {} as BuildConfig;
+
+    buildConfig.builder = selectedStack;
+    buildConfig.buildpacks = selectedBuildpacks?.map((buildpack) => {
+      return buildpack.buildpack;
+    });
+    if (typeof onChange === "function") {
+      onChange(buildConfig);
+    }
+  }, [selectedBuilder, selectedStack, selectedBuildpacks]);
+
+  useEffect(() => {
+    // api
+    //   .detectBuildpack<DetectBuildpackResponse>(
+    //     "<token>",
+    //     {
+    //       dir: folderPath || ".",
+    //     },
+    //     {
+    //       project_id: currentProject.id,
+    //       git_repo_id: actionConfig.git_repo_id,
+    //       kind: "github",
+    //       owner: actionConfig.git_repo.split("/")[0],
+    //       name: actionConfig.git_repo.split("/")[1],
+    //       branch: branch,
+    //     }
+    //   )
+    getMockData()
       .then(({ data }) => {
         const builders = data;
 
@@ -278,7 +302,7 @@ const BuildpackSelection: React.FC<{
         });
 
         setBuilders(builders);
-        setSelectedBuilder(defaultBuilder.name);
+        setSelectedBuilder(defaultBuilder.name.toLowerCase());
 
         setStacks(defaultBuilder.builders);
         setSelectedStack(defaultStack);
@@ -313,12 +337,35 @@ const BuildpackSelection: React.FC<{
     }));
   }, [stacks]);
 
-  const renderBuildpacksList = (buildpacks: Buildpack[]) => {
+  const handleSelectBuilder = (builderName: string) => {
+    const builder = builders.find(
+      (b) => b.name.toLowerCase() === builderName.toLowerCase()
+    );
+    const detectedBuildpacks = builder.detected;
+    const availableBuildpacks = builder.others;
+    const defaultStack = builder.builders.find((stack) => {
+      return stack === DEFAULT_HEROKU_STACK || stack === DEFAULT_PAKETO_STACK;
+    });
+    setSelectedBuilder(builderName);
+    setBuilders(builders);
+    setSelectedBuilder(builderName.toLowerCase());
+
+    setStacks(builder.builders);
+    setSelectedStack(defaultStack);
+
+    setSelectedBuildpacks(detectedBuildpacks);
+    setAvailableBuildpacks(availableBuildpacks);
+  };
+
+  const renderBuildpacksList = (
+    buildpacks: Buildpack[],
+    action: "remove" | "add"
+  ) => {
     return buildpacks?.map((buildpack) => {
       const icon = `devicon-${buildpack?.name?.toLowerCase()}-plain colored`;
 
       return (
-        <StyledCard onClick={() => console.log("some")} status={"some"}>
+        <StyledCard>
           <ContentContainer>
             <Icon className={icon} />
             <EventInformation>
@@ -326,11 +373,20 @@ const BuildpackSelection: React.FC<{
             </EventInformation>
           </ContentContainer>
           <ActionContainer>
-            <DeleteButton
-              onClick={() => handleRemoveBuildpack(buildpack.buildpack)}
-            >
-              <span className="material-icons">delete</span>
-            </DeleteButton>
+            {action === "add" && (
+              <DeleteButton
+                onClick={() => handleAddBuildpack(buildpack.buildpack)}
+              >
+                <span className="material-icons-outlined">add</span>
+              </DeleteButton>
+            )}
+            {action === "remove" && (
+              <DeleteButton
+                onClick={() => handleRemoveBuildpack(buildpack.buildpack)}
+              >
+                <span className="material-icons">delete</span>
+              </DeleteButton>
+            )}
           </ActionContainer>
         </StyledCard>
       );
@@ -357,35 +413,49 @@ const BuildpackSelection: React.FC<{
     });
   };
 
-  // const handleAddBuildpack = (buildpackToAdd: string) => {
-  //   setAvailableBuildpacks((avBuildpacks) => {
-  //     const tmpAvailableBuildpacks = [...avBuildpacks];
-  //     return []
-  //   })
-  // }
-
-  if (
-    !stackOptions?.length ||
-    !builderOptions?.length ||
-    !availableBuildpacks?.length
-  ) {
+  const handleAddBuildpack = (buildpackToAdd: string) => {
+    setAvailableBuildpacks((avBuildpacks) => {
+      const tmpAvailableBuildpacks = [...avBuildpacks];
+      const indexBuildpackToAdd = tmpAvailableBuildpacks.findIndex(
+        (buildpack) => buildpack.buildpack === buildpackToAdd
+      );
+      const buildpack = tmpAvailableBuildpacks[indexBuildpackToAdd];
+
+      setSelectedBuildpacks((selectedBuildpacks) => [
+        ...selectedBuildpacks,
+        buildpack,
+      ]);
+
+      tmpAvailableBuildpacks.splice(indexBuildpackToAdd, 1);
+      return [...tmpAvailableBuildpacks];
+    });
+  };
+
+  if (hide) {
+    return null;
+  }
+
+  if (!stackOptions?.length || !builderOptions?.length) {
     return <>Loading...</>;
   }
 
   return (
-    <>
+    <BuildpackConfigurationContainer>
       <>
-        <Selector
-          activeValue={selectedBuilder}
+        <SelectRow
+          value={selectedBuilder}
           width="100%"
           options={builderOptions}
-          setActiveValue={(option) => setSelectedBuilder(option)}
+          setActiveValue={(option) => handleSelectBuilder(option)}
+          label="Select a builder"
         />
-        <Selector
-          activeValue={selectedStack}
+
+        <SelectRow
+          value={selectedStack}
           width="100%"
           options={stackOptions}
           setActiveValue={(option) => setSelectedStack(option)}
+          label="Select your stack"
         />
         <Heading>Buildpacks</Heading>
         <Helper>
@@ -393,13 +463,108 @@ const BuildpackSelection: React.FC<{
           you want
         </Helper>
 
-        {availableBuildpacks?.length &&
-          renderBuildpacksList(availableBuildpacks)}
+        {!!selectedBuildpacks?.length &&
+          renderBuildpacksList(selectedBuildpacks, "remove")}
+
+        {!!availableBuildpacks?.length && (
+          <>
+            <Helper>Available buildpacks</Helper>
+            {renderBuildpacksList(availableBuildpacks, "add")}
+          </>
+        )}
       </>
-    </>
+    </BuildpackConfigurationContainer>
   );
 };
 
+const getMockData = () =>
+  new Promise<{ data: DetectBuildpackResponse }>((res) => {
+    setTimeout(() => res({ data: mock_data }), 1000);
+  });
+
+const mock_data: DetectBuildpackResponse = [
+  {
+    name: "Paketo",
+    builders: [
+      "paketobuildpacks/builder:full",
+      "paketobuildpacks/builder:tiny",
+      "paketobuildpacks/builder:base",
+    ],
+    detected: [
+      {
+        name: "NodeJS",
+        buildpack: "paketobuildpacks/nodejs",
+        config: null,
+      },
+    ],
+    others: [
+      {
+        name: "Python",
+        buildpack: "paketobuildpacks/python",
+        config: null,
+      },
+      {
+        name: "Go",
+        buildpack: "paketobuildpacks/go",
+        config: null,
+      },
+      {
+        name: "Ruby",
+        buildpack: "paketobuildpacks/ruby",
+        config: null,
+      },
+    ],
+  },
+  {
+    name: "Heroku",
+    builders: ["heroku/buildpacks:20", "heroku/buildpacks:18"],
+    detected: [
+      {
+        name: "NodeJS",
+        buildpack: "heroku/nodejs",
+        config: null,
+      },
+      {
+        name: "Python",
+        buildpack: "heroku/python",
+        config: null,
+      },
+    ],
+    others: [
+      {
+        name: "Go",
+        buildpack: "heroku/go",
+        config: null,
+      },
+      {
+        name: "Ruby",
+        buildpack: "heroku/ruby",
+        config: null,
+      },
+    ],
+  },
+];
+
+const fadeIn = keyframes`
+  from {
+    opacity: 0;
+  }
+  to {
+    opacity: 1;
+  }
+`;
+
+const BuildpackConfigurationContainer = styled.div`
+  animation: ${fadeIn} 0.75s;
+`;
+
+const HighlightTrigger = styled.span`
+  color: #8590ff;
+  text-decoration: none;
+  cursor: pointer;
+  display: inline;
+`;
+
 const Required = styled.div`
   margin-left: 8px;
   color: #fc4976;
@@ -536,16 +701,7 @@ const DarkMatter = styled.div`
   margin-bottom: -18px;
 `;
 
-const fadeIn = keyframes`
-  from {
-    opacity: 0;
-  }
-  to {
-    opacity: 1;
-  }
-`;
-
-const StyledCard = styled.div<{ status: string }>`
+const StyledCard = styled.div`
   display: flex;
   align-items: center;
   justify-content: space-between;

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

@@ -27,6 +27,7 @@ import discordLogo from "../../assets/discord.svg";
 import Onboarding from "./onboarding/Onboarding";
 import ModalHandler from "./ModalHandler";
 import { NewProjectFC } from "./new-project/NewProject";
+import { BuildpackSelection } from "components/repo-selector/ActionDetails";
 
 // Guarded components
 const GuardedProjectSettings = fakeGuardedRoute("settings", "", [

+ 2 - 0
dashboard/src/main/home/launch/launch-flow/LaunchFlow.tsx

@@ -64,6 +64,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
   const [folderPath, setFolderPath] = useState(null);
   const [selectedRegistry, setSelectedRegistry] = useState(null);
   const [shouldCreateWorkflow, setShouldCreateWorkflow] = useState(true);
+  const [buildConfig, setBuildConfig] = useState();
 
   const generateRandomName = () => {
     const randomTemplateName = randomWords({ exactly: 3, join: "-" });
@@ -328,6 +329,7 @@ const LaunchFlow: React.FC<PropsType> = (props) => {
           setProcfilePath={setProcfilePath}
           selectedRegistry={selectedRegistry}
           setSelectedRegistry={setSelectedRegistry}
+          setBuildConfig={setBuildConfig}
         />
       );
     }

+ 3 - 0
dashboard/src/main/home/launch/launch-flow/SourcePage.tsx

@@ -45,6 +45,7 @@ type PropsType = RouteComponentProps & {
   setFolderPath: (x: string) => void;
   selectedRegistry: any;
   setSelectedRegistry: (x: string) => void;
+  setBuildConfig: (x: any) => void;
 };
 
 type StateType = {};
@@ -144,6 +145,7 @@ class SourcePage extends Component<PropsType, StateType> {
       setFolderPath,
       selectedRegistry,
       setSelectedRegistry,
+      setBuildConfig,
     } = this.props;
     return (
       <StyledSourceBox>
@@ -207,6 +209,7 @@ class SourcePage extends Component<PropsType, StateType> {
           }}
           setSelectedRegistry={setSelectedRegistry}
           selectedRegistry={selectedRegistry}
+          setBuildConfig={setBuildConfig}
         />
         <br />
       </StyledSourceBox>

+ 1 - 0
dashboard/src/shared/api.tsx

@@ -321,6 +321,7 @@ const deployTemplate = baseApi<
     values?: any;
     name: string;
     github_action_config?: FullActionConfigType;
+    build_config?: any;
   },
   {
     id: number;