Explorar el Código

Implemented update source config display name on frontend

jnfrati hace 3 años
padre
commit
7204323a0f

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

@@ -218,7 +218,11 @@ const ExpandedStack = () => {
             component: (
               <>
                 <Gap></Gap>
-                <Settings stackName={stack.name} onDelete={handleDelete} />
+                <Settings
+                  stack={stack}
+                  onDelete={handleDelete}
+                  onUpdate={refreshStack}
+                />
               </>
             ),
           },

+ 108 - 72
dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/_SourceConfig.tsx

@@ -1,11 +1,9 @@
-import { Tooltip } from "@material-ui/core";
-import ImageSelector from "components/image-selector/ImageSelector";
 import SaveButton from "components/SaveButton";
-import React, { useContext, useMemo, useState } from "react";
+import React, { useContext, useReducer, useRef, useState } from "react";
 import api from "shared/api";
 import { Context } from "shared/Context";
 import styled from "styled-components";
-import { AppResource, FullStackRevision, SourceConfig, Stack } from "../types";
+import { FullStackRevision, SourceConfig } from "../types";
 import SourceEditorDocker from "./components/SourceEditorDocker";
 
 const _SourceConfig = ({
@@ -64,39 +62,13 @@ const _SourceConfig = ({
   return (
     <SourceConfigStyles.Wrapper>
       {revision.source_configs.map((sourceConfig) => {
-        const apps = getAppsFromSourceConfig(revision.resources, sourceConfig);
-
-        const appList = formatAppList(apps, 2);
         return (
-          <SourceConfigStyles.ItemContainer>
-            {appList.hiddenApps?.length ? (
-              <Tooltip
-                title={
-                  <>
-                    {appList.hiddenApps.map((appName) => (
-                      <SourceConfigStyles.TooltipItem>
-                        {appName}
-                      </SourceConfigStyles.TooltipItem>
-                    ))}
-                  </>
-                }
-                placement={"bottom-end"}
-              >
-                <SourceConfigStyles.ItemTitle>
-                  Used by {appList.value}
-                </SourceConfigStyles.ItemTitle>
-              </Tooltip>
-            ) : (
-              <SourceConfigStyles.ItemTitle>
-                Used by {appList.value}
-              </SourceConfigStyles.ItemTitle>
-            )}
-            <SourceEditorDocker
-              sourceConfig={sourceConfig}
-              onChange={handleChange}
-              readOnly={readOnly || buttonStatus === "loading"}
-            />
-          </SourceConfigStyles.ItemContainer>
+          <SourceConfigItem
+            sourceConfig={sourceConfig}
+            key={sourceConfig.id}
+            handleChange={handleChange}
+            disabled={readOnly || buttonStatus === "loading"}
+          />
         );
       })}
       {readOnly ? null : (
@@ -117,41 +89,6 @@ const _SourceConfig = ({
 
 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;
@@ -164,8 +101,17 @@ const SourceConfigStyles = {
   `,
   ItemTitle: styled.div`
     font-size: 16px;
-    width: fit-content;
     font-weight: 500;
+
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    gap: 10px;
+    > span {
+      overflow-x: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+    }
   `,
   TooltipItem: styled.div`
     font-size: 14px;
@@ -179,3 +125,93 @@ const SourceConfigStyles = {
     z-index: unset;
   `,
 };
+
+const SourceConfigItem = ({
+  sourceConfig,
+  handleChange,
+  disabled,
+}: {
+  sourceConfig: SourceConfig;
+  handleChange: (sourceConfig: SourceConfig) => void;
+  disabled: boolean;
+}) => {
+  const [editNameMode, toggleEditNameMode] = useReducer((prev) => !prev, false);
+  const prevName = useRef(sourceConfig.name);
+  const [name, setName] = useState(
+    sourceConfig.display_name || sourceConfig.name
+  );
+
+  const handleNameChange = (newName: string) => {
+    setName(newName);
+    handleChange({ ...sourceConfig, name: newName });
+  };
+
+  const handleNameChangeCancel = () => {
+    setName(prevName.current);
+    handleChange({ ...sourceConfig, name: prevName.current });
+    toggleEditNameMode();
+  };
+
+  return (
+    <SourceConfigStyles.ItemContainer>
+      {editNameMode && !disabled ? (
+        <>
+          <SourceConfigStyles.ItemTitle>
+            <PlainTextInput
+              value={name}
+              onChange={(e) => handleNameChange(e.target.value)}
+              type="text"
+              disabled={disabled}
+            />
+            <EditButton onClick={handleNameChangeCancel}>
+              <i className="material-icons-outlined">close</i>
+            </EditButton>
+          </SourceConfigStyles.ItemTitle>
+        </>
+      ) : (
+        <SourceConfigStyles.ItemTitle>
+          <span>{name}</span>
+
+          <EditButton onClick={toggleEditNameMode}>
+            <i className="material-icons-outlined">edit</i>
+          </EditButton>
+        </SourceConfigStyles.ItemTitle>
+      )}
+
+      <SourceEditorDocker
+        sourceConfig={sourceConfig}
+        onChange={handleChange}
+        readOnly={disabled}
+      />
+    </SourceConfigStyles.ItemContainer>
+  );
+};
+
+const EditButton = styled.button`
+  outline: none;
+  cursor: pointer;
+  color: white;
+  border: 1px solid rgba(255, 255, 255, 0.333);
+  background: rgba(255, 255, 255, 0.067);
+  height: 35px;
+  width: 35px;
+  border-radius: 24px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  > i {
+    font-size: 20px;
+  }
+`;
+
+const PlainTextInput = styled.input`
+  outline: none;
+  border: 1px solid #ffffff55;
+  border-radius: 3px;
+  font-size: 13px;
+  background: #ffffff11;
+  width: 100%;
+  color: white;
+  padding: 5px 10px;
+  height: 35px;
+`;

+ 67 - 5
dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/components/Settings.tsx

@@ -1,16 +1,30 @@
 import Heading from "components/form-components/Heading";
-import React, { useContext } from "react";
+import Helper from "components/form-components/Helper";
+import InputRow from "components/form-components/InputRow";
+import React, { useContext, useState } from "react";
+import api from "shared/api";
 import { Context } from "shared/Context";
 import styled from "styled-components";
+import { SubmitButton } from "../../launch/components/styles";
+import { Stack } from "../../types";
 
 const Settings = ({
-  stackName,
+  stack,
   onDelete,
+  onUpdate,
 }: {
-  stackName: string;
+  stack: Stack;
   onDelete: () => void;
+  onUpdate: () => Promise<void>;
 }) => {
-  const { setCurrentOverlay } = useContext(Context);
+  const {
+    currentCluster,
+    currentProject,
+    setCurrentOverlay,
+    setCurrentError,
+  } = useContext(Context);
+  const [stackName, setStackName] = useState(stack.name);
+  const [buttonStatus, setButtonStatus] = useState("");
 
   const handleDelete = () => {
     setCurrentOverlay({
@@ -22,10 +36,54 @@ const Settings = ({
       onNo: () => setCurrentOverlay(null),
     });
   };
+
+  const handleStackNameChange = async () => {
+    setButtonStatus("loading");
+    try {
+      await api.updateStack(
+        "<token>",
+        {
+          name: stackName,
+        },
+        {
+          project_id: currentProject.id,
+          cluster_id: currentCluster.id,
+          stack_id: stack.id,
+          namespace: stack.namespace,
+        }
+      );
+      await onUpdate();
+      setButtonStatus("successful");
+    } catch (err) {
+      setCurrentError(err);
+      setButtonStatus("Couldn't update the stack name. Try again later.");
+    }
+  };
+
   return (
     <Wrapper>
       <StyledSettingsSection>
-        <Heading>Settings</Heading>
+        <Heading>Update Stack name</Heading>
+
+        <InputRow
+          label="Stack name"
+          value={stackName}
+          setValue={setStackName as any}
+          type="text"
+          width="300px"
+        />
+        <SaveButton
+          text="Update"
+          onClick={handleStackNameChange}
+          disabled={stackName === stack.name}
+          makeFlush
+          clearPosition
+          statusPosition="right"
+          status={buttonStatus}
+        ></SaveButton>
+
+        <Heading>Additional Settings</Heading>
+
         <Button color="#b91133" onClick={handleDelete}>
           Delete stack
         </Button>
@@ -36,6 +94,10 @@ const Settings = ({
 
 export default Settings;
 
+const SaveButton = styled(SubmitButton)`
+  justify-content: flex-start;
+`;
+
 const Wrapper = styled.div`
   width: 100%;
   padding-bottom: 65px;

+ 20 - 2
dashboard/src/main/home/cluster-dashboard/stacks/launch/SelectSource.tsx

@@ -8,10 +8,11 @@ import Helper from "components/form-components/Helper";
 import Heading from "components/form-components/Heading";
 import styled from "styled-components";
 import TitleSection from "components/TitleSection";
+import InputRow from "components/form-components/InputRow";
 
 const SelectSource = () => {
   const { addSourceConfig } = useContext(StacksLaunchContext);
-
+  const [sourceName, setSourceName] = useState("");
   const [imageUrl, setImageUrl] = useState("");
   const [imageTag, setImageTag] = useState("");
   const { pushFiltered } = useRouting();
@@ -22,6 +23,7 @@ const SelectSource = () => {
     }
 
     const newSource: Omit<CreateStackBody["source_configs"][0], "name"> = {
+      display_name: sourceName,
       image_repo_uri: imageUrl,
       image_tag: imageTag,
     };
@@ -39,11 +41,23 @@ const SelectSource = () => {
         New Application Stack
       </TitleSection>
       <Heading>Stack Source</Heading>
+
+      <Br />
+      <InputRowWrapper>
+        <InputRow
+          label="Source Name"
+          value={sourceName}
+          setValue={(val) => setSourceName(val as string)}
+          type="text"
+          width="100%"
+          placeholder="Leave empty for auto-generated source config name"
+        />
+      </InputRowWrapper>
+
       <Helper>
         Specify a source to deploy all stack applications from:
         <Required>*</Required>
       </Helper>
-      <Br />
       <ImageSelector
         selectedImageUrl={imageUrl}
         setSelectedImageUrl={setImageUrl}
@@ -86,3 +100,7 @@ const Polymer = styled.div`
     margin-right: 18px;
   }
 `;
+
+const InputRowWrapper = styled.div`
+  width: 60%;
+`;

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

@@ -96,6 +96,9 @@ const StacksLaunchContextProvider: React.FC<{}> = ({ children }) => {
       source_configs: [
         ...prev.source_configs,
         {
+          display_name:
+            sourceConfig.display_name ||
+            newSourceConfigName(prev.source_configs.length),
           name: newSourceConfigName(prev.source_configs.length),
           ...sourceConfig,
         },

+ 2 - 0
dashboard/src/main/home/cluster-dashboard/stacks/types.ts

@@ -9,6 +9,7 @@ export type CreateStackBody = {
     values: unknown;
   }[];
   source_configs: {
+    display_name: string;
     name: string;
     image_repo_uri: string;
     image_tag: string;
@@ -80,6 +81,7 @@ export type StackRevision = {
 
 export type SourceConfig = {
   id: string;
+  display_name: string;
   name: string;
   created_at: string;
   updated_at: string;