diff --git a/src/components/ServiceProject/ServiceProjectProfile.jsx b/src/components/ServiceProject/ServiceProjectProfile.jsx index 3084a755..54af1442 100644 --- a/src/components/ServiceProject/ServiceProjectProfile.jsx +++ b/src/components/ServiceProject/ServiceProjectProfile.jsx @@ -4,8 +4,8 @@ import { useServiceProject } from "../../hooks/useServiceProject"; import { formatUTCToLocalTime } from "../../utils/dateUtils"; const ServiceProjectProfile = () => { - const { id } = useParams(); - const { data, isLoading, isError, error } = useServiceProject(id); + const { projectId } = useParams(); + const { data, isLoading, isError, error } = useServiceProject(projectId); if (isLoading) { return
Loadng.
; } diff --git a/src/components/ServiceProject/ServiceProjectTeam/ProjectTeam.jsx b/src/components/ServiceProject/ServiceProjectTeam/ProjectTeam.jsx index 6ce2a6fb..ffaf8b84 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ProjectTeam.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ProjectTeam.jsx @@ -1,15 +1,17 @@ import React from 'react' import { useModal } from '../../../hooks/useAuth' +import { useParams } from 'react-router-dom'; const ProjectTeam = () => { const {onOpen} = useModal("ServiceTeamAllocation"); + const {projectId}= useParams(); return (
-
+
{/* */} diff --git a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx index f2ac7a84..545f07da 100644 --- a/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx +++ b/src/components/ServiceProject/ServiceProjectTeam/ServiceProjectTeamAllocation.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useModal } from "../../../hooks/useAuth"; import Modal from "../../common/Modal"; import Label from "../../common/Label"; @@ -6,45 +6,233 @@ import { useTeamRole } from "../../../hooks/masterHook/useMaster"; import SelectField from "../../common/Forms/SelectField"; import SelectFieldServerSide from "../../common/Forms/SelectFieldServerSide"; import SelectEmployeeServerSide from "../../common/Forms/SelectFieldServerSide"; +import Avatar from "../../common/Avatar"; +import { useParams } from "react-router-dom"; +import { EmployeeChip } from "../../common/Chips"; +import { + useAllocationServiceProjectTeam, + useServiceProjectTeam, +} from "../../../hooks/useServiceProject"; +import { SpinnerLoader } from "../../common/Loader"; +import showToast from "../../../services/toastService"; const ServiceProjectTeamAllocation = () => { - const { isOpen, onClose } = useModal("ServiceTeamAllocation"); + const { isOpen, onClose, data: Project } = useModal("ServiceTeamAllocation"); + const [deletingId, setSeletingId] = useState(null); + const { + data: Team, + isLoading: isTeamLoading, + isError: isTeamError, + error: teamError, + } = useServiceProjectTeam(Project?.projectId, true); + const [isManageEmployee, setIsMenageEmployee] = useState(false); + const [nonDuplicateEmployee, setNonDuplicateEmployee] = useState([]); const [selectedTeam, setSelectTeam] = useState(null); - const [selectedEmployees, setSelectedEmployees] = useState(null); - const { data, isLoading, isError, error } = useTeamRole(); - console.log(selectedEmployees); - const TeamAllocation = ( -
-
- setSelectTeam(e)} - isLoading={isLoading} - /> + const [selectedEmployees, setSelectedEmployees] = useState([]); + const { data, isLoading } = useTeamRole(); + + const TeamAllocationColumns = [ + { + key: "team", + label: "Role", + getValue: (e) => e?.teamName || "-", + }, + ]; + const handleRemove = (id) => { + setSelectedEmployees((prev) => prev.filter((e) => e.id !== id)); + }; + const { mutate: AllocationTeam, isPending } = useAllocationServiceProjectTeam( + () => { + setSelectedEmployees([]); + setSeletingId(null); + } + ); + + const AssignEmployee = () => { + let payload = selectedEmployees.map((e) => { + return { + projectId: Project?.projectId, + employeeId: e.id, + teamRoleId: selectedTeam, + isActive: true, + }; + }); + AllocationTeam({ payload, isActive: true }); + }; + + const handleDeAllocation = (emp) => { + setSeletingId(emp?.id); + let payload = [ + { + projectId: Project?.projectId, + employeeId: emp.employee.id, + teamRoleId: emp.teamRole.id, + isActive: false, + }, + ]; + + AllocationTeam({payload:payload,isActive:false}); + }; + useEffect(() => { + if(selectedEmployees?.length > 0 && !selectedTeam){ + handleRemove(selectedEmployees[0]?.id) + showToast( + `Please select a first role`, + "warning" + ); + } + if (Team?.length > 0 && selectedEmployees?.length > 0) { + setNonDuplicateEmployee((prev) => { + let updated = [...prev]; + + selectedEmployees.forEach((selectedEmployee) => { + const alreadyAllocated = Team.some( + (e) => + e?.employee?.id === selectedEmployee?.id && + e?.teamRole?.id === selectedTeam + ); + if (alreadyAllocated) { + handleRemove(selectedEmployee?.id); + showToast( + `${selectedEmployee.firstName} ${selectedEmployee.lastName} already allocated with same role`, + "warning" + ); + } + }); + + return updated; + }); + } + }, [Team, selectedEmployees, selectedTeam]); + + const TeamAllocationBody = ( +
+
+
+ +
+ + {isManageEmployee && ( + <> +
+ { + setSelectedEmployees([]); + setSelectTeam(e); + }} + isLoading={isLoading} + /> +
+ +
+ +
+
+ {selectedEmployees.map((e) => ( + + ))} +
+ + )}
-
- + {(selectedEmployees?.length > 0 && isManageEmployee ) && ( +
+ {" "} + +
+ )} +
+ + + + + + + + + + + {Team?.length > 0 ? ( + Team?.map((emp) => ( + + + + + + )) + ) : ( + + + + )} + +
NameRoleAction
+
+ {" "} + + {`${emp?.employee?.firstName} ${emp?.employee?.lastName}`} +
+
{emp?.teamRole?.name} + {deletingId === emp.id ? ( +
+ ) : ( + + handleDeAllocation(emp)} + > + + )} +
+ {isTeamLoading ? ( + + ) : ( +
+ +

Please Add a Employee

+
+ )} +
); + return ( ); }; diff --git a/src/components/common/Chips.jsx b/src/components/common/Chips.jsx new file mode 100644 index 00000000..4b8bd8e3 --- /dev/null +++ b/src/components/common/Chips.jsx @@ -0,0 +1,45 @@ +import React from 'react' + +export const EmployeeChip = ({handleRemove,employee}) => { + return( + +
+ {employee?.photo ? ( + + {`${employee?.firstName + + ) : ( +
+ + {employee?.firstName?.[0] || ""} + {employee?.lastName?.[0] || ""} + +
+ )} + +
+ + {employee?.firstName} {employee?.lastName} + +
+
+ + handleRemove(employee?.id)} + aria-label={`Remove ${employee?.firstName}`} + title="Remove" + /> +
+ ) +} + diff --git a/src/components/common/Forms/SelectFieldServerSide.jsx b/src/components/common/Forms/SelectFieldServerSide.jsx index c2014ed6..75477ef8 100644 --- a/src/components/common/Forms/SelectFieldServerSide.jsx +++ b/src/components/common/Forms/SelectFieldServerSide.jsx @@ -128,7 +128,7 @@ const SelectEmployeeServerSide = ({ {/* DROPDOWN */} {open && (
    { const dispatch = useDispatch(); - const isOpen = useSelector( - (state) => state.localVariables.modals[modalType]?.isOpen + + const modalState = useSelector( + (state) => state.localVariables.modals[modalType] || {} ); - const onOpen = (data = {}) => dispatch(openModal({ modalType, data })); + const { isOpen = false, data = null } = modalState; + + const onOpen = (payload = {}) => + dispatch(openModal({ modalType, data: payload })); + const onClose = () => dispatch(closeModal({ modalType })); + const onToggle = () => dispatch(toggleModal({ modalType })); - return { isOpen, onOpen, onClose, onToggle }; + return { isOpen, data, onOpen, onClose, onToggle }; }; + + export const useSubscription = (frequency) => { return useQuery({ queryKey: ["subscriptionPlans", frequency], diff --git a/src/hooks/useServiceProject.jsx b/src/hooks/useServiceProject.jsx index 075e7038..c5527163 100644 --- a/src/hooks/useServiceProject.jsx +++ b/src/hooks/useServiceProject.jsx @@ -32,6 +32,20 @@ export const useServiceProject = (projectId) => { enabled: !!projectId, }); }; + +export const useServiceProjectTeam = (projectId, isActive) => { + return useQuery({ + queryKey: ["serviceProjectTeam", projectId, isActive], + queryFn: async () => { + const response = await ServiceProjectRepository.GetAllocatedEmployees( + projectId, + isActive + ); + return response.data; + }, + enabled: !!projectId, + }); +}; export const useCreateServiceProject = (onSuccessCallback) => { const queryClient = useQueryClient(); return useMutation({ @@ -104,14 +118,22 @@ export const useActiveInActiveServiceProject = (onSuccessCallback) => { }); }; -export const useAllocationTeam=()=>{ +export const useAllocationServiceProjectTeam = (onSuccessCallback) => { const queryClient = useQueryClient(); return useMutation({ - mutationFn:async(paylaod)=> await ServiceProjectRepository.AllocateEmployee(paylaod), - onSuccess: (data, variables) => { - // queryClient.invalidateQueries({ queryKey: ["serviceProjects"] }); + mutationFn: async ({ payload, isActive }) => + await ServiceProjectRepository.AllocateEmployee(payload), + onSuccess: (data, variables) => { + queryClient.invalidateQueries({ queryKey: ["serviceProjectTeam"] }); if (onSuccessCallback) onSuccessCallback(); - showToast(`${variables?.length} employee allocated successfully`, "success"); + if (variables.isActive) { + showToast( + `${variables?.payload.length} Employee allocated successfully`, + "success" + ); + } else { + showToast(`Employee DeAllocated successfully`, "success"); + } }, onError: (error) => { showToast( @@ -121,8 +143,9 @@ export const useAllocationTeam=()=>{ "error" ); }, - }) -} + }); +}; + //#endregion //#region Service Jobs diff --git a/src/repositories/ServiceProjectRepository.jsx b/src/repositories/ServiceProjectRepository.jsx index 20b5aa8e..97a445ec 100644 --- a/src/repositories/ServiceProjectRepository.jsx +++ b/src/repositories/ServiceProjectRepository.jsx @@ -11,7 +11,12 @@ export const ServiceProjectRepository = { api.put(`/api/ServiceProject/edit/${id}`, data), DeleteServiceProject: (id, isActive = false) => api.delete(`/api/ServiceProject/delete/${id}?isActive=${isActive}`), - AllocateEmployee: (data) => api.post(`/api/ServiceProject/manage/allocation`), + AllocateEmployee: (data) => + api.post(`/api/ServiceProject/manage/allocation`, data), + GetAllocatedEmployees: (projectId, isActive) => + api.get( + `/api/ServiceProject/get/allocation/list?projectId=${projectId}&isActive=${isActive} ` + ), //#region Job CreateJob: (data) => api.post(`/api/ServiceProject/job/create`, data), diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 86feec5f..1c95ec64 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -95,7 +95,7 @@ const router = createBrowserRouter( { path: "/projects", element: }, { path: "/projects/details", element: }, { path: "/project/manage/:projectId", element: }, - { path: "service-projects/:id", element: }, + { path: "/service-projects/:projectId", element: }, {path:"/service/job",element:}, { path: "/employees", element: }, diff --git a/src/slices/localVariablesSlice.jsx b/src/slices/localVariablesSlice.jsx index fba4390c..e05326ad 100644 --- a/src/slices/localVariablesSlice.jsx +++ b/src/slices/localVariablesSlice.jsx @@ -35,9 +35,9 @@ const localVariablesSlice = createSlice({ selfTenant: { tenantEnquireId: null, planId: null, - details:null, - frequency:null, - paymentDetailId:null + details: null, + frequency: null, + paymentDetailId: null, }, }, reducers: { @@ -94,25 +94,38 @@ const localVariablesSlice = createSlice({ openModal: (state, action) => { const { modalType, data } = action.payload; - state.modals[modalType] = { isOpen: true, ...data }; + + state.modals[modalType] = { + isOpen: true, + data: data ?? {}, + }; }, + closeModal: (state, action) => { const { modalType } = action.payload; - state.modals[modalType] = { ...state.modals[modalType], isOpen: false }; + state.modals[modalType] = { + isOpen: false, + data: null, + }; }, + toggleModal: (state, action) => { const { modalType } = action.payload; - state.modals[modalType].isOpen = !state.modals[modalType].isOpen; + const modal = state.modals[modalType] || {}; + modal.isOpen = !modal.isOpen; }, + setSelfTenant: (state, action) => { state.selfTenant.tenantEnquireId = action.payload.tenantEnquireId ?? state.selfTenant.tenantEnquireId; state.selfTenant.planId = action.payload.planId ?? state.selfTenant.planId; - state.selfTenant.details = + state.selfTenant.details = action.payload.details ?? state.selfTenant.details; - state.selfTenant.frequency = action.payload.frequency ?? state.selfTenant.frequency; - state.selfTenant.paymentDetailId = action.payload.paymentDetailId ?? state.selfTenant.paymentDetailId; + state.selfTenant.frequency = + action.payload.frequency ?? state.selfTenant.frequency; + state.selfTenant.paymentDetailId = + action.payload.paymentDetailId ?? state.selfTenant.paymentDetailId; }, }, });