فهرست منبع

fix doc links to point to new ones (#4009)

Feroze Mohideen 2 سال پیش
والد
کامیت
5aabc1b648

+ 5 - 3
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -220,8 +220,10 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
   const onSubmit = handleSubmit(async (data) => {
     try {
       setDeployError("");
-      const { validatedAppProto, variables, secrets } =
-        await validateApp(data, currentProject?.beta_features_enabled);
+      const { validatedAppProto, variables, secrets } = await validateApp(
+        data,
+        currentProject?.beta_features_enabled
+      );
       setValidatedAppProto(validatedAppProto);
       setFinalizedAppEnv({ variables, secrets });
 
@@ -596,7 +598,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                       <Spacer inline width="5px" />
                       <Link
                         hasunderline
-                        to="https://docs.porter.run/standard/deploying-applications/overview"
+                        to="https://docs.porter.run/deploy/overview"
                         target="_blank"
                       >
                         Learn more

+ 11 - 7
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Health.tsx

@@ -2,6 +2,7 @@ import React from "react";
 import { Controller, useFormContext } from "react-hook-form";
 
 import Checkbox from "components/porter/Checkbox";
+import Container from "components/porter/Container";
 import { ControlledInput } from "components/porter/ControlledInput";
 import Spacer from "components/porter/Spacer";
 import Text from "components/porter/Text";
@@ -21,18 +22,20 @@ const Health: React.FC<HealthProps> = ({ index }) => {
   return (
     <>
       <Spacer y={1} />
-      <Text>
-        <>
-          <span>Health checks</span>
+      <Text>Health checks</Text>
+      <Spacer y={0.25} />
+      <Container style={{ width: "400px" }}>
+        <Text color="helper">
+          Configure health checks to prevent downtime during deployments
           <a
-            href="https://docs.porter.run/enterprise/deploying-applications/zero-downtime-deployments#health-checks"
+            href="https://docs.porter.run/configure/health-checks"
             target="_blank"
             rel="noreferrer"
           >
             &nbsp;(?)
           </a>
-        </>
-      </Text>
+        </Text>
+      </Container>
       <Spacer y={0.5} />
       <Controller
         name={`app.services.${index}.config.healthCheck.enabled`}
@@ -57,10 +60,11 @@ const Health: React.FC<HealthProps> = ({ index }) => {
       />
       {healthCheckEnabled.value && (
         <>
+          <Spacer y={0.5} />
+          <Text>Health check endpoint</Text>
           <Spacer y={0.5} />
           <ControlledInput
             type="text"
-            label="Health Check Endpoint "
             placeholder="ex: /healthz"
             {...register(
               `app.services.${index}.config.healthCheck.httpPath.value`

+ 2 - 2
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Networking.tsx

@@ -141,7 +141,7 @@ const Networking: React.FC<NetworkingProps> = ({
           <Text color="helper">
             Custom domains
             <a
-              href="https://docs.porter.run/standard/deploying-applications/https-and-domains/custom-domains"
+              href="https://docs.porter.run/configure/custom-domains#custom-domains"
               target="_blank"
               rel="noreferrer"
             >
@@ -154,7 +154,7 @@ const Networking: React.FC<NetworkingProps> = ({
           <Text color="helper">
             Ingress Custom Annotations
             <a
-              href="https://docs.porter.run/standard/deploying-applications/runtime-configuration-options/web-applications#ingress-custom-annotations"
+              href="https://docs.porter.run/configure/advanced-networking#customizing-network-settings-for-an-application"
               target="_blank"
               rel="noreferrer"
             >

+ 358 - 329
dashboard/src/main/home/app-dashboard/validate-apply/services-settings/tabs/Resources.tsx

@@ -1,25 +1,33 @@
-import React, { useContext, useEffect, useState } from "react";
-import Spacer from "components/porter/Spacer";
-import { type ClientService } from "lib/porter-apps/services";
+import React, { useContext, useState } from "react";
+import { Switch } from "@material-ui/core";
 import { Controller, useFormContext } from "react-hook-form";
-import { type PorterAppFormData } from "lib/porter-apps";
-import { ControlledInput } from "components/porter/ControlledInput";
+import styled from "styled-components";
+import { match } from "ts-pattern";
+
+import Loading from "components/Loading";
 import Checkbox from "components/porter/Checkbox";
+import Container from "components/porter/Container";
+import { ControlledInput } from "components/porter/ControlledInput";
+import InputSlider from "components/porter/InputSlider";
+import Link from "components/porter/Link";
+import Spacer from "components/porter/Spacer";
+import Tag from "components/porter/Tag";
 import Text from "components/porter/Text";
-import { match } from "ts-pattern";
-import styled from "styled-components";
-import { Switch } from "@material-ui/core";
 import SmartOptModal from "main/home/app-dashboard/new-app-flow/tabs/SmartOptModal";
-import IntelligentSlider from "./IntelligentSlider";
-import InputSlider from "components/porter/InputSlider";
-import { closestMultiplier, lowestClosestResourceMultipler } from "lib/hooks/useClusterResourceLimits";
-import Loading from "components/Loading";
 import ProvisionClusterModal from "main/home/sidebar/ProvisionClusterModal";
+import {
+  closestMultiplier,
+  lowestClosestResourceMultipler,
+} from "lib/hooks/useClusterResourceLimits";
+import { type PorterAppFormData } from "lib/porter-apps";
+import { type ClientService } from "lib/porter-apps/services";
+
 import { Context } from "shared/Context";
-import Link from "components/porter/Link";
-import Tag from "components/porter/Tag";
+import addCircle from "assets/add-circle.png";
 import infra from "assets/cluster.svg";
 
+import IntelligentSlider from "./IntelligentSlider";
+
 type ResourcesProps = {
   index: number;
   maxCPU: number;
@@ -37,50 +45,51 @@ const Resources: React.FC<ResourcesProps> = ({
   clusterContainsGPUNodes,
   isPredeploy = false,
 }) => {
-  const { control, register, watch, setValue } = useFormContext<PorterAppFormData>();
+  const { control, register, watch, setValue } =
+    useFormContext<PorterAppFormData>();
   const [showNeedHelpModal, setShowNeedHelpModal] = useState(false);
-  const [clusterModalVisible, setClusterModalVisible] = useState<boolean>(false);
+  const [clusterModalVisible, setClusterModalVisible] =
+    useState<boolean>(false);
   const { currentCluster, currentProject } = useContext(Context);
 
   const autoscalingEnabled = watch(
-    `app.services.${index}.config.autoscaling.enabled`, {
-    readOnly: false,
-    value: false
-  }
+    `app.services.${index}.config.autoscaling.enabled`,
+    {
+      readOnly: false,
+      value: false,
+    }
   );
 
-  const smartOpt = watch(
-    `app.services.${index}.smartOptimization`, {
+  const smartOpt = watch(`app.services.${index}.smartOptimization`, {
     readOnly: false,
-    value: false
-  }
-  );
+    value: false,
+  });
 
-  const memory = watch(
-    `app.services.${index}.ramMegabytes`, {
+  const memory = watch(`app.services.${index}.ramMegabytes`, {
     readOnly: false,
-    value: 0
-  }
-  );
-  const cpu = watch(
-    `app.services.${index}.cpuCores`, {
+    value: 0,
+  });
+  const cpu = watch(`app.services.${index}.cpuCores`, {
     readOnly: false,
-    value: 0
-  }
-  );
+    value: 0,
+  });
 
   return (
     <>
       <Spacer y={1} />
       <Controller
-        name={isPredeploy ? `app.predeploy.${index}.smartOptimization` : `app.services.${index}.smartOptimization`}
+        name={
+          isPredeploy
+            ? `app.predeploy.${index}.smartOptimization`
+            : `app.services.${index}.smartOptimization`
+        }
         control={control}
         render={({ field: { value, onChange } }) => (
           <SmartOptHeader>
             <StyledIcon
               className="material-icons"
               onClick={() => {
-                setShowNeedHelpModal(true)
+                setShowNeedHelpModal(true);
               }}
             >
               help_outline
@@ -89,36 +98,47 @@ const Resources: React.FC<ResourcesProps> = ({
             <Switch
               size="small"
               color="primary"
-              disabled={memory.readOnly || cpu.readOnly || service.smartOptimization?.readOnly}
+              disabled={
+                memory.readOnly ||
+                cpu.readOnly ||
+                service.smartOptimization?.readOnly
+              }
               checked={value?.value}
-              onChange={
-                () => {
-                  if (!value?.value) {
-                    const lowestRAM = lowestClosestResourceMultipler(0, maxRAM, memory.value);
-                    const lowestCPU = lowestClosestResourceMultipler(0, maxCPU, cpu.value);
-                    const lowestFraction = Math.min(lowestRAM, lowestCPU);
-                    setValue(`app.services.${index}.cpuCores`, {
-                      readOnly: false,
-                      value: Number((maxCPU * lowestFraction).toFixed(2))
-                    });
-                    setValue(`app.services.${index}.ramMegabytes`, {
-                      readOnly: false,
-                      value: maxRAM * lowestFraction
-                    });
-                  }
-                  onChange({
-                    ...value,
-                    value: !value?.value,
+              onChange={() => {
+                if (!value?.value) {
+                  const lowestRAM = lowestClosestResourceMultipler(
+                    0,
+                    maxRAM,
+                    memory.value
+                  );
+                  const lowestCPU = lowestClosestResourceMultipler(
+                    0,
+                    maxCPU,
+                    cpu.value
+                  );
+                  const lowestFraction = Math.min(lowestRAM, lowestCPU);
+                  setValue(`app.services.${index}.cpuCores`, {
+                    readOnly: false,
+                    value: Number((maxCPU * lowestFraction).toFixed(2)),
+                  });
+                  setValue(`app.services.${index}.ramMegabytes`, {
+                    readOnly: false,
+                    value: maxRAM * lowestFraction,
                   });
                 }
-              }
-              inputProps={{ 'aria-label': 'controlled' }}
+                onChange({
+                  ...value,
+                  value: !value?.value,
+                });
+              }}
+              inputProps={{ "aria-label": "controlled" }}
             />
-          </SmartOptHeader>)} />
-      {showNeedHelpModal &&
-        <SmartOptModal
-          setModalVisible={setShowNeedHelpModal}
-        />}
+          </SmartOptHeader>
+        )}
+      />
+      {showNeedHelpModal && (
+        <SmartOptModal setModalVisible={setShowNeedHelpModal} />
+      )}
       <Controller
         name={
           isPredeploy
@@ -136,10 +156,13 @@ const Resources: React.FC<ResourcesProps> = ({
             value={value.value.toString()}
             setValue={(e) => {
               if (smartOpt?.value) {
-                setValue(
-                  `app.services.${index}.ramMegabytes`, {
+                setValue(`app.services.${index}.ramMegabytes`, {
                   readOnly: false,
-                  value: Number((closestMultiplier(0, maxCPU, value.value) * maxRAM).toFixed(0))
+                  value: Number(
+                    (
+                      closestMultiplier(0, maxCPU, value.value) * maxRAM
+                    ).toFixed(0)
+                  ),
                 });
               }
               onChange({
@@ -172,13 +195,17 @@ const Resources: React.FC<ResourcesProps> = ({
             min={0}
             max={maxRAM}
             color={"#3f51b5"}
-            value={(value.value).toString()}
+            value={value.value.toString()}
             setValue={(e) => {
               if (smartOpt?.value) {
                 setValue(`app.services.${index}.cpuCores`, {
                   readOnly: false,
-                  value: Number((closestMultiplier(0, maxRAM, value.value) * maxCPU).toFixed(2))
-                })
+                  value: Number(
+                    (
+                      closestMultiplier(0, maxRAM, value.value) * maxCPU
+                    ).toFixed(2)
+                  ),
+                });
               }
               onChange({
                 ...value,
@@ -195,246 +222,250 @@ const Resources: React.FC<ResourcesProps> = ({
         )}
       />
 
-      {(currentCluster.cloud_provider === "AWS" && currentProject.gpu_enabled) &&
-        <>
-          <Spacer y={1} />
-          <Controller
-            name={`app.services.${index}.gpuCoresNvidia`}
-            control={control}
-            render={({ field: { value, onChange } }) => (
-              <>
+      {currentCluster?.cloud_provider === "AWS" &&
+        currentProject?.gpu_enabled && (
+          <>
+            <Spacer y={1} />
+            <Controller
+              name={`app.services.${index}.gpuCoresNvidia`}
+              control={control}
+              render={({ field: { value, onChange } }) => (
                 <>
-                  <Switch
-                    size="small"
-                    color="primary"
-                    checked={value.value > 0}
-                    disabled={!clusterContainsGPUNodes}
-                    onChange={() => {
-                      if (value.value > 0) {
-                        onChange({
-                          ...value,
-                          value: 0
-                        });
-                      }
-                      else
-                        onChange({
-                          ...value,
-                          value: 1
-                        });
-                    }}
-
-                    inputProps={{ 'aria-label': 'controlled' }} /><Spacer inline x={.5} /><Text >
-                    <>
-                      <span>Enable GPU</span>
-                    </>
-                  </Text>
-                  {
-                    !clusterContainsGPUNodes &&
-                    <>
-
-                      <Spacer inline x={2} />
-                      <Text
-                        color="helper"
-                      >
-                        You cluster has no GPU nodes available.
-                      </Text>
-                      <Spacer inline x={.5} />
-                      <Link
-                        onClick={() => { setClusterModalVisible(true); }}
-                        hasunderline
-                      >
-                        Add GPU nodes
-                      </Link>
-                      {/* <a
-                        href="https://docs.porter.run/enterprise/deploying-applications/zero-downtime-deployments#health-checks"
-                        target="_blank" rel="noreferrer"
-                      >
-                        &nbsp;(?)
-                      </a> */}
-                    </>
-
-                  }
-                </>
-                <Spacer y={.5} />
-                {
-                  clusterModalVisible && <ProvisionClusterModal
-                    closeModal={() => {
-                      setClusterModalVisible(false);
-                    }}
-                    gpuModal={true}
-                  />
-                }
-              </>
-            )} />
-          {(currentCluster.status === "UPDATING" && clusterContainsGPUNodes) &&
-            < CheckItemContainer >
-              <CheckItemTop >
-                <Loading
-                  offset="0px"
-                  width="20px"
-                  height="20px" />
-                <Spacer inline x={1} />
-                <Text style={{ marginLeft: '10px', flex: 1 }}>{"Creating GPU nodes..."}</Text>
-                <Spacer inline x={1} />
-                <Tag>
-                  <Link
-                    to={`/cluster-dashboard`}
-                  >
-                    <TagIcon src={infra} />
-                    View Status
-                  </Link>
-                </Tag>
-              </CheckItemTop>
-            </CheckItemContainer>
-          }
-        </>
-      }
-      {
-        match(service.config)
-          .with({ type: "job" }, () => null)
-          .with({ type: "predeploy" }, () => null)
-          .otherwise((config) => (
-            <>
-              <Spacer y={1} />
-              <ControlledInput
-                type="text"
-                label="Instances"
-                placeholder="ex: 1"
-                disabled={service.instances.readOnly || autoscalingEnabled.value}
-                width="300px"
-                disabledTooltip={
-                  service.instances.readOnly
-                    ? "You may only edit this field in your porter.yaml."
-                    : "Disable autoscaling to specify instances."
-                }
-                {...register(`app.services.${index}.instances.value`)}
-              />
-              <Spacer y={1} />
-
-              {!clusterContainsGPUNodes && (<Controller
-                name={`app.services.${index}.config.autoscaling.enabled`}
-                control={control}
-                render={({ field: { value, onChange } }) => (
-                  <Checkbox
-                    checked={value.value}
-                    toggleChecked={() => {
-                      onChange({
-                        ...value,
-                        value: !value.value,
-                      });
-                    }}
-                    disabled={value.readOnly}
-                    disabledTooltip={
-                      "You may only edit this field in your porter.yaml."
-                    }
-                  >
-                    <Text color="helper">
-                      Enable autoscaling (overrides instances)
-                    </Text>
-                  </Checkbox>
-                )}
-              />)}
-
-
-              {autoscalingEnabled.value && (
-                <>
-                  <Spacer y={1} />
-                  <ControlledInput
-                    type="text"
-                    label="Min instances"
-                    placeholder="ex: 1"
-                    disabled={
-                      config.autoscaling?.minInstances?.readOnly ??
-                      !config.autoscaling?.enabled.value
-                    }
-                    width="300px"
-                    disabledTooltip={
-                      config.autoscaling?.minInstances?.readOnly
-                        ? "You may only edit this field in your porter.yaml."
-                        : "Enable autoscaling to specify min instances."
-                    }
-                    {...register(
-                      `app.services.${index}.config.autoscaling.minInstances.value`
-                    )}
-                  />
-                  <Spacer y={1} />
-                  <ControlledInput
-                    type="text"
-                    label="Max instances"
-                    placeholder="ex: 10"
-                    disabled={
-                      config.autoscaling?.maxInstances?.readOnly ??
-                      !config.autoscaling?.enabled.value
-                    }
-                    width="300px"
-                    disabledTooltip={
-                      config.autoscaling?.maxInstances?.readOnly
-                        ? "You may only edit this field in your porter.yaml."
-                        : "Enable autoscaling to specify max instances."
-                    }
-                    {...register(
-                      `app.services.${index}.config.autoscaling.maxInstances.value`
-                    )}
-                  />
-                  <Spacer y={1} />
-                  <Controller
-                    name={`app.services.${index}.config.autoscaling.cpuThresholdPercent`}
-                    control={control}
-                    render={({ field: { value, onChange } }) => (
-                      <InputSlider
-                        label="CPU threshold: "
-                        unit="%"
-                        min={0}
-                        max={100}
-                        value={value?.value.toString() ?? "50"}
-                        disabled={value?.readOnly || !config.autoscaling?.enabled}
-                        width="300px"
-                        setValue={(e) => {
+                  <Container row>
+                    <Switch
+                      size="small"
+                      color="primary"
+                      checked={value.value > 0}
+                      disabled={!clusterContainsGPUNodes}
+                      onChange={() => {
+                        if (value.value > 0) {
                           onChange({
                             ...value,
-                            value: e,
+                            value: 0,
                           });
-                        }}
-                        disabledTooltip={
-                          value?.readOnly
-                            ? "You may only edit this field in your porter.yaml."
-                            : "Enable autoscaling to specify CPU threshold."
-                        }
-                      />
-                    )}
-                  />
-                  <Spacer y={1} />
-                  <Controller
-                    name={`app.services.${index}.config.autoscaling.memoryThresholdPercent`}
-                    control={control}
-                    render={({ field: { value, onChange } }) => (
-                      <InputSlider
-                        label="RAM threshold: "
-                        unit="%"
-                        min={0}
-                        max={100}
-                        value={value?.value.toString() ?? "50"}
-                        disabled={value?.readOnly || !config.autoscaling?.enabled}
-                        width="300px"
-                        setValue={(e) => {
+                        } else
                           onChange({
                             ...value,
-                            value: e,
+                            value: 1,
                           });
-                        }}
-                        disabledTooltip={
-                          value?.readOnly
-                            ? "You may only edit this field in your porter.yaml."
-                            : "Enable autoscaling to specify RAM threshold."
-                        }
-                      />
+                      }}
+                      inputProps={{ "aria-label": "controlled" }}
+                    />
+                    <Spacer inline x={0.5} />
+                    <Text>
+                      <>
+                        <span>Enable GPU</span>
+                      </>
+                    </Text>
+                    {!clusterContainsGPUNodes && (
+                      <>
+                        <Spacer inline x={1} />
+                        <Text color="helper">
+                          You cluster has no GPU nodes available.
+                        </Text>
+                        <Spacer inline x={0.5} />
+                        <Tag>
+                          <Link
+                            onClick={() => {
+                              setClusterModalVisible(true);
+                            }}
+                          >
+                            <TagIcon src={addCircle} />
+                            Add GPU nodes
+                          </Link>
+                        </Tag>
+                      </>
                     )}
-                  />
+                  </Container>
+
+                  <Spacer y={0.5} />
+                  {clusterModalVisible && (
+                    <ProvisionClusterModal
+                      closeModal={() => {
+                        setClusterModalVisible(false);
+                      }}
+                      gpuModal={true}
+                    />
+                  )}
                 </>
               )}
-            </>
-          ))
-      }
+            />
+            {currentCluster.status === "UPDATING" &&
+              clusterContainsGPUNodes && (
+                <CheckItemContainer>
+                  <CheckItemTop>
+                    <Loading offset="0px" width="20px" height="20px" />
+                    <Spacer inline x={1} />
+                    <Text>{"Creating GPU nodes..."}</Text>
+                    <Spacer inline x={1} />
+                    <Tag>
+                      <Link to={`/cluster-dashboard`}>
+                        <TagIcon src={infra} />
+                        View Status
+                      </Link>
+                    </Tag>
+                  </CheckItemTop>
+                </CheckItemContainer>
+              )}
+          </>
+        )}
+      {match(service.config)
+        .with({ type: "job" }, () => null)
+        .with({ type: "predeploy" }, () => null)
+        .otherwise((config) => (
+          <>
+            <Spacer y={1} />
+            <Text>Instances</Text>
+            <Spacer y={0.5} />
+            <ControlledInput
+              type="text"
+              placeholder="ex: 1"
+              disabled={service.instances.readOnly || autoscalingEnabled.value}
+              width="300px"
+              disabledTooltip={
+                service.instances.readOnly
+                  ? "You may only edit this field in your porter.yaml."
+                  : "Disable autoscaling to specify instances."
+              }
+              {...register(`app.services.${index}.instances.value`)}
+            />
+            <Spacer y={1} />
+            {!clusterContainsGPUNodes && (
+              <>
+                <Text>
+                  Autoscaling
+                  <a
+                    href="https://docs.porter.run/configure/autoscaling"
+                    target="_blank"
+                    rel="noreferrer"
+                  >
+                    &nbsp;(?)
+                  </a>
+                </Text>
+                <Spacer y={0.5} />
+                <Controller
+                  name={`app.services.${index}.config.autoscaling.enabled`}
+                  control={control}
+                  render={({ field: { value, onChange } }) => (
+                    <Checkbox
+                      checked={value.value}
+                      toggleChecked={() => {
+                        onChange({
+                          ...value,
+                          value: !value.value,
+                        });
+                      }}
+                      disabled={value.readOnly}
+                      disabledTooltip={
+                        "You may only edit this field in your porter.yaml."
+                      }
+                    >
+                      <Text color="helper">
+                        Enable autoscaling (overrides instances)
+                      </Text>
+                    </Checkbox>
+                  )}
+                />
+              </>
+            )}
+            {autoscalingEnabled.value && (
+              <>
+                <Spacer y={1} />
+                <ControlledInput
+                  type="text"
+                  label="Min instances"
+                  placeholder="ex: 1"
+                  disabled={
+                    config.autoscaling?.minInstances?.readOnly ??
+                    !config.autoscaling?.enabled.value
+                  }
+                  width="300px"
+                  disabledTooltip={
+                    config.autoscaling?.minInstances?.readOnly
+                      ? "You may only edit this field in your porter.yaml."
+                      : "Enable autoscaling to specify min instances."
+                  }
+                  {...register(
+                    `app.services.${index}.config.autoscaling.minInstances.value`
+                  )}
+                />
+                <Spacer y={1} />
+                <ControlledInput
+                  type="text"
+                  label="Max instances"
+                  placeholder="ex: 10"
+                  disabled={
+                    config.autoscaling?.maxInstances?.readOnly ??
+                    !config.autoscaling?.enabled.value
+                  }
+                  width="300px"
+                  disabledTooltip={
+                    config.autoscaling?.maxInstances?.readOnly
+                      ? "You may only edit this field in your porter.yaml."
+                      : "Enable autoscaling to specify max instances."
+                  }
+                  {...register(
+                    `app.services.${index}.config.autoscaling.maxInstances.value`
+                  )}
+                />
+                <Spacer y={1} />
+                <Controller
+                  name={`app.services.${index}.config.autoscaling.cpuThresholdPercent`}
+                  control={control}
+                  render={({ field: { value, onChange } }) => (
+                    <InputSlider
+                      label="CPU threshold: "
+                      unit="%"
+                      min={0}
+                      max={100}
+                      value={value?.value.toString() ?? "50"}
+                      disabled={value?.readOnly || !config.autoscaling?.enabled}
+                      width="300px"
+                      setValue={(e) => {
+                        onChange({
+                          ...value,
+                          value: e,
+                        });
+                      }}
+                      disabledTooltip={
+                        value?.readOnly
+                          ? "You may only edit this field in your porter.yaml."
+                          : "Enable autoscaling to specify CPU threshold."
+                      }
+                    />
+                  )}
+                />
+                <Spacer y={1} />
+                <Controller
+                  name={`app.services.${index}.config.autoscaling.memoryThresholdPercent`}
+                  control={control}
+                  render={({ field: { value, onChange } }) => (
+                    <InputSlider
+                      label="RAM threshold: "
+                      unit="%"
+                      min={0}
+                      max={100}
+                      value={value?.value.toString() ?? "50"}
+                      disabled={value?.readOnly || !config.autoscaling?.enabled}
+                      width="300px"
+                      setValue={(e) => {
+                        onChange({
+                          ...value,
+                          value: e,
+                        });
+                      }}
+                      disabledTooltip={
+                        value?.readOnly
+                          ? "You may only edit this field in your porter.yaml."
+                          : "Enable autoscaling to specify RAM threshold."
+                      }
+                    />
+                  )}
+                />
+              </>
+            )}
+          </>
+        ))}
     </>
   );
 };
@@ -442,42 +473,40 @@ const Resources: React.FC<ResourcesProps> = ({
 export default Resources;
 
 const StyledIcon = styled.i`
-      cursor: pointer;
-      font-size: 16px;
-      margin-right : 5px;
-      &:hover {
-        color: #666;  
+  cursor: pointer;
+  font-size: 16px;
+  margin-right: 5px;
+  &:hover {
+    color: #666;
   }
-      `;
+`;
 
 const SmartOptHeader = styled.div`
-      display: flex;
-      align-items: center;
-      justify-content: flex-end;
-      `
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+`;
 
 const CheckItemContainer = styled.div`
-      display: flex;
-      flex-direction: column;
-      border: 1px solid ${props => props.theme.border};
-      border-radius: 5px;
-      font-size: 13px;
-      width: 100%;
-      margin-bottom: 10px;
-      padding-left: 10px;
-      cursor: ${props => (props.hasMessage ? 'pointer' : 'default')};
-      background: ${props => props.theme.clickable.bg};
-
-      `;
+  display: flex;
+  flex-direction: column;
+  border: 1px solid ${(props) => props.theme.border};
+  border-radius: 5px;
+  font-size: 13px;
+  width: 100%;
+  margin-bottom: 10px;
+  padding-left: 10px;
+  background: ${(props) => props.theme.clickable.bg};
+`;
 
 const CheckItemTop = styled.div`
-      display: flex;
-      align-items: center;
-      padding: 10px;
-      background: ${props => props.theme.clickable.bg};
-      `;
+  display: flex;
+  align-items: center;
+  padding: 10px;
+  background: ${(props) => props.theme.clickable.bg};
+`;
 
 const TagIcon = styled.img`
-      height: 12px;
-      margin-right: 3px;
-      `;
+  height: 12px;
+  margin-right: 3px;
+`;