diff --git a/services/frontend/package.json b/services/frontend/package.json
index 37512cd..bd23d2c 100644
--- a/services/frontend/package.json
+++ b/services/frontend/package.json
@@ -45,7 +45,8 @@
"typescript": "^4.5.5",
"uuid": "^8.3.2",
"web-vitals": "^2.1.4",
- "yaml": "^1.10.2"
+ "yaml": "^1.10.2",
+ "yup": "^0.32.11"
},
"scripts": {
"start": "react-scripts start",
diff --git a/services/frontend/src/components/Canvas/ServiceNode.tsx b/services/frontend/src/components/Canvas/ServiceNode.tsx
index 48e4a0b..cbd3d71 100644
--- a/services/frontend/src/components/Canvas/ServiceNode.tsx
+++ b/services/frontend/src/components/Canvas/ServiceNode.tsx
@@ -1,4 +1,6 @@
import { useEffect, useState } from "react";
+import { ServerIcon } from "@heroicons/react/outline";
+import { truncateStr } from "../../utils";
import { IServiceNodeItem, CallbackFunction } from "../../types";
import eventBus from "../../events/eventBus";
import { Popover } from "./Popover";
@@ -52,14 +54,21 @@ export default function ServiceNode(props: INodeProps) {
}}
>
)}
-
+
<>
-
- {node.canvasConfig.service_name}
-
-
- {node.serviceConfig.container_name}
-
+ {node.canvasConfig.service_name && (
+
+ {truncateStr(node.canvasConfig.service_name, 12)}
+
+ )}
+
+ {node.serviceConfig.container_name && (
+
+ {truncateStr(node.serviceConfig.container_name, 20)}
+
+ )}
+
+
>
diff --git a/services/frontend/src/components/Canvas/VolumeNode.tsx b/services/frontend/src/components/Canvas/VolumeNode.tsx
index b067d74..759f2dd 100644
--- a/services/frontend/src/components/Canvas/VolumeNode.tsx
+++ b/services/frontend/src/components/Canvas/VolumeNode.tsx
@@ -1,4 +1,6 @@
import { useEffect, useState } from "react";
+import { DatabaseIcon } from "@heroicons/react/outline";
+import { truncateStr } from "../../utils";
import { IVolumeNodeItem, CallbackFunction } from "../../types";
import eventBus from "../../events/eventBus";
import { Popover } from "./Popover";
@@ -52,11 +54,15 @@ export default function VolumeNode(props: INodeProps) {
}}
>
)}
-
+
<>
-
- {node.volumeConfig.name}
-
+ {node.volumeConfig.name && (
+
+ {truncateStr(node.volumeConfig.name, 20)}
+
+ )}
+
+
>
diff --git a/services/frontend/src/components/Modal/Network/General.tsx b/services/frontend/src/components/Modal/Network/General.tsx
index 00639c8..7d006fd 100644
--- a/services/frontend/src/components/Modal/Network/General.tsx
+++ b/services/frontend/src/components/Modal/Network/General.tsx
@@ -1,32 +1,11 @@
-const General = (props: any) => {
- const { formik } = props;
+import TextField from "../../global/FormElements/InputField";
+const General = () => {
return (
<>
-
+
>
);
};
+
export default General;
diff --git a/services/frontend/src/components/Modal/Network/IPam.tsx b/services/frontend/src/components/Modal/Network/IPam.tsx
index 05b471f..2ea3f71 100644
--- a/services/frontend/src/components/Modal/Network/IPam.tsx
+++ b/services/frontend/src/components/Modal/Network/IPam.tsx
@@ -1,6 +1,5 @@
-const IPam = (props: any) => {
- const { formik } = props;
-
+const IPam = () => {
return <>>;
};
+
export default IPam;
diff --git a/services/frontend/src/components/Modal/Network/Labels.tsx b/services/frontend/src/components/Modal/Network/Labels.tsx
index 9838a34..12d9e27 100644
--- a/services/frontend/src/components/Modal/Network/Labels.tsx
+++ b/services/frontend/src/components/Modal/Network/Labels.tsx
@@ -1,6 +1,5 @@
-const Labels = (props: any) => {
- const { formik } = props;
-
+const Labels = () => {
return <>>;
};
+
export default Labels;
diff --git a/services/frontend/src/components/Modal/Network/index.tsx b/services/frontend/src/components/Modal/Network/index.tsx
index c65ccf4..de0e46a 100644
--- a/services/frontend/src/components/Modal/Network/index.tsx
+++ b/services/frontend/src/components/Modal/Network/index.tsx
@@ -1,5 +1,6 @@
import { useState } from "react";
-import { useFormik } from "formik";
+import { Formik } from "formik";
+import * as yup from "yup";
import { XIcon } from "@heroicons/react/outline";
import General from "./General";
import IPam from "./IPam";
@@ -14,12 +15,14 @@ interface IModalNetworkProps {
const ModalNetwork = (props: IModalNetworkProps) => {
const { onHide } = props;
const [openTab, setOpenTab] = useState("General");
-
- const formik = useFormik({
- initialValues: {
- ...topLevelNetworkConfigInitialValues()
- },
- onSubmit: () => undefined
+ const handleCreate = (values: any, formik: any) => {
+ formik.resetForm();
+ };
+ const validationSchema = yup.object({
+ name: yup
+ .string()
+ .max(256, "name should be 256 characters or less")
+ .required("name is required")
});
const tabs = [
{
@@ -66,54 +69,70 @@ const ModalNetwork = (props: IModalNetworkProps) => {
-
-
+
{
+ handleCreate(values, formik);
+ }}
+ validationSchema={validationSchema}
+ >
+ {(formik) => (
+ <>
+
-
-
-
-
+
+ {openTab === "General" && }
+ {openTab === "IPam" && }
+ {openTab === "Labels" && }
+
-
-
-
+
+
+
+ >
+ )}
+
diff --git a/services/frontend/src/components/Modal/Service/Create.tsx b/services/frontend/src/components/Modal/Service/Create.tsx
index 3315933..aba5516 100644
--- a/services/frontend/src/components/Modal/Service/Create.tsx
+++ b/services/frontend/src/components/Modal/Service/Create.tsx
@@ -1,5 +1,6 @@
import { useState } from "react";
-import { useFormik } from "formik";
+import { Formik } from "formik";
+import * as yup from "yup";
import { XIcon } from "@heroicons/react/outline";
import General from "./General";
import Environment from "./Environment";
@@ -16,22 +17,23 @@ interface IModalServiceProps {
const ModalServiceCreate = (props: IModalServiceProps) => {
const { onHide, onAddEndpoint } = props;
const [openTab, setOpenTab] = useState("General");
-
- const formik = useFormik({
- initialValues: {
- canvasConfig: {
- ...serviceConfigCanvasInitialValues()
- },
- serviceConfig: {
- container_name: ""
- },
- key: "service",
- type: "SERVICE",
- inputs: ["op_source"],
- outputs: [],
- config: {}
- },
- onSubmit: () => undefined
+ const handleCreate = (values: any, formik: any) => {
+ onAddEndpoint(values);
+ formik.resetForm();
+ };
+ const validationSchema = yup.object({
+ canvasConfig: yup.object({
+ service_name: yup
+ .string()
+ .max(256, "service name should be 256 characters or less")
+ .required("service name is required")
+ }),
+ serviceConfig: yup.object({
+ container_name: yup
+ .string()
+ .max(256, "container name should be 256 characters or less")
+ .required("container name is required")
+ })
});
const tabs = [
{
@@ -84,56 +86,77 @@ const ModalServiceCreate = (props: IModalServiceProps) => {
-
-
+
{
+ handleCreate(values, formik);
+ }}
+ validationSchema={validationSchema}
+ >
+ {(formik) => (
+ <>
+
-
-
-
-
+
+ {openTab === "General" && }
+ {openTab === "Environment" && }
+ {openTab === "Volumes" && }
+ {openTab === "Labels" && }
+
-
-
-
+
+
+
+ >
+ )}
+
diff --git a/services/frontend/src/components/Modal/Service/Edit.tsx b/services/frontend/src/components/Modal/Service/Edit.tsx
index 2eb49be..0001646 100644
--- a/services/frontend/src/components/Modal/Service/Edit.tsx
+++ b/services/frontend/src/components/Modal/Service/Edit.tsx
@@ -1,17 +1,12 @@
import { useState, useEffect } from "react";
-import { useFormik } from "formik";
+import { Formik } from "formik";
+import * as yup from "yup";
import { XIcon } from "@heroicons/react/outline";
import General from "./General";
import Environment from "./Environment";
import Volumes from "./Volumes";
import Labels from "./Labels";
-import { serviceConfigCanvasInitialValues } from "../../../utils";
-import {
- CallbackFunction,
- ICanvasConfig,
- IServiceNodeItem,
- IService
-} from "../../../types";
+import { CallbackFunction, IServiceNodeItem } from "../../../types";
interface IModalServiceProps {
node: IServiceNodeItem;
@@ -23,16 +18,26 @@ const ModalServiceEdit = (props: IModalServiceProps) => {
const { node, onHide, onUpdateEndpoint } = props;
const [openTab, setOpenTab] = useState("General");
const [selectedNode, setSelectedNode] = useState();
- const formik = useFormik({
- initialValues: {
- canvasConfig: {
- ...serviceConfigCanvasInitialValues()
- },
- serviceConfig: {
- container_name: ""
- }
- },
- onSubmit: () => undefined
+
+ const handleUpdate = (values: any) => {
+ const updated = { ...selectedNode };
+ updated.canvasConfig = values.canvasConfig;
+ updated.serviceConfig = values.serviceConfig;
+ onUpdateEndpoint(updated);
+ };
+ const validationSchema = yup.object({
+ canvasConfig: yup.object({
+ service_name: yup
+ .string()
+ .max(256, "service name should be 256 characters or less")
+ .required("service name is required")
+ }),
+ serviceConfig: yup.object({
+ container_name: yup
+ .string()
+ .max(256, "container name should be 256 characters or less")
+ .required("container name is required")
+ })
});
const tabs = [
{
@@ -70,25 +75,6 @@ const ModalServiceEdit = (props: IModalServiceProps) => {
}
}, [node]);
- useEffect(() => {
- formik.resetForm();
-
- if (selectedNode) {
- formik.initialValues.canvasConfig = {
- ...selectedNode.canvasConfig
- } as ICanvasConfig;
- formik.initialValues.serviceConfig = {
- ...selectedNode.serviceConfig
- } as IService;
- }
- }, [selectedNode]);
-
- useEffect(() => {
- return () => {
- formik.resetForm();
- };
- }, []);
-
return (
@@ -110,58 +96,76 @@ const ModalServiceEdit = (props: IModalServiceProps) => {
-
-
-
-
-
+ {(formik) => (
+ <>
+
+
+
+ {openTab === "General" && }
+ {openTab === "Environment" && }
+ {openTab === "Volumes" && }
+ {openTab === "Labels" && }
+
+
+
+
+
+ >
+ )}
+
+ )}
diff --git a/services/frontend/src/components/Modal/Service/Environment.tsx b/services/frontend/src/components/Modal/Service/Environment.tsx
index f68aaa3..066dfb9 100644
--- a/services/frontend/src/components/Modal/Service/Environment.tsx
+++ b/services/frontend/src/components/Modal/Service/Environment.tsx
@@ -1,6 +1,5 @@
-const Environment = (props: any) => {
- const { formik } = props;
-
+const Environment = () => {
return <>>;
};
+
export default Environment;
diff --git a/services/frontend/src/components/Modal/Service/General.tsx b/services/frontend/src/components/Modal/Service/General.tsx
index 3ae0c59..40b26e4 100644
--- a/services/frontend/src/components/Modal/Service/General.tsx
+++ b/services/frontend/src/components/Modal/Service/General.tsx
@@ -1,56 +1,12 @@
-const General = (props: any) => {
- const { formik } = props;
+import TextField from "../../global/FormElements/InputField";
+const General = () => {
return (
<>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
>
);
};
+
export default General;
diff --git a/services/frontend/src/components/Modal/Service/Labels.tsx b/services/frontend/src/components/Modal/Service/Labels.tsx
index 9838a34..12d9e27 100644
--- a/services/frontend/src/components/Modal/Service/Labels.tsx
+++ b/services/frontend/src/components/Modal/Service/Labels.tsx
@@ -1,6 +1,5 @@
-const Labels = (props: any) => {
- const { formik } = props;
-
+const Labels = () => {
return <>>;
};
+
export default Labels;
diff --git a/services/frontend/src/components/Modal/Service/Volumes.tsx b/services/frontend/src/components/Modal/Service/Volumes.tsx
index 8ed7f94..054bed2 100644
--- a/services/frontend/src/components/Modal/Service/Volumes.tsx
+++ b/services/frontend/src/components/Modal/Service/Volumes.tsx
@@ -1,6 +1,5 @@
-const Volumes = (props: any) => {
- const { formik } = props;
-
+const Volumes = () => {
return <>>;
};
+
export default Volumes;
diff --git a/services/frontend/src/components/Modal/Volume/Create.tsx b/services/frontend/src/components/Modal/Volume/Create.tsx
index cde3c62..6d8ef0c 100644
--- a/services/frontend/src/components/Modal/Volume/Create.tsx
+++ b/services/frontend/src/components/Modal/Volume/Create.tsx
@@ -1,5 +1,6 @@
import { useState } from "react";
-import { useFormik } from "formik";
+import { Formik } from "formik";
+import * as yup from "yup";
import { XIcon } from "@heroicons/react/outline";
import General from "./General";
import Labels from "./Labels";
@@ -14,19 +15,17 @@ interface IModalVolumeCreate {
const ModalVolumeCreate = (props: IModalVolumeCreate) => {
const { onHide, onAddEndpoint } = props;
const [openTab, setOpenTab] = useState("General");
-
- const formik = useFormik({
- initialValues: {
- volumeConfig: {
- ...topLevelVolumeConfigInitialValues()
- },
- key: "volume",
- type: "VOLUME",
- inputs: [],
- outputs: [],
- config: {}
- },
- onSubmit: () => undefined
+ const handleCreate = (values: any, formik: any) => {
+ onAddEndpoint(values);
+ formik.resetForm();
+ };
+ const validationSchema = yup.object({
+ volumeConfig: yup.object({
+ name: yup
+ .string()
+ .max(256, "name should be 256 characters or less")
+ .required("name is required")
+ })
});
const tabs = [
{
@@ -67,54 +66,71 @@ const ModalVolumeCreate = (props: IModalVolumeCreate) => {
-
-
+
{
+ handleCreate(values, formik);
+ }}
+ validationSchema={validationSchema}
+ >
+ {(formik) => (
+ <>
+
-
-
-
-
+
+ {openTab === "General" && }
+ {openTab === "Labels" && }
+
-
-
-
+
+
+
+ >
+ )}
+
diff --git a/services/frontend/src/components/Modal/Volume/Edit.tsx b/services/frontend/src/components/Modal/Volume/Edit.tsx
index 06e452d..2424560 100644
--- a/services/frontend/src/components/Modal/Volume/Edit.tsx
+++ b/services/frontend/src/components/Modal/Volume/Edit.tsx
@@ -1,14 +1,10 @@
import { useEffect, useState } from "react";
-import { useFormik } from "formik";
+import { Formik } from "formik";
+import * as yup from "yup";
import { XIcon } from "@heroicons/react/outline";
import General from "./General";
import Labels from "./Labels";
-import { topLevelVolumeConfigInitialValues } from "../../../utils";
-import {
- CallbackFunction,
- IVolumeNodeItem,
- IVolumeTopLevel
-} from "../../../types";
+import { CallbackFunction, IVolumeNodeItem } from "../../../types";
interface IModalVolumeEdit {
node: IVolumeNodeItem;
@@ -20,14 +16,18 @@ const ModalVolumeEdit = (props: IModalVolumeEdit) => {
const { node, onHide, onUpdateEndpoint } = props;
const [openTab, setOpenTab] = useState("General");
const [selectedNode, setSelectedNode] = useState();
-
- const formik = useFormik({
- initialValues: {
- volumeConfig: {
- ...topLevelVolumeConfigInitialValues()
- }
- },
- onSubmit: () => undefined
+ const handleUpdate = (values: any) => {
+ const updated = { ...selectedNode };
+ updated.volumeConfig = values.volumeConfig;
+ onUpdateEndpoint(updated);
+ };
+ const validationSchema = yup.object({
+ volumeConfig: yup.object({
+ name: yup
+ .string()
+ .max(256, "name should be 256 characters or less")
+ .required("name is required")
+ })
});
const tabs = [
{
@@ -53,22 +53,6 @@ const ModalVolumeEdit = (props: IModalVolumeEdit) => {
}
}, [node]);
- useEffect(() => {
- formik.resetForm();
-
- if (selectedNode) {
- formik.initialValues.volumeConfig = {
- ...selectedNode.volumeConfig
- } as IVolumeTopLevel;
- }
- }, [selectedNode]);
-
- useEffect(() => {
- return () => {
- formik.resetForm();
- };
- }, []);
-
return (
@@ -92,55 +76,71 @@ const ModalVolumeEdit = (props: IModalVolumeEdit) => {
-
-
-
-
-
+ {(formik) => (
+ <>
+
+
+
+ {openTab === "General" && }
+ {openTab === "Labels" && }
+
+
+
+
+
+ >
+ )}
+
+ )}
diff --git a/services/frontend/src/components/Modal/Volume/General.tsx b/services/frontend/src/components/Modal/Volume/General.tsx
index 1e18ea2..01d817a 100644
--- a/services/frontend/src/components/Modal/Volume/General.tsx
+++ b/services/frontend/src/components/Modal/Volume/General.tsx
@@ -1,32 +1,11 @@
-const General = (props: any) => {
- const { formik } = props;
+import TextField from "../../global/FormElements/InputField";
+const General = () => {
return (
<>
-
+
>
);
};
+
export default General;
diff --git a/services/frontend/src/components/Modal/Volume/Labels.tsx b/services/frontend/src/components/Modal/Volume/Labels.tsx
index 9838a34..710b9e5 100644
--- a/services/frontend/src/components/Modal/Volume/Labels.tsx
+++ b/services/frontend/src/components/Modal/Volume/Labels.tsx
@@ -1,6 +1,4 @@
-const Labels = (props: any) => {
- const { formik } = props;
-
+const Labels = () => {
return <>>;
};
export default Labels;
diff --git a/services/frontend/src/components/Project/index.tsx b/services/frontend/src/components/Project/index.tsx
index ec7b9a2..19ffb9f 100644
--- a/services/frontend/src/components/Project/index.tsx
+++ b/services/frontend/src/components/Project/index.tsx
@@ -245,9 +245,9 @@ export default function Project() {
if (existingIndex !== -1) {
_connections.splice(existingIndex, 1);
+ setConnections(_connections);
+ stateConnectionsRef.current = _connections;
}
-
- setConnections(_connections);
};
const onConnectionAttached = (data: any) => {
diff --git a/services/frontend/src/components/global/FormElements/InputField.tsx b/services/frontend/src/components/global/FormElements/InputField.tsx
new file mode 100644
index 0000000..f3d0480
--- /dev/null
+++ b/services/frontend/src/components/global/FormElements/InputField.tsx
@@ -0,0 +1,50 @@
+import _ from "lodash";
+import { useFormikContext } from "formik";
+
+interface Props {
+ name: string;
+ help?: string;
+ [key: string]: any;
+}
+
+const TextField = (props: Props) => {
+ const { label, name, help, ...otherProps } = props;
+ const formik = useFormikContext();
+ const error = _.get(formik.touched, name) && _.get(formik.errors, name);
+
+ return (
+
+
+
+
+
+
+ {
+
+ {error &&
{error}
}
+ {!error && help}
+
+ }
+
+
+
+
+ );
+};
+
+export default TextField;