- Tasks Audit Trail Additions

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-04-08 13:26:37 -04:00
parent eb4e5d9576
commit bd3d86a6dd
7 changed files with 134 additions and 33 deletions

View File

@@ -11,6 +11,9 @@ import React, {useEffect} from "react";
import TaskListComponent from "./task-list.component.jsx";
import {notification} from "antd";
import {useTranslation} from "react-i18next";
import {useDispatch} from "react-redux";
import {insertAuditTrail} from "../../redux/application/application.actions.js";
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
export default function TaskListContainer({
bodyshop,
@@ -25,6 +28,7 @@ export default function TaskListContainer({
const {t} = useTranslation();
const searchParams = queryString.parse(useLocation().search);
const {page, sortcolumn, sortorder, deleted, completed, mine} = searchParams;
const dispatch = useDispatch();
const {loading, error, data, refetch} = useQuery(
query,
{
@@ -80,16 +84,30 @@ export default function TaskListContainer({
const toggleCompletedStatus = async (id, currentStatus) => {
const completed_at = !currentStatus ? new Date().toISOString() : null;
try {
await toggleTaskCompleted({
const toggledTask = await toggleTaskCompleted({
variables: {
id: id,
completed: !currentStatus,
completed_at: completed_at
}
});
// refetch().catch((e) => {
// console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
// });
if (!toggledTask.errors) {
dispatch(
insertAuditTrail({
jobid: toggledTask.data.update_tasks_by_pk.jobid,
operation: toggledTask?.data?.update_tasks_by_pk?.completed ? AuditTrailMapping.tasksCompleted(
toggledTask.data.update_tasks_by_pk.title,
currentUser.email
) : AuditTrailMapping.tasksUncompleted(
toggledTask.data.update_tasks_by_pk.title,
currentUser.email
),
type: toggledTask?.data?.update_tasks_by_pk?.completed ? "tasksCompleted" : "tasksUncompleted"
})
)
}
window.dispatchEvent(new CustomEvent('taskUpdated', {
detail: {message: 'A task has been completed.'},
}));
@@ -114,26 +132,42 @@ export default function TaskListContainer({
* @param currentStatus
* @returns {Promise<void>}
*/
const toggleDeletedStatus = async (id, currentStatus) => {
const deleted_at = !currentStatus ? new Date().toISOString() : null;
try {
await toggleTaskDeleted({
const toggledTask = await toggleTaskDeleted({
variables: {
id: id,
deleted: !currentStatus,
deleted_at: deleted_at
}
});
if (!toggledTask.errors) {
dispatch(
insertAuditTrail({
jobid: toggledTask.data.update_tasks_by_pk.jobid,
operation: toggledTask?.data?.update_tasks_by_pk?.deleted ? AuditTrailMapping.tasksDeleted(
toggledTask.data.update_tasks_by_pk.title,
currentUser.email
) : AuditTrailMapping.tasksUndeleted(
toggledTask.data.update_tasks_by_pk.title,
currentUser.email
),
type: toggledTask?.data?.update_tasks_by_pk?.deleted ? "tasksDeleted" : "tasksUndeleted"
})
)
}
window.dispatchEvent(new CustomEvent('taskUpdated', {
detail: {message: 'A task has been deleted.'},
}));
// refetch().catch((e) => {
// console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
// });
notification["success"]({
message: t("tasks.successes.deleted"),
});
} catch (err) {
console.dir(err);
notification["error"]({
message: t("tasks.failures.deleted"),
});

View File

@@ -18,6 +18,8 @@ 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,
@@ -26,6 +28,8 @@ const mapStateToProps = createStructuredSelector({
});
const mapDispatchToProps = (dispatch) => ({
toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")),
insertAuditTrail: ({ jobid, billid, operation, type }) =>
dispatch(insertAuditTrail({ jobid, billid, operation, type }))
});
export function TaskUpsertModalContainer({
@@ -33,6 +37,7 @@ export function TaskUpsertModalContainer({
currentUser,
taskUpsert,
toggleModalVisible,
insertAuditTrail
}) {
const {t} = useTranslation();
const history = useNavigate();
@@ -123,14 +128,28 @@ export function TaskUpsertModalContainer({
const handleExistingTask = async (values) => {
const isAssignedToDirty = values.assigned_to !== existingTask.assigned_to;
await updateTask({
const taskData = await updateTask({
variables: {
taskId: existingTask.id,
task: replaceUndefinedWithNull(values)
},
});
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,
@@ -163,7 +182,7 @@ export function TaskUpsertModalContainer({
};
const handleNewTask = async (values) => {
const newTaskID = (await insertTask({
const newTaskData = await insertTask({
variables: {
taskInput: [
{
@@ -173,30 +192,29 @@ export function TaskUpsertModalContainer({
},
],
},
// TODO: Consult Patrick, because this fails on relationship data, and an event emitter is just much easier to use
// update(cache) {
// cache.modify({
// fields: {
// tasks(existingTasks) {
// return [{
// ...values,
// jobid: selectedJobId || values.jobid,
// created_by: currentUser.email,
// bodyshopid: bodyshop.id
// }, ...existingTasks]
// },
// },
// });
// },
})).data.insert_tasks.returning[0].id;
});
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,

View File

@@ -330,8 +330,10 @@ export const MUTATION_TOGGLE_TASK_COMPLETED = gql`
mutation MUTATION_TOGGLE_TASK_COMPLETED($id: uuid!, $completed: Boolean!, $completed_at: timestamptz) {
update_tasks_by_pk(pk_columns: {id: $id}, _set: {completed: $completed, completed_at: $completed_at}) {
id
title
completed
completed_at
jobid
}
}
`;
@@ -344,8 +346,10 @@ export const MUTATION_TOGGLE_TASK_DELETED = gql`
mutation MUTATION_TOGGLE_TASK_DELETED($id: uuid!, $deleted: Boolean!, $deleted_at: timestamptz) {
update_tasks_by_pk(pk_columns: {id: $id}, _set: {deleted: $deleted, deleted_at: $deleted_at}) {
id
title
deleted
deleted_at
jobid
}
}
`;

View File

@@ -134,7 +134,13 @@
"jobstatuschange": "Job status changed to {{status}}.",
"jobsupplement": "Job supplement imported.",
"jobsuspend": "Suspend Toggle set to {{status}}",
"jobvoid": "Job has been voided."
"jobvoid": "Job has been voided.",
"tasks_created": "Task '{{title}}' created by {{createdBy}}",
"tasks_updated": "Task '{{title}}' updated by {{updatedBy}}",
"tasks_deleted": "Task '{{title}}' deleted by {{deletedBy}}",
"tasks_undeleted": "Task '{{title}}' undeleted by {{undeletedBy}}",
"tasks_completed": "Task '{{title}}' completed by {{completedBy}}",
"tasks_uncompleted": "Task '{{title}}' uncompleted by {{uncompletedBy}}"
}
},
"billlines": {

View File

@@ -134,7 +134,13 @@
"jobstatuschange": "",
"jobsupplement": "",
"jobsuspend": "",
"jobvoid": ""
"jobvoid": "",
"tasks_created": "",
"tasks_updated": "",
"tasks_deleted": "",
"tasks_undeleted": "",
"tasks_completed": "",
"tasks_uncompleted": ""
}
},
"billlines": {

View File

@@ -134,7 +134,13 @@
"jobstatuschange": "",
"jobsupplement": "",
"jobsuspend": "",
"jobvoid": ""
"jobvoid": "",
"tasks_created": "",
"tasks_updated": "",
"tasks_deleted": "",
"tasks_undeleted": "",
"tasks_completed": "",
"tasks_uncompleted": ""
}
},
"billlines": {

View File

@@ -39,7 +39,34 @@ const AuditTrailMapping = {
jobstatuschange: (status) => i18n.t("audit_trail.messages.jobstatuschange", { status }),
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
jobvoid: () => i18n.t("audit_trail.messages.jobvoid")
jobvoid: () => i18n.t("audit_trail.messages.jobvoid"),
// Tasks Entries
tasksCreated: (title, createdBy) => i18n.t("audit_trail.messages.tasks_created", {
title,
createdBy
}),
tasksUpdated: (title, updatedBy) => i18n.t("audit_trail.messages.tasks_updated", {
title,
updatedBy
}),
tasksDeleted: (title, deletedBy) => i18n.t("audit_trail.messages.tasks_deleted", {
title,
deletedBy
}),
tasksUndeleted: (title, undeletedBy) => i18n.t("audit_trail.messages.tasks_undeleted", {
title,
undeletedBy
}),
tasksCompleted: (title, completedBy) => i18n.t("audit_trail.messages.tasks_completed", {
title,
completedBy
}),
tasksUncompleted: (title, uncompletedBy) => i18n.t("audit_trail.messages.tasks_uncompleted", {
title,
uncompletedBy
}),
};
export default AuditTrailMapping;