Merge pull request #86 from nuxxapp/feat/toasts

Show toasts
pull/87/head
Samuel Rowe 3 years ago committed by GitHub
commit aef997fe43
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,6 +6,7 @@ import { CallbackFunction } from "../../../types";
import { getInitialValues, tabs, validationSchema } from "./form-utils"; import { getInitialValues, tabs, validationSchema } from "./form-utils";
import { classNames } from "../../../utils/styles"; import { classNames } from "../../../utils/styles";
import { Button, styled } from "@mui/joy"; import { Button, styled } from "@mui/joy";
import { reportErrorsAndSubmit } from "../../../utils/forms";
interface ICreateNetworkModalProps { interface ICreateNetworkModalProps {
onCreateNetwork: CallbackFunction; onCreateNetwork: CallbackFunction;
@ -69,7 +70,11 @@ const CreateNetworkModal: FunctionComponent<ICreateNetworkModalProps> = (
</div> </div>
<Actions> <Actions>
<Button size="sm" variant="solid" onClick={formik.submitForm}> <Button
size="sm"
variant="solid"
onClick={reportErrorsAndSubmit(formik)}
>
Create Create
</Button> </Button>
</Actions> </Actions>

@ -6,6 +6,7 @@ import { CallbackFunction } from "../../../types";
import { getInitialValues, tabs, validationSchema } from "./form-utils"; import { getInitialValues, tabs, validationSchema } from "./form-utils";
import { classNames } from "../../../utils/styles"; import { classNames } from "../../../utils/styles";
import { Button, styled } from "@mui/joy"; import { Button, styled } from "@mui/joy";
import { reportErrorsAndSubmit } from "../../../utils/forms";
interface IEditNetworkModalProps { interface IEditNetworkModalProps {
onUpdateNetwork: CallbackFunction; onUpdateNetwork: CallbackFunction;
@ -68,7 +69,11 @@ const EditNetworkModal = (props: IEditNetworkModalProps) => {
</div> </div>
<Actions> <Actions>
<Button size="sm" variant="solid" onClick={formik.submitForm}> <Button
size="sm"
variant="solid"
onClick={reportErrorsAndSubmit(formik)}
>
Save Save
</Button> </Button>
</Actions> </Actions>

@ -11,8 +11,8 @@ const Root = styled("div")`
const General = () => { const General = () => {
return ( return (
<Root> <Root>
<TextField label="Entry name" name="entryName" /> <TextField label="Entry name" name="entryName" required={true} />
<TextField label="Network name" name="networkName" /> <TextField label="Network name" name="networkName" required={true} />
<Records <Records
name="labels" name="labels"
title="Labels" title="Labels"
@ -26,7 +26,7 @@ const General = () => {
{ {
name: `labels[${index}].value`, name: `labels[${index}].value`,
placeholder: "Value", placeholder: "Value",
required: true, required: false,
type: "text" type: "text"
} }
]} ]}

@ -44,7 +44,7 @@ export const validationSchema = yup.object({
labels: yup.array( labels: yup.array(
yup.object({ yup.object({
key: yup.string().required("Key is required"), key: yup.string().required("Key is required"),
value: yup.string().required("Value is required") value: yup.string()
}) })
) )
}); });

@ -1,9 +1,9 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { XIcon } from "@heroicons/react/outline"; import { XIcon } from "@heroicons/react/outline";
import CreateNetworkModal from "./CreateNetworkModal"; import CreateNetworkModal from "./CreateNetworkModal";
import { CallbackFunction } from "../../../types"; import { CallbackFunction, IEditNetworkForm } from "../../../types";
import EditNetworkModal from "./EditNetworkModal"; import EditNetworkModal from "./EditNetworkModal";
import { attachUUID } from "../../../utils"; import { attachUUID, toaster } from "../../../utils";
import { getFinalValues } from "./form-utils"; import { getFinalValues } from "./form-utils";
import EmptyNetworks from "./EmptyNetworks"; import EmptyNetworks from "./EmptyNetworks";
import NetworkList from "./NetworkList"; import NetworkList from "./NetworkList";
@ -37,7 +37,8 @@ const ModalNetwork = (props: IModalNetworkProps) => {
} = props; } = props;
const [selectedNetwork, setSelectedNetwork] = useState<any | null>(); const [selectedNetwork, setSelectedNetwork] = useState<any | null>();
const [showCreate, setShowCreate] = useState(false); const [showCreate, setShowCreate] = useState(false);
const handleCreate = (values: any) => {
const handleCreate = (values: IEditNetworkForm) => {
const finalValues = getFinalValues(values); const finalValues = getFinalValues(values);
const uniqueKey = attachUUID(finalValues.key); const uniqueKey = attachUUID(finalValues.key);
const network = { const network = {
@ -46,12 +47,16 @@ const ModalNetwork = (props: IModalNetworkProps) => {
}; };
onCreateNetwork(network); onCreateNetwork(network);
setSelectedNetwork(network); setSelectedNetwork(network);
toaster(`Created "${values.entryName}" network successfully`, "success");
}; };
const handleUpdate = (values: any) => { const handleUpdate = (values: IEditNetworkForm) => {
const finalValues = getFinalValues(values, selectedNetwork); const finalValues = getFinalValues(values, selectedNetwork);
onUpdateNetwork(finalValues); onUpdateNetwork(finalValues);
setSelectedNetwork(finalValues); setSelectedNetwork(finalValues);
toaster(`Updated "${values.entryName}" network successfully`, "success");
}; };
const handleRemove = useCallback( const handleRemove = useCallback(

@ -15,6 +15,8 @@ import { styled } from "@mui/joy";
import Environment from "./Environment"; import Environment from "./Environment";
import Deploy from "./Deploy"; import Deploy from "./Deploy";
import { classNames } from "../../../utils/styles"; import { classNames } from "../../../utils/styles";
import { toaster } from "../../../utils";
import { reportErrorsAndSubmit } from "../../../utils/forms";
interface IModalServiceProps { interface IModalServiceProps {
onHide: CallbackFunction; onHide: CallbackFunction;
@ -60,6 +62,10 @@ const ModalServiceCreate = (props: IModalServiceProps) => {
onAddEndpoint(result); onAddEndpoint(result);
formik.resetForm(); formik.resetForm();
onHide(); onHide();
toaster(
`Created "${values.serviceName}" service successfully`,
"success"
);
}, },
[onAddEndpoint, onHide] [onAddEndpoint, onHide]
); );
@ -137,7 +143,7 @@ const ModalServiceCreate = (props: IModalServiceProps) => {
<button <button
className="btn-util" className="btn-util"
type="button" type="button"
onClick={formik.submitForm} onClick={reportErrorsAndSubmit(formik)}
> >
Add Add
</button> </button>

@ -15,6 +15,8 @@ import Environment from "./Environment";
import Build from "./Build"; import Build from "./Build";
import Deploy from "./Deploy"; import Deploy from "./Deploy";
import { classNames } from "../../../utils/styles"; import { classNames } from "../../../utils/styles";
import { toaster } from "../../../utils";
import { reportErrorsAndSubmit } from "../../../utils/forms";
export interface IModalServiceProps { export interface IModalServiceProps {
node: IServiceNodeItem; node: IServiceNodeItem;
@ -29,6 +31,7 @@ const ModalServiceEdit = (props: IModalServiceProps) => {
const handleUpdate = (values: any) => { const handleUpdate = (values: any) => {
onUpdateEndpoint(getFinalValues(values, selectedNode)); onUpdateEndpoint(getFinalValues(values, selectedNode));
toaster(`Updated "${values.serviceName}" service successfully`, "success");
}; };
const initialValues = useMemo( const initialValues = useMemo(
@ -112,9 +115,7 @@ const ModalServiceEdit = (props: IModalServiceProps) => {
<button <button
className="btn-util" className="btn-util"
type="button" type="button"
onClick={() => { onClick={reportErrorsAndSubmit(formik)}
formik.submitForm();
}}
> >
Save Save
</button> </button>

@ -9,8 +9,10 @@ import {
validationSchema validationSchema
} from "./form-utils"; } from "./form-utils";
import General from "./General"; import General from "./General";
import { CallbackFunction } from "../../../types"; import { CallbackFunction, IEditVolumeForm } from "../../../types";
import { classNames } from "../../../utils/styles"; import { classNames } from "../../../utils/styles";
import { toaster } from "../../../utils";
import { reportErrorsAndSubmit } from "../../../utils/forms";
interface ICreateVolumeModalProps { interface ICreateVolumeModalProps {
onHide: CallbackFunction; onHide: CallbackFunction;
@ -21,10 +23,12 @@ const CreateVolumeModal = (props: ICreateVolumeModalProps) => {
const { onHide, onAddEndpoint } = props; const { onHide, onAddEndpoint } = props;
const [openTab, setOpenTab] = useState("General"); const [openTab, setOpenTab] = useState("General");
const handleCreate = useCallback((values: any, formik: any) => { const handleCreate = useCallback((values: IEditVolumeForm, formik: any) => {
onAddEndpoint(getFinalValues(values)); onAddEndpoint(getFinalValues(values));
formik.resetForm(); formik.resetForm();
onHide(); onHide();
toaster(`Created "${values.entryName}" volume successfully`, "success");
}, []); }, []);
const initialValues = useMemo(() => getInitialValues(), []); const initialValues = useMemo(() => getInitialValues(), []);
@ -94,7 +98,7 @@ const CreateVolumeModal = (props: ICreateVolumeModalProps) => {
<button <button
className="btn-util" className="btn-util"
type="button" type="button"
onClick={formik.submitForm} onClick={reportErrorsAndSubmit(formik)}
> >
Add Add
</button> </button>

@ -2,7 +2,11 @@ import { useEffect, useMemo, useState } from "react";
import { Formik } from "formik"; import { Formik } from "formik";
import { XIcon } from "@heroicons/react/outline"; import { XIcon } from "@heroicons/react/outline";
import General from "./General"; import General from "./General";
import type { CallbackFunction, IVolumeNodeItem } from "../../../types"; import type {
CallbackFunction,
IEditVolumeForm,
IVolumeNodeItem
} from "../../../types";
import { import {
getFinalValues, getFinalValues,
getInitialValues, getInitialValues,
@ -10,6 +14,8 @@ import {
validationSchema validationSchema
} from "./form-utils"; } from "./form-utils";
import { classNames } from "../../../utils/styles"; import { classNames } from "../../../utils/styles";
import { toaster } from "../../../utils";
import { reportErrorsAndSubmit } from "../../../utils/forms";
interface IEditVolumeModal { interface IEditVolumeModal {
node: IVolumeNodeItem; node: IVolumeNodeItem;
@ -28,8 +34,10 @@ const EditVolumeModal = (props: IEditVolumeModal) => {
} }
}, [node]); }, [node]);
const handleUpdate = (values: any) => { const handleUpdate = (values: IEditVolumeForm) => {
onUpdateEndpoint(getFinalValues(values, selectedNode)); onUpdateEndpoint(getFinalValues(values, selectedNode));
toaster(`Updated "${values.entryName}" volume successfully`, "success");
}; };
const initialValues = useMemo( const initialValues = useMemo(
@ -103,7 +111,7 @@ const EditVolumeModal = (props: IEditVolumeModal) => {
<button <button
className="btn-util" className="btn-util"
type="button" type="button"
onClick={formik.submitForm} onClick={reportErrorsAndSubmit(formik)}
> >
Save Save
</button> </button>

@ -1,5 +1,5 @@
import lodash from "lodash"; import lodash from "lodash";
import { number } from "yup"; import { toaster } from ".";
export const checkArray = <T>(array: any, name: string): T => { export const checkArray = <T>(array: any, name: string): T => {
if (!Array.isArray(array)) { 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();
}
};

Loading…
Cancel
Save