diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index e73f6706..aca2dc05 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -836,7 +836,7 @@ progress { } .row { - --bs-gutter-x: 3.625rem; + --bs-gutter-x: 0.500rem; --bs-gutter-y: 0; display: flex; flex-wrap: wrap; @@ -2553,7 +2553,7 @@ progress { } .table-responsive { - /* overflow-x: auto; */ + overflow-x: auto; -webkit-overflow-scrolling: touch; } diff --git a/src/components/Activities/ReportTask.jsx b/src/components/Activities/ReportTask.jsx index 8194a5cc..a3550683 100644 --- a/src/components/Activities/ReportTask.jsx +++ b/src/components/Activities/ReportTask.jsx @@ -62,7 +62,12 @@ export const ReportTask = ({ report, closeModal }) => { checkList: [], }; - reportTask({ reportData, workAreaId: report?.workItem?.workArea?.id }); + reportTask({ + reportData, + workAreaId: report?.workItem?.workArea?.id, + buildingId: report?.workItem?.workArea?.floor?.building.id, + floorId: report?.workItem?.workArea?.floor?.id, + }); }; const handleClose = () => { closeModal(); diff --git a/src/components/Activities/SubTask.jsx b/src/components/Activities/SubTask.jsx index 43a3c049..4014673b 100644 --- a/src/components/Activities/SubTask.jsx +++ b/src/components/Activities/SubTask.jsx @@ -9,7 +9,7 @@ import { import showToast from "../../services/toastService"; import ProjectRepository from "../../repositories/ProjectRepository"; import { useTaskById } from "../../hooks/useTasks"; -import {useManageTask} from "../../hooks/useProjects"; +import { useManageTask } from "../../hooks/useProjects"; const subTaskSchema = z.object({ activityId: z.string().min(1, "Activity is required"), @@ -37,14 +37,13 @@ const SubTask = ({ activity, onClose }) => { }); const selectedActivityId = watch("activityId"); const selectedActivity = activities?.find((a) => a.id === selectedActivityId); - const {mutate:createSubTask,isPending } = useManageTask( { - onSuccessCallback: () => - { - showToast("Sub Task Created Successfully","success") + const { mutate: createSubTask, isPending } = useManageTask({ + onSuccessCallback: () => { + showToast("Sub Task Created Successfully", "success"); reset(); onClose(); - } - } ) + }, + }); useEffect(() => { setCategoryData(categories); @@ -73,7 +72,7 @@ const SubTask = ({ activity, onClose }) => { }; const onSubmitForm = async (formData) => { - let payload = { + let data = { workAreaID: Task.workItem.workAreaId, workCategoryId: formData.workCategoryId, activityID: formData.activityId, @@ -82,8 +81,19 @@ const SubTask = ({ activity, onClose }) => { parentTaskId: activity?.id, comment: formData.comment, }; - - createSubTask([payload]) + + const payload = [data]; + let buildingId = activity.workItem.workArea.floor.building.id; + let floorId = activity.workItem.workArea.floor.id; + let workAreaId = activity.workItem.workArea.id; + createSubTask({ + payload: payload, + buildingId: buildingId, + floorId: floorId, + workAreaId: workAreaId, + PreviousPlannedWork:0, + previousCompletedWork:0 + }); }; return (
@@ -147,15 +157,15 @@ const SubTask = ({ activity, onClose }) => { disabled > + {loading ? "Loading..." : "-- Select Activity --"} + - {!loading && - activities?.map((activity) => ( - - ))} + {!loading && + activities?.map((activity) => ( + + ))} {errors.activityId && (
{errors.activityId.message}
diff --git a/src/components/Project/Infrastructure/EditActivityModal.jsx b/src/components/Project/Infrastructure/EditActivityModal.jsx index 06a6e41e..14b64b53 100644 --- a/src/components/Project/Infrastructure/EditActivityModal.jsx +++ b/src/components/Project/Infrastructure/EditActivityModal.jsx @@ -116,7 +116,17 @@ useEffect(() => { floorId: floor?.id, workAreaId: workArea?.id, }; - UpdateTask([payload]) + let plannedTask = + workItem?.workItem?.plannedWork || workItem?.plannedWork || 0; + let completedTask = workItem?.workItem?.completedWork || workItem?.completedWork || 0 + UpdateTask({ + payload: [payload], + PreviousPlannedWork: plannedTask, + buildingId: building?.id, + floorId: floor?.id, + workAreaId: workArea?.id, + previousCompletedWork:completedTask + }); } return (
diff --git a/src/components/Project/Infrastructure/TaskModel.jsx b/src/components/Project/Infrastructure/TaskModel.jsx index 12cf8c87..06261603 100644 --- a/src/components/Project/Infrastructure/TaskModel.jsx +++ b/src/components/Project/Infrastructure/TaskModel.jsx @@ -69,10 +69,9 @@ const TaskModel = ({ project, onSubmit, onClose }) => { const selectedCategory = categoryData?.find((c) => c.id === watchCategoryId); const { mutate: CreateTask, isPending } = useManageTask({ - onSuccessCallback: ( response ) => - { - showToast( response?.message, "success" ) - onClose?.() + onSuccessCallback: (response) => { + showToast(response?.message, "success"); + onClose?.(); }, }); useEffect(() => { @@ -96,8 +95,10 @@ const TaskModel = ({ project, onSubmit, onClose }) => { }, [categories]); const onSubmitForm = async (data) => { - const payload = [data]; - CreateTask(payload); + const payload = [data]; + CreateTask({payload:payload,buildingId: data.buildingID, + floorId: data.floorId, + workAreaId: data.workAreaId, PreviousPlannedWork:0,previousCompletedWork:0}); }; return ( diff --git a/src/components/Project/Infrastructure/WorkItem.jsx b/src/components/Project/Infrastructure/WorkItem.jsx index 1116991c..df1f2bc1 100644 --- a/src/components/Project/Infrastructure/WorkItem.jsx +++ b/src/components/Project/Infrastructure/WorkItem.jsx @@ -10,7 +10,10 @@ import { } from "../../../utils/constants"; import ConfirmModal from "../../common/ConfirmModal"; import ProjectRepository from "../../../repositories/ProjectRepository"; -import { useDeleteProjectTask, useProjectDetails } from "../../../hooks/useProjects"; +import { + useDeleteProjectTask, + useProjectDetails, +} from "../../../hooks/useProjects"; import showToast from "../../../services/toastService"; import { cacheData, @@ -19,7 +22,7 @@ import { } from "../../../slices/apiDataManager"; import { refreshData } from "../../../slices/localVariablesSlice"; import GlobalModel from "../../common/GlobalModel"; -import {useDeleteMasterItem} from "../../../hooks/masterHook/useMaster"; +import { useDeleteMasterItem } from "../../../hooks/masterHook/useMaster"; const WorkItem = ({ workItem, @@ -40,19 +43,18 @@ const WorkItem = ({ const [loadingDelete, setLoadingDelete] = useState(false); const project = getCachedData("projectInfo"); - const openModal = () => setIsModalOpen(true); - const closeModal = () => setIsModalOpen( false ); - - const showModalDelete = () => setShowModal2(true); + const closeModal = () => setIsModalOpen(false); + + const showModalDelete = () => setShowModal2(true); const closeModalDelete = () => setShowModal2(false); const getProgress = (planned, completed) => { return (completed * 100) / planned + "%"; }; - - const {mutate:DeleteTask,isPending } = useDeleteProjectTask(() => { - closeModalDelete?.(); - }); + + const { mutate: DeleteTask, isPending } = useDeleteProjectTask(() => { + closeModalDelete?.(); + }); const handleAssignTask = () => { setItemName(""); @@ -61,15 +63,15 @@ const WorkItem = ({ setNewWorkItem(workItem); }, [workItem]); - const refreshWorkItem = (plannedTask) =>{ + const refreshWorkItem = (plannedTask) => { if (workItem) { - const updated = { - ...workItem, - todaysAssigned: (workItem.todaysAssigned || 0) + plannedTask, - }; - setNewWorkItem(updated); - } - } + const updated = { + ...workItem, + todaysAssigned: (workItem.todaysAssigned || 0) + plannedTask, + }; + setNewWorkItem(updated); + } + }; let assigndata = { building: forBuilding, floor: forFloor, @@ -85,15 +87,17 @@ const WorkItem = ({ tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); }, []); - - - - - const handleSubmit = async () => { let WorkItemId = workItem.workItemId || workItem.id; - DeleteTask({workItemId:WorkItemId,workAreaId:forWorkArea?.id}) - + debugger + DeleteTask({ + workItemId: WorkItemId, + workAreaId: forWorkArea?.id, + completedTask: workItem?.completedWork, + plannedTask: workItem?.plannedWork, + buildingId: forBuilding?.id, + floorId: forFloor?.id, + }); }; const PlannedWork = @@ -104,18 +108,26 @@ const WorkItem = ({ <> {isModalOpen && ( - + )} {showModal && ( - setShowModal(false)}> + setShowModal(false)} + > setShowModal(false)} + onClose={() => setShowModal(false)} /> )} @@ -167,7 +179,6 @@ const WorkItem = ({ : "NA"} - {hasWorkItem @@ -195,9 +206,7 @@ const WorkItem = ({ {hasWorkItem ? `${ - NewWorkItem?.todaysAssigned ?? - workItem?.todaysAssigned ?? - "0" + NewWorkItem?.todaysAssigned ?? workItem?.todaysAssigned ?? "0" }` : "NA"} @@ -251,7 +260,7 @@ const WorkItem = ({ setShowModal(true)} + onClick={() => setShowModal(true)} role="button" > setShowModal(true) } + onClick={() => setShowModal(true)} > Edit diff --git a/src/components/Project/ProjectCard.jsx b/src/components/Project/ProjectCard.jsx index 015312fe..7ff4350b 100644 --- a/src/components/Project/ProjectCard.jsx +++ b/src/components/Project/ProjectCard.jsx @@ -1,6 +1,6 @@ import React, { useEffect, useState } from "react"; import moment from "moment"; -import { formatNumber, getDateDifferenceInDays } from "../../utils/dateUtils"; +import { formatNumber, getCompletionPercentage, getDateDifferenceInDays } from "../../utils/dateUtils"; import { useNavigate } from "react-router-dom"; import { useProjectDetails, useUpdateProject } from "../../hooks/useProjects"; import ManageProjectInfo from "./ManageProjectInfo"; @@ -227,12 +227,7 @@ const ProjectCard = ({ projectData, recall }) => { Task: {formatNumber(projectInfo.completedWork)} / {formatNumber(projectInfo.plannedWork)} - {Math.floor( - getProgressInNumber( - projectInfo.plannedWork, - projectInfo.completedWork - ) - ) || 0}{" "} + {getCompletionPercentage(projectInfo.completedWork, projectInfo.plannedWork)} % Completed
diff --git a/src/components/Project/ProjectOverview.jsx b/src/components/Project/ProjectOverview.jsx index c489c339..f258ce8b 100644 --- a/src/components/Project/ProjectOverview.jsx +++ b/src/components/Project/ProjectOverview.jsx @@ -3,7 +3,7 @@ import { useEmployeesByProjectAllocated, useProjects, } from "../../hooks/useProjects"; -import { formatNumber } from "../../utils/dateUtils"; +import { formatNumber, getCompletionPercentage } from "../../utils/dateUtils"; import ProgressBar from "../common/ProgressBar"; const ProjectOverview = ({ project }) => { @@ -49,9 +49,8 @@ const ProjectOverview = ({ project }) => { ) ) || 0}{" "} */} { - (formatNumber(project_detail.plannedWork), - "/", - formatNumber(project_detail.completedWork)) + getCompletionPercentage( formatNumber(project_detail.completedWork),formatNumber(project_detail.plannedWork)) + } % Completed diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index 2cc5ee1d..daf705fa 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -6,7 +6,12 @@ import { useDispatch, useSelector } from "react-redux"; import { setProjectId } from "../slices/localVariablesSlice"; import EmployeeList from "../components/Directory/EmployeeList"; import eventBus from "../services/eventBus"; -import {Mutation, useMutation, useQuery, useQueryClient} from "@tanstack/react-query"; +import { + Mutation, + useMutation, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import showToast from "../services/toastService"; // export const useProjects = () => { @@ -211,7 +216,7 @@ export const useProjects = () => { error, refetch, } = useQuery({ - queryKey: ['ProjectsList'], + queryKey: ["ProjectsList"], queryFn: async () => { const response = await ProjectRepository.getProjectList(); return response.data; @@ -227,97 +232,112 @@ export const useProjects = () => { }; }; -export const useEmployeesByProjectAllocated = (selectedProject) => -{ - const {data = [], isLoading, refetch, error} = useQuery( { - queryKey: ["empListByProjectAllocated", selectedProject ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectAllocation( selectedProject ); - return res.data || res +export const useEmployeesByProjectAllocated = (selectedProject) => { + const { + data = [], + isLoading, + refetch, + error, + } = useQuery({ + queryKey: ["empListByProjectAllocated", selectedProject], + queryFn: async () => { + const res = await ProjectRepository.getProjectAllocation(selectedProject); + return res.data || res; }, enabled: !!selectedProject, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Allocated Employees", "error"); - } - } ) - + onError: (error) => { + showToast( + error.message || "Error while Fetching project Allocated Employees", + "error" + ); + }, + }); + return { projectEmployees: data, - loading:isLoading, + loading: isLoading, error, - refetch - } -} + refetch, + }; +}; -export const useProjectDetails = ( projectId,isAuto = true ) => -{ - const {data: projects_Details, isLoading, error, refetch} = useQuery( { - queryKey: [ "projectInfo", projectId ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectByprojectId( projectId ); +export const useProjectDetails = (projectId, isAuto = true) => { + const { + data: projects_Details, + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["projectInfo", projectId], + queryFn: async () => { + const res = await ProjectRepository.getProjectByprojectId(projectId); return res.data || res; }, enabled: !!projectId && isAuto, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Details", "error"); - } - } ) - return { projects_Details, loading:isLoading, error, refetch }; -} + onError: (error) => { + showToast( + error.message || "Error while Fetching project Details", + "error" + ); + }, + }); + return { projects_Details, loading: isLoading, error, refetch }; +}; -export const useProjectsByEmployee = (employeeId) => -{ - const { data:projectNameList =[],isLoading,error,refetch} = useQuery( { - queryKey: [ "ProjectsByEmployee", employeeId ], - queryFn: async () => - { - const res = await ProjectRepository.getProjectsByEmployee( employeeId ); +export const useProjectsByEmployee = (employeeId) => { + const { + data: projectNameList = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["ProjectsByEmployee", employeeId], + queryFn: async () => { + const res = await ProjectRepository.getProjectsByEmployee(employeeId); return res.data || res; }, enabled: !!employeeId, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Employee", "error"); - } - }) - return {projectList, loading:isLoading,error,refetch } -} + onError: (error) => { + showToast( + error.message || "Error while Fetching project Employee", + "error" + ); + }, + }); + return { projectList, loading: isLoading, error, refetch }; +}; -export const useProjectName = () => -{ - const {data = [],isLoading,error,refetch} = useQuery( { - queryKey: [ "basicProjectNameList" ], - queryFn: async () => - { +export const useProjectName = () => { + const { + data = [], + isLoading, + error, + refetch, + } = useQuery({ + queryKey: ["basicProjectNameList"], + queryFn: async () => { const res = await ProjectRepository.projectNameList(); return res.data || res; }, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Name", "error"); - } - } ) - return {projectNames:data,loading:isLoading,Error:error,refetch} -} + onError: (error) => { + showToast(error.message || "Error while Fetching project Name", "error"); + }, + }); + return { projectNames: data, loading: isLoading, Error: error, refetch }; +}; - - export const useProjectInfra = (projectId) => { const { data: projectInfra, isLoading, error, } = useQuery({ - queryKey: ["ProjectInfra", projectId], + queryKey: ["ProjectInfra", projectId], queryFn: async () => { const res = await ProjectRepository.getProjectInfraByproject(projectId); return res.data; }, - enabled: !!projectId , + enabled: !!projectId, onError: (error) => { showToast(error.message || "Error while fetching project infra", "error"); }, @@ -326,30 +346,27 @@ export const useProjectInfra = (projectId) => { return { projectInfra, isLoading, error }; }; - -export const useProjectTasks = (workAreaId,IsExpandedArea=false) => -{ - const { data:ProjectTaskList,isLoading,error } = useQuery( { - queryKey: [ "WorkItems",workAreaId ], - queryFn: async () => - { +export const useProjectTasks = (workAreaId, IsExpandedArea = false) => { + const { + data: ProjectTaskList, + isLoading, + error, + } = useQuery({ + queryKey: ["WorkItems", workAreaId], + queryFn: async () => { const res = await ProjectRepository.getProjectTasksByWorkArea(workAreaId); return res.data; }, enabled: !!workAreaId && !!IsExpandedArea, - onError: ( error ) => - { - showToast(error.message || "Error while Fetching project Tasks", "error"); - } - } ) - return {ProjectTaskList,isLoading,error} -} - + onError: (error) => { + showToast(error.message || "Error while Fetching project Tasks", "error"); + }, + }); + return { ProjectTaskList, isLoading, error }; +}; // -- -------------Mutation------------------------------- - - export const useCreateProject = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); @@ -360,8 +377,8 @@ export const useCreateProject = ({ onSuccessCallback }) => { }, onSuccess: (data) => { // Invalidate the cache - queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} ); - queryClient.invalidateQueries({queryKey:['basicProjectNameList']}); + queryClient.invalidateQueries({ queryKey: ["ProjectsList"] }); + queryClient.invalidateQueries({ queryKey: ["basicProjectNameList"] }); // Emit event for consumers (like useProjects or others) eventBus.emit("project", { @@ -381,27 +398,19 @@ export const useCreateProject = ({ onSuccessCallback }) => { }); }; - export const useUpdateProject = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - } = useMutation({ - mutationFn: async ( {projectId, updatedData} ) => - { + const { mutate, isPending, isSuccess, isError } = useMutation({ + mutationFn: async ({ projectId, updatedData }) => { return await ProjectRepository.updateProject(projectId, updatedData); }, - onSuccess: ( data, variables ) => - { + onSuccess: (data, variables) => { const { projectId } = variables; - - queryClient.invalidateQueries({queryKey:["ProjectsList"]}); - queryClient.invalidateQueries( {queryKey: [ "projectInfo", projectId ]} ); - queryClient.invalidateQueries({queryKey:['basicProjectNameList']}); + + queryClient.invalidateQueries({ queryKey: ["ProjectsList"] }); + queryClient.invalidateQueries({ queryKey: ["projectInfo", projectId] }); + queryClient.invalidateQueries({ queryKey: ["basicProjectNameList"] }); eventBus.emit("project", { keyword: "Update_Project", @@ -415,9 +424,8 @@ export const useUpdateProject = ({ onSuccessCallback }) => { } }, - onError: ( error ) => - { - console.log(error) + onError: (error) => { + console.log(error); showToast(error?.message || "Error while updating project", "error"); }, }); @@ -430,20 +438,16 @@ export const useUpdateProject = ({ onSuccessCallback }) => { }; }; - -export const useManageProjectInfra = ( {onSuccessCallback} ) => -{ +export const useManageProjectInfra = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {infraObject, projectId} ) => - { + mutationFn: async ({ infraObject, projectId }) => { return await ProjectRepository.manageProjectInfra(infraObject); }, - onSuccess: ( data, variables ) => - { - const { projectId } = variables; - queryClient.invalidateQueries({queryKey:["ProjectInfra", projectId]}); - if (onSuccessCallback) onSuccessCallback(data,variables); + onSuccess: (data, variables) => { + const { projectId } = variables; + queryClient.invalidateQueries({ queryKey: ["ProjectInfra", projectId] }); + if (onSuccessCallback) onSuccessCallback(data, variables); }, onError: (error) => { showToast(error.message || "Failed to update Project Infra", "error"); @@ -451,39 +455,36 @@ export const useManageProjectInfra = ( {onSuccessCallback} ) => }); }; - export const useManageProjectAllocation = ({ onSuccessCallback, onErrorCallback, }) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - } = useMutation({ - mutationFn: async ( {items} ) => - { + const { mutate, isPending, isSuccess, isError } = useMutation({ + mutationFn: async ({ items }) => { const response = await ProjectRepository.manageProjectAllocation(items); return response.data; }, onSuccess: (data, variables, context) => { - queryClient.invalidateQueries({queryKey:['empListByProjectAllocated']}); - queryClient.removeQueries({queryKey:["projectEmployees"]}) + queryClient.invalidateQueries({ + queryKey: ["empListByProjectAllocated"], + }); + queryClient.removeQueries({ queryKey: ["projectEmployees"] }); if (variables?.added) { - showToast('Employee Assigned Successfully', 'success'); + showToast("Employee Assigned Successfully", "success"); } else { - showToast('Removed Employee Successfully', 'success'); + showToast("Removed Employee Successfully", "success"); } if (onSuccessCallback) onSuccessCallback(data, context); }, onError: (error) => { const message = - error?.response?.data?.message || error.message || 'Error occurred during API call'; - showToast(message, 'error'); + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(message, "error"); if (onErrorCallback) onErrorCallback(error); }, }); @@ -496,48 +497,199 @@ export const useManageProjectAllocation = ({ }; }; -export const useManageTask = ({onSuccessCallback}) => -{ +export const useManageTask = ({ onSuccessCallback }) => { const queryClient = useQueryClient(); + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + return useMutation({ + mutationFn: async ({ + payload, + PreviousPlannedWork, + previousCompletedWork, + buildingId, + floorId, + workAreaId, + }) => await ProjectRepository.manageProjectTasks(payload), + onSuccess: (data, variables) => { + const { + workAreaId, + buildingId, + floorId, + PreviousPlannedWork, + previousCompletedWork, + } = variables; + queryClient.invalidateQueries({ queryKey: ["WorkItems"] }); + const getPlannedDelta = (previous, current) => current - previous; + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + const { workAreaId, floorId, buildingId } = variables; + const updatedData = JSON.parse(JSON.stringify(oldData)); + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; - return useMutation( { - mutationFn: async ( payload ) => await ProjectRepository.manageProjectTasks( payload ), - onSuccess: ( data, variables ) => - { - queryClient.invalidateQueries({ queryKey: ["WorkItems"] }) + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + + const previousPlanned = PreviousPlannedWork ?? 0; + const currentPlanned = + data?.data[0]?.workItem.plannedWork ?? 0; + + const plannedWorkDelta = getPlannedDelta( + previousPlanned, + currentPlanned + ); + const updatedPlannedWork = + (area.plannedWork ?? 0) + plannedWorkDelta; + const previousCompleted = previousCompletedWork; + + const currentCompleted = + data?.data[0]?.workItem.completedWork ?? 0; + const completedWorkDelta = getPlannedDelta( + previousCompleted, + currentCompleted + ); + const updatedCompletedWork = + (area.completedWork ?? 0) + completedWorkDelta; + return { + ...area, + plannedWork: updatedPlannedWork, + completedWork: updatedCompletedWork, + }; + }), + }; + }), + }; + }); + }); + + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + + return projects.map((project) => { + if (project.id !== selectedProject) return project; + + const previousPlanned = PreviousPlannedWork ?? 0; + const currentPlanned = data?.data[0]?.workItem.plannedWork ?? 0; + + const plannedWorkDelta = getPlannedDelta( + previousPlanned, + currentPlanned + ); + const updatedPlannedWork = + (project.plannedWork ?? 0) + plannedWorkDelta; + const previousCompleted = previousCompletedWork; + const currentCompleted = data?.data[0]?.workItem.completedWork ?? 0; + const completedWorkDelta = getPlannedDelta( + previousCompleted, + currentCompleted + ); + const updatedCompletedWork = + (project.completedWork ?? 0) + completedWorkDelta; + return { + ...project, + plannedWork: updatedPlannedWork, + completedWork: updatedCompletedWork, + }; + }); + }); if (onSuccessCallback) onSuccessCallback(data); }, - onError: (error) => - { - const message = - error?.response?.data?.message || error.message || 'Error occurred during API call'; - showToast(message, 'error'); - } - - }) -} - -export const useDeleteProjectTask = (onSuccessCallback) => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: async ( {workItemId, workAreaId} ) => - { - return await ProjectRepository.deleteProjectTask(workItemId); - }, - onSuccess: ( _, variables ) => - { - showToast("Task deleted successfully", "success"); - queryClient.invalidateQueries({queryKey:[ "WorkItems",variables.workAreaId]}); - if (onSuccessCallback) onSuccessCallback(); - }, onError: (error) => { - showToast( - error?.response?.data?.message || error.message || "Failed to delete task", - "error" - ); - if (onSuccessCallback) onSuccessCallback(); + const message = + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(message, "error"); }, }); }; +export const useDeleteProjectTask = (onSuccessCallback) => { + const queryClient = useQueryClient(); + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + return useMutation({ + mutationFn: async ({ + workItemId, + workAreaId, + completedTask, + plannedTask, + }) => { + return await ProjectRepository.deleteProjectTask(workItemId); + }, + onSuccess: (_, variables) => { + const { completedTask, plannedTask, workAreaId, buildingId, floorId } = + variables; + + queryClient.invalidateQueries({ + queryKey: ["WorkItems", variables.workAreaId], + }); + + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + + const { workAreaId, floorId, buildingId, plannedTask, completedTask } = + variables; + + const updatedData = JSON.parse(JSON.stringify(oldData)); + + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; + + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + + return { + ...area, + plannedWork: (area.plannedWork ?? 0) - plannedTask, + completedWork: (area.completedWork ?? 0) - completedTask, + }; + }), + }; + }), + }; + }); + }); + + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + + return projects.map((project) => { + if (project.id !== selectedProject) return project; + + return { + ...project, + plannedWork: (project.plannedWork ?? 0) - plannedTask, + completedWork: (project.completedWork ?? 0) - completedTask, + }; + }); + }); + showToast("Task deleted successfully", "success"); + if (onSuccessCallback) onSuccessCallback(); + }, + onError: (error) => { + showToast( + error?.response?.data?.message || + error.message || + "Failed to delete task", + "error" + ); + if (onSuccessCallback) onSuccessCallback(); + }, + }); +}; diff --git a/src/hooks/useTasks.js b/src/hooks/useTasks.js index 1cf531c0..b793ced4 100644 --- a/src/hooks/useTasks.js +++ b/src/hooks/useTasks.js @@ -1,11 +1,10 @@ import { useEffect, useState } from "react"; import { TasksRepository } from "../repositories/TaskRepository"; import { cacheData, getCachedData } from "../slices/apiDataManager"; -import {MasterRespository} from "../repositories/MastersRepository"; +import { MasterRespository } from "../repositories/MastersRepository"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; import showToast from "../services/toastService"; -import {useSelector} from "react-redux"; - +import { useSelector } from "react-redux"; // ---------Query--------------------------------- @@ -28,7 +27,6 @@ export const useTaskList = (projectId, dateFrom, toDate) => { return response.data; }, enabled, - }); return { TaskList, loading, error, refetch }; @@ -46,7 +44,7 @@ export const useTaskById = (TaskId) => { const res = await TasksRepository.getTaskById(TaskId); return res.data; }, - enabled: !!TaskId, + enabled: !!TaskId, }); return { Task, loading, error, refetch }; @@ -75,47 +73,80 @@ export const useAuditStatus = () => { const res = await MasterRespository.getAuditStatus(); return res.data; }, - }); return { status, loading, error, refetch }; }; - // -----------------------Mutation------------------------ -const toDate = new Date().toISOString().split('T')[0]; -const dateFrom = new Date(Date.now() - 6 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; +const toDate = new Date().toISOString().split("T")[0]; +const dateFrom = new Date(Date.now() - 6 * 24 * 60 * 60 * 1000) + .toISOString() + .split("T")[0]; - - -export const useReportTask = ( {onSuccessCallback, onErrorCallback} = {} ) => -{ +export const useReportTask = ({ onSuccessCallback, onErrorCallback } = {}) => { const queryClient = useQueryClient(); - const { - mutate, - isPending, - isSuccess, - isError, - error, - } = useMutation({ - mutationFn: async ( {reportData,workAreaId} ) => - { - debugger + const selectedProject = useSelector( + (store) => store.localVariables.projectId + ); + const { mutate, isPending, isSuccess, isError, error } = useMutation({ + mutationFn: async ({ reportData, workAreaId }) => { return await TasksRepository.reportTask(reportData); }, - onSuccess: ( data, variables ) => - { - const {workAreaId} = variables; - queryClient.invalidateQueries( {queryKey: [ "taskList" ]} ); - queryClient.invalidateQueries( {queryKey: [ "WorkItems", workAreaId ]} ); - queryClient.invalidateQueries( {queryKey: [ 'ProjectsList' ]} ); - showToast( "Task Reported Successfully.", "success" ); + onSuccess: (data, variables) => { + const { workAreaId, buildingId, floorId } = variables; + queryClient.invalidateQueries({ queryKey: ["taskList"] }); + queryClient.invalidateQueries({ queryKey: ["WorkItems", workAreaId] }); + queryClient.setQueryData(["ProjectInfra", selectedProject], (oldData) => { + if (!oldData) return oldData; + const { workAreaId, floorId, buildingId } = variables; + const updatedData = JSON.parse(JSON.stringify(oldData)); + + return updatedData.map((building) => { + if (building.id !== buildingId) return building; + return { + ...building, + floors: building.floors.map((floor) => { + if (floor.id !== floorId) return floor; + + return { + ...floor, + workAreas: floor.workAreas.map((area) => { + if (area.id !== workAreaId) return area; + return { + ...area, + completedWork: + (area.completedWork ?? 0) + + (data?.data?.completedTask ?? 0), + }; + }), + }; + }), + }; + }); + }); + queryClient.setQueryData(["ProjectsList"], (projects) => { + if (!projects) return projects; + const updatedProject = JSON.parse(JSON.stringify(projects)); + return updatedProject.map((project) => { + if (project.id !== selectedProject) return project; + + return { + ...project, + completedWork: + (project.completedWork ?? 0) + (data?.data?.completedTask ?? 0), + }; + }); + }); + showToast("Task Reported Successfully.", "success"); if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { const msg = - error?.response?.data?.message || error.message || "Error occurred during API call"; - showToast( msg, "error" ); + error?.response?.data?.message || + error.message || + "Error occurred during API call"; + showToast(msg, "error"); }, }); @@ -145,16 +176,12 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { return response.data; }, - onSuccess: ( data,variables ) => - { - + onSuccess: (data, variables) => { const workAreaId = variables?.commentsData?.workItem?.workArea?.id; queryClient.invalidateQueries({ queryKey: ["taskList"] }); if (actionAllow) { - showToast( "Review submitted successfully.", "success" ); - - } else - { + showToast("Review submitted successfully.", "success"); + } else { showToast("Comment sent successfully.", "success"); } @@ -162,7 +189,10 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { }, onError: (error) => { - const msg = error?.response?.data?.message || error.message || "Error during API call"; + const msg = + error?.response?.data?.message || + error.message || + "Error during API call"; showToast(msg, "error"); }, }); @@ -170,24 +200,23 @@ export const useSubmitTaskComment = ({ actionAllow, onSuccessCallback }) => { return { submitComment: mutate, isPending }; }; -export const useCreateTask = ( {onSuccessCallback, onErrorCallback} = {} ) => -{ +export const useCreateTask = ({ onSuccessCallback, onErrorCallback } = {}) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ({payload,workAreaId}) => { + mutationFn: async ({ payload, workAreaId }) => { return await TasksRepository.assignTask(payload); }, - onSuccess: ( _, variables ) => - { - queryClient.invalidateQueries( {queryKey: [ "taskList" ]} ); - queryClient.invalidateQueries( {queryKey: [ "WorkItems", variables?.workAreaId ]} ); - showToast( "Task Assigned Successfully.", "success" ); + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ queryKey: ["taskList"] }); + queryClient.invalidateQueries({ + queryKey: ["WorkItems", variables?.workAreaId], + }); + showToast("Task Assigned Successfully.", "success"); if (onSuccessCallback) onSuccessCallback(variables); }, - onError: ( error ) => - { + onError: (error) => { showToast("Something went wrong. Please try again.", "error"); if (onErrorCallback) onErrorCallback(error); }, }); -}; \ No newline at end of file +}; diff --git a/src/pages/Activities/DailyTask.jsx b/src/pages/Activities/DailyTask.jsx index dba18d18..fb121a89 100644 --- a/src/pages/Activities/DailyTask.jsx +++ b/src/pages/Activities/DailyTask.jsx @@ -14,6 +14,8 @@ import GlobalModel from "../../components/common/GlobalModel"; import AssignTask from "../../components/Project/AssignTask"; import SubTask from "../../components/Activities/SubTask"; import {formatNumber} from "../../utils/dateUtils"; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import { APPROVE_TASK, ASSIGN_REPORT_TASK } from "../../utils/constants"; const DailyTask = () => { const [searchParams] = useSearchParams(); @@ -32,6 +34,8 @@ const DailyTask = () => { const [dateRange, setDateRange] = useState({ startDate: "", endDate: "" }); + const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK) + const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK) const { TaskList, @@ -386,6 +390,7 @@ const DailyTask = () => {
+ { ReportTaskRights && - {task.reportedDate && ( + } + {(ApprovedTaskRights && task.reportedDate ) && ( - - {[...Array(totalPages)].map((_, i) => ( -
  • - -
  • - ))} -
  • - -
  • - - -)} - + » + + + + + )}
    ); diff --git a/src/utils/constants.jsx b/src/utils/constants.jsx index fca560af..a8dc1c2d 100644 --- a/src/utils/constants.jsx +++ b/src/utils/constants.jsx @@ -11,6 +11,11 @@ export const VIEW_PROJECTS = "6ea44136-987e-44ba-9e5d-1cf8f5837ebc" export const MANAGE_EMPLOYEES = "a97d366a-c2bb-448d-be93-402bd2324566" +export const VIEW_ALL_EMPLOYEES = "60611762-7f8a-4fb5-b53f-b1139918796b" + +export const VIEW_TEAM_MEMBERS = "b82d2b7e-0d52-45f3-997b-c008ea460e7f" + + export const MANAGE_PROJECT_INFRA = "cf2825ad-453b-46aa-91d9-27c124d63373" export const VIEW_PROJECT_INFRA = "8d7cc6e3-9147-41f7-aaa7-fa507e450bd4" @@ -24,6 +29,8 @@ export const INFRASTRUCTURE = "9666de86-d7c7-4d3d-acaa-fcd6d6b81f3c"; export const MANAGE_TASK = "08752f33-3b29-4816-b76b-ea8a968ed3c5" +export const APPROVE_TASK = "db4e40c5-2ba9-4b6d-b8a6-a16a250ff99c" + export const VIEW_TASK = "9fcc5f87-25e3-4846-90ac-67a71ab92e3c" export const ASSIGN_REPORT_TASK = "6a32379b-8b3f-49a6-8c48-4b7ac1b55dc2" diff --git a/src/utils/dateUtils.jsx b/src/utils/dateUtils.jsx index 0105d4aa..4d6643b6 100644 --- a/src/utils/dateUtils.jsx +++ b/src/utils/dateUtils.jsx @@ -69,4 +69,13 @@ export const formatNumber = (num) => { }; export const formatUTCToLocalTime = (datetime) =>{ return moment.utc(datetime).local().format("MMMM DD, YYYY [at] hh:mm A"); +} + +export const getCompletionPercentage = (completedWork, plannedWork)=> { + if (!plannedWork || plannedWork === 0) return 0; + + const percentage = (completedWork / plannedWork) * 100; + const clamped = Math.min(Math.max(percentage, 0), 100); + + return clamped.toFixed(2); } \ No newline at end of file