diff --git a/services/frontend/src/components/Modal/network/CreateNetworkModal.tsx b/services/frontend/src/components/Modal/network/CreateNetworkModal.tsx index 4cef26e..8ccc155 100644 --- a/services/frontend/src/components/Modal/network/CreateNetworkModal.tsx +++ b/services/frontend/src/components/Modal/network/CreateNetworkModal.tsx @@ -6,6 +6,7 @@ import { CallbackFunction } from "../../../types"; import { getInitialValues, tabs, validationSchema } from "./form-utils"; import { classNames } from "../../../utils/styles"; import { Button, styled } from "@mui/joy"; +import { reportErrorsAndSubmit } from "../../../utils/forms"; interface ICreateNetworkModalProps { onCreateNetwork: CallbackFunction; @@ -69,7 +70,11 @@ const CreateNetworkModal: FunctionComponent = ( - diff --git a/services/frontend/src/components/Modal/network/EditNetworkModal.tsx b/services/frontend/src/components/Modal/network/EditNetworkModal.tsx index aa389a1..2a32490 100644 --- a/services/frontend/src/components/Modal/network/EditNetworkModal.tsx +++ b/services/frontend/src/components/Modal/network/EditNetworkModal.tsx @@ -6,6 +6,7 @@ import { CallbackFunction } from "../../../types"; import { getInitialValues, tabs, validationSchema } from "./form-utils"; import { classNames } from "../../../utils/styles"; import { Button, styled } from "@mui/joy"; +import { reportErrorsAndSubmit } from "../../../utils/forms"; interface IEditNetworkModalProps { onUpdateNetwork: CallbackFunction; @@ -68,7 +69,11 @@ const EditNetworkModal = (props: IEditNetworkModalProps) => { - diff --git a/services/frontend/src/components/Modal/network/General.tsx b/services/frontend/src/components/Modal/network/General.tsx index 867a53a..df01fb9 100644 --- a/services/frontend/src/components/Modal/network/General.tsx +++ b/services/frontend/src/components/Modal/network/General.tsx @@ -11,8 +11,8 @@ const Root = styled("div")` const General = () => { return ( - - + + { { name: `labels[${index}].value`, placeholder: "Value", - required: true, + required: false, type: "text" } ]} diff --git a/services/frontend/src/components/Modal/network/form-utils.ts b/services/frontend/src/components/Modal/network/form-utils.ts index 7d3619d..8aad568 100644 --- a/services/frontend/src/components/Modal/network/form-utils.ts +++ b/services/frontend/src/components/Modal/network/form-utils.ts @@ -44,7 +44,7 @@ export const validationSchema = yup.object({ labels: yup.array( yup.object({ key: yup.string().required("Key is required"), - value: yup.string().required("Value is required") + value: yup.string() }) ) }); diff --git a/services/frontend/src/components/Modal/network/index.tsx b/services/frontend/src/components/Modal/network/index.tsx index 4832e3c..1340842 100644 --- a/services/frontend/src/components/Modal/network/index.tsx +++ b/services/frontend/src/components/Modal/network/index.tsx @@ -1,9 +1,9 @@ import { useCallback, useState } from "react"; import { XIcon } from "@heroicons/react/outline"; import CreateNetworkModal from "./CreateNetworkModal"; -import { CallbackFunction } from "../../../types"; +import { CallbackFunction, IEditNetworkForm } from "../../../types"; import EditNetworkModal from "./EditNetworkModal"; -import { attachUUID } from "../../../utils"; +import { attachUUID, toaster } from "../../../utils"; import { getFinalValues } from "./form-utils"; import EmptyNetworks from "./EmptyNetworks"; import NetworkList from "./NetworkList"; @@ -37,7 +37,8 @@ const ModalNetwork = (props: IModalNetworkProps) => { } = props; const [selectedNetwork, setSelectedNetwork] = useState(); const [showCreate, setShowCreate] = useState(false); - const handleCreate = (values: any) => { + + const handleCreate = (values: IEditNetworkForm) => { const finalValues = getFinalValues(values); const uniqueKey = attachUUID(finalValues.key); const network = { @@ -46,12 +47,16 @@ const ModalNetwork = (props: IModalNetworkProps) => { }; onCreateNetwork(network); setSelectedNetwork(network); + + toaster(`Created "${values.entryName}" network successfully`, "success"); }; - const handleUpdate = (values: any) => { + const handleUpdate = (values: IEditNetworkForm) => { const finalValues = getFinalValues(values, selectedNetwork); onUpdateNetwork(finalValues); setSelectedNetwork(finalValues); + + toaster(`Updated "${values.entryName}" network successfully`, "success"); }; const handleRemove = useCallback( diff --git a/services/frontend/src/components/Modal/service/Create.tsx b/services/frontend/src/components/Modal/service/Create.tsx index 688055d..f91a4f1 100644 --- a/services/frontend/src/components/Modal/service/Create.tsx +++ b/services/frontend/src/components/Modal/service/Create.tsx @@ -15,6 +15,8 @@ import { styled } from "@mui/joy"; import Environment from "./Environment"; import Deploy from "./Deploy"; import { classNames } from "../../../utils/styles"; +import { toaster } from "../../../utils"; +import { reportErrorsAndSubmit } from "../../../utils/forms"; interface IModalServiceProps { onHide: CallbackFunction; @@ -60,6 +62,10 @@ const ModalServiceCreate = (props: IModalServiceProps) => { onAddEndpoint(result); formik.resetForm(); onHide(); + toaster( + `Created "${values.serviceName}" service successfully`, + "success" + ); }, [onAddEndpoint, onHide] ); @@ -137,7 +143,7 @@ const ModalServiceCreate = (props: IModalServiceProps) => { diff --git a/services/frontend/src/components/Modal/service/Edit.tsx b/services/frontend/src/components/Modal/service/Edit.tsx index b42c10a..31d8f12 100644 --- a/services/frontend/src/components/Modal/service/Edit.tsx +++ b/services/frontend/src/components/Modal/service/Edit.tsx @@ -15,6 +15,8 @@ import Environment from "./Environment"; import Build from "./Build"; import Deploy from "./Deploy"; import { classNames } from "../../../utils/styles"; +import { toaster } from "../../../utils"; +import { reportErrorsAndSubmit } from "../../../utils/forms"; export interface IModalServiceProps { node: IServiceNodeItem; @@ -29,6 +31,7 @@ const ModalServiceEdit = (props: IModalServiceProps) => { const handleUpdate = (values: any) => { onUpdateEndpoint(getFinalValues(values, selectedNode)); + toaster(`Updated "${values.serviceName}" service successfully`, "success"); }; const initialValues = useMemo( @@ -112,9 +115,7 @@ const ModalServiceEdit = (props: IModalServiceProps) => { diff --git a/services/frontend/src/components/Modal/volume/CreateVolumeModal.tsx b/services/frontend/src/components/Modal/volume/CreateVolumeModal.tsx index abdd642..951dcc0 100644 --- a/services/frontend/src/components/Modal/volume/CreateVolumeModal.tsx +++ b/services/frontend/src/components/Modal/volume/CreateVolumeModal.tsx @@ -9,8 +9,10 @@ import { validationSchema } from "./form-utils"; import General from "./General"; -import { CallbackFunction } from "../../../types"; +import { CallbackFunction, IEditVolumeForm } from "../../../types"; import { classNames } from "../../../utils/styles"; +import { toaster } from "../../../utils"; +import { reportErrorsAndSubmit } from "../../../utils/forms"; interface ICreateVolumeModalProps { onHide: CallbackFunction; @@ -21,10 +23,12 @@ const CreateVolumeModal = (props: ICreateVolumeModalProps) => { const { onHide, onAddEndpoint } = props; const [openTab, setOpenTab] = useState("General"); - const handleCreate = useCallback((values: any, formik: any) => { + const handleCreate = useCallback((values: IEditVolumeForm, formik: any) => { onAddEndpoint(getFinalValues(values)); formik.resetForm(); onHide(); + + toaster(`Created "${values.entryName}" volume successfully`, "success"); }, []); const initialValues = useMemo(() => getInitialValues(), []); @@ -94,7 +98,7 @@ const CreateVolumeModal = (props: ICreateVolumeModalProps) => { diff --git a/services/frontend/src/components/Modal/volume/EditVolumeModal.tsx b/services/frontend/src/components/Modal/volume/EditVolumeModal.tsx index cc20a9f..215245c 100644 --- a/services/frontend/src/components/Modal/volume/EditVolumeModal.tsx +++ b/services/frontend/src/components/Modal/volume/EditVolumeModal.tsx @@ -2,7 +2,11 @@ import { useEffect, useMemo, useState } from "react"; import { Formik } from "formik"; import { XIcon } from "@heroicons/react/outline"; import General from "./General"; -import type { CallbackFunction, IVolumeNodeItem } from "../../../types"; +import type { + CallbackFunction, + IEditVolumeForm, + IVolumeNodeItem +} from "../../../types"; import { getFinalValues, getInitialValues, @@ -10,6 +14,8 @@ import { validationSchema } from "./form-utils"; import { classNames } from "../../../utils/styles"; +import { toaster } from "../../../utils"; +import { reportErrorsAndSubmit } from "../../../utils/forms"; interface IEditVolumeModal { node: IVolumeNodeItem; @@ -28,8 +34,10 @@ const EditVolumeModal = (props: IEditVolumeModal) => { } }, [node]); - const handleUpdate = (values: any) => { + const handleUpdate = (values: IEditVolumeForm) => { onUpdateEndpoint(getFinalValues(values, selectedNode)); + + toaster(`Updated "${values.entryName}" volume successfully`, "success"); }; const initialValues = useMemo( @@ -103,7 +111,7 @@ const EditVolumeModal = (props: IEditVolumeModal) => { diff --git a/services/frontend/src/utils/forms.ts b/services/frontend/src/utils/forms.ts index ccff7ff..af22bf6 100644 --- a/services/frontend/src/utils/forms.ts +++ b/services/frontend/src/utils/forms.ts @@ -1,5 +1,5 @@ import lodash from "lodash"; -import { number } from "yup"; +import { toaster } from "."; export const checkArray = (array: any, name: string): T => { if (!Array.isArray(array)) { @@ -195,3 +195,22 @@ export const packArrayAsStrings = ( ) ); }; + +/** + * Formik is configured to validate fields automatically using Yup. + * The problem is Formik does not call `onSubmit` function when errors + * are present. Therefore, a work around was to call `formik.submitForm` + * after showing errors to the user. The `reportErrorsAndSubmit` utility + * function basically implements this. + */ +export const reportErrorsAndSubmit = (formik: any) => () => { + const errors = Object.entries(formik.errors); + if (errors.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + for (const [_field, message] of errors) { + toaster(message as string, "error"); + } + } else { + formik.submitForm(); + } +};