فهرست منبع

Merge pull request #2700 from porter-dev/capi-provisioner-fe

Form UI
jusrhee 3 سال پیش
والد
کامیت
db87915fd5
26فایلهای تغییر یافته به همراه243 افزوده شده و 175 حذف شده
  1. 6 1
      dashboard/src/components/SaveButton.tsx
  2. 1 1
      dashboard/src/components/form-components/Heading.tsx
  3. 1 1
      dashboard/src/components/form-components/Helper.tsx
  4. 20 9
      dashboard/src/components/porter-form/PorterForm.tsx
  5. 1 0
      dashboard/src/components/porter-form/PorterFormWrapper.tsx
  6. 2 2
      dashboard/src/components/repo-selector/ActionConfEditor.tsx
  7. 2 2
      dashboard/src/components/repo-selector/ActionDetails.tsx
  8. 2 1
      dashboard/src/components/repo-selector/RepoList.tsx
  9. 16 12
      dashboard/src/main/home/cluster-dashboard/dashboard/ClusterSettings.tsx
  10. 37 5
      dashboard/src/main/home/cluster-dashboard/dashboard/Dashboard.tsx
  11. 70 56
      dashboard/src/main/home/cluster-dashboard/env-groups/CreateEnvGroup.tsx
  12. 9 31
      dashboard/src/main/home/cluster-dashboard/env-groups/ExpandedEnvGroup.tsx
  13. 2 2
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedJobChart.tsx
  14. 10 8
      dashboard/src/main/home/cluster-dashboard/expanded-chart/RevisionSection.tsx
  15. 6 4
      dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx
  16. 4 1
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ValuesYaml.tsx
  17. 1 1
      dashboard/src/main/home/cluster-dashboard/expanded-chart/build-settings/BuildSettingsTab.tsx
  18. 2 2
      dashboard/src/main/home/cluster-dashboard/stacks/ExpandedStack/_RevisionList.tsx
  19. 12 8
      dashboard/src/main/home/integrations/create-integration/ECRForm.tsx
  20. 12 9
      dashboard/src/main/home/integrations/create-integration/GARForm.tsx
  21. 11 7
      dashboard/src/main/home/integrations/create-integration/GCRForm.tsx
  22. 3 2
      dashboard/src/main/home/launch/launch-flow/SettingsPage.tsx
  23. 7 4
      dashboard/src/main/home/launch/launch-flow/SourcePage.tsx
  24. 1 1
      dashboard/src/main/home/launch/launch-flow/WorkflowPage.tsx
  25. 0 5
      dashboard/src/main/home/navbar/Help.tsx
  26. 5 0
      go.work.sum

+ 6 - 1
dashboard/src/components/SaveButton.tsx

