- Tasks Audit Trail Additions
Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
@@ -11,6 +11,9 @@ import React, {useEffect} from "react";
|
|||||||
import TaskListComponent from "./task-list.component.jsx";
|
import TaskListComponent from "./task-list.component.jsx";
|
||||||
import {notification} from "antd";
|
import {notification} from "antd";
|
||||||
import {useTranslation} from "react-i18next";
|
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({
|
export default function TaskListContainer({
|
||||||
bodyshop,
|
bodyshop,
|
||||||
@@ -25,6 +28,7 @@ export default function TaskListContainer({
|
|||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
const searchParams = queryString.parse(useLocation().search);
|
const searchParams = queryString.parse(useLocation().search);
|
||||||
const {page, sortcolumn, sortorder, deleted, completed, mine} = searchParams;
|
const {page, sortcolumn, sortorder, deleted, completed, mine} = searchParams;
|
||||||
|
const dispatch = useDispatch();
|
||||||
const {loading, error, data, refetch} = useQuery(
|
const {loading, error, data, refetch} = useQuery(
|
||||||
query,
|
query,
|
||||||
{
|
{
|
||||||
@@ -80,16 +84,30 @@ export default function TaskListContainer({
|
|||||||
const toggleCompletedStatus = async (id, currentStatus) => {
|
const toggleCompletedStatus = async (id, currentStatus) => {
|
||||||
const completed_at = !currentStatus ? new Date().toISOString() : null;
|
const completed_at = !currentStatus ? new Date().toISOString() : null;
|
||||||
try {
|
try {
|
||||||
await toggleTaskCompleted({
|
const toggledTask = await toggleTaskCompleted({
|
||||||
variables: {
|
variables: {
|
||||||
id: id,
|
id: id,
|
||||||
completed: !currentStatus,
|
completed: !currentStatus,
|
||||||
completed_at: completed_at
|
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', {
|
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
||||||
detail: {message: 'A task has been completed.'},
|
detail: {message: 'A task has been completed.'},
|
||||||
}));
|
}));
|
||||||
@@ -114,26 +132,42 @@ export default function TaskListContainer({
|
|||||||
* @param currentStatus
|
* @param currentStatus
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const toggleDeletedStatus = async (id, currentStatus) => {
|
const toggleDeletedStatus = async (id, currentStatus) => {
|
||||||
const deleted_at = !currentStatus ? new Date().toISOString() : null;
|
const deleted_at = !currentStatus ? new Date().toISOString() : null;
|
||||||
try {
|
try {
|
||||||
await toggleTaskDeleted({
|
const toggledTask = await toggleTaskDeleted({
|
||||||
variables: {
|
variables: {
|
||||||
id: id,
|
id: id,
|
||||||
deleted: !currentStatus,
|
deleted: !currentStatus,
|
||||||
deleted_at: deleted_at
|
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', {
|
window.dispatchEvent(new CustomEvent('taskUpdated', {
|
||||||
detail: {message: 'A task has been deleted.'},
|
detail: {message: 'A task has been deleted.'},
|
||||||
}));
|
}));
|
||||||
// refetch().catch((e) => {
|
|
||||||
// console.error(`Something went wrong fetching tasks: ${e.message || ''}`);
|
|
||||||
// });
|
|
||||||
notification["success"]({
|
notification["success"]({
|
||||||
message: t("tasks.successes.deleted"),
|
message: t("tasks.successes.deleted"),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
console.dir(err);
|
||||||
notification["error"]({
|
notification["error"]({
|
||||||
message: t("tasks.failures.deleted"),
|
message: t("tasks.failures.deleted"),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ import {replaceUndefinedWithNull} from "../../utils/undefinedtonull.js";
|
|||||||
import {useNavigate} from "react-router-dom";
|
import {useNavigate} from "react-router-dom";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import dayjs from '../../utils/day';
|
import dayjs from '../../utils/day';
|
||||||
|
import {insertAuditTrail} from "../../redux/application/application.actions.js";
|
||||||
|
import AuditTrailMapping from "../../utils/AuditTrailMappings.js";
|
||||||
|
|
||||||
const mapStateToProps = createStructuredSelector({
|
const mapStateToProps = createStructuredSelector({
|
||||||
currentUser: selectCurrentUser,
|
currentUser: selectCurrentUser,
|
||||||
@@ -26,6 +28,8 @@ const mapStateToProps = createStructuredSelector({
|
|||||||
});
|
});
|
||||||
const mapDispatchToProps = (dispatch) => ({
|
const mapDispatchToProps = (dispatch) => ({
|
||||||
toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")),
|
toggleModalVisible: () => dispatch(toggleModalVisible("taskUpsert")),
|
||||||
|
insertAuditTrail: ({ jobid, billid, operation, type }) =>
|
||||||
|
dispatch(insertAuditTrail({ jobid, billid, operation, type }))
|
||||||
});
|
});
|
||||||
|
|
||||||
export function TaskUpsertModalContainer({
|
export function TaskUpsertModalContainer({
|
||||||
@@ -33,6 +37,7 @@ export function TaskUpsertModalContainer({
|
|||||||
currentUser,
|
currentUser,
|
||||||
taskUpsert,
|
taskUpsert,
|
||||||
toggleModalVisible,
|
toggleModalVisible,
|
||||||
|
insertAuditTrail
|
||||||
}) {
|
}) {
|
||||||
const {t} = useTranslation();
|
const {t} = useTranslation();
|
||||||
const history = useNavigate();
|
const history = useNavigate();
|
||||||
@@ -123,14 +128,28 @@ export function TaskUpsertModalContainer({
|
|||||||
const handleExistingTask = async (values) => {
|
const handleExistingTask = async (values) => {
|
||||||
const isAssignedToDirty = values.assigned_to !== existingTask.assigned_to;
|
const isAssignedToDirty = values.assigned_to !== existingTask.assigned_to;
|
||||||
|
|
||||||
await updateTask({
|
const taskData = await updateTask({
|
||||||
variables: {
|
variables: {
|
||||||
taskId: existingTask.id,
|
taskId: existingTask.id,
|
||||||
task: replaceUndefinedWithNull(values)
|
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) {
|
if (isAssignedToDirty) {
|
||||||
|
// TODO This is being moved serverside
|
||||||
axios.post("/sendemail", {
|
axios.post("/sendemail", {
|
||||||
from: {
|
from: {
|
||||||
name: bodyshop.shopname,
|
name: bodyshop.shopname,
|
||||||
@@ -163,7 +182,7 @@ export function TaskUpsertModalContainer({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleNewTask = async (values) => {
|
const handleNewTask = async (values) => {
|
||||||
const newTaskID = (await insertTask({
|
const newTaskData = await insertTask({
|
||||||
variables: {
|
variables: {
|
||||||
taskInput: [
|
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({
|
const newTask = newTaskData?.data?.insert_tasks?.returning[0];
|
||||||
// fields: {
|
const newTaskID = newTask?.id;
|
||||||
// tasks(existingTasks) {
|
|
||||||
// return [{
|
if (!newTaskData.errors) {
|
||||||
// ...values,
|
insertAuditTrail({
|
||||||
// jobid: selectedJobId || values.jobid,
|
jobid: newTask.jobid,
|
||||||
// created_by: currentUser.email,
|
operation: AuditTrailMapping.tasksCreated(
|
||||||
// bodyshopid: bodyshop.id
|
newTask.title,
|
||||||
// }, ...existingTasks]
|
currentUser.email
|
||||||
// },
|
),
|
||||||
// },
|
type: "tasksCreated"
|
||||||
// });
|
});
|
||||||
// },
|
}
|
||||||
})).data.insert_tasks.returning[0].id;
|
|
||||||
|
|
||||||
if (refetch) await refetch();
|
if (refetch) await refetch();
|
||||||
|
|
||||||
form.resetFields();
|
form.resetFields();
|
||||||
|
|
||||||
toggleModalVisible();
|
toggleModalVisible();
|
||||||
|
|
||||||
// send notification to the assigned user
|
// send notification to the assigned user
|
||||||
|
// TODO: This is being moved serverside
|
||||||
axios.post("/sendemail", {
|
axios.post("/sendemail", {
|
||||||
from: {
|
from: {
|
||||||
name: bodyshop.shopname,
|
name: bodyshop.shopname,
|
||||||
|
|||||||
@@ -330,8 +330,10 @@ export const MUTATION_TOGGLE_TASK_COMPLETED = gql`
|
|||||||
mutation MUTATION_TOGGLE_TASK_COMPLETED($id: uuid!, $completed: Boolean!, $completed_at: timestamptz) {
|
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}) {
|
update_tasks_by_pk(pk_columns: {id: $id}, _set: {completed: $completed, completed_at: $completed_at}) {
|
||||||
id
|
id
|
||||||
|
title
|
||||||
completed
|
completed
|
||||||
completed_at
|
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) {
|
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}) {
|
update_tasks_by_pk(pk_columns: {id: $id}, _set: {deleted: $deleted, deleted_at: $deleted_at}) {
|
||||||
id
|
id
|
||||||
|
title
|
||||||
deleted
|
deleted
|
||||||
deleted_at
|
deleted_at
|
||||||
|
jobid
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -134,7 +134,13 @@
|
|||||||
"jobstatuschange": "Job status changed to {{status}}.",
|
"jobstatuschange": "Job status changed to {{status}}.",
|
||||||
"jobsupplement": "Job supplement imported.",
|
"jobsupplement": "Job supplement imported.",
|
||||||
"jobsuspend": "Suspend Toggle set to {{status}}",
|
"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": {
|
"billlines": {
|
||||||
|
|||||||
@@ -134,7 +134,13 @@
|
|||||||
"jobstatuschange": "",
|
"jobstatuschange": "",
|
||||||
"jobsupplement": "",
|
"jobsupplement": "",
|
||||||
"jobsuspend": "",
|
"jobsuspend": "",
|
||||||
"jobvoid": ""
|
"jobvoid": "",
|
||||||
|
"tasks_created": "",
|
||||||
|
"tasks_updated": "",
|
||||||
|
"tasks_deleted": "",
|
||||||
|
"tasks_undeleted": "",
|
||||||
|
"tasks_completed": "",
|
||||||
|
"tasks_uncompleted": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
|
|||||||
@@ -134,7 +134,13 @@
|
|||||||
"jobstatuschange": "",
|
"jobstatuschange": "",
|
||||||
"jobsupplement": "",
|
"jobsupplement": "",
|
||||||
"jobsuspend": "",
|
"jobsuspend": "",
|
||||||
"jobvoid": ""
|
"jobvoid": "",
|
||||||
|
"tasks_created": "",
|
||||||
|
"tasks_updated": "",
|
||||||
|
"tasks_deleted": "",
|
||||||
|
"tasks_undeleted": "",
|
||||||
|
"tasks_completed": "",
|
||||||
|
"tasks_uncompleted": ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"billlines": {
|
"billlines": {
|
||||||
|
|||||||
@@ -39,7 +39,34 @@ const AuditTrailMapping = {
|
|||||||
jobstatuschange: (status) => i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
jobstatuschange: (status) => i18n.t("audit_trail.messages.jobstatuschange", { status }),
|
||||||
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
jobsupplement: () => i18n.t("audit_trail.messages.jobsupplement"),
|
||||||
jobsuspend: (status) => i18n.t("audit_trail.messages.jobsuspend", { status }),
|
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;
|
export default AuditTrailMapping;
|
||||||
|
|||||||
Reference in New Issue
Block a user