Files
bodyshop/client/src/components/task-upsert-modal/task-upsert-modal.container.jsx
Dave Richer 3d753a2d19 - PR Change Requests (Progress)
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-04-10 16:48:06 -04:00

297 lines
9.5 KiB
JavaScript

import { useMutation, useQuery } from "@apollo/client";
import { Form, Modal, notification } from "antd";
import React, { useEffect, useState } from "react";
import { useTranslation } from "react-i18next";
import { connect } from "react-redux";
import { createStructuredSelector } from "reselect";
import { MUTATION_INSERT_NEW_TASK, MUTATION_UPDATE_TASK, QUERY_GET_TASK_BY_ID } from "../../graphql/tasks.queries";
import { QUERY_GET_TASKS_JOB_DETAILS_BY_ID } from "../../graphql/jobs.queries.js";
import { toggleModalVisible } from "../../redux/modals/modals.actions";
import { selectTaskUpsert } from "../../redux/modals/modals.selectors";
import { selectBodyshop, selectCurrentUser } from "../../redux/user/user.selectors";
import TaskUpsertModalComponent from "./task-upsert-modal.component";
import { replaceUndefinedWithNull } from "../../utils/undefinedtonull.js";
import { useNavigate } from "react-router-dom";
import axios from "axios";
import dayjs from "../../utils/day";
import { insertAuditTrail } from "../../redux/application/application.actions.js";
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
const mapStateToProps = createStructuredSelector({
currentUser: selectCurrentUser,
bodyshop: selectBodyshop,
taskUpsert: selectTaskUpsert
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")),
insertAuditTrail: ({ jobid, billid, operation, type }) =>
dispatch(insertAuditTrail({ jobid, billid, operation, type }))
});
export function TaskUpsertModalContainer({ bodyshop, currentUser, taskUpsert, toggleModalVisible, insertAuditTrail }) {
const { t } = useTranslation();
const history = useNavigate();
const [insertTask] = useMutation(MUTATION_INSERT_NEW_TASK);
const [updateTask] = useMutation(MUTATION_UPDATE_TASK);
const { open, context, actions } = taskUpsert;
const { jobid, joblineid, billid, partsorderid, taskId, existingTask, query } = context;
const { refetch } = actions;
const [form] = Form.useForm();
const [selectedJobId, setSelectedJobId] = useState(null);
const [selectedJobDetails, setSelectedJobDetails] = useState(null);
const [jobIdState, setJobIdState] = useState(null);
const { loading, error, data } = useQuery(QUERY_GET_TASKS_JOB_DETAILS_BY_ID, {
variables: { id: jobIdState },
skip: !jobIdState
});
const {
loading: taskLoading,
error: taskError,
data: taskData
} = useQuery(QUERY_GET_TASK_BY_ID, {
variables: { id: taskId },
skip: !taskId
});
// Use Effect to hydrate existing task if only a taskid is provided
useEffect(() => {
if (!taskLoading && !taskError && taskData && taskData?.tasks_by_pk) {
form.setFieldsValue(taskData.tasks_by_pk);
setSelectedJobId(taskData.tasks_by_pk.jobid);
}
}, [taskLoading, taskError, taskData, form]);
// Use Effect to hydrate selected job details
useEffect(() => {
if (!loading && !error && data) {
setSelectedJobDetails(data.jobs_by_pk);
}
}, [loading, error, data]);
// Use Effect to toggle to set jobid state
useEffect(() => {
if (selectedJobId) {
setJobIdState(selectedJobId);
}
}, [selectedJobId]);
// Use Effect to hydrate form fields
useEffect(() => {
if (jobid || existingTask?.id) {
setSelectedJobId(jobid || existingTask.jobid);
}
if (existingTask && open) {
form.setFieldsValue(existingTask);
} else if (!existingTask && open) {
form.resetFields();
if (joblineid) form.setFieldsValue({ joblineid });
if (billid) form.setFieldsValue({ billid });
if (partsorderid) form.setFieldsValue({ partsorderid });
}
return () => {
setSelectedJobId(null);
};
}, [jobid, existingTask, form, open, joblineid, billid, partsorderid]);
/**
* Remove the taskid from the URL
*/
const removeTaskIdFromUrl = () => {
const urlParams = new URLSearchParams(window.location.search);
if (!urlParams.has("taskid")) return;
urlParams.delete("taskid");
history(`${window.location.pathname}?${urlParams}`);
};
/**
* Handle existing task
* @param values
* @returns {Promise<void>}
*/
const handleExistingTask = async (values) => {
const isAssignedToDirty = values.assigned_to !== existingTask.assigned_to;
const taskObject = {
variables: {
taskId: existingTask.id,
task: replaceUndefinedWithNull(values)
},
refetchQueries: ["GET_JOB_BY_PK"]
};
if (query && Object.keys(query).length) {
taskObject.refetchQueries.push(Object.keys(query)[0]);
}
const taskData = await updateTask(taskObject);
if (!taskData.errors) {
const oldTask = taskData?.data?.update_tasks?.returning[0];
insertAuditTrail({
jobid: oldTask.jobid,
operation: AuditTrailMapping.tasksUpdated(oldTask.title, currentUser.email),
type: "tasksUpdated"
});
}
if (isAssignedToDirty) {
// TODO This is being moved serverside
axios
.post("/sendemail", {
from: {
name: bodyshop.shopname,
address: bodyshop.email
},
ReplyTo: {
Email: "noreply@imex.online"
},
to: values.assigned_to,
subject: `A Task has been re-assigned to you on ${bodyshop.shopname} - ${values.title}`,
templateStrings: {
header: values.title,
subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format("MM/DD/YYYY")}` : ""}`,
body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${existingTask.id}">Please sign in to your account to view the task details.</a>`
}
})
.catch((e) =>
console.error(`Something went wrong sending email to Assigned party on Task creation. ${e.message || ""}`)
);
}
notification["success"]({
message: t("tasks.successes.updated")
});
if (refetch) await refetch();
toggleModalVisible();
};
const handleNewTask = async (values) => {
const newTaskObject = {
variables: {
taskInput: [
{
...values,
created_by: currentUser.email,
bodyshopid: bodyshop.id
}
]
},
refetchQueries: ["GET_JOB_BY_PK"]
// update(cache, { data }) {
// cache.modify({
// fields: {
// tasks(cached) {
// const newTasks = data?.insert_tasks?.returning.map(task => cache.writeFragment({
// data: task,
// fragment: PARTIAL_TASK_FIELDS_WRAPPER
// }));
// return [...cached, ...newTasks];
// }
// }
// });
// }
};
if (query && Object.keys(query).length) {
newTaskObject.refetchQueries.push(Object.keys(query)[0]);
}
const newTaskData = await insertTask(newTaskObject);
const newTask = newTaskData?.data?.insert_tasks?.returning[0];
const newTaskID = newTask?.id;
if (!newTaskData.errors) {
insertAuditTrail({
jobid: newTask.jobid,
operation: AuditTrailMapping.tasksCreated(newTask.title, currentUser.email),
type: "tasksCreated"
});
}
if (refetch) await refetch();
form.resetFields();
toggleModalVisible();
// send notification to the assigned user
// TODO: This is being moved serverside
axios
.post("/sendemail", {
from: {
name: bodyshop.shopname,
address: bodyshop.email
},
replyTo: {
Email: "noreply@imex.online"
},
to: values.assigned_to,
subject: `A new Task has been assigned to you on ${bodyshop.shopname} - ${values.title}`,
templateName: "taskAssigned",
templateStrings: {
header: values.title,
subHeader: `Assigned by ${currentUser.email} ${values.due_at ? `| Due on ${dayjs(values.due_at).format("MM/DD/YYYY")}` : ""}`,
body: `<a href="${window.location.protocol}//${window.location.host}/manage/tasks/alltasks?taskid=${newTaskID}">Please sign to your account to view the task details.</a>`
}
})
.catch((e) =>
console.error(`Something went wrong sending email to Assigned party on Task edit. ${e.message || ""}`)
);
notification["success"]({
message: t("tasks.successes.created")
});
};
/**
* Handle the form submit
* @param formValues
* @returns {Promise<[{jobid, bodyshopid, created_by},...*]>}
*/
const handleFinish = async (formValues) => {
const { ...values } = formValues;
if (existingTask) {
await handleExistingTask(values);
} else {
await handleNewTask(values);
}
};
return (
<Modal
title={existingTask ? t("tasks.actions.edit") : t("tasks.actions.new")}
open={open}
okText={t("general.actions.save")}
width="50%"
onOk={() => {
removeTaskIdFromUrl();
form.submit();
}}
onCancel={() => {
removeTaskIdFromUrl();
toggleModalVisible();
}}
destroyOnClose
>
<Form form={form} onFinish={handleFinish} layout="vertical">
<TaskUpsertModalComponent
form={form}
loading={loading || (taskId && taskLoading)}
error={error}
data={data}
selectedJobId={selectedJobId}
setSelectedJobId={setSelectedJobId}
selectedJobDetails={selectedJobDetails}
/>
</Form>
</Modal>
);
}
export default connect(mapStateToProps, mapDispatchToProps)(TaskUpsertModalContainer);