@@ -19,6 +19,8 @@ type Props = {
   // Provide the classname to modify styles from other components
   className?: string;
   successText?: string;
+
+  absoluteSave?: boolean;
 };
 
 const SaveButton: React.FC<Props> = (props) => {
@@ -68,6 +70,7 @@ const SaveButton: React.FC<Props> = (props) => {
 
   return (
     <ButtonWrapper
+      absoluteSave={props.absoluteSave}
       makeFlush={props.makeFlush}
       clearPosition={props.clearPosition}
       className={props.className}
@@ -147,9 +150,11 @@ const StatusWrapper = styled.div<{
 `;
 
 const ButtonWrapper = styled.div`
-  ${(props: { makeFlush: boolean; clearPosition?: boolean }) => {
+  ${(props: { makeFlush: boolean; clearPosition?: boolean; absoluteSave: boolean }) => {
     const baseStyles = `
       display: flex;
+      position: ${props.absoluteSave ? "absolute" : ""};
+      bottom: ${props.absoluteSave ? "5px" : ""};
       align-items: center;
       z-index: 99;
     `;

+ 1 - 1
dashboard/src/components/form-components/Heading.tsx

@@ -20,9 +20,9 @@ export default function Heading(props: {
 
 const StyledHeading = styled.div<{ isAtTop: boolean }>`
   color: white;
+  margin-top: ${props => props.isAtTop ? "" : "40px"};
   font-weight: 500;
   font-size: 16px;
-  margin-top: ${(props) => (props.isAtTop ? "0" : "30px")};
   margin-bottom: 5px;
   display: flex;
   align-items: center;

+ 1 - 1
dashboard/src/components/form-components/Helper.tsx

@@ -5,7 +5,7 @@ export const Helper = styled.div<{ color?: string }>`
   color: ${({ color }) => (color ? color : "#aaaabb")};
   line-height: 1.6em;
   font-size: 13px;
-  margin-bottom: 35px;
+  margin-bottom: 20px;
   margin-top: 20px;
 `;
 

+ 20 - 9
dashboard/src/components/porter-form/PorterForm.tsx

@@ -53,6 +53,8 @@ interface Props {
   // The tab to redirect to after saving the form
   redirectTabAfterSave?: string;
   injectedProps?: InjectedProps;
+
+  absoluteSave: boolean;
 }
 
 const PorterForm: React.FC<Props> = (props) => {
@@ -66,7 +68,7 @@ const PorterForm: React.FC<Props> = (props) => {
 
   const { currentTab, setCurrentTab } = props;
 
-  const renderSectionField = (field: FormField): JSX.Element => {
+  const renderSectionField = (field: FormField, num?: number, i?: number): JSX.Element => {
     const injected = props.injectedProps?.[field.type];
 
     const bundledProps = {
@@ -77,7 +79,9 @@ const PorterForm: React.FC<Props> = (props) => {
 
     switch (field.type) {
       case "heading":
-        return <Heading>{field.label}</Heading>;
+        // Remove top margin from heading if it's the first form element in the tab
+        // TODO: Handle Job form and form variables more gracefully
+        return <Heading isAtTop={num + i < 1 || (formData.name === "Job" && num + i === 1)}>{field.label}</Heading>;
       case "subtitle":
         return <Helper>{field.label}</Helper>;
       case "input":
@@ -106,13 +110,13 @@ const PorterForm: React.FC<Props> = (props) => {
     return <p>Not Implemented: {(field as any).type}</p>;
   };
 
-  const renderSection = (section: Section): JSX.Element => {
+  const renderSection = (section: Section, num: number): JSX.Element => {
     return (
       <>
         {section.contents?.map((field, i) => {
           return (
             <React.Fragment key={field.id}>
-              {renderSectionField(field)}
+              {renderSectionField(field, num, i)}
             </React.Fragment>
           );
         })}
@@ -172,10 +176,10 @@ const PorterForm: React.FC<Props> = (props) => {
 
     return (
       <StyledPorterForm showSave={showSaveButton()}>
-        {tab.sections?.map((section) => {
+        {tab.sections?.map((section, i) => {
           return (
             <React.Fragment key={section.name}>
-              {renderSection(section)}
+              {renderSection(section, i)}
             </React.Fragment>
           );
         })}
@@ -221,12 +225,15 @@ const PorterForm: React.FC<Props> = (props) => {
       <br />
       {showSaveButton() && (
         <SaveButton
-          text={props.saveButtonText || "Deploy"}
+          text={props.saveButtonText || "Deploy app"}
           onClick={submit}
+          absoluteSave={props.absoluteSave}
+          clearPosition={true}
           makeFlush={!props.isInModal}
           status={
             validationInfo.validated ? renderSaveStatus() : validationInfo.error
           }
+          statusPosition="right"
           disabled={isDisabled()}
         />
       )}
@@ -252,11 +259,15 @@ const Spacer = styled.div`
 const StyledPorterForm = styled.div<{ showSave?: boolean }>`
   width: 100%;
   height: ${(props) => (props.showSave ? "calc(100% - 50px)" : "100%")};
-  background: #ffffff11;
   color: #ffffff;
-  padding: 0px 35px 20px;
   position: relative;
   border-radius: 8px;
   font-size: 13px;
   overflow: auto;
+  padding: 30px;
+  margin-bottom: 5px;
+  font-size: 13px;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
 `;

+ 1 - 0
dashboard/src/components/porter-form/PorterFormWrapper.tsx

@@ -116,6 +116,7 @@ const PorterFormWrapper: React.FC<PropsType> = ({
           hideSpacer={hideBottomSpacer}
           redirectTabAfterSave={redirectTabAfterSave}
           injectedProps={injectedProps}
+          absoluteSave
         />
       </PorterFormContextProvider>
     </React.Fragment>

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

@@ -65,7 +65,7 @@ const ActionConfEditor: React.FC<Props> = (props) => {
           }}
         >
           <i className="material-icons">keyboard_backspace</i>
-          Select Repo
+          Select repo
         </BackButton>
       </>
     );
@@ -103,7 +103,7 @@ const ActionConfEditor: React.FC<Props> = (props) => {
           }}
         >
           <i className="material-icons">keyboard_backspace</i>
-          Select Branch
+          Select branch
         </BackButton>
       </>
     );

+ 2 - 2
dashboard/src/components/repo-selector/ActionDetails.tsx

@@ -112,7 +112,7 @@ const ActionDetails: React.FC<PropsType> = (props) => {
   return (
     <>
       <DarkMatter />
-      <Heading>GitHub Settings</Heading>
+      <Heading>GitHub settings</Heading>
       <InputRow
         disabled={true}
         label="Git repository"
@@ -183,7 +183,7 @@ const ActionDetails: React.FC<PropsType> = (props) => {
           }}
         >
           <i className="material-icons">keyboard_backspace</i>
-          Select Folder
+          Select folder
         </BackButton>
         {selectedRegistry ? (
           <StatusWrapper successful={true}>

+ 2 - 1
dashboard/src/components/repo-selector/RepoList.tsx

@@ -453,7 +453,8 @@ const ProviderSelectorStyles = {
     }
   `,
   Icon: styled.span`
-    font-size: 24px;
+    font-size: 20px;
+    filter: invert(1);
     margin-left: 9px;
     margin-right: -29px;
     color: white;

+ 16 - 12
dashboard/src/main/home/cluster-dashboard/dashboard/ClusterSettings.tsx

@@ -162,14 +162,14 @@ const ClusterSettings: React.FC = () => {
     if (successfulRotate) {
       keyRotationSection = (
         <div>
-          <Heading>Credential Rotation</Heading>
+          <Heading>Credential rotation</Heading>
           <Helper>Successfully rotated credentials!</Helper>
         </div>
       );
     } else if (startRotateCreds) {
       keyRotationSection = (
         <div>
-          <Heading>Credential Rotation</Heading>
+          <Heading>Credential rotation</Heading>
           <Helper>Input the new credentials for the EKS cluster.</Helper>
           <InputRow
             type="text"
@@ -197,12 +197,12 @@ const ClusterSettings: React.FC = () => {
     } else {
       keyRotationSection = (
         <div>
-          <Heading>Credential Rotation</Heading>
+          <Heading>Credential rotation</Heading>
           <Helper>
             Rotate the credentials that Porter uses to connect to the cluster.
           </Helper>
           <Button color="#616FEEcc" onClick={() => setStartRotateCreds(true)}>
-            Rotate Credentials
+            Rotate credentials
           </Button>
         </div>
       );
@@ -225,7 +225,7 @@ const ClusterSettings: React.FC = () => {
 
   let renameClusterSection = (
     <div>
-      <Heading>Rename Cluster</Heading>
+      <Heading>Rename cluster</Heading>
       <InputRow
         type="text"
         value={newClusterName}
@@ -244,7 +244,7 @@ const ClusterSettings: React.FC = () => {
 
   let enableAgentIntegration = (
     <div>
-      <Heading>Enable Agent</Heading>
+      <Heading>Enable agent</Heading>
       <CheckboxRow
         label={"Allow the Porter agent to be installed on the cluster"}
         toggle={() => setEnableAgent(!enableAgent)}
@@ -268,7 +268,7 @@ const ClusterSettings: React.FC = () => {
     } else {
       enablePreviewEnvironments = (
         <div>
-          <Heading>Enable Preview Environments</Heading>
+          <Heading>Enable preview environments</Heading>
           <CheckboxRow
             label={"Create preview environments on this cluster"}
             toggle={() => setEnablePreviewEnvs(!enablePreviewEnvs)}
@@ -289,7 +289,7 @@ const ClusterSettings: React.FC = () => {
   if (successfulRename) {
     renameClusterSection = (
       <div>
-        <Heading>Credential Rotation</Heading>
+        <Heading>Credential rotation</Heading>
         <Helper>Successfully renamed the cluster! Reload the page.</Helper>
       </div>
     );
@@ -298,6 +298,7 @@ const ClusterSettings: React.FC = () => {
   return (
     <div>
       <StyledSettingsSection>
+        <DarkMatter />
         {enableAgentIntegration}
         <DarkMatter />
         {enablePreviewEnvironments}
@@ -307,13 +308,13 @@ const ClusterSettings: React.FC = () => {
         {/* Disabled this field due to https://discord.com/channels/542888846271184896/856554532972134420/1042497537912864788 */}
         {/* {renameClusterSection}
         <DarkMatter /> */}
-        <Heading>Delete Cluster</Heading>
+        <Heading>Delete cluster</Heading>
         {helperText}
         <Button
           color="#b91133"
           onClick={() => setCurrentModal("UpdateClusterModal")}
         >
-          Delete Cluster
+          Delete cluster
         </Button>
       </StyledSettingsSection>
     </div>
@@ -330,13 +331,16 @@ const DarkMatter = styled.div`
 const StyledSettingsSection = styled.div`
   margin-top: 35px;
   width: 100%;
-  background: #ffffff11;
-  padding: 0 35px;
+  padding: 30px;
+  padding-top: 5px;
   padding-bottom: 15px;
   position: relative;
   border-radius: 8px;
   overflow: auto;
   height: 100%;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
 `;
 
 const Button = styled.button`

+ 37 - 5
dashboard/src/main/home/cluster-dashboard/dashboard/Dashboard.tsx

@@ -3,6 +3,7 @@ import styled from "styled-components";
 
 import { Context } from "shared/Context";
 import TabSelector from "components/TabSelector";
+import Heading from "components/form-components/Heading";
 import TitleSection from "components/TitleSection";
 import api from "shared/api";
 
@@ -19,13 +20,15 @@ import CopyToClipboard from "components/CopyToClipboard";
 import Loading from "components/Loading";
 
 import { DetailedIngressError } from "shared/types";
+import SelectRow from "components/form-components/SelectRow";
 
-type TabEnum = "nodes" | "settings" | "namespaces" | "metrics" | "incidents";
+type TabEnum = "nodes" | "settings" | "namespaces" | "metrics" | "incidents" | "configuration";
 
 const tabOptions: {
   label: string;
   value: TabEnum;
 }[] = [
+  // { label: "Configuration", value: "configuration" },
   { label: "Nodes", value: "nodes" },
   /*
   { label: "Incidents", value: "incidents" },
@@ -52,7 +55,25 @@ export const Dashboard: React.FunctionComponent = () => {
         return <Metrics />;
       case "namespaces":
         return <NamespaceList />;
-      case "nodes":
+      /*
+      case "configuration":
+        return (
+          <FormWrapper>
+            <Heading isAtTop>
+              Cluster configuration
+            </Heading>
+            <SelectRow
+              value={"us-east-1"}
+              width="150px"
+              options={[
+                { label: "us-east-1", value: "us-east-1" }
+              ]}
+              setActiveValue={(option) => null}
+              label="AWS region"
+            />
+          </FormWrapper>
+        );
+      */
       default:
         return <NodeList />;
     }
@@ -112,15 +133,18 @@ export const Dashboard: React.FunctionComponent = () => {
     }
 
     return (
+      <>
+      <Bolded>To configure custom domains for your apps, add a CNAME record pointing to the following Ingress IP:</Bolded>
+      <br /><br />
       <CopyToClipboard
         as={Url}
         text={ingressIp}
         wrapperProps={{ onClick: (e: any) => e.stopPropagation() }}
       >
-        <Bolded>Ingress IP:</Bolded>
         <span>{ingressIp}</span>
         <i className="material-icons-outlined">content_copy</i>
       </CopyToClipboard>
+      </>
     );
   };
 
@@ -169,7 +193,6 @@ export const Dashboard: React.FunctionComponent = () => {
         currentTab={currentTab}
         setCurrentTab={(value: TabEnum) => setCurrentTab(value)}
       />
-
       {renderTab()}
     </>
   );
@@ -221,7 +244,7 @@ const InfoSection = styled.div`
   margin-top: 36px;
   font-family: "Work Sans", sans-serif;
   margin-left: 0px;
-  margin-bottom: 35px;
+  margin-bottom: 30px;
 `;
 
 const Url = styled.a`
@@ -248,3 +271,12 @@ const Bolded = styled.span`
   margin-right: 6px;
   white-space: nowrap;
 `;
+
+const FormWrapper = styled.div<{ showSave?: boolean }>`
+  width: 100%;
+  margin-top: 35px;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  padding: 30px;
+`;

+ 70 - 56
dashboard/src/main/home/cluster-dashboard/env-groups/CreateEnvGroup.tsx

@@ -147,67 +147,71 @@ export default class CreateEnvGroup extends Component<PropsType, StateType> {
               <i className="material-icons">keyboard_backspace</i>
               Back
             </Button>
-            <Title>Create an Environment Group</Title>
+            <Title>Create an environment group</Title>
           </HeaderSection>
-          <DarkMatter antiHeight="-13px" />
-          <Heading isAtTop={true}>Name</Heading>
-          <Subtitle>
-            <Warning
-              makeFlush={true}
-              highlight={
-                !isAlphanumeric(this.state.envGroupName) &&
-                this.state.envGroupName !== ""
-              }
-            >
-              Lowercase letters, numbers, and "-" only.
-            </Warning>
-          </Subtitle>
-          <DarkMatter antiHeight="-29px" />
-          <InputRow
-            type="text"
-            value={this.state.envGroupName}
-            setValue={(x: string) => this.setState({ envGroupName: x })}
-            placeholder="ex: doctor-scientist"
-            width="100%"
-          />
+          <Wrapper>
+            <DarkMatter antiHeight="-13px" />
+            <Heading isAtTop={true}>Name</Heading>
+            <Subtitle>
+              <Warning
+                makeFlush={true}
+                highlight={
+                  !isAlphanumeric(this.state.envGroupName) &&
+                  this.state.envGroupName !== ""
+                }
+              >
+                Lowercase letters, numbers, and "-" only.
+              </Warning>
+            </Subtitle>
+            <DarkMatter antiHeight="-29px" />
+            <InputRow
+              type="text"
+              value={this.state.envGroupName}
+              setValue={(x: string) => this.setState({ envGroupName: x })}
+              placeholder="ex: doctor-scientist"
+              width="100%"
+            />
 
-          <Heading>Destination</Heading>
-          <Subtitle>
-            Specify the namespace you would like to create this environment
-            group in.
-          </Subtitle>
-          <DestinationSection>
-            <NamespaceLabel>
-              <i className="material-icons">view_list</i>Namespace
-            </NamespaceLabel>
-            <Selector
-              key={"namespace"}
-              activeValue={this.state.selectedNamespace}
-              setActiveValue={(namespace: string) =>
-                this.setState({ selectedNamespace: namespace })
-              }
-              options={this.state.namespaceOptions}
-              width="250px"
-              dropdownWidth="335px"
-              closeOverlay={true}
+            <Heading>Destination</Heading>
+            <Subtitle>
+              Specify the namespace you would like to create this environment
+              group in.
+            </Subtitle>
+            <DestinationSection>
+              <NamespaceLabel>
+                <i className="material-icons">view_list</i>Namespace
+              </NamespaceLabel>
+              <Selector
+                key={"namespace"}
+                activeValue={this.state.selectedNamespace}
+                setActiveValue={(namespace: string) =>
+                  this.setState({ selectedNamespace: namespace })
+                }
+                options={this.state.namespaceOptions}
+                width="250px"
+                dropdownWidth="335px"
+                closeOverlay={true}
+              />
+            </DestinationSection>
+
+            <Heading>Environment variables</Heading>
+            <Helper>
+              Set environment variables for your secrets and environment-specific
+              configuration.
+            </Helper>
+            <EnvGroupArray
+              namespace={this.state.selectedNamespace}
+              values={this.state.envVariables}
+              setValues={(x: any) => this.setState({ envVariables: x })}
+              fileUpload={true}
+              secretOption={true}
             />
-          </DestinationSection>
-
-          <Heading>Environment variables</Heading>
-          <Helper>
-            Set environment variables for your secrets and environment-specific
-            configuration.
-          </Helper>
-          <EnvGroupArray
-            namespace={this.state.selectedNamespace}
-            values={this.state.envVariables}
-            setValues={(x: any) => this.setState({ envVariables: x })}
-            fileUpload={true}
-            secretOption={true}
-          />
-          <SaveButton
+           </Wrapper> 
+           <SaveButton
             disabled={this.isDisabled()}
             text="Create env group"
+            clearPosition={true}
+            statusPosition="right"
             onClick={this.onSubmit}
             status={
               this.isDisabled()
@@ -225,6 +229,16 @@ export default class CreateEnvGroup extends Component<PropsType, StateType> {
 
 CreateEnvGroup.contextType = Context;
 
+const Wrapper = styled.div`
+  padding: 30px;
+  padding-bottom: 25px;
+  border-radius: 5px;
+  margin-top: -15px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  margin-bottom: 30px;
+`;
+
 const Buffer = styled.div`
   width: 100%;
   height: 150px;

+ 9 - 31
dashboard/src/main/home/cluster-dashboard/env-groups/ExpandedEnvGroup.tsx

@@ -479,7 +479,7 @@ const EnvGroupVariablesEditor = ({
   return (
     <TabWrapper>
       <InnerWrapper>
-        <Heading>Environment variables</Heading>
+        <Heading isAtTop>Environment variables</Heading>
         <Helper>
           Set environment variables for your secrets and environment-specific
           configuration.
@@ -507,6 +507,8 @@ const EnvGroupVariablesEditor = ({
           onClick={() => handleUpdateValues()}
           status={buttonStatus}
           makeFlush={true}
+          clearPosition={true}
+          statusPosition="right"
         />
       )}
     </TabWrapper>
@@ -570,33 +572,7 @@ const EnvGroupSettings = ({
     <TabWrapper>
       {isAuthorized("env_group", "", ["get", "delete"]) && (
         <InnerWrapper full={true}>
-          {/* <Heading>Name</Heading>
-                <Subtitle>
-                  <Warning makeFlush={true} highlight={!isEnvGroupNameValid}>
-                    Lowercase letters, numbers, and "-" only.
-                  </Warning>
-                </Subtitle>
-                <DarkMatter antiHeight="-29px" />
-                <InputRow
-                  type="text"
-                  value={newName}
-                  setValue={(x: string) =>
-                    this.setState({ newEnvGroupName: x })
-                  }
-                  placeholder="ex: doctor-scientist"
-                  width="100%"
-                />
-                <Button
-                  color="#616FEEcc"
-                  disabled={!(isEnvGroupNameDifferent && isEnvGroupNameValid)}
-                  onClick={this.handleRename}
-                >
-                  Rename {name}
-                </Button>
-
-                <DarkMatter /> */}
-
-          <Heading>Manage environment group</Heading>
+          <Heading isAtTop>Manage environment group</Heading>
           <Helper>
             Permanently delete this set of environment variables. This action
             cannot be undone.
@@ -895,12 +871,14 @@ const CloneButton = styled(Button)`
 const InnerWrapper = styled.div<{ full?: boolean }>`
   width: 100%;
   height: ${(props) => (props.full ? "100%" : "calc(100% - 65px)")};
-  background: #ffffff11;
-  padding: 0 35px;
+  padding: 30px;
   padding-bottom: 15px;
   position: relative;
-  border-radius: 8px;
   overflow: auto;
+  margin-bottom: 30px;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
 `;
 
 const TabWrapper = styled.div`

+ 2 - 2
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedJobChart.tsx

@@ -286,7 +286,7 @@ export const ExpandedJobChartFC: React.FC<{
               setCurrentOverlay(null);
             }
           }}
-          saveButtonText="Save Config"
+          saveButtonText="Save config"
         />
       );
     }
@@ -376,7 +376,7 @@ export const ExpandedJobChartFC: React.FC<{
               leftTabOptions={leftTabOptions}
               rightTabOptions={rightTabOptions}
               saveValuesStatus={saveStatus}
-              saveButtonText="Save Config"
+              saveButtonText="Save config"
               includeHiddenFields
               addendum={
                 <TabButton

+ 10 - 8
dashboard/src/main/home/cluster-dashboard/expanded-chart/RevisionSection.tsx

@@ -324,8 +324,8 @@ class RevisionSection extends Component<PropsType, StateType> {
         >
           <RevisionPreview>
             {isCurrent
-              ? `Current Revision`
-              : `Previewing Revision (Not Deployed)`}{" "}
+              ? `Current revision`
+              : `Previewing revision (not deployed)`}{" "}
             - <Revision>No. {this.props.chart.version}</Revision>
             <i className="material-icons">arrow_drop_down</i>
           </RevisionPreview>
@@ -472,12 +472,10 @@ const RevisionHeader = styled.div`
   width: 100%;
   padding-left: 15px;
   cursor: pointer;
-  background: ${(props: { showRevisions: boolean; isCurrent: boolean }) =>
-    props.showRevisions ? "#ffffff11" : ""};
   :hover {
-    background: #ffffff18;
+    background: ${props => props.showRevisions && "#ffffff18"};
     > div > i {
-      background: #ffffff22;
+      background: ${props => props.showRevisions && "#ffffff22"};
     }
   }
 
@@ -497,10 +495,14 @@ const StyledRevisionSection = styled.div`
   width: 100%;
   max-height: ${(props: { showRevisions: boolean }) =>
     props.showRevisions ? "255px" : "40px"};
-  background: #ffffff11;
   margin: 20px 0px 18px;
   overflow: hidden;
-  border-radius: 8px;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  :hover {
+    border: 1px solid #7a7b80;
+  }
   animation: ${(props: { showRevisions: boolean }) =>
     props.showRevisions ? "expandRevisions 0.3s" : ""};
   animation-timing-function: ease-out;

+ 6 - 4
dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx

@@ -216,7 +216,7 @@ const SettingsSection: React.FC<PropsType> = ({
         {!currentChart.stack_id?.length &&
         !PORTER_IMAGE_TEMPLATES.includes(selectedImageUrl) ? (
           <>
-            <Heading>Source Settings</Heading>
+            <Heading isAtTop>Source settings</Heading>
             <Helper>Specify an image tag to use.</Helper>
             <ImageSelector
               selectedTag={selectedTag}
@@ -234,7 +234,7 @@ const SettingsSection: React.FC<PropsType> = ({
                 <SaveButton
                   clearPosition={true}
                   statusPosition="right"
-                  text="Save Source Settings"
+                  text="Save source settings"
                   status={saveValuesStatus}
                   onClick={handleSubmit}
                 />
@@ -475,13 +475,15 @@ const Wrapper = styled.div`
 
 const StyledSettingsSection = styled.div`
   width: 100%;
-  background: #ffffff11;
-  padding: 0 35px;
+  padding: 30px;
   padding-bottom: 15px;
   position: relative;
   border-radius: 8px;
   overflow: auto;
   height: calc(100% - 55px);
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
 `;
 
 const Holder = styled.div`

+ 4 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/ValuesYaml.tsx

@@ -105,9 +105,11 @@ export default class ValuesYaml extends Component<PropsType, StateType> {
         </Wrapper>
         {!this.props.disabled && (
           <SaveButton
-            text="Update Values"
+            text="Update values"
             onClick={this.handleSaveValues}
             status={this.state.saveValuesStatus}
+            statusPosition="right"
+            clearPosition={true}
             makeFlush={true}
           />
         )}
@@ -121,6 +123,7 @@ ValuesYaml.contextType = Context;
 const Wrapper = styled.div`
   overflow: auto;
   border-radius: 8px;
+  margin-bottom: 30px;
   border: 1px solid #ffffff33;
 `;
 

+ 1 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/build-settings/BuildSettingsTab.tsx

@@ -357,7 +357,7 @@ const BuildSettingsTab: React.FC<Props> = ({
 
         {!chart.git_action_config.dockerfile_path ? (
           <>
-            <Heading>Buildpack Settings</Heading>
+            <Heading>Buildpack settings</Heading>
             <BuildpackConfigSection
               ref={buildpackConfigRef}
               currentChart={chart}

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

@@ -148,8 +148,8 @@ const _RevisionList = ({
         >
           <RevisionPreview>
             {currentRevision.id === latestRevision.id
-              ? `Current Revision`
-              : `Previewing Revision (Not Deployed)`}{" "}
+              ? `Current revision`
+              : `Previewing revision (not deployed)`}{" "}
             - <Revision>No. {currentRevision.id}</Revision>
             <i className="material-icons">arrow_drop_down</i>
           </RevisionPreview>

+ 12 - 8
dashboard/src/main/home/integrations/create-integration/ECRForm.tsx

@@ -75,7 +75,7 @@ export default class ECRForm extends Component<PropsType, StateType> {
     return (
       <StyledForm>
         <CredentialWrapper>
-          <Heading>Porter Settings</Heading>
+          <Heading isAtTop>Porter settings</Heading>
           <Helper>
             Give a name to this set of registry credentials (just for Porter).
           </Helper>
@@ -87,13 +87,13 @@ export default class ECRForm extends Component<PropsType, StateType> {
             placeholder="ex: paper-straw"
             width="100%"
           />
-          <Heading>AWS Settings</Heading>
+          <Heading>AWS settings</Heading>
           <Helper>AWS access credentials.</Helper>
           <InputRow
             type="text"
             value={this.state.awsRegion}
             setValue={(x: string) => this.setState({ awsRegion: x })}
-            label="📍 AWS Region"
+            label="📍 AWS region"
             placeholder="ex: mars-north-12"
             width="100%"
           />
@@ -101,7 +101,7 @@ export default class ECRForm extends Component<PropsType, StateType> {
             type="text"
             value={this.state.awsAccessId}
             setValue={(x: string) => this.setState({ awsAccessId: x })}
-            label="👤 AWS Access ID"
+            label="👤 AWS access ID"
             placeholder="ex: AKIAIOSFODNN7EXAMPLE"
             width="100%"
           />
@@ -109,14 +109,16 @@ export default class ECRForm extends Component<PropsType, StateType> {
             type="password"
             value={this.state.awsSecretKey}
             setValue={(x: string) => this.setState({ awsSecretKey: x })}
-            label="🔒 AWS Secret Key"
+            label="🔒 AWS secret key"
             placeholder="○ ○ ○ ○ ○ ○ ○ ○ ○"
             width="100%"
           />
         </CredentialWrapper>
         <SaveButton
-          text="Save Settings"
+          text="Save settings"
           makeFlush={true}
+          clearPosition={true}
+          statusPosition="right"
           disabled={this.isDisabled()}
           onClick={this.isDisabled() ? null : this.handleSubmit}
         />
@@ -128,9 +130,11 @@ export default class ECRForm extends Component<PropsType, StateType> {
 ECRForm.contextType = Context;
 
 const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
+  padding: 30px;
   border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  margin-bottom: 30px;
 `;
 
 const StyledForm = styled.div`

+ 12 - 9
dashboard/src/main/home/integrations/create-integration/GARForm.tsx

@@ -107,7 +107,7 @@ const GARForm = (props: { closeForm: () => void }) => {
   return (
     <StyledForm>
       <CredentialWrapper>
-        <Heading>Porter Settings</Heading>
+        <Heading isAtTop>Porter settings</Heading>
         <Helper>
           Give a name to this set of registry credentials (just for Porter).
         </Helper>
@@ -118,21 +118,20 @@ const GARForm = (props: { closeForm: () => void }) => {
             setCredentialsName(credentialsName)
           }
           isRequired={true}
-          label="🏷️ Registry Name"
+          label="🏷️ Registry name"
           placeholder="ex: paper-straw"
           width="100%"
         />
-        <Heading>GCP Settings</Heading>
+        <Heading>GCP settings</Heading>
         <Helper>Service account credentials for GCP permissions.</Helper>
         <UploadArea
           setValue={(x: any) => setServiceAccountKey(x)}
-          label="🔒 GCP Key Data (JSON)"
+          label="🔒 GCP key data (JSON)"
           placeholder="Choose a file or drag it here."
           width="100%"
           height="100%"
           isRequired={true}
         />
-        <Helper>GAR Region</Helper>
         <SelectRow
           options={GAR_REGION_OPTIONS}
           width="100%"
@@ -142,13 +141,15 @@ const GARForm = (props: { closeForm: () => void }) => {
           setActiveValue={(x: string) => {
             setRegion(x);
           }}
-          label="📍 GCP Region"
+          label="📍 GAR region"
         />
       </CredentialWrapper>
       <SaveButton
-        text="Save Settings"
+        text="Save settings"
         status={buttonStatus}
         makeFlush={true}
+        clearPosition={true}
+        statusPosition="right"
         disabled={!isValid()}
         onClick={!isValid() ? null : handleSubmit}
       />
@@ -159,9 +160,11 @@ const GARForm = (props: { closeForm: () => void }) => {
 export default GARForm;
 
 const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
+  padding: 30px;
   border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  margin-bottom: 30px;
 `;
 
 const StyledForm = styled.div`

+ 11 - 7
dashboard/src/main/home/integrations/create-integration/GCRForm.tsx

@@ -76,7 +76,7 @@ export default class GCRForm extends Component<PropsType, StateType> {
     return (
       <StyledForm>
         <CredentialWrapper>
-          <Heading>Porter Settings</Heading>
+          <Heading isAtTop>Porter settings</Heading>
           <Helper>
             Give a name to this set of registry credentials (just for Porter).
           </Helper>
@@ -87,15 +87,15 @@ export default class GCRForm extends Component<PropsType, StateType> {
               this.setState({ credentialsName })
             }
             isRequired={true}
-            label="🏷️ Registry Name"
+            label="🏷️ Registry name"
             placeholder="ex: paper-straw"
             width="100%"
           />
-          <Heading>GCP Settings</Heading>
+          <Heading>GCP settings</Heading>
           <Helper>Service account credentials for GCP permissions.</Helper>
           <UploadArea
             setValue={(x: any) => this.setState({ serviceAccountKey: x })}
-            label="🔒 GCP Key Data (JSON)"
+            label="🔒 GCP key data (JSON)"
             placeholder="Choose a file or drag it here."
             width="100%"
             height="100%"
@@ -117,8 +117,10 @@ export default class GCRForm extends Component<PropsType, StateType> {
           />
         </CredentialWrapper>
         <SaveButton
-          text="Save Settings"
+          text="Save settings"
           makeFlush={true}
+          clearPosition={true}
+          statusPosition="right"
           disabled={this.isDisabled()}
           onClick={this.isDisabled() ? null : this.handleSubmit}
         />
@@ -130,9 +132,11 @@ export default class GCRForm extends Component<PropsType, StateType> {
 GCRForm.contextType = Context;
 
 const CredentialWrapper = styled.div`
-  padding: 5px 40px 25px;
-  background: #ffffff11;
+  padding: 30px;
   border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
+  margin-bottom: 30px;
 `;
 
 const StyledForm = styled.div`

+ 3 - 2
dashboard/src/main/home/launch/launch-flow/SettingsPage.tsx

@@ -134,7 +134,7 @@ class SettingsPage extends Component<PropsType, StateType> {
       } = this.props;
       return (
         <FadeWrapper>
-          <Heading>Additional Settings</Heading>
+          <Heading>Additional settings</Heading>
           <Helper>
             Configure additional settings for this template. (Optional)
           </Helper>
@@ -229,7 +229,7 @@ class SettingsPage extends Component<PropsType, StateType> {
       return (
         <BackButton width="155px" onClick={() => setPage("source")}>
           <i className="material-icons">first_page</i>
-          Source Settings
+          Source settings
         </BackButton>
       );
     }
@@ -438,6 +438,7 @@ const PaddingWrapper = styled.div`
 
 const StyledSettingsPage = styled.div`
   position: relative;
+  padding-bottom: 20px;
 `;
 
 const Subtitle = styled.div`

+ 7 - 4
dashboard/src/main/home/launch/launch-flow/SourcePage.tsx

@@ -274,7 +274,7 @@ class SourcePage extends Component<PropsType, StateType> {
             width="470px"
           />
         </InputWrapper>
-        <Heading>Deployment Method</Heading>
+        <Heading>Deployment method</Heading>
         <Helper>
           Deploy from a Git repository or a Docker registry:
           <Required>*</Required>
@@ -296,6 +296,8 @@ class SourcePage extends Component<PropsType, StateType> {
           disabled={!this.checkSourceSelected()}
           onClick={this.handleContinue}
           status={this.getButtonStatus()}
+          clearPosition={true}
+          statusPosition="right"
           makeFlush={true}
           helper={this.getButtonHelper()}
         />
@@ -324,7 +326,7 @@ const StyledSourcePage = styled.div`
 
 const Buffer = styled.div`
   width: 100%;
-  height: 35px;
+  height: 15px;
 `;
 
 const Br = styled.div`
@@ -476,12 +478,13 @@ const Highlight = styled.a`
 
 const StyledSourceBox = styled.div`
   width: 100%;
-  background: #ffffff11;
   color: #ffffff;
   padding: 14px 35px 20px;
   position: relative;
-  border-radius: 5px;
   font-size: 13px;
   margin-top: 6px;
   margin-bottom: 25px;
+  border-radius: 5px;
+  background: #26292e;
+  border: 1px solid #494b4f;
 `;

+ 1 - 1
dashboard/src/main/home/launch/launch-flow/WorkflowPage.tsx

@@ -72,7 +72,7 @@ const WorkflowPage: React.FC<PropsType> = (props) => {
 
   return (
     <StyledWorkflowPage>
-      <Heading>GitHub Actions</Heading>
+      <Heading isAtTop>GitHub Actions</Heading>
       <Helper>
         To auto-deploy each time you push changes, Porter will write GitHub
         Secrets and this GitHub Actions workflow to your repository.

+ 0 - 5
dashboard/src/main/home/navbar/Help.tsx

@@ -44,11 +44,6 @@ export default class Help extends Component<PropsType, StateType> {
               <Icon src={discordLogo} />
               Community
             </Option>
-            <Line />
-            <Option id={"intercom_help"}>
-              <i className="material-icons-outlined">message</i>
-              Message us
-            </Option>
           </Dropdown>
         </>
       );

+ 5 - 0
go.work.sum

@@ -5,11 +5,16 @@ github.com/containerd/stargz-snapshotter v0.11.3 h1:D3PoF563XmOBdtfx2G6AkhbHueqw
 github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk=
 github.com/go-redis/redis v6.15.8+incompatible h1:BKZuG6mCnRj5AOaWJXoCgf6rqTYnYJLe4en2hxT7r9o=
 github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/google/go-github/v50 v50.0.0 h1:gdO1AeuSZZK4iYWwVbjni7zg8PIQhp7QfmPunr016Jk=
+github.com/google/go-github/v50 v50.0.0/go.mod h1:Ev4Tre8QoKiolvbpOSG3FIi4Mlon3S2Nt9W5JYqKiwA=
 github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0=
 github.com/jackc/pgproto3 v1.1.0 h1:FYYE4yRw+AgI8wXIinMlNjBbp/UitDJwfj5LqqewP1A=
 github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck=
+golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ=
+golang.org/x/net v0.0.0-20210226172049-e18ecbb05110 h1:qWPm9rbaAMKs8Bq/9LRpbMqxWRVUAQwMI9fVrssnTfw=
 golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
 golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
+golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be h1:vEDujvNQGv4jgYKudGeI/+DAX4Jffq6hpD55MmoEvKs=
 golang.org/x/oauth2 v0.0.0-20221014153046-6fdb5e3db783/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
 golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=