Files
Checkmate/client/src/Pages/Notifications/create/index.jsx
T

285 lines
7.2 KiB
React

// Components
import Stack from "@mui/material/Stack";
import Typography from "@mui/material/Typography";
import Breadcrumbs from "../../../Components/Breadcrumbs";
import Button from "@mui/material/Button";
import ConfigBox from "../../../Components/ConfigBox";
import Box from "@mui/material/Box";
import Select from "../../../Components/Inputs/Select";
import TextInput from "../../../Components/Inputs/TextInput";
import Dialog from "../../../Components/Dialog";
// Utils
import { useState } from "react";
import { useTheme } from "@emotion/react";
import {
useCreateNotification,
useGetNotificationById,
useEditNotification,
useTestNotification,
useDeleteNotification,
} from "../../../Hooks/useNotifications";
import { notificationValidation } from "../../../Validation/validation";
import { createToast } from "../../../Utils/toastUtils";
import { useTranslation } from "react-i18next";
import { useParams, useNavigate } from "react-router-dom";
import {
NOTIFICATION_TYPES,
TITLE_MAP,
DESCRIPTION_MAP,
LABEL_MAP,
PLACEHOLDER_MAP,
} from "../utils";
// Setup
const CreateNotifications = () => {
const { notificationId } = useParams();
const navigate = useNavigate();
const theme = useTheme();
const [createNotification, isCreating] = useCreateNotification();
const [editNotification, isEditing] = useEditNotification();
const [testNotification, isTesting] = useTestNotification();
const [deleteNotification, isDeleting] = useDeleteNotification();
const BREADCRUMBS = [
{ name: "notifications", path: "/notifications" },
{
name: notificationId ? "edit" : "create",
path: notificationId ? `/notifications/${notificationId}` : "/notifications/create",
},
];
// Redux state
// local state
const [notification, setNotification] = useState({
notificationName: "",
address: "",
type: NOTIFICATION_TYPES[0]._id,
});
const [errors, setErrors] = useState({});
const { t } = useTranslation();
const [notificationIsLoading] = useGetNotificationById(notificationId, setNotification);
const [isOpen, setIsOpen] = useState(false);
const getNotificationTypeValue = (typeId) => {
return NOTIFICATION_TYPES.find((type) => type._id === typeId)?.value || "email";
};
const extractError = (error, field) =>
error?.details.find((d) => d.path.includes(field))?.message;
// handlers
const onSubmit = (e) => {
e.preventDefault();
const form = {
...notification,
type: getNotificationTypeValue(notification.type),
};
let error = null;
error = notificationValidation.validate(form, { abortEarly: false }).error;
if (error) {
const newErrors = {};
error.details.forEach((err) => {
newErrors[err.path[0]] = err.message;
});
createToast({ body: Object.values(newErrors)[0] });
setErrors(newErrors);
return;
}
if (notificationId) {
editNotification(notificationId, form);
} else {
createNotification(form);
}
};
const onChange = (e) => {
const { name, value } = e.target;
let rawNotification = { ...notification, [name]: value };
let newNotification = {
...rawNotification,
type: getNotificationTypeValue(rawNotification.type),
};
const { error } = notificationValidation.validate(newNotification, {
abortEarly: false,
});
let validationError = { ...errors };
if (name === "type") {
validationError["type"] = extractError(error, "type");
validationError["address"] = extractError(error, "address");
} else {
validationError[name] = extractError(error, name);
}
setNotification(rawNotification);
setErrors(validationError);
};
const onTestNotification = () => {
const form = {
...notification,
type: getNotificationTypeValue(notification.type),
};
let error = null;
error = notificationValidation.validate(form, { abortEarly: false }).error;
if (error) {
const newErrors = {};
error.details.forEach((err) => {
newErrors[err.path[0]] = err.message;
});
createToast({ body: Object.values(newErrors)[0] });
setErrors(newErrors);
return;
}
testNotification(form);
};
const onDelete = () => {
if (notificationId) {
deleteNotification(notificationId, () => navigate("/notifications"));
}
};
const type = getNotificationTypeValue(notification.type);
return (
<Stack gap={theme.spacing(10)}>
<Breadcrumbs list={BREADCRUMBS} />
<Typography variant="h1">{t("createNotifications.title")}</Typography>
<Stack
component="form"
onSubmit={onSubmit}
noValidate
gap={theme.spacing(12)}
mt={theme.spacing(6)}
>
<ConfigBox>
<Box>
<Typography
component="h2"
variant="h2"
>
{t("createNotifications.nameSettings.title")}
</Typography>
<Typography component="p">
{t("createNotifications.nameSettings.description")}
</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<TextInput
label={t("createNotifications.nameSettings.nameLabel")}
name="notificationName"
placeholder={t("createNotifications.nameSettings.namePlaceholder")}
value={notification.notificationName}
onChange={onChange}
error={Boolean(errors.notificationName)}
helperText={errors["notificationName"]}
/>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography
component="h2"
variant="h2"
>
{t("createNotifications.typeSettings.title")}
</Typography>
<Typography component="p">
{t("createNotifications.typeSettings.description")}
</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<Select
items={NOTIFICATION_TYPES}
label="Type"
name="type"
value={notification.type}
onChange={onChange}
/>
</Stack>
</ConfigBox>
<ConfigBox>
<Box>
<Typography
component="h2"
variant="h2"
>
{t(TITLE_MAP[type])}
</Typography>
<Typography component="p">{t(DESCRIPTION_MAP[type])}</Typography>
</Box>
<Stack gap={theme.spacing(12)}>
<TextInput
label={t(LABEL_MAP[type])}
name="address"
placeholder={t(PLACEHOLDER_MAP[type])}
value={notification.address}
onChange={onChange}
error={Boolean(errors.address)}
helperText={errors["address"]}
/>
</Stack>
</ConfigBox>
<Stack
direction="row"
justifyContent="flex-end"
spacing={theme.spacing(2)}
>
<Button
loading={isTesting}
variant="contained"
color="secondary"
onClick={onTestNotification}
>
{t("createNotifications.testNotification")}
</Button>
{notificationId && (
<Button
loading={isDeleting}
variant="contained"
color="error"
onClick={() => setIsOpen(true)}
>
{t("delete")}
</Button>
)}
<Button
loading={isCreating || isEditing || notificationIsLoading}
type="submit"
variant="contained"
color="accent"
>
{t("submit")}
</Button>
</Stack>
</Stack>
<Dialog
open={isOpen}
onClose={() => setIsOpen(false)}
onCancel={() => setIsOpen(false)}
title={t("createNotifications.dialogDeleteTitle")}
confirmationButtonLabel={t("createNotifications.dialogDeleteConfirm")}
onConfirm={onDelete}
isLoading={isDeleting}
/>
</Stack>
);
};
export default CreateNotifications;