diff --git a/backend/src/controllers/taskController.js b/backend/src/controllers/taskController.js
index d635dd8f..f3f4f6b1 100644
--- a/backend/src/controllers/taskController.js
+++ b/backend/src/controllers/taskController.js
@@ -89,6 +89,23 @@ export default class TaskController {
});
}
+ updateTaskProject(req) {
+ return new Promise((resolve, reject) => {
+ const TaskModel = new Task();
+ TaskModel.updateTaskProject(
+ req.params.taskId,
+ req.body.project_id,
+ (err, result) => {
+ if (err) {
+ reject({ error: err });
+ }
+ this.isTaskLoaded = false;
+ resolve(result);
+ }
+ );
+ });
+ }
+
//add subtask given taskID
saveSubtaskByTask(req) {
return new Promise((resolve, reject) => {
diff --git a/backend/src/models/task.js b/backend/src/models/task.js
index 412c1344..f863caa5 100644
--- a/backend/src/models/task.js
+++ b/backend/src/models/task.js
@@ -90,7 +90,24 @@ export class Task {
result(error, null);
} else {
result(null, {
- result: `Task of ${taskId} Saved Successfully for Task ID: ${taskDescription}`,
+ result: `Task of ${taskId} Saved Successfully for Task ID: ${taskId}`,
+ });
+ }
+ }
+ );
+ }
+
+ updateTaskProject(taskId, taskProject, result) {
+ con.query(
+ "CALL update_task_project(?, ?)",
+ [taskId, taskProject],
+ function (error, _) {
+ if (error) {
+ console.log("error: ", error);
+ result(error, null);
+ } else {
+ result(null, {
+ result: `Task of ${taskId} Saved Successfully for Task ID: ${taskId}`,
});
}
}
diff --git a/backend/src/routes/taskRoute.js b/backend/src/routes/taskRoute.js
index 31089518..7c1eb3db 100644
--- a/backend/src/routes/taskRoute.js
+++ b/backend/src/routes/taskRoute.js
@@ -55,6 +55,17 @@ router.post("/description/:taskId", authorize(), (req, res) => {
});
});
+router.post("/project/:taskId", authorize(), (req, res) => {
+ taskController
+ .updateTaskProject(req)
+ .then((response) => {
+ res.status(200).json(response);
+ })
+ .catch((err) => {
+ res.status(404).json(err);
+ });
+});
+
//add subtask given task_id
router.post("/addsubtask/:taskId", authorize(), (req, res) => {
if (!req.body) {
diff --git a/database/procedures/ticketManagement/saveProcedures.sql b/database/procedures/ticketManagement/saveProcedures.sql
index b5a65f48..d75703b6 100644
--- a/database/procedures/ticketManagement/saveProcedures.sql
+++ b/database/procedures/ticketManagement/saveProcedures.sql
@@ -5,6 +5,7 @@ DROP procedure IF EXISTS `save_subtask`;
DROP procedure IF EXISTS `update_task_status`;
DROP procedure IF EXISTS `update_task_title`;
DROP procedure IF EXISTS `update_task_description`;
+DROP procedure IF EXISTS `update_task_project`;
DROP procedure IF EXISTS `update_subtask_status`;
DELIMITER $$
@@ -74,6 +75,16 @@ UPDATE `subtasks` SET `subtask_description`=`_task_description`, `subtask_update
END $$
+CREATE PROCEDURE `update_task_project` (
+ IN `_task_uuid` VARCHAR(50),
+ IN `_task_project` VARCHAR(250)
+
+) BEGIN
+UPDATE `tasks` SET `fk_project_id`=`_task_project`, `task_updated` = NOW() WHERE `task_uuid`=`_task_uuid`;
+UPDATE `billable` SET `fk_project_id`=`_task_project` WHERE `task_uuid`=`_task_uuid`;
+
+END $$
+
CREATE PROCEDURE `save_subtask` (
IN `_subtask_uuid` VARCHAR(50),
IN `_subtask_title` VARCHAR(100),
diff --git a/frontend/src/components/TicketBoard/TicketInfo/index.js b/frontend/src/components/TicketBoard/TicketInfo/index.js
index 1b59c844..1a688d25 100644
--- a/frontend/src/components/TicketBoard/TicketInfo/index.js
+++ b/frontend/src/components/TicketBoard/TicketInfo/index.js
@@ -10,6 +10,7 @@ import {
SET_ACTIVE_TICKET,
UNASSIGN_USER,
UPDATE_TICKET_DESCRIPTION,
+ UPDATE_TICKET_PROJECT,
UPDATE_TICKET_STATUS,
UPDATE_TICKET_TITLE,
} from "../../../redux/actions/ticketActions";
@@ -22,6 +23,7 @@ import Subtasks from "../Subtasks";
import ServiceList from "../ServiceList";
import { SuccessToast } from "../../Toasts";
import AWS from "aws-sdk";
+import { GET_PROJECT } from "../../../redux/actions/billingActions";
export const getColorNum = (id, colorArray) => {
if (colorArray) {
@@ -48,11 +50,15 @@ export const TicketInfo = () => {
const currentTicketAttachments = useSelector(
(state) => state.ticketReducer.currentTicketAttachments
);
+ const projectList = useSelector((state) => state.projectReducer.projectList);
+ const [currentProject, setCurrentProject] = useState();
const [assigneeAddModal, setAssigneeAddModal] = useState(false);
+ const [projectSwitchModal, setProjectSwitchModal] = useState(false);
useEffect(() => {
dispatch({ type: LOAD_EMPLOYEE });
+ dispatch({ type: GET_PROJECT });
dispatch({ type: GET_TICKET_BOARD });
if (currentTicket?.id) {
dispatch({
@@ -70,6 +76,13 @@ export const TicketInfo = () => {
}
}, [dispatch, currentTicket]);
+ useEffect(() => {
+ if (currentTicket?.id) {
+ const currentProj = projectList.find((proj) => proj.project_id === currentTicket.project_id);
+ setCurrentProject(currentProj);
+ }
+ }, [projectList, currentTicket])
+
return (
{
onClick={() => {
if (assigneeAddModal) {
setAssigneeAddModal(false);
+ setProjectSwitchModal(false);
} else {
const inputs = document.querySelectorAll("input");
for (let i = 0; i < inputs.length; i++) {
@@ -90,6 +104,7 @@ export const TicketInfo = () => {
className="ticketDetail"
onClick={(event) => {
setAssigneeAddModal(false);
+ setProjectSwitchModal(false);
event.stopPropagation();
}}
>
@@ -108,6 +123,49 @@ export const TicketInfo = () => {
});
}}
/>
+
+ {projectSwitchModal ? (
+
+
Change project from {currentProject?.project_name} to:
+ {/*
NOTE: Changing projects will reset custom service costs to the default values set for the organization
*/}
+
+ {projectList.map((project) => {
+ return (
+
{
+ dispatch({
+ type: UPDATE_TICKET_PROJECT,
+ payload: {
+ ticketId: currentTicket.task_uuid,
+ project_id: project.project_id,
+ },
+ });
+ setCurrentProject(project);
+ }}>{project.project_name}
+ );
+ })}
+
+
+ ) : null}