From 94413b9bebfe352f8c908ad8c82f851c9eec6dae Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Tue, 11 Nov 2025 18:00:34 +0530 Subject: [PATCH] Implementing ManageReporting at Employee. --- src/components/Employee/EmployeeSchema.jsx | 14 +- src/components/Employee/ManageReporting.jsx | 214 ++++++++++++------ src/components/common/PmsEmployeeInputTag.jsx | 2 + src/hooks/useEmployees.js | 38 ++++ src/repositories/EmployeeRepository.jsx | 20 +- 5 files changed, 204 insertions(+), 84 deletions(-) diff --git a/src/components/Employee/EmployeeSchema.jsx b/src/components/Employee/EmployeeSchema.jsx index ba1da9e1..b8c9f37a 100644 --- a/src/components/Employee/EmployeeSchema.jsx +++ b/src/components/Employee/EmployeeSchema.jsx @@ -123,11 +123,13 @@ export const defatEmployeeObj = { hasApplicationAccess: false } -export const ManageReportingSchema = { - -} +export const ManageReportingSchema = z.object({ + primaryNotifyTo: z.array(z.string()).min(1, "Primary Reporting Manager is required"), + secondaryNotifyTo: z.array(z.string()).optional(), +}); -export const defaultManageRportion = { - -} +export const defaultManageReporting = { + primaryNotifyTo: [], + secondaryNotifyTo: [], +}; diff --git a/src/components/Employee/ManageReporting.jsx b/src/components/Employee/ManageReporting.jsx index cc79137a..1f3b6a8a 100644 --- a/src/components/Employee/ManageReporting.jsx +++ b/src/components/Employee/ManageReporting.jsx @@ -1,77 +1,153 @@ -import React from 'react' -import { useForm } from 'react-hook-form' -import Label from '../common/Label' -import PmsEmployeeInputTag from '../common/PmsEmployeeInputTag' +import React, { useEffect } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import Label from "../common/Label"; +import PmsEmployeeInputTag from "../common/PmsEmployeeInputTag"; +import { useManageEmployeeHierarchy, useOrganizationHierarchy } from "../../hooks/useEmployees"; +import { ManageReportingSchema, defaultManageReporting } from "./EmployeeSchema"; -const ManageReporting = ({ onClosed }) => { - const { handleSubmit, control, watch, reset } = useForm() - - const handleClose = () => { - reset(); - onClosed(); - }; +const ManageReporting = ({ onClosed, employeeId }) => { + const { + handleSubmit, + control, + reset, + formState: { errors }, + watch, + } = useForm({ + resolver: zodResolver(ManageReportingSchema), + defaultValues: defaultManageReporting, + }); - return ( -
-
-
Manage Reporting
+ const { data, isLoading } = useOrganizationHierarchy(employeeId); - {/* Primary */} -
-
-
- -
- -
-
-
-
+ // mutation hook + const { mutate: manageHierarchy, isPending } = useManageEmployeeHierarchy( + employeeId, + onClosed + ); - {/* Secondary */} -
-
-
- -
- -
-
-
-
+ const primaryValue = watch("primaryNotifyTo"); + const secondaryValue = watch("secondaryNotifyTo"); -
- + // Prefill hierarchy data + useEffect(() => { + if (data && Array.isArray(data)) { + const primary = data.find((r) => r.isPrimary); + const secondary = data.filter((r) => !r.isPrimary); - -
-
+ reset({ + primaryNotifyTo: primary ? [primary.reportTo.id] : [], + secondaryNotifyTo: secondary.map((r) => r.reportTo.id), + }); + } + }, [data, reset]); + + const handleClose = () => { + reset(defaultManageReporting); + onClosed(); + }; + + const onSubmit = (formData) => { + // Build set of currently selected IDs + const selectedIds = new Set([ + ...(formData.primaryNotifyTo || []), + ...(formData.secondaryNotifyTo || []), + ]); + + // Build payload including previous assignments, setting isActive true/false accordingly + const payload = (data || []).map((item) => ({ + reportToId: item.reportTo.id, + isPrimary: item.isPrimary, + isActive: selectedIds.has(item.reportTo.id), + })); + + // Add any new IDs that were not previously assigned + if (formData.primaryNotifyTo?.length) { + const primaryId = formData.primaryNotifyTo[0]; + if (!data?.some((d) => d.reportTo.id === primaryId)) { + payload.push({ + reportToId: primaryId, + isPrimary: true, + isActive: true, + }); + } + } + + if (formData.secondaryNotifyTo?.length) { + formData.secondaryNotifyTo.forEach((id) => { + if (!data?.some((d) => d.reportTo.id === id)) { + payload.push({ + reportToId: id, + isPrimary: false, + isActive: true, + }); + } + }); + } + + console.log("🚀 Final Payload:", payload); + manageHierarchy(payload); + }; + + return ( +
+
+
Update Reporting Manager
+ + {/* Primary */} +
+ + 0} + /> + {errors.primaryNotifyTo && ( +
+ {errors.primaryNotifyTo.message} +
+ )}
- ) -} -export default ManageReporting + {/* Secondary */} +
+ + +
+ + +
+ + + +
+
+
+ ); +}; + +export default ManageReporting; \ No newline at end of file diff --git a/src/components/common/PmsEmployeeInputTag.jsx b/src/components/common/PmsEmployeeInputTag.jsx index 8cad7aac..fe57cee6 100644 --- a/src/components/common/PmsEmployeeInputTag.jsx +++ b/src/components/common/PmsEmployeeInputTag.jsx @@ -11,6 +11,7 @@ const PmsEmployeeInputTag = ({ projectId, forAll, isApplicationUser = false, + disabled }) => { const { field: { value = [], onChange }, @@ -215,6 +216,7 @@ const PmsEmployeeInputTag = ({ autoComplete="off" aria-expanded={showDropdown} aria-haspopup="listbox" + disabled={disabled} /> {showDropdown && ( diff --git a/src/hooks/useEmployees.js b/src/hooks/useEmployees.js index e33084a6..b4e40df2 100644 --- a/src/hooks/useEmployees.js +++ b/src/hooks/useEmployees.js @@ -341,3 +341,41 @@ export const useUpdateEmployeeRoles = ({ error: mutation.error, }; }; + + +export const useOrganizationHierarchy=(employeeId)=>{ +return useQuery({ + queryKey:["organizationHierarchy",employeeId], + queryFn:async()=> { + const resp = await EmployeeRepository.getOrganizaionHierarchy(employeeId); + return resp.data; + }, + enabled:!!employeeId +}) +} + + + +export const useManageEmployeeHierarchy = (employeeId, onSuccessCallBack) => { + const queryClient = useQueryClient(); + return useMutation({ + mutationFn: async (payload) => { + return await EmployeeRepository.manageOrganizationHierarchy(employeeId, payload); + }, + onSuccess: (_, variables) => { + queryClient.invalidateQueries({ + queryKey: ["organizationHierarchy", employeeId], + }); + showToast("Reporting hierarchy updated successfully", "success"); + if (onSuccessCallBack) onSuccessCallBack(); + }, + onError: (error) => { + showToast( + error?.response?.data?.message || + error.message || + "Something went wrong, please try again!", + "error" + ); + }, + }); +}; \ No newline at end of file diff --git a/src/repositories/EmployeeRepository.jsx b/src/repositories/EmployeeRepository.jsx index d257825b..da51038c 100644 --- a/src/repositories/EmployeeRepository.jsx +++ b/src/repositories/EmployeeRepository.jsx @@ -10,18 +10,20 @@ const EmployeeRepository = { updateEmployee: (id, data) => api.put(`/users/${id}`, data), // deleteEmployee: ( id ) => api.delete( `/users/${ id }` ), getEmployeeProfile: (id) => api.get(`/api/employee/profile/get/${id}`), - deleteEmployee: (id,active) => api.delete(`/api/employee/${id}?active=${active}`), - getEmployeeName: (projectId, search,allEmployee) => { - const params = new URLSearchParams(); + deleteEmployee: (id, active) => api.delete(`/api/employee/${id}?active=${active}`), + getEmployeeName: (projectId, search, allEmployee) => { + const params = new URLSearchParams(); - if (projectId) params.append("projectId", projectId); - if (search) params.append("searchString", search); - if(allEmployee) params.append("allEmployee",allEmployee) + if (projectId) params.append("projectId", projectId); + if (search) params.append("searchString", search); + if (allEmployee) params.append("allEmployee", allEmployee) - const query = params.toString(); - return api.get(`/api/Employee/basic${query ? `?${query}` : ""}`); -} + const query = params.toString(); + return api.get(`/api/Employee/basic${query ? `?${query}` : ""}`); + }, + getOrganizaionHierarchy: (employeeId) => api.get(`/api/organization/hierarchy/list/${employeeId}`), + manageOrganizationHierarchy: (employeeId, data) => api.post(`/api/organization/hierarchy/manage/${employeeId}`, data), }; export default EmployeeRepository;