Prechádzať zdrojové kódy

Merge pull request #1834 from porter-dev/nico/implement-cronjob-input

[Improvement] Implement improved cronjob input
Nicolas Frati 4 rokov pred
rodič
commit
63e924da4e

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 14068 - 1
dashboard/package-lock.json


+ 2 - 0
dashboard/package.json

@@ -25,6 +25,8 @@
     "clipboard": "^2.0.8",
     "cohere-js": "^1.0.19",
     "core-js": "^3.16.1",
+    "cron-validator": "^1.3.1",
+    "cronstrue": "^2.2.0",
     "d3-array": "^2.11.0",
     "d3-time-format": "^3.0.0",
     "dotenv": "^8.2.0",

+ 4 - 2
dashboard/src/components/form-components/InputRow.tsx

@@ -15,6 +15,7 @@ type PropsType = {
   isRequired?: boolean;
   className?: string;
   maxLength?: number;
+  hasError?: boolean;
 };
 
 type StateType = {
@@ -65,7 +66,7 @@ export default class InputRow extends Component<PropsType, StateType> {
             {this.props.isRequired && <Required>{" *"}</Required>}
           </Label>
         )}
-        <InputWrapper>
+        <InputWrapper hasError={this.props.hasError}>
           <Input
             readOnly={this.state.readOnly}
             onFocus={() => this.setState({ readOnly: false })}
@@ -103,7 +104,8 @@ const InputWrapper = styled.div`
   display: flex;
   margin-bottom: -1px;
   align-items: center;
-  border: 1px solid #ffffff55;
+  border: 1px solid
+    ${(props: { hasError: boolean }) => (props.hasError ? "red" : "#ffffff55")};
   border-radius: 3px;
 `;
 

+ 4 - 0
dashboard/src/components/porter-form/PorterForm.tsx

@@ -2,6 +2,7 @@ import React, { useContext } from "react";
 import {
   ArrayInputField,
   CheckboxField,
+  CronField,
   FormField,
   InputField,
   KeyValueArrayField,
@@ -24,6 +25,7 @@ import Select from "./field-components/Select";
 import ServiceIPList from "./field-components/ServiceIPList";
 import ResourceList from "./field-components/ResourceList";
 import VeleroForm from "./field-components/VeleroForm";
+import CronInput from "./field-components/CronInput";
 
 interface Props {
   leftTabOptions?: TabOption[];
@@ -84,6 +86,8 @@ const PorterForm: React.FC<Props> = (props) => {
         return <ResourceList {...(bundledProps as ResourceListField)} />;
       case "velero-create-backup":
         return <VeleroForm />;
+      case "cron":
+        return <CronInput {...(bundledProps as CronField)} />;
     }
     return <p>Not Implemented: {(field as any).type}</p>;
   };

+ 88 - 0
dashboard/src/components/porter-form/field-components/CronInput.tsx

@@ -0,0 +1,88 @@
+import InputRow from "components/form-components/InputRow";
+import React from "react";
+import useFormField from "../hooks/useFormField";
+import { CronField } from "../types";
+import { hasSetValue } from "../utils";
+import { isValidCron } from "cron-validator";
+import CronParser from "cronstrue";
+import styled from "styled-components";
+import DocsHelper from "components/DocsHelper";
+import DynamicLink from "components/DynamicLink";
+
+const CronInput: React.FC<CronField> = (props) => {
+  const { id, variable, label, placeholder, value } = props;
+
+  const { state, variables, setVars, setValidation, validation } = useFormField(
+    id,
+    {
+      initValidation: {
+        validated: hasSetValue(props) ? isValidCron(value[0]) : true,
+      },
+      initVars: {
+        [variable]: hasSetValue(props) ? value[0] : undefined,
+      },
+    }
+  );
+
+  if (!state || validation[id]?.validated === undefined) {
+    return null;
+  }
+
+  return (
+    <>
+      <InputRow
+        type="text"
+        label={label}
+        placeholder={placeholder}
+        value={variables[variable]}
+        setValue={(x: string) => {
+          setVars((vars) => {
+            return {
+              ...vars,
+              [variable]: x,
+            };
+          });
+          setValidation((prev) => {
+            return {
+              ...prev,
+              validated: isValidCron(x),
+            };
+          });
+        }}
+        width={"100%"}
+        hasError={!validation[id]?.validated}
+      />
+      <Label error={!validation[id]?.validated}>
+        {!validation[id]?.validated ? (
+          <>
+            The expresion is not valid, to learn more about cron jobs please
+            click{" "}
+            <DynamicLink
+              style={{ color: "red", textDecoration: "underline" }}
+              to="https://docs.porter.run/running-jobs/deploying-jobs#deploying-a-cron-job"
+            >
+              here
+            </DynamicLink>
+          </>
+        ) : (
+          <>
+            {CronParser.toString(variables[variable], {
+              throwExceptionOnParseError: false,
+              verbose: true,
+            })}
+          </>
+        )}
+      </Label>
+    </>
+  );
+};
+
+const Label = styled.label`
+  ${(props: { error: boolean }) => {
+    if (props.error) {
+      return "color: red;";
+    }
+  }}
+`;
+
+export default CronInput;

+ 2 - 0
dashboard/src/components/porter-form/hooks/useFormField.tsx

@@ -9,6 +9,7 @@ import {
 interface FormFieldData<T> {
   state: T;
   variables: PorterFormVariableList;
+  validation: { [key: string]: PorterFormFieldValidationState };
   setState: (setFunc: (prev: T) => Partial<T>) => void;
   setVars: (
     setFunc: (vars: PorterFormVariableList) => PorterFormVariableList
@@ -89,6 +90,7 @@ const useFormField = <T extends PorterFormFieldFieldState>(
   return {
     state: formState.components[fieldId]?.state as T,
     variables: formState.variables,
+    validation: formState.validation,
     setState,
     setVars,
     setValidation,

+ 12 - 2
dashboard/src/components/porter-form/types.ts

@@ -85,7 +85,7 @@ export interface KeyValueArrayField extends GenericInputField {
   settings?: {
     options?: {
       enable_synced_env_groups: boolean;
-    },
+    };
     type: "env" | "normal";
   };
 }
@@ -119,6 +119,15 @@ export interface VariableField extends GenericInputField {
   };
 }
 
+export interface CronField extends GenericInputField {
+  type: "cron";
+  label: string;
+  placeholder: string;
+  settings: {
+    default: string;
+  };
+}
+
 export type FormField =
   | HeadingField
   | SubtitleField
@@ -130,7 +139,8 @@ export type FormField =
   | ServiceIPListField
   | ResourceListField
   | VeleroBackupField
-  | VariableField;
+  | VariableField
+  | CronField;
 
 export interface ShowIfAnd {
   and: ShowIf[];

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov