From ca7b0cda1376677ab556db51b4548be5aa9b48e3 Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Mon, 22 Sep 2025 18:44:05 +0530 Subject: [PATCH 01/57] initially setup of service management --- src/components/master/MasterModal.jsx | 4 + .../master/Services/ManageGroup.jsx | 71 ++ .../master/Services/ServicesGroups.jsx | 133 +++ .../master/Services/ServicesSchema.js | 8 + src/hooks/masterHook/useMaster.js | 805 +++++++++--------- src/pages/master/MasterTable.jsx | 15 + src/repositories/MastersRepository.jsx | 5 + 7 files changed, 648 insertions(+), 393 deletions(-) create mode 100644 src/components/master/Services/ManageGroup.jsx create mode 100644 src/components/master/Services/ServicesGroups.jsx diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index bd7f9ba1..2c71ea71 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -17,6 +17,7 @@ import ManageExpenseStatus from "./ManageExpenseStatus"; import ManageDocumentCategory from "./ManageDocumentCategory"; import ManageDocumentType from "./ManageDocumentType"; import ManageServices from "./Services/ManageServices"; +import ServiceGroups from "./Services/ServicesGroups"; const MasterModal = ({ modaldata, closeModal }) => { if (!modaldata?.modalType || modaldata.modalType === "delete") { @@ -67,6 +68,9 @@ const MasterModal = ({ modaldata, closeModal }) => { "Edit-Services": ( ), + "Manage-Services": ( + + ), }; return modalComponents[modalType] || null; diff --git a/src/components/master/Services/ManageGroup.jsx b/src/components/master/Services/ManageGroup.jsx new file mode 100644 index 00000000..1a31f2f7 --- /dev/null +++ b/src/components/master/Services/ManageGroup.jsx @@ -0,0 +1,71 @@ +import { useForm } from "react-hook-form"; +import { useCreateActivityGroup } from "../../../hooks/masterHook/useMaster"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { ActivityGroupSchema } from "./ServicesSchema"; +import Label from "../../common/Label"; + +const ManageGroup = ({ group = null, close }) => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + resolver: zodResolver(ActivityGroupSchema), + defaultValues: { name: "", description: "" }, + }); + const { mutate: createGroup, isPending } = useCreateActivityGroup(); + + const onSubmit = (payload) => { + console.log(payload); + // createGroup + }; + return ( +
+
+ + + {errors.name &&

{errors.name.message}

} +
+
+ + + + {errors.description && ( +

{errors.description.message}

+ )} +
+ +
+ + +
+
+ ); +}; + +export default ManageGroup; \ No newline at end of file diff --git a/src/components/master/Services/ServicesGroups.jsx b/src/components/master/Services/ServicesGroups.jsx new file mode 100644 index 00000000..a554ea6a --- /dev/null +++ b/src/components/master/Services/ServicesGroups.jsx @@ -0,0 +1,133 @@ +import { useState } from "react"; +import { + useActivitiesByGroups, + useGroups, +} from "../../../hooks/masterHook/useMaster"; +import ManageGroup from "./ManageGroup"; + +const ServiceGroups = ({ service }) => { + const [openService, setOpenService] = useState(true); + const [activeGroupId, setActiveGroupId] = useState(null); // track selected group + const [isManageGroup,setManageGroup] = useState({isOpen:false,group:true}) + + const { data: groups, isLoading } = useGroups(service?.id); + const { data: activities, isLoading: actLoading } = + useActivitiesByGroups(activeGroupId); + + if (isLoading) return
Loading groups...
; + + return ( +
+

Manage Service

+
+
+ {/* Service Header */} +
+

{service.name}

+ setOpenService(!openService)} + className="text-end cursor-pointer" + data-bs-toggle="collapse" + data-bs-target="#accordionOne" + aria-expanded={openService} + aria-controls="accordionOne" + > + + +
+ + {/* Groups Section */} +
+
+
+ {groups?.data?.map((group) => { + const isOpen = activeGroupId === group.id; + + return ( +
+
+

{group.name}

+
+ +
+ setManageGroup({isOpen:true,group:null})}> + + +
+ setActiveGroupId(isOpen ? null : group.id) + } + className="text-end cursor-pointer" + data-bs-toggle="collapse" + data-bs-target={`#accordionGroup${group.id}`} + aria-expanded={isOpen} + aria-controls={`accordionGroup${group.id}`} + > + +
+
+ {isManageGroup.isOpen ? setManageGroup({isOpen:false,group:null})}/> : ( +
+
+ {isOpen && actLoading &&

Loading activities...

} + {isOpen && activities?.data?.length > 0 ? ( +
    + {activities.data.map((activity) => ( +
    +
  • + {activity.name} +
  • {" "} +
    + + + +
    +
    + ))} +
+ ) : ( + isOpen &&

No activities found

+ )} +
+
+ ) } + + +
+ ); + })} +
+
+
+
+
+ ); +}; + +export default ServiceGroups; diff --git a/src/components/master/Services/ServicesSchema.js b/src/components/master/Services/ServicesSchema.js index ceaa8bf5..72f28e50 100644 --- a/src/components/master/Services/ServicesSchema.js +++ b/src/components/master/Services/ServicesSchema.js @@ -6,4 +6,12 @@ const schema = z.object({ .string() .min(1, { message: "Description is required" }) .max(255, { message: "Description cannot exceed 255 characters" }), +}); + +export const ActivityGroupSchema = z.object({ + name: z.string().min(1, { message: "Group Name is required" }), + description: z + .string() + .min(1, { message: "Description is required" }) + .max(255, { message: "Description cannot exceed 255 characters" }), }); \ No newline at end of file diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index 6e589efe..7846fa77 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -1,52 +1,75 @@ -import {useState,useEffect, useCallback} from "react" +import { useState, useEffect, useCallback } from "react"; import { MasterRespository } from "../../repositories/MastersRepository"; -import { cacheData,getCachedData } from "../../slices/apiDataManager"; +import { cacheData, getCachedData } from "../../slices/apiDataManager"; import { useSelector } from "react-redux"; -import {useMutation, useQueries, useQuery, useQueryClient} from "@tanstack/react-query"; +import { + useMutation, + useQueries, + useQuery, + useQueryClient, +} from "@tanstack/react-query"; import showToast from "../../services/toastService"; - - - - -export const useServices = ()=>{ +export const useServices = () => { return useQuery({ - queryKey:["services"], - queryFn:async()=> await MasterRespository.getMasterServices() - }) -} -export const useGlobalServices = ()=>{ - return useQuery({ - queryKey:["globalServices"], - queryFn:async()=> await MasterRespository.getGlobalServices() - }) -} + queryKey: ["services"], + queryFn: async () => await MasterRespository.getMasterServices(), + }); +}; -export const useMasterMenu = ()=>{ +export const useGroups = (serviceId) => { + return useQuery({ + queryFn: ["groups", serviceId], + queryFn: async () => await MasterRespository.getActivityGrops(serviceId), + }) +}; +export const useActivitiesByGroups = (groupId) => { + return useQuery({ + queryFn: ["activties", groupId], + queryFn: async () => await MasterRespository.getActivityGrops(groupId), + }) +}; +export const useGlobalServices = () => { return useQuery({ - queryKey:["MasterMenu"], - queryFn:async()=> { + queryKey: ["globalServices"], + queryFn: async () => await MasterRespository.getGlobalServices(), + }); +}; + +export const useMasterMenu = () => { + return useQuery({ + queryKey: ["MasterMenu"], + queryFn: async () => { const resp = await MasterRespository.getMasterMenus(); - return resp.data - } - }) -} + return resp.data; + }, + }); +}; -export const useActivitiesMaster = () => -{ - const { data:activities=[],isLoading:loading,error} = useQuery( { - queryKey: [ "ActivityMaster" ], - queryFn: async() => - { + + +export const useActivitiesMaster = () => { + const { + data: activities = [], + isLoading: loading, + error, + } = useQuery({ + queryKey: ["ActivityMaster"], + queryFn: async () => { const response = await MasterRespository.getActivites(); - return response.data + return response.data; }, - onError: (error) => { - showToast(error?.response?.data?.message || error.message || "Failed to fetch ActivityMasters", "error"); + onError: (error) => { + showToast( + error?.response?.data?.message || + error.message || + "Failed to fetch ActivityMasters", + "error" + ); }, - } ) - return {activities,loading,error} -} + }); + return { activities, loading, error }; +}; export const useWorkCategoriesMaster = () => { const { data: categories = [], @@ -71,22 +94,28 @@ export const useWorkCategoriesMaster = () => { return { categories, categoryLoading, categoryError }; }; - -export const useContactCategory = () => -{ - const {data:contactCategory=[],isLoading:loading,error:Error } = useQuery( { - queryKey: [ "Contact Category" ], - queryFn: async () => - { +export const useContactCategory = () => { + const { + data: contactCategory = [], + isLoading: loading, + error: Error, + } = useQuery({ + queryKey: ["Contact Category"], + queryFn: async () => { let resp = await MasterRespository.getContactCategory(); - return resp.data + return resp.data; }, - onError: (error) => { - showToast(error?.response?.data?.message || error.message || "Failed to fetch Contact categories", "error"); + onError: (error) => { + showToast( + error?.response?.data?.message || + error.message || + "Failed to fetch Contact categories", + "error" + ); }, - } ) - return { contactCategory,loading,Error} -} + }); + return { contactCategory, loading, Error }; +}; export const useContactTags = () => { const { @@ -112,16 +141,15 @@ export const useContactTags = () => { return { contactTags, loading, error }; }; - -export const useExpenseType =()=>{ -const { +export const useExpenseType = () => { + const { data: ExpenseTypes = [], isLoading: loading, error, } = useQuery({ queryKey: ["Expense Type"], queryFn: async () => { - const res = await MasterRespository.getExpenseType() + const res = await MasterRespository.getExpenseType(); return res.data; }, onError: (error) => { @@ -135,16 +163,16 @@ const { }); return { ExpenseTypes, loading, error }; -} -export const usePaymentMode =()=>{ -const { +}; +export const usePaymentMode = () => { + const { data: PaymentModes = [], isLoading: loading, error, } = useQuery({ queryKey: ["Payment Mode"], queryFn: async () => { - const res = await MasterRespository.getPaymentMode() + const res = await MasterRespository.getPaymentMode(); return res.data; }, onError: (error) => { @@ -158,16 +186,16 @@ const { }); return { PaymentModes, loading, error }; -} -export const useExpenseStatus =()=>{ -const { +}; +export const useExpenseStatus = () => { + const { data: ExpenseStatus = [], isLoading: loading, error, } = useQuery({ queryKey: ["Expense Status"], queryFn: async () => { - const res = await MasterRespository.getExpenseStatus() + const res = await MasterRespository.getExpenseStatus(); return res.data; }, onError: (error) => { @@ -181,20 +209,20 @@ const { }); return { ExpenseStatus, loading, error }; -} -export const useDocumentTypes =(category)=>{ -const { +}; +export const useDocumentTypes = (category) => { + const { data: DocumentTypes = [], error, isError, - isLoading + isLoading, } = useQuery({ - queryKey: ["Document Type",category], + queryKey: ["Document Type", category], queryFn: async () => { - const res = await MasterRespository.getDocumentTypes(category) + const res = await MasterRespository.getDocumentTypes(category); return res.data; }, - enabled:!!category, + enabled: !!category, onError: (error) => { showToast( error?.response?.data?.message || @@ -206,20 +234,20 @@ const { }); return { DocumentTypes, isError, isLoading, error }; -} -export const useDocumentCategories =(EntityType)=>{ -const { +}; +export const useDocumentCategories = (EntityType) => { + const { data: DocumentCategories = [], error, isError, - isLoading + isLoading, } = useQuery({ - queryKey: ["Document Category",EntityType], + queryKey: ["Document Category", EntityType], queryFn: async () => { - const res = await MasterRespository.getDocumentCategories(EntityType) + const res = await MasterRespository.getDocumentCategories(EntityType); return res.data; }, - enabled:!!EntityType, + enabled: !!EntityType, onError: (error) => { showToast( error?.response?.data?.message || @@ -231,13 +259,13 @@ const { }); return { DocumentCategories, isError, isLoading, error }; -} -export const useOrganizationType =()=>{ +}; +export const useOrganizationType = () => { return useQuery({ - queryKey:["orgType"], - queryFn:async()=>await MasterRespository.getOrganizationType() - }) -} + queryKey: ["orgType"], + queryFn: async () => await MasterRespository.getOrganizationType(), + }); +}; // ===Application Masters Query================================================= const fetchMasterData = async (masterType) => { @@ -248,7 +276,7 @@ const fetchMasterData = async (masterType) => { return (await MasterRespository.getJobRole()).data; case "Activity": return (await MasterRespository.getActivites()).data; - case "Services": + case "Services": return (await MasterRespository.getService()).data; case "Work Category": return (await MasterRespository.getWorkCategory()).data; @@ -293,8 +321,13 @@ const fetchMasterData = async (masterType) => { }; const useMaster = () => { - const selectedMaster = useSelector((store) => store.localVariables.selectedMaster); - const queryFn = useCallback(() => fetchMasterData(selectedMaster), [selectedMaster]); + const selectedMaster = useSelector( + (store) => store.localVariables.selectedMaster + ); + const queryFn = useCallback( + () => fetchMasterData(selectedMaster), + [selectedMaster] + ); const { data = [], isLoading, @@ -303,11 +336,16 @@ const useMaster = () => { queryKey: ["masterData", selectedMaster], queryFn, enabled: !!selectedMaster, - staleTime: 1000 * 60 * 10, + staleTime: 1000 * 60 * 10, refetchOnWindowFocus: false, onError: (error) => { - showToast(error?.response?.data?.message || error.message || `Failed to fetch ${selectedMaster} Maseter`, "error"); - }, + showToast( + error?.response?.data?.message || + error.message || + `Failed to fetch ${selectedMaster} Maseter`, + "error" + ); + }, }); return { data, loading: isLoading, error }; @@ -353,7 +391,7 @@ export const useCreateJobRole = (onSuccessCallback) => { onSuccess: (data) => { showToast("JobRole added successfully.", "success"); - queryClient.invalidateQueries({queryKey:["masterData", "Job Role"]}); + queryClient.invalidateQueries({ queryKey: ["masterData", "Job Role"] }); if (onSuccessCallback) onSuccessCallback(data); }, @@ -365,350 +403,315 @@ export const useCreateJobRole = (onSuccessCallback) => { // Application Role------------------------------------------- -export const useCreateApplicationRole = (onSuccessCallback) => -{ +export const useCreateApplicationRole = (onSuccessCallback) => { const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { - const resp = await MasterRespository.createRole( payload ); + return useMutation({ + mutationFn: async (payload) => { + const resp = await MasterRespository.createRole(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Application Role" ]} ) - showToast( "Application Role added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Application Role"], + }); + showToast("Application Role added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; -export const useUpdateApplicationRole = (onSuccessCallback) => -{ - const queryClient = useQueryClient(); +export const useUpdateApplicationRole = (onSuccessCallback) => { + const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {id, payload} ) => - { - const response = await MasterRespository.updateRoles(id,payload); + mutationFn: async ({ id, payload }) => { + const response = await MasterRespository.updateRoles(id, payload); return response.data; }, onSuccess: (data, variables) => { - queryClient.invalidateQueries({ queryKey: ["masterData", "Application Role"], }); showToast("Application Role updated successfully.", "success"); - + if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { showToast(error.message || "Something went wrong", "error"); }, }); -} +}; // Activity------------------------------ -export const useCreateActivity = (onSuccessCallback) => -{ +export const useCreateActivity = (onSuccessCallback) => { const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createActivity(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Activity" ]} ) - showToast( "Activity added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["masterData", "Activity"] }); + showToast("Activity added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; -export const useUpdateActivity = (onSuccessCallback) => -{ - const queryClient = useQueryClient(); +export const useUpdateActivity = (onSuccessCallback) => { + const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {id, payload} ) => - { - const response = await MasterRespository.updateActivity(id,payload); + mutationFn: async ({ id, payload }) => { + const response = await MasterRespository.updateActivity(id, payload); return response.data; }, onSuccess: (data, variables) => { - queryClient.invalidateQueries({ queryKey: ["masterData", "Activity"], }); showToast("Activity updated successfully.", "success"); - + if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { showToast(error.message || "Something went wrong", "error"); }, }); -} - +}; //-----Create work Category------------------------------- -export const useCreateWorkCategory = (onSuccessCallback) => -{ +export const useCreateWorkCategory = (onSuccessCallback) => { const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createWorkCategory(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries({queryKey: [ "masterData", "Work Category" ]} ) - showToast( "Work Category added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Work Category"], + }); + showToast("Work Category added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; -export const useUpdateWorkCategory = (onSuccessCallback) => -{ - const queryClient = useQueryClient(); +export const useUpdateWorkCategory = (onSuccessCallback) => { + const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {id, payload} ) => - { - const response = await MasterRespository.updateWorkCategory(id,payload); + mutationFn: async ({ id, payload }) => { + const response = await MasterRespository.updateWorkCategory(id, payload); return response.data; }, onSuccess: (data, variables) => { - queryClient.invalidateQueries({ queryKey: ["masterData", "Work Category"], }); showToast("Work Category updated successfully.", "success"); - + if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { showToast(error.message || "Something went wrong", "error"); }, }); -} +}; //-- Contact Category--------------------------- - -export const useCreateContactCategory = (onSuccessCallback) => -{ +export const useCreateContactCategory = (onSuccessCallback) => { const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createContactCategory(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Contact Category" ]} ) - showToast( "Contact Category added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Contact Category"], + }); + showToast("Contact Category added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; - -export const useUpdateContactCategory = (onSuccessCallback) => -{ - const queryClient = useQueryClient(); +export const useUpdateContactCategory = (onSuccessCallback) => { + const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {id, payload} ) => - { - const response = await MasterRespository.updateContactCategory(id,payload); + mutationFn: async ({ id, payload }) => { + const response = await MasterRespository.updateContactCategory( + id, + payload + ); return response.data; }, onSuccess: (data, variables) => { - queryClient.invalidateQueries({ queryKey: ["masterData", "Contact Category"], }); showToast("Contact Category updated successfully.", "success"); - + if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { showToast(error.message || "Something went wrong", "error"); }, }); -} +}; // ---------Contact Tag------------------- -export const useCreateContactTag = (onSuccessCallback) => -{ +export const useCreateContactTag = (onSuccessCallback) => { const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createContactTag(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Contact Tag" ]} ) - showToast( "Contact Tag added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Contact Tag"], + }); + showToast("Contact Tag added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; - -export const useUpdateContactTag = (onSuccessCallback) => -{ - const queryClient = useQueryClient(); +export const useUpdateContactTag = (onSuccessCallback) => { + const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {id, payload} ) => - { - debugger - const response = await MasterRespository.updateContactTag(id,payload); + mutationFn: async ({ id, payload }) => { + debugger; + const response = await MasterRespository.updateContactTag(id, payload); return response.data; }, onSuccess: (data, variables) => { - queryClient.invalidateQueries({ queryKey: ["masterData", "Contact Tag"], }); showToast("Contact Tag updated successfully.", "success"); - + if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { showToast(error.message || "Something went wrong", "error"); }, }); -} +}; // ----------------------Expense Type------------------ -export const useCreateExpenseType = (onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useCreateExpenseType = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createExpenseType(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Expense Type" ]} ) - showToast( "Expense Type added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Expense Type"], + }); + showToast("Expense Type added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} -export const useUpdateExpenseType = (onSuccessCallback) => -{ - const queryClient = useQueryClient(); + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; +export const useUpdateExpenseType = (onSuccessCallback) => { + const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {id, payload} ) => - { - const response = await MasterRespository.updateExpenseType(id,payload); + mutationFn: async ({ id, payload }) => { + const response = await MasterRespository.updateExpenseType(id, payload); return response.data; }, onSuccess: (data, variables) => { - queryClient.invalidateQueries({ queryKey: ["masterData", "Expense Type"], }); showToast("Expense Type updated successfully.", "success"); - + if (onSuccessCallback) onSuccessCallback(data); }, onError: (error) => { showToast(error.message || "Something went wrong", "error"); }, }); -} +}; // -----------------Payment Mode ------------- -export const useCreatePaymentMode = (onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useCreatePaymentMode = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createPaymentMode(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Payment Mode" ]} ) - showToast( "Payment Mode added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Payment Mode"], + }); + showToast("Payment Mode added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; -export const useUpdatePaymentMode = (onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useUpdatePaymentMode = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( {id,payload} ) => - { - const resp = await MasterRespository.updatePaymentMode(id,payload); + return useMutation({ + mutationFn: async ({ id, payload }) => { + const resp = await MasterRespository.updatePaymentMode(id, payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Payment Mode" ]} ) - showToast( "Payment Mode Updated successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Payment Mode"], + }); + showToast("Payment Mode Updated successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} - + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; // Services------------------------------- @@ -739,27 +742,26 @@ export const useCreateService = (onSuccessCallback) => { return useMutation({ mutationFn: async (payload) => { - debugger; + debugger; const resp = await MasterRespository.createService(payload); - debugger; + debugger; return resp.data; }, onSuccess: (data) => { - debugger; + debugger; queryClient.invalidateQueries({ queryKey: ["masterData", "Services"] }); showToast(data?.message || "Service added successfully", "success"); - if (onSuccessCallback) onSuccessCallback(data?.data); + if (onSuccessCallback) onSuccessCallback(data?.data); }, onError: (error) => { - debugger; + debugger; showToast(error.message || "Something went wrong", "error"); }, }); }; - export const useUpdateService = (onSuccessCallback) => { const queryClient = useQueryClient(); @@ -783,154 +785,169 @@ export const useUpdateService = (onSuccessCallback) => { }); }; +export const useCreateActivityGroup =()=>{ + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (payload) => { + const response = await MasterRespository.createActivityGroup(payload) + return response; + }, + onSuccess: (data, variables) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Services"], + }); + + showToast(data.message || "Activity Group created successfully.", "success"); + + if (onSuccessCallback) onSuccessCallback(data); + }, + onError: (error) => { + showToast(error?.message || "Something went wrong", "error"); + }, + }); +} // -------------------Expense Status---------------------------------- -export const useCreateExpenseStatus =(onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useCreateExpenseStatus = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createExpenseStatus(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Expense Status" ]} ) - showToast( "Expense Status added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Expense Status"], + }); + showToast("Expense Status added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} -export const useUpdateExpenseStatus = (onSuccessCallback)=>{ - const queryClient = useQueryClient(); + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; +export const useUpdateExpenseStatus = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( {id,payload} ) => - { - const resp = await MasterRespository.updateExepnseStatus(id,payload); + return useMutation({ + mutationFn: async ({ id, payload }) => { + const resp = await MasterRespository.updateExepnseStatus(id, payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Expense Status" ]} ) - showToast( "Expense Status Updated successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Expense Status"], + }); + showToast("Expense Status Updated successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} - - + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; // --------------------Document-Category-------------------------------- -export const useCreateDocumentCatgory =(onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useCreateDocumentCatgory = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createDocumenyCategory(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Category" ]} ) - queryClient.invalidateQueries( {queryKey:[ "Document Category" ]} ) - showToast( "Document Category added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Document Category"], + }); + queryClient.invalidateQueries({ queryKey: ["Document Category"] }); + showToast("Document Category added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; -export const useUpdateDocumentCategory =(onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useUpdateDocumentCategory = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( {id,payload} ) => - { - const resp = await MasterRespository.updateDocumentCategory(id,payload); + return useMutation({ + mutationFn: async ({ id, payload }) => { + const resp = await MasterRespository.updateDocumentCategory(id, payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Category" ]} ) - queryClient.invalidateQueries( {queryKey:[ "Document Category" ]} ) - showToast( "Document Category Updated successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Document Category"], + }); + queryClient.invalidateQueries({ queryKey: ["Document Category"] }); + showToast("Document Category Updated successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; // ------------------------------Document-Type----------------------------------- -export const useCreateDocumentType =(onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useCreateDocumentType = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( payload ) => - { + return useMutation({ + mutationFn: async (payload) => { const resp = await MasterRespository.createDocumentType(payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Type" ]} ) - showToast( "Document Type added successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Document Type"], + }); + showToast("Document Type added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; -export const useUpdateDocumentType =(onSuccessCallback)=>{ - const queryClient = useQueryClient(); +export const useUpdateDocumentType = (onSuccessCallback) => { + const queryClient = useQueryClient(); - return useMutation( { - mutationFn: async ( {id,payload} ) => - { - const resp = await MasterRespository.updateDocumentType(id,payload); + return useMutation({ + mutationFn: async ({ id, payload }) => { + const resp = await MasterRespository.updateDocumentType(id, payload); return resp.data; }, - onSuccess: ( data ) => - { - queryClient.invalidateQueries( {queryKey:[ "masterData", "Document Type" ]} ) - showToast( "Document Type Updated successfully", "success" ); - if(onSuccessCallback) onSuccessCallback(data) + onSuccess: (data) => { + queryClient.invalidateQueries({ + queryKey: ["masterData", "Document Type"], + }); + showToast("Document Type Updated successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); }, - onError: ( error ) => - { - showToast(error.message || "Something went wrong", "error"); - } - }) -} + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; // -Delete Master -------- export const useDeleteMasterItem = () => { const queryClient = useQueryClient(); return useMutation({ - mutationFn: async ( {masterType, item} ) => - { + mutationFn: async ({ masterType, item }) => { const deleteFn = MasterRespository[masterType]; if (!deleteFn) { - throw new Error(`No delete strategy defined for master type: ${masterType}`); + throw new Error( + `No delete strategy defined for master type: ${masterType}` + ); } await deleteFn(item.id); @@ -945,8 +962,10 @@ export const useDeleteMasterItem = () => { onError: (error) => { const message = - error?.response?.data?.message || error?.message || "Error occurred during deletion"; + error?.response?.data?.message || + error?.message || + "Error occurred during deletion"; showToast(message, "error"); }, }); -}; \ No newline at end of file +}; diff --git a/src/pages/master/MasterTable.jsx b/src/pages/master/MasterTable.jsx index b2285842..8facfdb7 100644 --- a/src/pages/master/MasterTable.jsx +++ b/src/pages/master/MasterTable.jsx @@ -158,6 +158,21 @@ const MasterTable = ({ data, columns, loading, handleModalData }) => { ) : ( <> + {selectedMaster === "Services" && ( + + )} + + - {
- + -
- +// {/* ✅ Date Picker Aligned Left with Padding */} +//
+//
+// +//
+//
- {/* Tabs */} -
    -
  • - -
  • -
  • - -
  • -
+// {/* Tabs */} +//
    +//
  • +// +//
  • +//
  • +// +//
  • +//
-
- {activeTab === "all" && ( -
-
- {isLoading ? ( -

Loading activity data...

- ) : isError ? ( -

No data available.

- ) : ( - ActivityData && ( - <> -
- Allocated Task -
-

- {ActivityData.totalCompletedWork?.toLocaleString()}/ - {ActivityData.totalPlannedWork?.toLocaleString()} -

- Completed / Assigned -
- -
- - ) - )} -
+//
+// {activeTab === "all" && ( +//
+//
+// {isLoading ? ( +//

Loading activity data...

+// ) : isError ? ( +//

No data available.

+// ) : ( +// ActivityData && ( +// <> +//
+// Allocated Task +//
+//

+// {ActivityData.totalCompletedWork?.toLocaleString()}/ +// {ActivityData.totalPlannedWork?.toLocaleString()} +//

+// Completed / Assigned +//
+// +//
+// +// ) +// )} +//
-
- {!isLoading && !isError && ActivityData && ( - <> -
- Activities -
-

- {ActivityData.totalCompletedWork?.toLocaleString()}/ - {ActivityData.totalPlannedWork?.toLocaleString()} -

- Pending / Assigned -
- -
- - )} -
-
- )} +//
+// {!isLoading && !isError && ActivityData && ( +// <> +//
+// Activities +//
+//

+// {ActivityData.totalCompletedWork?.toLocaleString()}/ +// {ActivityData.totalPlannedWork?.toLocaleString()} +//

+// Pending / Assigned +//
+// +//
+// +// )} +//
+//
+// )} - {activeTab === "logs" && ( -
- - - - - - - - - {[{ - activity: "Code Review / Remote", - assignedToday: 3, - completed: 2 - }].map((log, index) => ( - - - - - ))} - -
Activity / LocationAssigned / Completed
{log.activity}{log.assignedToday} / {log.completed}
-
- )} -
-
- ); -}; +// {activeTab === "logs" && ( +//
+// +// +// +// +// +// +// +// +// {[{ +// activity: "Code Review / Remote", +// assignedToday: 3, +// completed: 2 +// }].map((log, index) => ( +// +// +// +// +// ))} +// +//
Activity / LocationAssigned / Completed
{log.activity}{log.assignedToday} / {log.completed}
+//
+// )} +// +// +// ); +// }; -export default Activity; +// export default Activity; diff --git a/src/components/master/CreateActivity.jsx b/src/components/master/CreateActivity.jsx index d707593d..1db2bb79 100644 --- a/src/components/master/CreateActivity.jsx +++ b/src/components/master/CreateActivity.jsx @@ -24,7 +24,7 @@ const schema = z.object({ .optional(), }); -const CreateActivity = ({ onClose }) => { +const CreateActivity = ({ activity = null, whichGroup = null, close }) => { const maxDescriptionLength = 255; const { mutate: createActivity, isPending: isLoading } = useCreateActivity(() => onClose?.()); @@ -86,25 +86,19 @@ const CreateActivity = ({ onClose }) => { const onSubmit = (formData) => { createActivity(formData); }; - // const onSubmit = (data) => { - // setIsLoading(true); - - // MasterRespository.createActivity(data) - // .then( ( resp ) => - // { - - // const cachedData = getCachedData("Activity"); - // const updatedData = [ ...cachedData, resp?.data ]; - // cacheData("Activity", updatedData); - // showToast("Activity Successfully Added.", "success"); - // setIsLoading(false); - // handleClose() - // }) - // .catch((error) => { - // showToast(error.message, "error"); - // setIsLoading(false); - // }); - // }; + + useEffect(()=>{ + if (activity) { + reset({ + activityName: activity.activityName || '', + unitOfMeasurement: activity.unitOfMeasurement || '', + checkList: activity.checkList?.map((check) => ({ + description: check.description || '', + isMandatory: check.isMandatory || false, + })) || [{ description: '', isMandatory: false }], + }); + } + },[activity,reset]) const handleClose = useCallback(() => { reset(); onClose(); diff --git a/src/components/master/EditActivity.jsx b/src/components/master/EditActivity.jsx index 43816f4c..15e85255 100644 --- a/src/components/master/EditActivity.jsx +++ b/src/components/master/EditActivity.jsx @@ -24,7 +24,7 @@ const schema = z.object({ }); -const UpdateActivity = ({ activityData, onClose }) => { +const UpdateActivity = ({ activity = null, whichService = null, close }) => { const { mutate: updateActivity, isPending: isLoading } = useUpdateActivity(() => onClose?.()); const { @@ -40,10 +40,10 @@ const UpdateActivity = ({ activityData, onClose }) => { } = useForm({ resolver: zodResolver(schema), defaultValues: { - id: activityData?.id, - activityName: activityData?.activityName, - unitOfMeasurement: activityData?.unitOfMeasurement, - checkList: activityData?.checkLists || [], + id: activity?.id, + activityName: activity?.activityName, + unitOfMeasurement: activity?.unitOfMeasurement, + checkList: activity?.checkLists || [], }, }); @@ -53,15 +53,15 @@ const UpdateActivity = ({ activityData, onClose }) => { }); useEffect(() => { - if (activityData) { + if (activity) { reset({ - id: activityData.id, - activityName: activityData.activityName, - unitOfMeasurement: activityData.unitOfMeasurement, - checkList: activityData.checkLists || [], + id: activity.id, + activityName: activity.activityName, + unitOfMeasurement: activity.unitOfMeasurement, + checkList: activity.checkLists || [], }); } - }, [activityData, reset]); + }, [activity, reset]); const addChecklistItem = () => { const values = getValues("checkList"); @@ -91,36 +91,10 @@ const UpdateActivity = ({ activityData, onClose }) => { }; const onSubmit = (formData) => { - const payload = { ...formData, id: activityData.id }; - updateActivity({ id: activityData.id, payload }); + const payload = { ...formData, id: activity.id }; + updateActivity({ id: activity.id, payload }); }; - // const onSubmit = async(data) => { - // setIsLoading(true); - // const Activity = {...data, id:activityData.id} - // try - // { - // const response = await MasterRespository.updateActivity( activityData?.id, Activity ); - // const updatedActivity = response.data; - // const cachedData = getCachedData("Activity") - - // if (cachedData) { - // const updatedActivities = cachedData.map((activity) => - // activity.id === updatedActivity.id ? { ...activity, ...updatedActivity } : activity - // ); - - // cacheData( "Activity", updatedActivities ); - // onClose() - // } - // setIsLoading( false ) - // showToast("Activity Successfully Updated", "success"); - // } catch ( err ) - // { - // setIsLoading( false ) - - // showToast("error.message", "error"); - // } - // }; useEffect(() => { const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')); @@ -129,7 +103,6 @@ const UpdateActivity = ({ activityData, onClose }) => { return ( - {/*
Update Activity
*/}
{/* Activity Name */}
diff --git a/src/components/master/Services/ManageActivity.jsx b/src/components/master/Services/ManageActivity.jsx new file mode 100644 index 00000000..e3d0fa2c --- /dev/null +++ b/src/components/master/Services/ManageActivity.jsx @@ -0,0 +1,274 @@ +import React, { useState, useEffect, useCallback } from "react"; +import { useFieldArray, useForm } from "react-hook-form"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; + +import { + useCreateActivity, + useUpdateActivity, +} from "../../../hooks/masterHook/useMaster"; +import Label from "../../common/Label"; + +const schema = z.object({ + activityName: z.string().min(1, { message: "Activity Name is required" }), + unitOfMeasurement: z + .string() + .min(1, { message: "Unit of Measurement is required" }), + checkList: z + .array( + z.object({ + description: z + .string() + .min(1, { message: "descriptionlist item cannot be empty" }), + isMandatory: z.boolean().default(false), + id: z.any().default(null), + }) + ) + .optional(), +}); + +const ManageActivity = ({ activity = null, whichGroup = null, close }) => { + const maxDescriptionLength = 255; + const { mutate: createActivity, isPending: isLoading } = useCreateActivity( + () => close?.() + ); + const { mutate: UpdateActivity, isPending: isUpdating } = useUpdateActivity( + () => close?.() + ); + + const { + register, + handleSubmit, + control, + setValue, + clearErrors, + setError, + getValues, + reset, + formState: { errors }, + } = useForm({ + resolver: zodResolver(schema), + defaultValues: { + activityName: "", + unitOfMeasurement: "", + checkList: [], + }, + }); + + const { + fields: checkListItems, + append, + remove, + } = useFieldArray({ + control, + name: "checkList", + }); + + const addChecklistItem = useCallback(() => { + const values = getValues("checkList"); + const lastIndex = checkListItems.length - 1; + + if ( + checkListItems.length > 0 && + (!values?.[lastIndex] || values[lastIndex].description.trim() === "") + ) { + setError(`checkList.${lastIndex}.description`, { + type: "manual", + message: "Please fill this checklist item before adding another.", + }); + return; + } + + clearErrors(`checkList.${lastIndex}.description`); + append({ id: null, description: "", isMandatory: false }); + }, [checkListItems, getValues, append, setError, clearErrors]); + + const removeChecklistItem = useCallback( + (index) => { + remove(index); + }, + [remove] + ); + + const handleChecklistChange = useCallback( + (index, value) => { + setValue(`checkList.${index}`, value); + }, + [setValue] + ); + + const onSubmit = (formData) => { + let payload = { + ...formData, + activityGroupId: whichGroup, + }; + if (activity) { + UpdateActivity({ id: activity.id, payload: payload }); + } else { + createActivity(payload); + } + }; + + useEffect(() => { + if (activity) { + reset({ + activityName: activity.activityName || "", + unitOfMeasurement: activity.unitOfMeasurement || "", + checkList: activity.checkLists?.map((check) => ({ + id: check.id || null, // Use the ID provided in the checklist + description: check.description || "", + isMandatory: check.isMandatory || false, + })) || [{ description: "", isMandatory: false }], // Default to an empty checklist item + }); + } + }, [activity, reset]); + const handleClose = useCallback(() => { + reset(); + close(); + }, [reset, close]); + + useEffect(() => { + const tooltipTriggerList = Array.from( + document.querySelectorAll('[data-bs-toggle="tooltip"]') + ); + tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); + }, []); + let isPending = isLoading || isUpdating; + return ( + + {/*
Create Activity
*/} +
+
+ + + {errors.activityName && ( +

{errors.activityName.message}

+ )} +
+ +
+ + + {errors.unitOfMeasurement && ( +

{errors.unitOfMeasurement.message}

+ )} +
+ +
+

+ {checkListItems.length > 0 ? "Check List" : "Add Check List"} +

+ {checkListItems.length > 0 && ( + + + + + + + + + + {checkListItems.map((item, index) => ( + + + + + + ))} + +
+ Name + + Is Mandatory + Action
+ + + handleChecklistChange(index, e.target.value) + } + /> + {errors.checkList?.[index]?.description && ( + + {errors.checkList[index]?.description?.message} + + )} + + + + +
+ )} + +
+ +
+ + +
+
+ + ); +}; + +export default ManageActivity; diff --git a/src/components/master/Services/ManageGroup.jsx b/src/components/master/Services/ManageGroup.jsx index 1a31f2f7..56ec49f5 100644 --- a/src/components/master/Services/ManageGroup.jsx +++ b/src/components/master/Services/ManageGroup.jsx @@ -1,24 +1,57 @@ import { useForm } from "react-hook-form"; -import { useCreateActivityGroup } from "../../../hooks/masterHook/useMaster"; +import { + useCreateActivityGroup, + useUpdateActivityGroup, +} from "../../../hooks/masterHook/useMaster"; import { zodResolver } from "@hookform/resolvers/zod"; import { ActivityGroupSchema } from "./ServicesSchema"; import Label from "../../common/Label"; +import { useEffect } from "react"; -const ManageGroup = ({ group = null, close }) => { +const ManageGroup = ({ group = null, whichService = null, close }) => { const { register, handleSubmit, + reset, formState: { errors }, } = useForm({ resolver: zodResolver(ActivityGroupSchema), defaultValues: { name: "", description: "" }, }); - const { mutate: createGroup, isPending } = useCreateActivityGroup(); + const { mutate: createGroup, isPending: isCreating } = useCreateActivityGroup( + () => close() + ); + const { mutate: UpdateGroup, isPending: isUpdating } = useUpdateActivityGroup( + () => close() + ); - const onSubmit = (payload) => { - console.log(payload); - // createGroup + useEffect(() => { + if (group) { + reset({ + name: group.name || " ", + description: group.description || "", + }); + } + }, [group, reset]); + const onSubmit = (formdata) => { + if (group) { + let payload = { + ...formdata, + serviceId: whichService, + id: group.id, + }; + UpdateGroup({ id: group.id, payload: payload }); + } else { + let payload = { + ...formdata, + serviceId: whichService, + }; + + createGroup(payload); + } }; + + let isPending = isCreating || isUpdating; return (
@@ -28,9 +61,13 @@ const ManageGroup = ({ group = null, close }) => { - {errors.name &&

{errors.name.message}

} + {errors.name && ( +

{errors.name.message}

+ )}
diff --git a/src/hooks/masterHook/useMaster.js b/src/hooks/masterHook/useMaster.js index 7846fa77..9afee253 100644 --- a/src/hooks/masterHook/useMaster.js +++ b/src/hooks/masterHook/useMaster.js @@ -18,16 +18,20 @@ export const useServices = () => { }; export const useGroups = (serviceId) => { - return useQuery({ - queryFn: ["groups", serviceId], + return useQuery({ + queryKey: ["groups", serviceId], queryFn: async () => await MasterRespository.getActivityGrops(serviceId), - }) + enabled: !!serviceId, + }); }; export const useActivitiesByGroups = (groupId) => { - return useQuery({ - queryFn: ["activties", groupId], - queryFn: async () => await MasterRespository.getActivityGrops(groupId), - }) + return useQuery({ + queryKey: ["activties", groupId], + queryFn: async () => await MasterRespository.getActivitesByGroup(groupId), + + enabled: !!groupId, + + }); }; export const useGlobalServices = () => { return useQuery({ @@ -46,8 +50,6 @@ export const useMasterMenu = () => { }); }; - - export const useActivitiesMaster = () => { const { data: activities = [], @@ -446,47 +448,7 @@ export const useUpdateApplicationRole = (onSuccessCallback) => { }); }; -// Activity------------------------------ -export const useCreateActivity = (onSuccessCallback) => { - const queryClient = useQueryClient(); - return useMutation({ - mutationFn: async (payload) => { - const resp = await MasterRespository.createActivity(payload); - return resp.data; - }, - onSuccess: (data) => { - queryClient.invalidateQueries({ queryKey: ["masterData", "Activity"] }); - showToast("Activity added successfully", "success"); - if (onSuccessCallback) onSuccessCallback(data); - }, - onError: (error) => { - showToast(error.message || "Something went wrong", "error"); - }, - }); -}; - -export const useUpdateActivity = (onSuccessCallback) => { - const queryClient = useQueryClient(); - - return useMutation({ - mutationFn: async ({ id, payload }) => { - const response = await MasterRespository.updateActivity(id, payload); - return response.data; - }, - onSuccess: (data, variables) => { - queryClient.invalidateQueries({ - queryKey: ["masterData", "Activity"], - }); - showToast("Activity updated successfully.", "success"); - - if (onSuccessCallback) onSuccessCallback(data); - }, - onError: (error) => { - showToast(error.message || "Something went wrong", "error"); - }, - }); -}; //-----Create work Category------------------------------- export const useCreateWorkCategory = (onSuccessCallback) => { @@ -785,20 +747,23 @@ export const useUpdateService = (onSuccessCallback) => { }); }; -export const useCreateActivityGroup =()=>{ - const queryClient = useQueryClient(); +export const useCreateActivityGroup = (onSuccessCallback) => { + const queryClient = useQueryClient(); return useMutation({ mutationFn: async (payload) => { - const response = await MasterRespository.createActivityGroup(payload) + const response = await MasterRespository.createActivityGroup(payload); return response; }, onSuccess: (data, variables) => { queryClient.invalidateQueries({ - queryKey: ["masterData", "Services"], + queryKey: ["groups"], }); - showToast(data.message || "Activity Group created successfully.", "success"); + showToast( + data.message || "Activity Group created successfully.", + "success" + ); if (onSuccessCallback) onSuccessCallback(data); }, @@ -806,7 +771,73 @@ export const useCreateActivityGroup =()=>{ showToast(error?.message || "Something went wrong", "error"); }, }); -} +}; +export const useUpdateActivityGroup = (onSuccessCallback) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({id,payload}) => { + const response = await MasterRespository.updateActivityGrop(id,payload); + return response; + }, + onSuccess: (data, variables) => { + queryClient.invalidateQueries({ + queryKey: ["groups"], + }); + + showToast( + data.message || "Activity Group Updated successfully.", + "success" + ); + + if (onSuccessCallback) onSuccessCallback(data); + }, + onError: (error) => { + showToast(error?.message || "Something went wrong", "error"); + }, + }); +}; +// Activity------------------------------ +export const useCreateActivity = (onSuccessCallback) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (payload) => { + const resp = await MasterRespository.createActivity(payload); + return resp.data; + }, + onSuccess: (data) => { + queryClient.invalidateQueries({ queryKey: ["activties"] }); + showToast("Activity added successfully", "success"); + if (onSuccessCallback) onSuccessCallback(data); + }, + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; + +export const useUpdateActivity = (onSuccessCallback) => { + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async ({ id, payload }) => { + const response = await MasterRespository.updateActivity(id, payload); + return response.data; + }, + onSuccess: (data, variables) => { + queryClient.invalidateQueries({ + queryKey: ["activties"], + }); + showToast("Activity updated successfully.", "success"); + + if (onSuccessCallback) onSuccessCallback(data); + }, + onError: (error) => { + showToast(error.message || "Something went wrong", "error"); + }, + }); +}; // -------------------Expense Status---------------------------------- export const useCreateExpenseStatus = (onSuccessCallback) => { @@ -969,3 +1000,48 @@ export const useDeleteMasterItem = () => { }, }); }; + + +export const useDeleteServiceGroup =()=>{ + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (id)=>await MasterRespository.deleteActivityGroup(id), + onSuccess: ({_,variable}) => { + + queryClient.invalidateQueries({ queryKey: ["groups"] }); + + showToast(`Group deleted successfully.`, "success"); + }, + + onError: (error) => { + const message = + error?.response?.data?.message || + error?.message || + "Error occurred during deletion"; + showToast(message, "error"); + }, + }); +} +export const useDeleteActivity =()=>{ + const queryClient = useQueryClient(); + + return useMutation({ + mutationFn: async (id)=>await MasterRespository.deleteActivity(id), + onSuccess: ({_,variable}) => { + + + queryClient.invalidateQueries({ queryKey: ["activties"] }); + + showToast(`Acivity deleted successfully.`, "success"); + }, + + onError: (error) => { + const message = + error?.response?.data?.message || + error?.message || + "Error occurred during deletion"; + showToast(message, "error"); + }, + }); +} diff --git a/src/pages/master/MasterPage.jsx b/src/pages/master/MasterPage.jsx index b187efd1..323e3442 100644 --- a/src/pages/master/MasterPage.jsx +++ b/src/pages/master/MasterPage.jsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo, useEffect } from "react"; +import React, { useState, useMemo, useEffect, createContext, useContext } from "react"; import { useDispatch, useSelector } from "react-redux"; import { useQueryClient } from "@tanstack/react-query"; import Breadcrumb from "../../components/common/Breadcrumb"; @@ -6,7 +6,9 @@ import MasterModal from "../../components/master/MasterModal"; import ConfirmModal from "../../components/common/ConfirmModal"; import MasterTable from "./MasterTable"; import useMaster, { + useDeleteActivity, useDeleteMasterItem, + useDeleteServiceGroup, useMasterMenu, } from "../../hooks/masterHook/useMaster"; import { changeMaster } from "../../slices/localVariablesSlice"; @@ -14,6 +16,16 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { MANAGE_MASTER } from "../../utils/constants"; import GlobalModel from "../../components/common/GlobalModel"; + +export const MasterContext = createContext(); +export const useMasterContext = () => { + const context = useContext(MasterContext); + if (!context) { + throw new Error("useMasterContext must be used within an MasterProvider"); + } + return context; +}; + const MasterPage = () => { const dispatch = useDispatch(); const queryClient = useQueryClient(); @@ -34,6 +46,9 @@ const MasterPage = () => { isError: isMasterError, } = useMaster(); const { mutate: DeleteMaster, isPending: isDeleting } = useDeleteMasterItem(); + const [isDeleletingServiceItem,setDeleletingServiceItem] = useState({isOpen:false,ItemId:null,whichItem:null}) + const {mutate:DeleteSericeGroup,isPending:deletingGroup} =useDeleteServiceGroup() + const {mutate:DeleteAcivity,isPending:deletingActivity} = useDeleteActivity() const [modalConfig, setModalConfig] = useState(null); const [deleteData, setDeleteData] = useState(null); @@ -73,6 +88,18 @@ const MasterPage = () => { ); }; + + const handleDeleteServiceItem =()=>{ + if(!isDeleletingServiceItem.ItemId) return + if(isDeleletingServiceItem.whichItem == "activiy"){ + DeleteAcivity(isDeleletingServiceItem.ItemId,{onSuccess:()=>setDeleletingServiceItem({isOpen:false,ItemId:null,whichItem:null})}) + }else{ + DeleteSericeGroup(isDeleletingServiceItem.ItemId,{onSuccess:()=>setDeleletingServiceItem({isOpen:false,ItemId:null,whichItem:null})}) + } + + + } + if (menuErrorFlag || isMasterError) return (
@@ -87,7 +114,7 @@ const MasterPage = () => { ); return ( - <> + {modalConfig && ( { /> )} + { onSubmit={handleDeleteSubmit} onClose={() => setDeleteData(null)} /> + + setDeleletingServiceItem({isOpen:false,ItemId:null,whichItem:null})} + />
{
- + ); }; diff --git a/src/repositories/MastersRepository.jsx b/src/repositories/MastersRepository.jsx index 00778dc1..3e9fac8f 100644 --- a/src/repositories/MastersRepository.jsx +++ b/src/repositories/MastersRepository.jsx @@ -32,11 +32,11 @@ export const MasterRespository = { getActivites: () => api.get("api/master/activities"), createActivity: (data) => api.post("api/master/activity", data), -//Services + //Services getService: () => api.get("api/master/service/list"), createService: (data) => api.post("api/master/service/create", data), updateService: (id, data) => api.put(`api/master/service/edit/${id}`, data), - "Services": (id) => api.delete(`/api/master/service/delete/${id}`), + Services: (id) => api.delete(`/api/master/service/delete/${id}`), updateActivity: (id, data) => api.post(`api/master/activity/edit/${id}`, data), @@ -114,15 +114,20 @@ export const MasterRespository = { updateDocumentType: (id, data) => api.put(`/api/Master/document-type/edit/${id}`, data), + getGlobalServices: () => api.get("/api/Master/global-service/list"), + getMasterServices: () => api.get("/api/Master/service/list"), + getActivityGrops: (serviceId) => + api.get(`/api/Master/activity-group/list?serviceId=${serviceId}`), + createActivityGroup: (data) => + api.post(`/api/Master/activity-group/create`, data), + updateActivityGrop: (serviceId, data) => + api.put(`/api/Master/activity-group/edit/${serviceId}`, data), + getActivitesByGroup: (activityGroupId) => + api.get(`api/master/activities?activityGroupId=${activityGroupId}`), + deleteActivityGroup:(id)=>api.delete(`/api/Master/activity-group/delete/${id}`), - getGlobalServices:()=>api.get("/api/Master/global-service/list"), - getMasterServices:()=>api.get("/api/Master/service/list"), - getActivityGrops:(serviceId)=>api.get(`/api/Master/activity-group/list?serviceId=${serviceId}`), - createActivityGroup:(data)=>api.post(`/api/Master/activity-group/create`), - getActivitesByGroup: (serviceId) => api.get(`api/master/activities/activityGroupId=${activityGroupId}`), + deleteActivity:(id)=>api.delete(`/api/Master/activity/delete/${id}`), - - - getOrganizationType:()=>api.get('/api/Master/organization-type/list') + getOrganizationType: () => api.get("/api/Master/organization-type/list"), }; From 4e315aafcf4b5710f937862dc73b1cc0443899a4 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Wed, 24 Sep 2025 12:49:00 +0530 Subject: [PATCH 05/57] added small ui changed like table b-padding, button size and fixd delete activity and group --- src/components/Organization/AssignOrg.jsx | 2 +- .../Organization/OrgPickerFromSPId.jsx | 15 +++-- .../Organization/OrgPickerfromTenant.jsx | 57 ++++++++++++------- .../Organization/OrganizationModal.jsx | 2 +- .../Organization/OrganizationsList.jsx | 2 +- .../ProjectAssignedOrgs.jsx | 18 ++++-- .../Project/ProjectOrganizations.jsx | 4 +- .../master/Services/ManageActivity.jsx | 51 ++++++++--------- .../master/Services/ManageGroup.jsx | 2 +- .../master/Services/ServicesGroups.jsx | 12 ++-- src/hooks/masterHook/useMaster.js | 19 ++++--- src/pages/Organization/OrganizationPage.jsx | 2 +- src/pages/master/MasterPage.jsx | 3 +- 13 files changed, 106 insertions(+), 83 deletions(-) diff --git a/src/components/Organization/AssignOrg.jsx b/src/components/Organization/AssignOrg.jsx index 270fac0d..c460de9b 100644 --- a/src/components/Organization/AssignOrg.jsx +++ b/src/components/Organization/AssignOrg.jsx @@ -248,7 +248,7 @@ const AssignOrg = ({ setStep }) => { ? "Please wait..." : flowType === "default" ? "Assign Organization" - : "Add"} + : "Assign Project"}
diff --git a/src/components/Organization/OrgPickerFromSPId.jsx b/src/components/Organization/OrgPickerFromSPId.jsx index e0bdced0..0a46778c 100644 --- a/src/components/Organization/OrgPickerFromSPId.jsx +++ b/src/components/Organization/OrgPickerFromSPId.jsx @@ -84,10 +84,7 @@ const OrgPickerFromSPId = ({ title, placeholder }) => { height={50} /> -
onOpen({ startStep: 3, orgData: org })} - > +
{org.name}
{ Address:
{org.address}
+
+ {" "} + +
))} diff --git a/src/components/Organization/OrgPickerfromTenant.jsx b/src/components/Organization/OrgPickerfromTenant.jsx index 5471df2b..7d39fa60 100644 --- a/src/components/Organization/OrgPickerfromTenant.jsx +++ b/src/components/Organization/OrgPickerfromTenant.jsx @@ -85,7 +85,7 @@ const OrgPickerfromTenant = ({ title }) => { {isLoading ? (
Loading....
) : data && data?.data?.length > 0 ? ( -
+
@@ -99,28 +99,41 @@ const OrgPickerfromTenant = ({ title }) => { - - {Array.isArray(data.data) && data.data.length > 0 - ? data.data.map((row, i) => ( - - {contactList.map((col) => ( - - ))} - - - )) - : null} -
- {col.getValue(row)} - - -
+ +
+ + + {Array.isArray(data.data) && data.data.length > 0 + ? data.data.map((row, i) => ( + + {contactList.map((col) => ( + + ))} + + + )) + : null} + +
+ {col.getValue(row)} + +
+ +
+
+
) : null}
diff --git a/src/components/Organization/OrganizationModal.jsx b/src/components/Organization/OrganizationModal.jsx index da742427..7361c41f 100644 --- a/src/components/Organization/OrganizationModal.jsx +++ b/src/components/Organization/OrganizationModal.jsx @@ -60,7 +60,7 @@ const OrganizationModal = () => { if (startStep === 1) { return orgData && orgData !== null ? "Add Organization" - : "Choose Organization1"; + : "Choose Organization"; } if (startStep === 2) { diff --git a/src/components/Organization/OrganizationsList.jsx b/src/components/Organization/OrganizationsList.jsx index d5243e87..b7ea28e5 100644 --- a/src/components/Organization/OrganizationsList.jsx +++ b/src/components/Organization/OrganizationsList.jsx @@ -93,7 +93,7 @@ const OrganizationsList = ({searchText}) => { if (isError) return
{error?.message || "Something went wrong"}
; return ( -
+
diff --git a/src/components/Project/ProjectOrganization/ProjectAssignedOrgs.jsx b/src/components/Project/ProjectOrganization/ProjectAssignedOrgs.jsx index 4625a30b..53a2365a 100644 --- a/src/components/Project/ProjectOrganization/ProjectAssignedOrgs.jsx +++ b/src/components/Project/ProjectOrganization/ProjectAssignedOrgs.jsx @@ -7,7 +7,7 @@ const ProjectAssignedOrgs = () => { const { data, isLoading, isError, error } = useProjectAssignedOrganizations(selectedProject); - const contactList = [ + const orgList = [ { key: "name", label: "Organization Name", @@ -23,6 +23,16 @@ const ProjectAssignedOrgs = () => { ), align: "text-start", + }, + { + key: "service", + label: "Service Name", + getValue: (org) => ( +
+ N/A +
+ ), + align: "text-start", }, { key: "sprid", @@ -61,7 +71,7 @@ const ProjectAssignedOrgs = () => {
- {contactList.map((col) => ( + {orgList.map((col) => ( @@ -72,7 +82,7 @@ const ProjectAssignedOrgs = () => { {Array.isArray(data) && data.length > 0 ? ( data.map((row, i) => ( - {contactList.map((col) => ( + {orgList.map((col) => ( @@ -82,7 +92,7 @@ const ProjectAssignedOrgs = () => { ) : ( {Array.isArray(data.data) && data.data.length > 0 ? data.data.map((row, i) => ( - - {contactList.map((col) => ( - - ))} - + {contactList.map((col) => ( + - - )) + ))} + + + )) : null}
{col.label}
{col.getValue(row)}
Not Assigned yet diff --git a/src/components/Project/ProjectOrganizations.jsx b/src/components/Project/ProjectOrganizations.jsx index d18a9375..b5a91fa6 100644 --- a/src/components/Project/ProjectOrganizations.jsx +++ b/src/components/Project/ProjectOrganizations.jsx @@ -7,12 +7,12 @@ const ProjectOrganizations = () => { const { onOpen, startStep, flowType } = useOrganizationModal(); const selectedProject = useSelectedProject(); return ( -
+
+ ):(

Not Yet Added

)} +
-
diff --git a/src/components/master/Services/ManageGroup.jsx b/src/components/master/Services/ManageGroup.jsx index 56ec49f5..04342777 100644 --- a/src/components/master/Services/ManageGroup.jsx +++ b/src/components/master/Services/ManageGroup.jsx @@ -53,7 +53,7 @@ const ManageGroup = ({ group = null, whichService = null, close }) => { let isPending = isCreating || isUpdating; return ( - +
- {col.getValue(row)} - -
- -
+
+ {col.getValue(row)}
+
+ + onOpen({ startStep: 3, orgData: row }) + } + > + + +
+
From 57edd92dce40be645d14e8e32625fc94ae9e8ca3 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Thu, 25 Sep 2025 12:36:55 +0530 Subject: [PATCH 13/57] fixed blocking of dailyprogress by using pagination --- .../TaskReportFilterPanel.jsx | 11 + .../DailyProgressRport/TaskReportList.jsx | 292 ++++++++++++++++++ .../TaskRepprtListSkeleton.jsx | 62 ++++ src/components/common/HoverPopup.jsx | 65 ++++ src/hooks/useTasks.js | 23 +- .../DailyProgrssReport.jsx | 114 +++++++ .../authentication/TenantSelectionPage.jsx | 2 +- src/repositories/AuthRepository.jsx | 2 +- src/repositories/TaskRepository.jsx | 24 +- src/router/AppRoutes.jsx | 3 +- 10 files changed, 569 insertions(+), 29 deletions(-) create mode 100644 src/components/DailyProgressRport/TaskReportFilterPanel.jsx create mode 100644 src/components/DailyProgressRport/TaskReportList.jsx create mode 100644 src/components/DailyProgressRport/TaskRepprtListSkeleton.jsx create mode 100644 src/components/common/HoverPopup.jsx create mode 100644 src/pages/DailyProgressReport/DailyProgrssReport.jsx diff --git a/src/components/DailyProgressRport/TaskReportFilterPanel.jsx b/src/components/DailyProgressRport/TaskReportFilterPanel.jsx new file mode 100644 index 00000000..0216a7e7 --- /dev/null +++ b/src/components/DailyProgressRport/TaskReportFilterPanel.jsx @@ -0,0 +1,11 @@ +import React from 'react' + +const TaskReportFilterPanel = () => { + return ( +
+ +
+ ) +} + +export default TaskReportFilterPanel diff --git a/src/components/DailyProgressRport/TaskReportList.jsx b/src/components/DailyProgressRport/TaskReportList.jsx new file mode 100644 index 00000000..254b1ddf --- /dev/null +++ b/src/components/DailyProgressRport/TaskReportList.jsx @@ -0,0 +1,292 @@ +import React, { useState, useEffect, useMemo } from "react"; +import { useTaskList } from "../../hooks/useTasks"; +import { useSelectedProject } from "../../slices/apiDataManager"; +import { useProjectName } from "../../hooks/useProjects"; +import DailyProgrssReport, { + useDailyProgrssContext, +} from "../../pages/DailyProgressReport/DailyProgrssReport"; +import { useDispatch } from "react-redux"; +import { setProjectId } from "../../slices/localVariablesSlice"; +import { + APPROVE_TASK, + ASSIGN_REPORT_TASK, + ITEMS_PER_PAGE, +} from "../../utils/constants"; +import { formatNumber, formatUTCToLocalTime } from "../../utils/dateUtils"; +import { useHasUserPermission } from "../../hooks/useHasUserPermission"; +import Pagination from "../common/Pagination"; +import { TaskReportListSkeleton } from "./TaskRepprtListSkeleton"; +import HoverPopup from "../common/HoverPopup"; + +const TaskReportList = () => { + const [currentPage, setCurrentPage] = useState(1); + const [filters, setFilters] = useState({ + selectedBuilding: "", + selectedFloors: [], + selectedActivities: [], + }); + const dispatch = useDispatch(); + const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK); + const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK); + + const { service, openModal, closeModal } = useDailyProgrssContext(); + const selectedProject = useSelectedProject(); + const { projectNames } = useProjectName(); + + const { data, isLoading, isError, error } = useTaskList( + selectedProject, + ITEMS_PER_PAGE, + currentPage, + service + ); + +const ProgrssReportColumn = [ + { + key: "activity", + label: "Activity", + getValue: (task) => task.workItem.activityMaster?.activityName || "N/A", + align: "text-start", + }, + { + key: "assigned", + label: "Total Assigned", + getValue: (task) => task.plannedTask ?? "N/A", + align: "text-start", + }, + { + key: "completed", + label: "Completed", + getValue: (task) => task.completedTask ?? "N/A", + align: "text-start", + }, + { + key: "assignAt", + label: "Assign Date", + getValue: (task) => + task.assignmentDate ? formatUTCToLocalTime(task.assignmentDate) : "N/A", + align: "text-start", + }, + { + key: "team", + label: "Team", + getValue: (task) => + task.teamMembers?.map((m) => `${m.firstName} ${m.lastName}`).join(", ") || + "N/A", + align: "text-start", + }, + ]; + + const paginate = (page) => { + if (page >= 1 && page <= (data?.totalPages ?? 1)) { + setCurrentPage(page); + } + }; + + useEffect(() => { + if (!selectedProject && projectNames.length > 0) { + dispatch(setProjectId(projectNames[0].id)); + } + }, [selectedProject, projectNames, dispatch]); + + useEffect(() => { + setFilters({ + selectedBuilding: "", + selectedFloors: [], + selectedActivities: [], + }); + }, [selectedProject]); + + // Filter and Group wise data + + const filteredTasks = useMemo(() => { + if (!data?.data) return []; + return data?.data.filter((task) => { + const { selectedBuilding, selectedFloors, selectedActivities } = filters; + + if ( + selectedBuilding && + task?.workItem?.workArea?.floor?.building?.name !== selectedBuilding + ) + return false; + if ( + selectedFloors.length > 0 && + !selectedFloors.includes(task?.workItem?.workArea?.floor?.floorName) + ) + return false; + if ( + selectedActivities.length > 0 && + !selectedActivities.includes( + task?.workItem?.activityMaster?.activityName + ) + ) + return false; + + return true; + }); + }, [data?.data, filters, currentPage]); + + const groupedTasks = useMemo(() => { + const groups = {}; + filteredTasks.forEach((task) => { + const date = task.assignmentDate.split("T")[0]; + if (!groups[date]) groups[date] = []; + groups[date].push(task); + }); + return Object.keys(groups) + .sort((a, b) => new Date(b) - new Date(a)) + .map((date) => ({ date, tasks: groups[date] })); + }, [filteredTasks, paginate, currentPage, selectedProject]); + + const renderTeamMembers = (task, refIndex) => ( +
+ ${task.teamMembers + .map( + (m) => ` +
+
+ + ${m?.firstName?.charAt(0) || ""}${ + m?.lastName?.charAt(0) || "" + } + +
+ ${m.firstName} ${m.lastName} +
` + ) + .join("")} +
+ `} + > + {task.teamMembers.slice(0, 3).map((m) => ( +
+ + {m?.firstName.slice(0, 1)} + +
+ ))} + {task.teamMembers.length > 3 && ( +
+ + +{task.teamMembers.length - 3} + +
+ )} +
+ ); + + if (isLoading) return ; + if (isError) return
Loading....
; + return ( +
+ + + + + + + + + + + + + {groupedTasks.length === 0 && ( + + + + )} + + {groupedTasks.map(({ date, tasks }) => ( + + + + + {tasks.map((task, idx) => ( + + + + + + + + + ))} + + ))} + +
ActivityTotal Pending + + Reported/Planned{" "} + This shows the reported versus planned tasks for each activity. on that Date

} + > + +
+
+
Assign DateTeamActions
+ No reports available +
+ {formatUTCToLocalTime(date)} +
+
+ {task.workItem.activityMaster?.activityName || "No Activity Name"} +
+
+ {task.workItem.workArea?.floor?.building?.name} ›{" "} + {task.workItem.workArea?.floor?.floorName} ›{" "} + {task.workItem.workArea?.areaName} +
+
+ {formatNumber(task.workItem.plannedWork)} + {`${formatNumber(task.completedTask)} / ${formatNumber(task.plannedTask)}`}{formatUTCToLocalTime(task.assignmentDate)}{renderTeamMembers(task, idx)} +
+ {ReportTaskRights && !task.reportedDate && ( + + )} + {ApprovedTaskRights && task.reportedDate && !task.approvedBy && ( + + )} + +
+
+ {data?.data?.length > 0 && ( + + )} +
+ ); +}; + +export default TaskReportList; diff --git a/src/components/DailyProgressRport/TaskRepprtListSkeleton.jsx b/src/components/DailyProgressRport/TaskRepprtListSkeleton.jsx new file mode 100644 index 00000000..5580e924 --- /dev/null +++ b/src/components/DailyProgressRport/TaskRepprtListSkeleton.jsx @@ -0,0 +1,62 @@ + +const SkeletonLine = ({ height = 20, width = "100%", className = "" }) => ( +
+); +export const TaskReportListSkeleton = () => { + const skeletonRows = 8; // Number of placeholder rows + + return ( +
+ + + + + + + + + + + + + {[...Array(skeletonRows)].map((_, idx) => ( + + + + + + + + + ))} + +
ActivityAssignedCompletedAssign OnTeamActions
+ + + + + + + + + +
+ {[...Array(3)].map((_, i) => ( + + ))} +
+
+
+ + +
+
+
+ ); +}; diff --git a/src/components/common/HoverPopup.jsx b/src/components/common/HoverPopup.jsx new file mode 100644 index 00000000..ddf0a062 --- /dev/null +++ b/src/components/common/HoverPopup.jsx @@ -0,0 +1,65 @@ +import React, { useEffect, useRef, useState } from "react"; + +const HoverPopup = ({ title, content, children }) => { + const [visible, setVisible] = useState(false); + const triggerRef = useRef(null); + const popupRef = useRef(null); + + // Toggle on hover or click + const handleMouseEnter = () => setVisible(true); + const handleClick = () => setVisible((prev) => !prev); + + // Hide on outside click + useEffect(() => { + const handleDocumentClick = (e) => { + if ( + !popupRef.current?.contains(e.target) && + !triggerRef.current?.contains(e.target) + ) { + setVisible(false); + } + }; + + if (visible) { + document.addEventListener("click", handleDocumentClick); + } + + return () => { + document.removeEventListener("click", handleDocumentClick); + }; + }, [visible]); + + return ( +
+ {children} + + {visible && ( +
+ {title &&
{title}
} +
{content}
+
+ )} +
+ ); +}; + +export default HoverPopup; + diff --git a/src/hooks/useTasks.js b/src/hooks/useTasks.js index b793ced4..5073c115 100644 --- a/src/hooks/useTasks.js +++ b/src/hooks/useTasks.js @@ -8,30 +8,25 @@ import { useSelector } from "react-redux"; // ---------Query--------------------------------- -export const useTaskList = (projectId, dateFrom, toDate) => { - const enabled = !!projectId && !!dateFrom && !!toDate; +export const useTaskList = (projectId, pageSize, pageNumber, serviceId, filter) => { - const { - data: TaskList = [], - isLoading: loading, - error, - refetch, - } = useQuery({ - queryKey: ["taskList", projectId, dateFrom, toDate], + return useQuery({ + queryKey: ["taskList", projectId, pageSize, pageNumber, serviceId, filter], queryFn: async () => { const response = await TasksRepository.getTaskList( projectId, - dateFrom, - toDate + pageSize, + pageNumber, + serviceId, + filter ); return response.data; }, - enabled, + enabled:!!projectId }); - - return { TaskList, loading, error, refetch }; }; + export const useTaskById = (TaskId) => { const { data: Task = null, diff --git a/src/pages/DailyProgressReport/DailyProgrssReport.jsx b/src/pages/DailyProgressReport/DailyProgrssReport.jsx new file mode 100644 index 00000000..72b360b4 --- /dev/null +++ b/src/pages/DailyProgressReport/DailyProgrssReport.jsx @@ -0,0 +1,114 @@ +import React, { createContext, useContext, useEffect, useState } from "react"; +import Breadcrumb from "../../components/common/Breadcrumb"; +import { useServices } from "../../hooks/masterHook/useMaster"; +import TaskReportList from "../../components/DailyProgressRport/TaskReportList"; +import GlobalModel from "../../components/common/GlobalModel"; +import ReportTaskComments from "../../components/Activities/ReportTaskComments"; +import ReportTask from "../../components/Activities/ReportTask"; +import TaskReportFilterPanel from "../../components/DailyProgressRport/TaskReportFilterPanel"; +import { useFab } from "../../Context/FabContext"; + +const DailyProgrssContext = createContext(); +export const useDailyProgrssContext = () => { + const context = useContext(DailyProgrssContext); + if (!context) { + throw new Error( + "useDailyTaskContext must be used within a DailyTaskProvider" + ); + } + return context; +}; + +const DailyProgrssReport = () => { + const [service, setService] = useState(""); + + const { setOffcanvasContent, setShowTrigger } = useFab(); + const { data, isLoading, isError, error } = useServices(); + + const [modal, setModal] = useState({ type: null, data: null }); + + const openModal = (type, data = null) => setModal({ type, data }); + const closeModal = () => setModal({ type: null, data: null }); + + const contextObj = { + service, + openModal, + closeModal, + }; + + useEffect(() => { + setShowTrigger(true); + setOffcanvasContent("Report Filter", ); + + return () => { + setShowTrigger(false); + setOffcanvasContent("", null); + }; + }, []); + return ( +
+ + {modal.type === "report" && ( + + + + )} + {modal.type === "comments" && ( + + { + if (isSubTask) openModal("subtask", modal.data.task); + else closeModal(); + }} + closeModal={closeModal} + /> + + )} + {modal.type === "subtask" && ( + + + + )} + + + +
+
+ +
+
+ +
+
+
+
+ ); +}; + +export default DailyProgrssReport; diff --git a/src/pages/authentication/TenantSelectionPage.jsx b/src/pages/authentication/TenantSelectionPage.jsx index 349c054c..22b72292 100644 --- a/src/pages/authentication/TenantSelectionPage.jsx +++ b/src/pages/authentication/TenantSelectionPage.jsx @@ -23,7 +23,7 @@ const TenantSelectionPage = () => { useEffect(() => { if (localStorage.getItem("ctnt")) { - navigate("/dashboard"); + chooseTenant(localStorage.getItem("ctnt")) } }, [navigate]); diff --git a/src/repositories/AuthRepository.jsx b/src/repositories/AuthRepository.jsx index cf3f6e31..700020fd 100644 --- a/src/repositories/AuthRepository.jsx +++ b/src/repositories/AuthRepository.jsx @@ -2,7 +2,7 @@ import { api } from "../utils/axiosClient"; const AuthRepository = { // Public routes (no auth token required) - login: (data) => api.postPublic("/api/auth/login/v1", data), + login: (data) => api.postPublic("/api/auth/login", data), refreshToken: (data) => api.postPublic("/api/auth/refresh-token", data), forgotPassword: (data) => api.postPublic("/api/auth/forgot-password", data), resetPassword: (data) => api.postPublic("/api/auth/reset-password", data), diff --git a/src/repositories/TaskRepository.jsx b/src/repositories/TaskRepository.jsx index 40ad8750..9afc47a0 100644 --- a/src/repositories/TaskRepository.jsx +++ b/src/repositories/TaskRepository.jsx @@ -1,25 +1,25 @@ import { api } from "../utils/axiosClient"; export const TasksRepository = { - getTaskList: (id, fromdate = null, todate = null) => { - let url = `api/task/list?projectId=${id}`; + getTaskList: (projectId, pageSize, pageNumber, serviceId, filter) => { + const payloadJsonString = JSON.stringify(filter); + let url = `api/task/list?projectId=${projectId}&pageSize=${pageSize}&pageNumber=${pageNumber}`; - if (fromdate) { - url += `&dateFrom=${fromdate}`; + if (serviceId) { + url += `&serviceId=${serviceId}`; } - - if (todate) { - url += `&dateTo=${todate}`; + if (filter && Object.keys(filter).length > 0) { + const payloadJsonString = encodeURIComponent(JSON.stringify(filter)); + url += `&filter=${payloadJsonString}`; } return api.get(url); }, - getTaskById:(id)=>api.get(`/api/task/get/${id}`), + getTaskById: (id) => api.get(`/api/task/get/${id}`), reportTask: (data) => api.post("api/task/report", data), - taskComments: ( data ) => api.post( "api/task/comment", data ), - auditTask: ( data ) => api.post( '/api/task/approve', data ), - - assignTask:(data) =>api.post('/api/task/assign',data) + taskComments: (data) => api.post("api/task/comment", data), + auditTask: (data) => api.post("/api/task/approve", data), + assignTask: (data) => api.post("/api/task/assign", data), }; diff --git a/src/router/AppRoutes.jsx b/src/router/AppRoutes.jsx index 70052cad..9c30e768 100644 --- a/src/router/AppRoutes.jsx +++ b/src/router/AppRoutes.jsx @@ -52,6 +52,7 @@ import CreateTenant from "../pages/Tenant/CreateTenant"; import OrganizationPage from "../pages/Organization/OrganizationPage"; import LandingPage from "../pages/Home/LandingPage"; import TenantSelectionPage from "../pages/authentication/TenantSelectionPage"; +import DailyProgrssReport from "../pages/DailyProgressReport/DailyProgrssReport"; const router = createBrowserRouter( [ { @@ -89,7 +90,7 @@ const router = createBrowserRouter( { path: "/directory", element: }, { path: "/inventory", element: }, { path: "/activities/attendance", element: }, - { path: "/activities/records/:projectId?", element: }, + { path: "/activities/records/:projectId?", element: }, { path: "/activities/task", element: }, { path: "/activities/reports", element: }, { path: "/gallary", element: }, From 92b1531b75ed383d762d88e0b3ea141403e567dc Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Thu, 25 Sep 2025 14:11:04 +0530 Subject: [PATCH 14/57] daily task planning filtering according to service --- .../Project/Infrastructure/Floor.jsx | 3 +- .../Project/Infrastructure/InfraTable.jsx | 3 +- .../Project/Infrastructure/WorkArea.jsx | 13 +- src/components/Project/ProjectInfra.jsx | 134 +++++--- src/hooks/useProjects.js | 10 + src/pages/Activities/DailyTask.jsx | 289 ------------------ src/pages/Activities/TaskPlannng.jsx | 90 +++--- .../DailyProgrssReport.jsx | 4 +- src/router/AppRoutes.jsx | 1 - src/slices/globalVariablesSlice.jsx | 6 +- 10 files changed, 151 insertions(+), 402 deletions(-) delete mode 100644 src/pages/Activities/DailyTask.jsx diff --git a/src/components/Project/Infrastructure/Floor.jsx b/src/components/Project/Infrastructure/Floor.jsx index 0f7f00f7..2ee5d84b 100644 --- a/src/components/Project/Infrastructure/Floor.jsx +++ b/src/components/Project/Infrastructure/Floor.jsx @@ -1,6 +1,6 @@ import React from "react"; import WorkArea from "./WorkArea"; -const Floor = ({ floor, workAreas, forBuilding,serviceId }) => { +const Floor = ({ floor, workAreas, forBuilding }) => { return ( {workAreas && workAreas.length > 0 ? ( @@ -10,7 +10,6 @@ const Floor = ({ floor, workAreas, forBuilding,serviceId }) => { key={workArea.id} workArea={workArea} floor={floor} - serviceId={serviceId} /> )) ) : ( diff --git a/src/components/Project/Infrastructure/InfraTable.jsx b/src/components/Project/Infrastructure/InfraTable.jsx index 3805857f..cb6b6cb0 100644 --- a/src/components/Project/Infrastructure/InfraTable.jsx +++ b/src/components/Project/Infrastructure/InfraTable.jsx @@ -11,7 +11,7 @@ import { getCachedData, } from "../../../slices/apiDataManager"; -const InfraTable = ({ buildings, projectId, serviceId }) => { +const InfraTable = ({ buildings, projectId }) => { const [projectBuilding, setProjectBuilding] = useState([]); const [expandedBuildings, setExpandedBuildings] = useState([]); const [showFloorModal, setShowFloorModal] = useState(false); @@ -90,7 +90,6 @@ const InfraTable = ({ buildings, projectId, serviceId }) => { forBuilding={building} floor={floor} workAreas={floor.workAreas} - serviceId={serviceId} /> )) ) : ( diff --git a/src/components/Project/Infrastructure/WorkArea.jsx b/src/components/Project/Infrastructure/WorkArea.jsx index b6fc3e35..c38af26d 100644 --- a/src/components/Project/Infrastructure/WorkArea.jsx +++ b/src/components/Project/Infrastructure/WorkArea.jsx @@ -1,7 +1,7 @@ import React, { useEffect, useState } from "react"; import WorkItem from "./WorkItem"; -import { useProjectDetails, useProjectTasks } from "../../../hooks/useProjects"; -import { cacheData } from "../../../slices/apiDataManager"; +import { useCurrentService, useProjectDetails, useProjectTasks } from "../../../hooks/useProjects"; +import { cacheData, useSelectedProject } from "../../../slices/apiDataManager"; import { useDispatch, useSelector } from "react-redux"; import { refreshData } from "../../../slices/localVariablesSlice"; import ProjectRepository from "../../../repositories/ProjectRepository"; @@ -15,9 +15,11 @@ import { import { useParams } from "react-router-dom"; import ProgressBar from "../../common/ProgressBar"; import {formatNumber} from "../../../utils/dateUtils"; +import { useServices } from "../../../hooks/masterHook/useMaster"; -const WorkArea = ({ workArea, floor, forBuilding,serviceId = null }) => { - const selectedProject = useSelector((store) => store.localVariables.projectId); +const WorkArea = ({ workArea, floor, forBuilding }) => { + const selectedProject = useSelectedProject() + const selectedService = useCurrentService() const { projects_Details, loading } = useProjectDetails(selectedProject); const [IsExpandedArea, setIsExpandedArea] = useState(false); const dispatch = useDispatch(); @@ -25,8 +27,7 @@ const WorkArea = ({ workArea, floor, forBuilding,serviceId = null }) => { const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA); const ManageAndAssignTak = useHasUserPermission(ASSIGN_REPORT_TASK); - - const { ProjectTaskList, isLoading } = useProjectTasks(workArea.id,serviceId, IsExpandedArea); + const { ProjectTaskList, isLoading } = useProjectTasks(workArea.id,selectedService, IsExpandedArea); const [workAreaStatus, setWorkAreaStatus] = useState({ completed: 0, diff --git a/src/components/Project/ProjectInfra.jsx b/src/components/Project/ProjectInfra.jsx index cd7fafbc..29a0b283 100644 --- a/src/components/Project/ProjectInfra.jsx +++ b/src/components/Project/ProjectInfra.jsx @@ -17,33 +17,37 @@ import { getCachedData, useSelectedProject, } from "../../slices/apiDataManager"; -import { useProjectAssignedServices, useProjectDetails, useProjectInfra } from "../../hooks/useProjects"; +import { + useCurrentService, + useProjectAssignedServices, + useProjectDetails, + useProjectInfra, +} from "../../hooks/useProjects"; import { useDispatch, useSelector } from "react-redux"; import { refreshData } from "../../slices/localVariablesSlice"; import eventBus from "../../services/eventBus"; import { useParams } from "react-router-dom"; import GlobalModel from "../common/GlobalModel"; +import { setService } from "../../slices/globalVariablesSlice"; const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => { const projectId = useSelectedProject(); + const selectedService = useCurrentService(); const reloadedData = useSelector((store) => store.localVariables.reload); const [expandedBuildings, setExpandedBuildings] = useState([]); - const { projectInfra, isLoading, error } = useProjectInfra(projectId) + const { projectInfra, isLoading, error } = useProjectInfra(projectId); const { projects_Details, refetch, loading } = useProjectDetails(data?.id); const [project, setProject] = useState(projects_Details); const ManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA); - const ManageTask = useHasUserPermission(MANAGE_TASK) + const ManageTask = useHasUserPermission(MANAGE_TASK); const [showModalFloor, setshowModalFloor] = useState(false); const [showModalWorkArea, setshowModalWorkArea] = useState(false); const [showModalTask, setshowModalTask] = useState(false); const [showModalBuilding, setshowModalBuilding] = useState(false); const dispatch = useDispatch(); - const { data: assignedServices, isLoading: servicesLoading } = useProjectAssignedServices(projectId); + const { data: assignedServices, isLoading: servicesLoading } = + useProjectAssignedServices(projectId); - const [selectedService, setSelectedService] = useState(""); - const handleServiceChange = (e) => { - setSelectedService(e.target.value); - }; useEffect(() => { setProject(projectInfra); @@ -51,34 +55,58 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => { const signalRHandler = (response) => { setProject(response); - } + }; return ( <> - {showModalBuilding && setshowModalBuilding(false)}> - setshowModalBuilding(false)} - /> - } - {showModalFloor && setshowModalFloor(false)}> - setshowModalFloor(false)} - /> - } - {showModalWorkArea && setshowModalWorkArea(false)} > - setshowModalWorkArea(false)} - /> - } - {showModalTask && ( setshowModalTask(false)}> - setshowModalTask(false)} - /> - )} + {showModalBuilding && ( + setshowModalBuilding(false)} + > + setshowModalBuilding(false)} + /> + + )} + {showModalFloor && ( + setshowModalFloor(false)} + > + setshowModalFloor(false)} + /> + + )} + {showModalWorkArea && ( + setshowModalWorkArea(false)} + > + setshowModalWorkArea(false)} + /> + + )} + {showModalTask && ( + setshowModalTask(false)} + > + setshowModalTask(false)} + /> + + )}
@@ -88,8 +116,9 @@ const ProjectInfra = ({ data, onDataChange, eachSiteEngineer }) => { className="dataTables_length text-start py-2 px-6 col-md-4 col-12" id="DataTables_Table_0_length" > - {!servicesLoading && assignedServices?.length > 0 && ( - assignedServices.length > 1 ? ( + {!servicesLoading && + assignedServices?.length > 0 && + (assignedServices.length > 1 ? ( - ) : ( -
{assignedServices[0].name}
- ) - )} -
-
- - {/* --- Right: DateRangePicker --- */} -
- - -
- -
- {/* --- Table --- */} -
- - - - - - - - - - - - - {taskLoading && ( - - - - )} - {!taskLoading && groupedTasks.length === 0 && ( - - - - )} - {!taskLoading && - groupedTasks.map(({ date, tasks }) => ( - - - - - {tasks.map((task, idx) => ( - - - - - - - - - ))} - - ))} - -
ActivityAssignedCompletedAssign OnTeamActions
- -
- No reports available for the selected date range. -
{formatUTCToLocalTime(date)}
-
{task.workItem.activityMaster?.activityName || "No Activity Name"}
-
- {task.workItem.workArea?.floor?.building?.name} › {task.workItem.workArea?.floor?.floorName} › {task.workItem.workArea?.areaName} -
-
{formatNumber(task.plannedTask)} / {formatNumber(task.workItem.plannedWork - task.workItem.completedWork)}{task.completedTask}{formatUTCToLocalTime(task.assignmentDate)}{renderTeamMembers(task, idx)} -
- {ReportTaskRights && !task.reportedDate && ( - - )} - {ApprovedTaskRights && task.reportedDate && !task.approvedBy && ( - - )} - -
-
-
-
-
-
- - ); -}; -export default DailyTask; \ No newline at end of file diff --git a/src/pages/Activities/TaskPlannng.jsx b/src/pages/Activities/TaskPlannng.jsx index c9356dfd..e3e74f9c 100644 --- a/src/pages/Activities/TaskPlannng.jsx +++ b/src/pages/Activities/TaskPlannng.jsx @@ -1,32 +1,34 @@ import React, { useEffect, useState } from "react"; import Breadcrumb from "../../components/common/Breadcrumb"; import InfraPlanning from "../../components/Activities/InfraPlanning"; -import { useProjectName } from "../../hooks/useProjects"; +import { useCurrentService, useProjectName } from "../../hooks/useProjects"; import { useDispatch } from "react-redux"; import { setProjectId } from "../../slices/localVariablesSlice"; import { useSelectedProject } from "../../slices/apiDataManager"; import { useProjectAssignedServices } from "../../hooks/useProjects"; +import { setService } from "../../slices/globalVariablesSlice"; -const TaskPlannng = () => { +const TaskPlanning = () => { const selectedProject = useSelectedProject(); + const selectedService = useCurrentService(); const dispatch = useDispatch(); + const { projectNames = [], loading: projectLoading } = useProjectName(); - // Service dropdown state - const { data: assignedServices, isLoading: servicesLoading } = + const { data, isLoading: servicesLoading } = useProjectAssignedServices(selectedProject); - const [selectedService, setSelectedService] = useState(""); + // Set default project if none selected useEffect(() => { - if (!selectedProject && projectNames?.length > 0) { + if (!selectedProject && projectNames.length > 0) { dispatch(setProjectId(projectNames[0]?.id)); } }, [projectNames, selectedProject, dispatch]); - const handleServiceChange = (e) => { - setSelectedService(e.target.value); - }; - + // Loading state + if (projectLoading) { + return
Loading...
; + } return (
{ ]} /> -
-
- {/* Service Dropdown */} - -
- {!servicesLoading && assignedServices?.length > 0 && ( - assignedServices.length > 1 ? ( - - ) : ( -
{assignedServices[0].name}
- ) - )} -
- - - {/* Infra Planning Component */} - {selectedProject ? ( - +
+
+ {data?.length === 0 ? ( +

Service not assigned

) : ( -
Please Select Project
+ )}
+ + {/* Planning Component */} + {selectedProject ? ( + + ) : ( +
Please select a project
+ )}
); }; -export default TaskPlannng; +export default TaskPlanning; diff --git a/src/pages/DailyProgressReport/DailyProgrssReport.jsx b/src/pages/DailyProgressReport/DailyProgrssReport.jsx index 72b360b4..79147d5a 100644 --- a/src/pages/DailyProgressReport/DailyProgrssReport.jsx +++ b/src/pages/DailyProgressReport/DailyProgrssReport.jsx @@ -83,7 +83,7 @@ const DailyProgrssReport = () => {
{
)} + {/* Activity Group (Organization) Selection */} + {selectedService && ( +
+ + +
+ )} + {/* Activity Selection */} - {selectedWorkArea && ( + {selectedOrg && (
+ {servicesLoading && } {assignedServices?.map((service) => ( ))} - {errors.buildingID && ( -

{errors.buildingID.message}

- )}
)} @@ -225,43 +239,36 @@ const TaskModel = ({ project, onSubmit, onClose }) => { + {errors.activityGroupId &&

{errors.activityGroupId.message}

}
)} + {/* Activity Selection */} - {selectedOrg && ( + {selectedGroup && (
- - {activityData.map((a) => ( - + {activitiesLoading && } + {activities?.map((a) => ( + ))} - {errors.activityID && ( -

{errors.activityID.message}

- )} + {errors.activityID &&

{errors.activityID.message}

}
)} - - {selectedWorkArea && (
From d2b10495bd45fd4904cfa40bac19952bb6172179 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Thu, 25 Sep 2025 11:19:36 +0530 Subject: [PATCH 17/57] Added Services Column in Edit activity modal --- .../Infrastructure/EditActivityModal.jsx | 79 +++++++++++-------- .../Project/Infrastructure/TaskModel.jsx | 9 ++- 2 files changed, 52 insertions(+), 36 deletions(-) diff --git a/src/components/Project/Infrastructure/EditActivityModal.jsx b/src/components/Project/Infrastructure/EditActivityModal.jsx index c3d12648..e57b6a30 100644 --- a/src/components/Project/Infrastructure/EditActivityModal.jsx +++ b/src/components/Project/Infrastructure/EditActivityModal.jsx @@ -32,9 +32,8 @@ const EditActivityModal = ({ building, floor, onClose, -} ) => -{ - +}) => { + const { activities, loading: loadingActivities } = useActivitiesMaster(); const { categories, loading: loadingCategories } = useWorkCategoriesMaster(); const [selectedActivity, setSelectedActivity] = useState(null); @@ -57,13 +56,12 @@ const EditActivityModal = ({ comment: "", }, }); -const { mutate: UpdateTask, isPending } = useManageTask({ - onSuccessCallback: (response) => - { - showToast( response?.message, "success" ) - onClose() - } -} ); + const { mutate: UpdateTask, isPending } = useManageTask({ + onSuccessCallback: (response) => { + showToast(response?.message, "success") + onClose() + } + }); @@ -82,34 +80,33 @@ const { mutate: UpdateTask, isPending } = useManageTask({ [categories] ); -useEffect(() => { - if (!workItem) return; - console.log(workItem) - reset({ - activityID: String( - workItem?.workItem?.activityId || workItem?.activityMaster?.id - ), - workCategoryId: String( - workItem?.workItem?.workCategoryId || workItem?.workCategoryMaster?.id - ), - plannedWork: - workItem?.workItem?.plannedWork || workItem?.plannedWork || 0, - completedWork: - workItem?.workItem?.completedWork || workItem?.completedWork || 0, - comment: workItem?.workItem?.description || workItem?.description || "", - }); -}, [workItem?.id,selectedActivity]); + useEffect(() => { + if (!workItem) return; + console.log(workItem) + reset({ + activityID: String( + workItem?.workItem?.activityId || workItem?.activityMaster?.id + ), + workCategoryId: String( + workItem?.workItem?.workCategoryId || workItem?.workCategoryMaster?.id + ), + plannedWork: + workItem?.workItem?.plannedWork || workItem?.plannedWork || 0, + completedWork: + workItem?.workItem?.completedWork || workItem?.completedWork || 0, + comment: workItem?.workItem?.description || workItem?.description || "", + }); + }, [workItem?.id, selectedActivity]); useEffect(() => { const selected = activities?.find((a) => a.id === activityID); - setSelectedActivity( selected || null ); + setSelectedActivity(selected || null); }, [activityID, activities]); - const onSubmitForm = (data) => - { - const payload = { + const onSubmitForm = (data) => { + const payload = { ...data, id: workItem?.workItem?.id ?? workItem?.id, buildingID: building?.id, @@ -125,9 +122,9 @@ useEffect(() => { buildingId: building?.id, floorId: floor?.id, workAreaId: workArea?.id, - previousCompletedWork:completedTask + previousCompletedWork: completedTask }); - } + } return (
@@ -162,14 +159,26 @@ useEffect(() => { disabled />
+
+ + +
+
{ + handleServiceChange(e); + setValue("serviceId", e.target.value); + }} > {servicesLoading && } From 1cd3bf6c7f6bdec25eb5ae407899f394fde4bbfc Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Thu, 25 Sep 2025 14:29:02 +0530 Subject: [PATCH 18/57] updated assigned services poject whenver assigned new service --- src/hooks/useOrganization.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/hooks/useOrganization.js b/src/hooks/useOrganization.js index 2d77438b..dea19874 100644 --- a/src/hooks/useOrganization.js +++ b/src/hooks/useOrganization.js @@ -103,8 +103,12 @@ export const useAssignOrgToProject = (onSuccessCallback) => { mutationFn: async (payload) => await OrganizationRepository.assignOrganizationToProject(payload), onSuccess: (_, variables) => { + const {projectId} = variables useClient.invalidateQueries({ queryKey: ["projectAssignedOrganiztions"], + }); + useClient.invalidateQueries({ + queryKey: ["projectAssignedOrganization", projectId], }); showToast("Organization successfully", "success"); if (onSuccessCallback) onSuccessCallback(); From 1d218056ac802515c1d830e4664a011f1c584284 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Wed, 24 Sep 2025 15:19:39 +0530 Subject: [PATCH 19/57] Incorrect button sequence in Tenant Edit form --- src/components/Tenant/EditProfile.jsx | 30 +++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/components/Tenant/EditProfile.jsx b/src/components/Tenant/EditProfile.jsx index 8e355737..41a99f27 100644 --- a/src/components/Tenant/EditProfile.jsx +++ b/src/components/Tenant/EditProfile.jsx @@ -72,15 +72,15 @@ const EditProfile = ({ TenantId, onClose }) => { return ( -
Edit Tenant
+
Edit Tenant
-
+
{errors.firstName &&
{errors.firstName.message}
}
-
+
{errors.lastName &&
{errors.lastName.message}
} @@ -88,32 +88,32 @@ const EditProfile = ({ TenantId, onClose }) => { -
+
{errors.contactNumber &&
{errors.contactNumber.message}
}
-
+
{errors.domainName &&
{errors.domainName.message}
}
-
+
{errors.taxId &&
{errors.taxId.message}
}
-
+
{errors.officeNumber &&
{errors.officeNumber.message}
}
-
+
{reference.map((org) => ( @@ -134,7 +134,7 @@ const EditProfile = ({ TenantId, onClose }) => { {errors.reference &&
{errors.reference.message}
}
-
+
@@ -154,19 +154,19 @@ const EditProfile = ({ TenantId, onClose }) => { )}
-
+
+ +
+
+ + + {errors.name && ( +

{errors.name.message}

+ )} +
+
+ + - {errors.description && ( -

{errors.description.message}

- )} -
+ {errors.description && ( +

{errors.description.message}

+ )} +
-
- - +
+ + +
); From 2dbf08e3307566f1bf73d7e129154092663da19b Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Fri, 26 Sep 2025 15:02:03 +0530 Subject: [PATCH 32/57] Removing create and Edit activity component. --- src/components/master/CreateActivity.jsx | 235 ----------------------- src/components/master/EditActivity.jsx | 231 ---------------------- 2 files changed, 466 deletions(-) delete mode 100644 src/components/master/CreateActivity.jsx delete mode 100644 src/components/master/EditActivity.jsx diff --git a/src/components/master/CreateActivity.jsx b/src/components/master/CreateActivity.jsx deleted file mode 100644 index 1db2bb79..00000000 --- a/src/components/master/CreateActivity.jsx +++ /dev/null @@ -1,235 +0,0 @@ -import React, { useState, useEffect, useCallback } from "react"; -import { useFieldArray, useForm } from "react-hook-form"; -import { z } from "zod"; -import { zodResolver } from "@hookform/resolvers/zod"; - -import { MasterRespository } from "../../repositories/MastersRepository"; -import { clearApiCacheKey } from "../../slices/apiCacheSlice"; -import { getCachedData, cacheData } from "../../slices/apiDataManager"; -import showToast from "../../services/toastService"; -import { useCreateActivity } from "../../hooks/masterHook/useMaster"; -import Label from "../common/Label"; - -const schema = z.object({ - activityName: z.string().min(1, { message: "Activity Name is required" }), - unitOfMeasurement: z.string().min(1, { message: "Unit of Measurement is required" }), - checkList: z - .array( - z.object({ - description: z.string().min(1, { message: "descriptionlist item cannot be empty" }), - isMandatory: z.boolean().default(false), - id: z.any().default(null), - }) - ) - .optional(), -}); - -const CreateActivity = ({ activity = null, whichGroup = null, close }) => { - const maxDescriptionLength = 255; - const { mutate: createActivity, isPending: isLoading } = useCreateActivity(() => onClose?.()); - - const { - register, - handleSubmit, - control, - setValue, - clearErrors, - setError, - getValues, - reset, - formState: { errors }, - } = useForm({ - resolver: zodResolver(schema), - defaultValues: { - activityName: "", - unitOfMeasurement: "", - checkList: [], - }, - }); - - const { - fields: checkListItems, - append, - remove, - } = useFieldArray({ - control, - name: "checkList", - }); - - const addChecklistItem = useCallback(() => { - const values = getValues("checkList"); - const lastIndex = checkListItems.length - 1; - - if ( - checkListItems.length > 0 && - (!values?.[lastIndex] || values[lastIndex].description.trim() === "") - ) { - setError(`checkList.${lastIndex}.description`, { - type: "manual", - message: "Please fill this checklist item before adding another.", - }); - return; - } - - clearErrors(`checkList.${lastIndex}.description`); - append({ id: null, description: "", isMandatory: false }); - }, [checkListItems, getValues, append, setError, clearErrors]); - - const removeChecklistItem = useCallback((index) => { - remove(index); - }, [remove]); - - const handleChecklistChange = useCallback((index, value) => { - setValue(`checkList.${index}`, value); - }, [setValue]); - - const onSubmit = (formData) => { - createActivity(formData); - }; - - useEffect(()=>{ - if (activity) { - reset({ - activityName: activity.activityName || '', - unitOfMeasurement: activity.unitOfMeasurement || '', - checkList: activity.checkList?.map((check) => ({ - description: check.description || '', - isMandatory: check.isMandatory || false, - })) || [{ description: '', isMandatory: false }], - }); - } - },[activity,reset]) - const handleClose = useCallback(() => { - reset(); - onClose(); - }, [reset, onClose]); - - useEffect(() => { - const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')); - tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); - }, []); - - return ( -
- {/*
Create Activity
*/} -
-
- - - {errors.activityName && ( -

{errors.activityName.message}

- )} -
- -
- - - {errors.unitOfMeasurement && ( -

{errors.unitOfMeasurement.message}

- )} -
- -
-

{checkListItems.length > 0 ? "Check List" : "Add Check List"}

- {checkListItems.length > 0 && ( - - - - - - - - - - {checkListItems.map((item, index) => ( - - - - - - ))} - -
- Name - - Is Mandatory - Action
- - - handleChecklistChange(index, e.target.value) - } - /> - {errors.checkList?.[index]?.description && ( - - {errors.checkList[index]?.description?.message} - - )} - - - - -
- )} - -
- -
- - - -
-
-
- ); -}; - -export default CreateActivity; diff --git a/src/components/master/EditActivity.jsx b/src/components/master/EditActivity.jsx deleted file mode 100644 index 15e85255..00000000 --- a/src/components/master/EditActivity.jsx +++ /dev/null @@ -1,231 +0,0 @@ -import React, { useEffect, useState } from "react"; -import { useForm, useFieldArray } from "react-hook-form"; -import { z } from "zod"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { MasterRespository } from "../../repositories/MastersRepository"; -import showToast from "../../services/toastService"; -import { getCachedData, cacheData } from "../../slices/apiDataManager"; -import { useUpdateActivity } from "../../hooks/masterHook/useMaster"; -import Label from "../common/Label"; - - -const schema = z.object({ - activityName: z.string().min(1, { message: "Activity name is required" }), - unitOfMeasurement: z.string().min(1, { message: "Measurement is required" }), - checkList: z - .array( - z.object({ - id: z.any().default(null), - description: z.string().min(1, { message: "Checklist item cannot be empty" }), - isMandatory: z.boolean().default(false), - }) - ) - .optional(), -}); - - -const UpdateActivity = ({ activity = null, whichService = null, close }) => { - const { mutate: updateActivity, isPending: isLoading } = useUpdateActivity(() => onClose?.()); - - const { - register, - handleSubmit, - control, - setValue, - reset, - setError, - clearErrors, - getValues, - formState: { errors }, - } = useForm({ - resolver: zodResolver(schema), - defaultValues: { - id: activity?.id, - activityName: activity?.activityName, - unitOfMeasurement: activity?.unitOfMeasurement, - checkList: activity?.checkLists || [], - }, - }); - - const { fields: checkListItems, append, remove } = useFieldArray({ - control, - name: "checkList", - }); - - useEffect(() => { - if (activity) { - reset({ - id: activity.id, - activityName: activity.activityName, - unitOfMeasurement: activity.unitOfMeasurement, - checkList: activity.checkLists || [], - }); - } - }, [activity, reset]); - - const addChecklistItem = () => { - const values = getValues("checkList"); - const lastIndex = checkListItems.length - 1; - - if ( - checkListItems.length > 0 && - (!values?.[lastIndex] || values[lastIndex].description.trim() === "") - ) { - setError(`checkList.${lastIndex}.description`, { - type: "manual", - message: "Please fill this checklist item before adding another.", - }); - return; - } - - clearErrors(`checkList.${lastIndex}.description`); - append({ id: null, description: "", isMandatory: false }); - }; - - const removeChecklistItem = (index) => { - remove(index); - }; - - const handleChecklistChange = (index, value) => { - setValue(`checkList.${index}`, value); - }; - - const onSubmit = (formData) => { - const payload = { ...formData, id: activity.id }; - updateActivity({ id: activity.id, payload }); - }; - - - useEffect(() => { - const tooltipTriggerList = Array.from(document.querySelectorAll('[data-bs-toggle="tooltip"]')); - tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); - }, []); - - return ( -
-
- {/* Activity Name */} -
- - - {errors.activityName && ( -
{errors.activityName.message}
- )} -
- - {/* Unit of Measurement */} -
- - - {errors.unitOfMeasurement && ( -
{errors.unitOfMeasurement.message}
- )} -
- - {/* Checklist */} -
-

{checkListItems.length > 0 ? "Check List" : "Add Check List"}

- {checkListItems.length > 0 && ( - - - - - - - - - - {checkListItems.map((item, index) => ( - - - - - - ))} - -
- Name - - Is Mandatory - Action
- - - handleChecklistChange(index, e.target.value) - } - /> - {errors.checkList?.[index]?.description && ( - - {errors.checkList[index]?.description?.message} - - )} - - - - -
- )} - - - -
- - {/* Submit / Cancel */} -
- - - -
-
-
- ); -}; - -export default UpdateActivity; \ No newline at end of file From 1a3890e8372f7266c196e807c34bc96edd7954c9 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Fri, 26 Sep 2025 15:34:08 +0530 Subject: [PATCH 33/57] Removing Create and Activity from MasterModal.jsx file. --- src/components/master/MasterModal.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/master/MasterModal.jsx b/src/components/master/MasterModal.jsx index 2c71ea71..38e1d591 100644 --- a/src/components/master/MasterModal.jsx +++ b/src/components/master/MasterModal.jsx @@ -3,8 +3,6 @@ import CreateRole from "./CreateRole"; import EditRole from "./EditRole"; import CreateJobRole from "./CreateJobRole"; import EditJobRole from "./EditJobRole"; -import CreateActivity from "./CreateActivity"; -import EditActivity from "./EditActivity"; import CreateWorkCategory from "./CreateWorkCategory"; import EditWorkCategory from "./EditWorkCategory"; import CreateCategory from "./CreateContactCategory"; @@ -35,9 +33,7 @@ const MasterModal = ({ modaldata, closeModal }) => { ), "Job Role": , - "Edit-Job Role": , - "Activity": , - "Edit-Activity": , + "Edit-Job Role": , "Work Category": , "Edit-Work Category": , "Contact Category": , From 1fb8eb9ef15a5c44ae7a9bc586f951ffe4103cb0 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Fri, 26 Sep 2025 16:21:37 +0530 Subject: [PATCH 34/57] Create Task popup will open at all the time while submitting. --- src/components/Project/Infrastructure/TaskModel.jsx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Project/Infrastructure/TaskModel.jsx b/src/components/Project/Infrastructure/TaskModel.jsx index efa2006f..4bbf1555 100644 --- a/src/components/Project/Infrastructure/TaskModel.jsx +++ b/src/components/Project/Infrastructure/TaskModel.jsx @@ -112,7 +112,7 @@ const TaskModel = ({ project, onSubmit, onClose }) => { const { mutate: CreateTask, isPending } = useManageTask({ onSuccessCallback: (response) => { showToast(response?.message, "success"); - onClose?.(); + // onClose?.(); }, }); useEffect(() => { @@ -356,9 +356,10 @@ const TaskModel = ({ project, onSubmit, onClose }) => {
From 3fddb686d3cafe79db4fa66d996c02cb5a6f6238 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 14:58:39 +0530 Subject: [PATCH 35/57] change get employee at assign task to employee --- src/components/Project/AssignTask.jsx | 339 ++++++++++++++----------- src/hooks/useProjects.js | 8 + src/repositories/ProjectRepository.jsx | 13 + 3 files changed, 207 insertions(+), 153 deletions(-) diff --git a/src/components/Project/AssignTask.jsx b/src/components/Project/AssignTask.jsx index 0f755176..8c8aab22 100644 --- a/src/components/Project/AssignTask.jsx +++ b/src/components/Project/AssignTask.jsx @@ -1,24 +1,25 @@ import React, { useState, useEffect, useRef, useCallback } from "react"; import { useDispatch, useSelector } from "react-redux"; import { changeMaster } from "../../slices/localVariablesSlice"; -import useMaster from "../../hooks/masterHook/useMaster"; +import useMaster, { useServices } from "../../hooks/masterHook/useMaster"; import { useForm, Controller } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; -import { clearCacheKey, getCachedData } from "../../slices/apiDataManager"; +import { useSelectedProject } from "../../slices/apiDataManager"; import { useEmployeesAllOrByProjectId } from "../../hooks/useEmployees"; import { TasksRepository } from "../../repositories/ProjectRepository"; import showToast from "../../services/toastService"; -import { useProjectDetails } from "../../hooks/useProjects"; +import { + useEmployeeForTaskAssign, + useProjectAssignedOrganizations, + useProjectDetails, +} from "../../hooks/useProjects"; import eventBus from "../../services/eventBus"; import { useCreateTask } from "../../hooks/useTasks"; import Label from "../common/Label"; -const AssignTask = ({ assignData, onClose, setAssigned }) => { - const maxPlanned = - assignData?.workItem?.plannedWork - assignData?.workItem?.completedWork; - - const schema = z.object({ +const TaskSchema = (maxPlanned) => { + return z.object({ selectedEmployees: z .array(z.string()) .min(1, { message: "At least one employee must be selected" }), @@ -37,20 +38,26 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { }) ), }); +}; + +const AssignTask = ({ assignData, onClose, setAssigned }) => { + const planned = assignData?.workItem?.plannedWork || 0; + const completed = assignData?.workItem?.completedWork || 0; + const maxPlanned = planned - completed; const [isHelpVisibleTarget, setIsHelpVisibleTarget] = useState(false); const helpPopupRefTarget = useRef(null); const [isHelpVisible, setIsHelpVisible] = useState(false); + const [selectedService, setSelectedService] = useState(null); + const [selectedOrganization, setSelectedOrganization] = useState(null); + const { mutate: assignTask, isPending: isSubmitting } = useCreateTask({ - onSuccessCallback: () => { - closedModel(); - }, + onSuccessCallback: closedModel, }); const dropdownRef = useRef(null); const [open, setOpen] = useState(false); - // Close dropdown on outside click useEffect(() => { const handleClickOutside = (event) => { if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { @@ -63,48 +70,47 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { const infoRef = useRef(null); const infoRef1 = useRef(null); - - // State for search term const [searchTerm, setSearchTerm] = useState(""); useEffect(() => { if (typeof bootstrap !== "undefined") { - if (infoRef.current) { + infoRef.current && new bootstrap.Popover(infoRef.current, { trigger: "focus", placement: "right", html: true, content: `
Total Pending tasks of the Activity
`, }); - } - if (infoRef1.current) { + infoRef1.current && new bootstrap.Popover(infoRef1.current, { trigger: "focus", placement: "right", html: true, content: `
Target task for today
`, }); - } } else { console.warn("Bootstrap is not available. Popovers might not function."); } }, []); - const selectedProject = useSelector( - (store) => store.localVariables.projectId - ); - const { - employees, - loading: employeeLoading, - recallEmployeeData, - } = useEmployeesAllOrByProjectId(false, selectedProject, false); - const dispatch = useDispatch(); - const { loading } = useMaster(); - const { data: jobRoleData } = useMaster(); - // Changed to an array to hold multiple selected roles + const selectedProject = useSelectedProject(); + const { data: serviceList, isLoading: isServiceLoading } = useServices(); + const { data: organizationList, isLoading: isOrgLoading } = + useProjectAssignedOrganizations(selectedProject); + const { data: employees, isLoading: isEmployeeLoading } = + useEmployeeForTaskAssign( + selectedProject, + selectedService, + selectedOrganization + ); + + const dispatch = useDispatch(); + const { loading, data: jobRoleData } = useMaster(); + const [selectedRoles, setSelectedRoles] = useState(["all"]); const [displayedSelection, setDisplayedSelection] = useState(""); + const { handleSubmit, control, @@ -114,133 +120,98 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { reset, trigger, } = useForm({ - defaultValues: { - selectedEmployees: [], - description: "", - plannedTask: "", - }, - resolver: zodResolver(schema), + defaultValues: { selectedEmployees: [], description: "", plannedTask: "" }, + resolver: zodResolver(TaskSchema(maxPlanned)), }); const handleCheckboxChange = (event, user) => { - const isChecked = event.target.checked; - let updatedSelectedEmployees = watch("selectedEmployees") || []; + const updatedSelectedEmployees = event.target.checked + ? [...(watch("selectedEmployees") || []), user.id].filter( + (v, i, a) => a.indexOf(v) === i + ) + : (watch("selectedEmployees") || []).filter((id) => id !== user.id); - if (isChecked) { - if (!updatedSelectedEmployees.includes(user.id)) { - updatedSelectedEmployees = [...updatedSelectedEmployees, user.id]; - } - } else { - updatedSelectedEmployees = updatedSelectedEmployees?.filter( - (id) => id !== user.id - ); - } setValue("selectedEmployees", updatedSelectedEmployees); trigger("selectedEmployees"); }; useEffect(() => { dispatch(changeMaster("Job Role")); - // Initial state should reflect "All Roles" selected setSelectedRoles(["all"]); }, [dispatch]); - // Modified handleRoleChange to handle multiple selections const handleRoleChange = (event, roleId) => { - // If 'all' is selected, clear other selections - if (roleId === "all") { - setSelectedRoles(["all"]); - } else { - setSelectedRoles((prevSelectedRoles) => { - // If "all" was previously selected, remove it - const newRoles = prevSelectedRoles.filter((role) => role !== "all"); - if (newRoles.includes(roleId)) { - // If role is already selected, unselect it - return newRoles.filter((id) => id !== roleId); - } else { - // If role is not selected, add it - return [...newRoles, roleId]; - } - }); - } + setSelectedRoles((prev) => { + if (roleId === "all") return ["all"]; + const newRoles = prev.filter((r) => r !== "all"); + return newRoles.includes(roleId) + ? newRoles.filter((r) => r !== roleId) + : [...newRoles, roleId]; + }); }; useEffect(() => { - // Update displayedSelection based on selectedRoles if (selectedRoles.includes("all")) { setDisplayedSelection("All Roles"); } else if (selectedRoles.length > 0) { - const selectedRoleNames = selectedRoles.map(roleId => { - const role = jobRoleData?.find(r => String(r.id) === roleId); - return role ? role.name : ''; - }).filter(Boolean); // Filter out empty strings for roles not found - setDisplayedSelection(selectedRoleNames.join(', ')); - } else { - setDisplayedSelection("Select Roles"); - } + setDisplayedSelection( + selectedRoles + .map((id) => jobRoleData?.find((r) => String(r.id) === id)?.name) + .filter(Boolean) + .join(", ") + ); + } else setDisplayedSelection("Select Roles"); }, [selectedRoles, jobRoleData]); + const handleSearchChange = (e) => setSearchTerm(e.target.value); - const handleSearchChange = (event) => { - setSearchTerm(event.target.value); - }; - - // Filter employees first by role, then by search term AND job role name - const filteredEmployees = employees?.filter((emp) => { + const filteredEmployees = employees?.data?.filter((emp) => { const matchesRole = - selectedRoles.includes("all") || selectedRoles.includes(String(emp.jobRoleId)); - // Convert both first and last names and job role name to lowercase for case-insensitive matching - const fullName = `${emp.firstName} ${emp.lastName}`.toLowerCase(); - - const jobRoleName = jobRoleData?.find((role) => role.id === emp.jobRoleId)?.name?.toLowerCase() || ""; - + selectedRoles.includes("all") || + selectedRoles.includes(String(emp.jobRoleId)); const searchLower = searchTerm.toLowerCase(); - // Check if the full name OR job role name includes the search term - const matchesSearch = fullName.includes(searchLower) || jobRoleName.includes(searchLower); - return matchesRole && matchesSearch; + const fullName = `${emp.firstName} ${emp.lastName}`.toLowerCase(); + const jobRoleName = + jobRoleData + ?.find((role) => role.id === emp.jobRoleId) + ?.name?.toLowerCase() || ""; + return ( + matchesRole && + (fullName.includes(searchLower) || jobRoleName.includes(searchLower)) + ); }); - // Determine unique job role IDs from the filtered employees (for dropdown options) - const uniqueJobRoleIdsInFilteredEmployees = new Set( - employees?.map(emp => emp.jobRoleId).filter(Boolean) + const jobRolesForDropdown = jobRoleData?.filter((role) => + new Set(employees?.data?.map((emp) => emp.jobRoleId).filter(Boolean)).has( + role.id + ) ); - // Filter jobRoleData to only include roles present in the uniqueJobRoleIdsInFilteredEmployees - const jobRolesForDropdown = jobRoleData?.filter(role => - uniqueJobRoleIdsInFilteredEmployees.has(role.id) - ); - - // Calculate the count of selected roles for display const selectedRolesCount = selectedRoles.includes("all") - ? 0 // "All Roles" doesn't contribute to a specific count + ? 0 : selectedRoles.length; const onSubmit = (data) => { - const selectedEmployeeIds = data.selectedEmployees; - - const taskTeamWithDetails = selectedEmployeeIds - ?.map((empId) => empId) - ?.filter(Boolean); - const formattedData = { - taskTeam: taskTeamWithDetails, - plannedTask: data.plannedTask, - description: data.description, - assignmentDate: new Date().toISOString(), - workItemId: assignData?.workItem.id, - }; assignTask({ - payload: formattedData, + payload: { + taskTeam: data.selectedEmployees.filter(Boolean), + plannedTask: data.plannedTask, + description: data.description, + assignmentDate: new Date().toISOString(), + workItemId: assignData?.workItem.id, + }, workAreaId: assignData?.workArea?.id, }); }; - const closedModel = () => { + function closedModel() { reset(); onClose(); - }; + } + return (
-

Assign Task

+

Assign Task

@@ -252,7 +223,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { assignData?.workArea?.areaName, assignData?.workItem?.activityMaster?.activityName, ] - .filter(Boolean) // Filter out any undefined/null values + .filter(Boolean) .map((item, index, array) => ( {item} @@ -268,20 +239,64 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {

-
+
+
+
+ +
+
+ +
+
@@ -381,19 +403,19 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { > {selectedRoles?.length > 0 && (
- {employeeLoading ? ( + {isEmployeeLoading ? (

Loading employees...

) : filteredEmployees?.length > 0 ? ( - filteredEmployees.map((emp) => { + filteredEmployees.map((emp,index) => { const jobRole = jobRoleData?.find( (role) => role?.id === emp?.jobRoleId ); return (
@@ -441,7 +463,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { ) : (

- No employees found for the selected role. + No employees found for the selected filter.

)} @@ -456,12 +478,14 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { {watch("selectedEmployees")?.length > 0 && (
- {watch("selectedEmployees")?.map((empId) => { - const emp = employees.find((emp) => emp.id === empId); + {watch("selectedEmployees")?.map((empId,ind) => { + const emp = employees?.data?.find( + (emp) => emp.id === empId + ); return ( emp && ( {emp.firstName} {emp.lastName} @@ -506,7 +530,12 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { {assignData?.workItem?.plannedWork - assignData?.workItem?.completedWork} {" "} - {assignData?.workItem?.activityMaster?.unitOfMeasurement} + + { + assignData?.workItem?.activityMaster + ?.unitOfMeasurement + } +
@@ -537,8 +566,15 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { className="form-control form-control-sm" {...field} /> - - {assignData?.workItem?.activityMaster?.unitOfMeasurement} + + + { + assignData?.workItem?.activityMaster + ?.unitOfMeasurement + } +
)} @@ -546,19 +582,17 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {
{errors.plannedTask && ( -
{errors.plannedTask.message}
+
+ {errors.plannedTask.message} +
)}
- - {/* */} - { {isSubmitting ? "Please Wait" : "Submit"}
-
diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index eeac3c31..bdbd01c6 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -308,6 +308,14 @@ export const useProjectAssignedServices = (projectId) => { }); }; + +export const useEmployeeForTaskAssign = (projectId,serviceId,organizationId)=>{ + return useQuery({ + queryKey:["EmployeeForTaskAssign",projectId,serviceId,organizationId], + queryFn:async()=> await ProjectRepository.getEmployeeForTaskAssign(projectId,serviceId,organizationId) + }) +} + // -- -------------Mutation------------------------------- export const useCreateProject = ({ onSuccessCallback }) => { diff --git a/src/repositories/ProjectRepository.jsx b/src/repositories/ProjectRepository.jsx index 42dde36a..b1b0727f 100644 --- a/src/repositories/ProjectRepository.jsx +++ b/src/repositories/ProjectRepository.jsx @@ -82,6 +82,19 @@ const ProjectRepository = { api.get(`/api/Project/get/assigned/services/${projectId}`), getProjectAssignedOrganizations: (projectId) => api.get(`/api/Project/get/assigned/organization/${projectId}`), + + getEmployeeForTaskAssign: (projectId, serviceId, organizationId) => { + let url = `/api/Project/get/task/team/${projectId}`; + + const params = []; + if (serviceId) params.push(`serviceId=${serviceId}`); + if (organizationId) params.push(`organizationId=${organizationId}`); + + if (params.length > 0) { + url += `?${params.join("&")}`; + } + return api.get(url); + }, }; export const TasksRepository = { From 3233043cf21bee04eeadc63fedfbadd65790570a Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Fri, 26 Sep 2025 11:38:06 +0530 Subject: [PATCH 36/57] initial setup for assign emp to project --- .../Project/Team/TeamAssignToProject.jsx | 42 ++++++++ .../Project/Team/TeamEmployeeList.jsx | 12 +++ src/components/Project/{ => Team}/Teams.jsx | 98 ++++++++++++------- src/hooks/useProjectAccess.js | 15 +-- src/pages/project/ProjectDetails.jsx | 2 +- 5 files changed, 124 insertions(+), 45 deletions(-) create mode 100644 src/components/Project/Team/TeamAssignToProject.jsx create mode 100644 src/components/Project/Team/TeamEmployeeList.jsx rename src/components/Project/{ => Team}/Teams.jsx (85%) diff --git a/src/components/Project/Team/TeamAssignToProject.jsx b/src/components/Project/Team/TeamAssignToProject.jsx new file mode 100644 index 00000000..102b0be7 --- /dev/null +++ b/src/components/Project/Team/TeamAssignToProject.jsx @@ -0,0 +1,42 @@ +import React from "react"; + +const TeamAssignToProject = () => { + return ( +
+

Assign Employee To Project

+
+
+ + +
+ +
+ + +
+
+
+ + +
+ +
+ +
+
+ ); +}; + +export default TeamAssignToProject; diff --git a/src/components/Project/Team/TeamEmployeeList.jsx b/src/components/Project/Team/TeamEmployeeList.jsx new file mode 100644 index 00000000..da62ac91 --- /dev/null +++ b/src/components/Project/Team/TeamEmployeeList.jsx @@ -0,0 +1,12 @@ +import React from 'react' + +const TeamEmployeeList = ({serviceId,organizationId}) => { + const {} = use + return ( +
+ +
+ ) +} + +export default TeamEmployeeList diff --git a/src/components/Project/Teams.jsx b/src/components/Project/Team/Teams.jsx similarity index 85% rename from src/components/Project/Teams.jsx rename to src/components/Project/Team/Teams.jsx index 3e31c0cb..f684347a 100644 --- a/src/components/Project/Teams.jsx +++ b/src/components/Project/Team/Teams.jsx @@ -1,30 +1,32 @@ import React, { useState, useEffect, useCallback } from "react"; -import MapUsers from "./MapUsers"; +import MapUsers from "../MapUsers"; import { Link, NavLink, useNavigate, useParams } from "react-router-dom"; -import showToast from "../../services/toastService"; -import Avatar from "../common/Avatar"; +import showToast from "../../../services/toastService"; +import Avatar from "../../common/Avatar"; import moment from "moment"; -import ProjectRepository from "../../repositories/ProjectRepository"; +import ProjectRepository from "../../../repositories/ProjectRepository"; import { useDispatch, useSelector } from "react-redux"; -import { changeMaster } from "../../slices/localVariablesSlice"; -import useMaster from "../../hooks/masterHook/useMaster"; -import { useHasUserPermission } from "../../hooks/useHasUserPermission"; -import { ASSIGN_TO_PROJECT } from "../../utils/constants"; -import ConfirmModal from "../common/ConfirmModal"; -import eventBus from "../../services/eventBus"; +import { changeMaster } from "../../../slices/localVariablesSlice"; +import useMaster from "../../../hooks/masterHook/useMaster"; +import { useHasUserPermission } from "../../../hooks/useHasUserPermission"; +import { ASSIGN_TO_PROJECT } from "../../../utils/constants"; +import ConfirmModal from "../../common/ConfirmModal"; +import eventBus from "../../../services/eventBus"; import { useEmployeesByProjectAllocated, useManageProjectAllocation, useProjectAssignedServices, -} from "../../hooks/useProjects"; -import { useSelectedProject } from "../../slices/apiDataManager"; +} from "../../../hooks/useProjects"; +import { useSelectedProject } from "../../../slices/apiDataManager"; +import GlobalModel from "../../common/GlobalModel"; +import TeamAssignToProject from "./TeamAssignToProject"; const Teams = () => { const projectId = useSelectedProject(); const dispatch = useDispatch(); - + const [AssigTeam,setAssignTeam] = useState(false) const { data, loading } = useMaster(); const [isModalOpen, setIsModelOpen] = useState(false); const [error, setError] = useState(""); @@ -38,9 +40,9 @@ const Teams = () => { const [searchTerm, setSearchTerm] = useState(""); // State for search term const [selectedService, setSelectedService] = useState(null); - const { data: assignedServices, isLoading: servicesLoading } = useProjectAssignedServices(projectId); - const handleToggleActive = e => setActiveEmployee(e.target.checked); - + const { data: assignedServices, isLoading: servicesLoading } = + useProjectAssignedServices(projectId); + const handleToggleActive = (e) => setActiveEmployee(e.target.checked); const handleServiceChange = (e) => { setSelectedService(e.target.value); @@ -167,7 +169,8 @@ const Teams = () => { const lowercasedSearchTerm = searchTerm.toLowerCase(); const searchedAndFiltered = statusFiltered.filter((item) => { - const fullName = `${item.firstName} ${item.middleName} ${item.lastName}`.toLowerCase(); + const fullName = + `${item.firstName} ${item.middleName} ${item.lastName}`.toLowerCase(); const roleName = getRole(item.jobRoleId).toLowerCase(); const orgName = (item.organizationName || "").toLowerCase(); const serviceName = (item.serviceName || "").toLowerCase(); @@ -183,7 +186,6 @@ const Teams = () => { setFilteredEmployees(searchedAndFiltered); }, [employees, activeEmployee, searchTerm, getRole]); - useEffect(() => { filterAndSearchEmployees(); }, [employees, activeEmployee, searchTerm, filterAndSearchEmployees]); @@ -252,6 +254,13 @@ const Teams = () => { >
+ + {AssigTeam && ( + setAssignTeam(false)}> + + + )} + {IsDeleteModal && ( {
- {!servicesLoading && assignedServices?.length > 0 && ( - assignedServices.length > 1 ? ( + {!servicesLoading && + assignedServices?.length > 0 && + (assignedServices.length > 1 ? (
+ {HasAssignUserPermission && ( + + className={`link-button btn-primary btn-sm `} + // data-bs-toggle="modal" + // data-bs-target="#user-model" + onClick={()=>setAssignTeam(true)} + > + + Assign Employee + + )}
@@ -387,7 +406,9 @@ const Teams = () => { {!activeEmployee && ( {item.reAllocationDate - ? moment(item.reAllocationDate).format("DD-MMM-YYYY") + ? moment(item.reAllocationDate).format( + "DD-MMM-YYYY" + ) : "Present"} )} @@ -410,7 +431,9 @@ const Teams = () => { className="spinner-border spinner-border-sm text-primary" role="status" > - Loading... + + Loading... +
) : ( @@ -424,7 +447,6 @@ const Teams = () => { ))} - )} {!employeeLodaing && filteredEmployees.length === 0 && (
diff --git a/src/hooks/useProjectAccess.js b/src/hooks/useProjectAccess.js index a3ff27ac..d932b157 100644 --- a/src/hooks/useProjectAccess.js +++ b/src/hooks/useProjectAccess.js @@ -6,21 +6,24 @@ import { VIEW_PROJECTS } from "../utils/constants"; import showToast from "../services/toastService"; export const useProjectAccess = (projectId) => { + const navigate = useNavigate(); + const { data: projectPermissions, isLoading, isFetched } = useAllProjectLevelPermissions(projectId); - const canView = useHasUserPermission(VIEW_PROJECTS); - const navigate = useNavigate(); + const canView = useHasUserPermission(VIEW_PROJECTS); + + const loading = isLoading || !isFetched; useEffect(() => { - if (projectId && isFetched && !isLoading && !canView) { + if (projectId && !loading && !canView) { showToast("You don't have permission to view project details", "warning"); navigate("/projects"); } - }, [projectId, isFetched, isLoading, canView, navigate]); + }, [projectId, loading, canView, navigate]); return { canView, - loading: isLoading || !isFetched, + loading, }; -}; +}; \ No newline at end of file diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index 7503f6ce..8d2f8277 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -5,7 +5,7 @@ import { useNavigate } from "react-router-dom"; import ProjectOverview from "../../components/Project/ProjectOverview"; import AboutProject from "../../components/Project/AboutProject"; import ProjectNav from "../../components/Project/ProjectNav"; -import Teams from "../../components/Project/Teams"; +import Teams from "../../components/Project/Team/Teams"; import ProjectInfra from "../../components/Project/ProjectInfra"; import Loader from "../../components/common/Loader"; import WorkPlan from "../../components/Project/WorkPlan"; From 2ef1fcfd1db611f9545fe98ed5525aea38778af3 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 01:15:07 +0530 Subject: [PATCH 37/57] successfullly assigned employe to project --- public/assets/vendor/css/core.css | 4 + .../Project/Team/TeamAssignToProject.jsx | 94 ++-- .../Project/Team/TeamEmployeeList.jsx | 256 ++++++++++- src/components/Project/Team/Teams.jsx | 432 ++++++------------ src/hooks/useOrganization.js | 25 +- src/hooks/useProjects.js | 12 +- src/repositories/OrganizationRespository.jsx | 28 +- src/repositories/ProjectRepository.jsx | 22 +- 8 files changed, 516 insertions(+), 357 deletions(-) diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index 9be75669..5acc33c5 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -18613,6 +18613,10 @@ li:not(:first-child) .dropdown-item, min-height: 70vh !important; } +.modal-min-h-{ + min-height: 60vh !important; +} + .flex-fill { flex: 1 1 auto !important; } diff --git a/src/components/Project/Team/TeamAssignToProject.jsx b/src/components/Project/Team/TeamAssignToProject.jsx index 102b0be7..0bab7756 100644 --- a/src/components/Project/Team/TeamAssignToProject.jsx +++ b/src/components/Project/Team/TeamAssignToProject.jsx @@ -1,40 +1,78 @@ -import React from "react"; +import React, { useState } from "react"; +import TeamEmployeeList from "./TeamEmployeeList"; +import { useOrganization } from "../../../hooks/useDirectory"; +import { useOrganizationsList } from "../../../hooks/useOrganization"; +import { useProjectAssignedOrganizations } from "../../../hooks/useProjects"; +import { useSelectedProject } from "../../../slices/apiDataManager"; -const TeamAssignToProject = () => { +const TeamAssignToProject = ({ closeModal }) => { + const [searchText, setSearchText] = useState(""); + const [selectedOrg, setSelectedOrg] = useState(null); + const project = useSelectedProject(); + const { data, isLoading, isError, error } = + useProjectAssignedOrganizations(project); return (

Assign Employee To Project

-
-
- - -
-
- - +
+
+
+ {isLoading ? ( + + ) : data?.length === 0 ? ( +

No organizations found

+ ) : ( + <> + + + + )} +
+
+
+
+ + setSearchText(e.target.value)} + /> +
-
- - +
- -
- -
); }; diff --git a/src/components/Project/Team/TeamEmployeeList.jsx b/src/components/Project/Team/TeamEmployeeList.jsx index da62ac91..920d1f6f 100644 --- a/src/components/Project/Team/TeamEmployeeList.jsx +++ b/src/components/Project/Team/TeamEmployeeList.jsx @@ -1,12 +1,250 @@ -import React from 'react' +import React, { useState, useEffect } from "react"; +import Avatar from "../../common/Avatar"; +import { useDebounce } from "../../../utils/appUtils"; +import { useSelectedProject } from "../../../slices/apiDataManager"; +import { useOrganizationEmployees } from "../../../hooks/useOrganization"; +import { + useEmployeesByProjectAllocated, + useManageProjectAllocation, +} from "../../../hooks/useProjects"; +import useMaster, { useServices } from "../../../hooks/masterHook/useMaster"; +import showToast from "../../../services/toastService"; -const TeamEmployeeList = ({serviceId,organizationId}) => { - const {} = use - return ( -
- -
- ) +const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => { + const selectedProject = useSelectedProject(); + const debounceSearchTerm = useDebounce(searchTerm, 500); + + const { + data: employeesData = [], + isLoading, + isError, + error, + } = useOrganizationEmployees( + selectedProject, + organizationId, + debounceSearchTerm + ); + + const { projectEmployees, loading: employeeLodaing } = + useEmployeesByProjectAllocated(selectedProject, null); + + const { data: jobRoles } = useMaster(); + const { data: services } = useServices(); + + const [employees, setEmployees] = useState([]); + + const { mutate: handleAssignEmployee, isPending } = + useManageProjectAllocation({ + onSuccessCallback: () => { + closeModal(); + }, + onErrorCallback: () => { + closeModal(); + }, + }); + + useEffect(() => { + if (employeesData?.data?.length > 0) { + const available = employeesData.data.filter((emp) => { + const projEmp = projectEmployees.find((pe) => pe.employeeId === emp.id); + return !projEmp || projEmp.isActive === false; + }); + + setEmployees( + available.map((emp) => ({ + ...emp, + isChecked: false, + jobRole: emp?.jobRoleId || null, + serviceId: "", + errors: {}, + })) + ); + } + }, [employeesData, projectEmployees, organizationId]); + + const handleCheckboxChange = (index) => { + setEmployees((prev) => { + const newArr = [...prev]; + newArr[index].isChecked = !newArr[index].isChecked; + newArr[index].errors = {}; + return newArr; + }); + }; + + const handleSelectChange = (index, field, value) => { + setEmployees((prev) => { + const newArr = [...prev]; + newArr[index][field] = value; + newArr[index].errors[field] = ""; + return newArr; + }); + }; + + const onSubmit = () => { + const checkedEmployees = employees.filter((emp) => emp.isChecked); + + setEmployees((prev) => prev.map((emp) => ({ ...emp, errors: {} }))); + + if (checkedEmployees.length === 0) { + showToast("Select at least one employee", "info"); + return; + } + + let hasError = false; + const newEmployees = employees.map((emp) => { + const empErrors = {}; + if (emp.isChecked) { + if (!emp.jobRole) { + empErrors.jobRole = "Job role is required"; + hasError = true; + } + if (!emp.serviceId) { + empErrors.serviceId = "Service is required"; + hasError = true; + } + } + return { ...emp, errors: empErrors }; + }); + + setEmployees(newEmployees); + + if (hasError) return; // stop submit if validation fails + + const payload = checkedEmployees.map((emp) => ({ + employeeId: emp.id, + jobRoleId: emp.jobRole, + serviceId: emp.serviceId, + projectId: selectedProject, + status: true, + })); + + handleAssignEmployee({ payload }); + + setEmployees((prev) => + prev.map((emp) => ({ + ...emp, + isChecked: false, + jobRole: "", + serviceId: "", + errors: {}, + })) + ); + }; + +if (isLoading) { + return (

Loading employees...

) ; } -export default TeamEmployeeList +if (isError) { + return ( +
+ {error?.status === 400 ? ( +

Enter employee you want to find.

+ ) : ( +

Something went wrong. Please try again later.

+ )} +
+ + ); +} + +if (employees.length === 0) { + return(

No available employees to assign.

) ; +} + + + return ( +
+ + + + + + + + + + + {employees.map((emp, index) => ( + + + + + + + ))} + +
EmployeeServiceJob RoleSelect
+
+ + + {emp.firstName} {emp.lastName} + +
+
+ + {emp.errors.serviceId && ( +
{emp.errors.serviceId}
+ )} +
+ + {emp.errors.jobRole && ( +
{emp.errors.jobRole}
+ )} +
+ handleCheckboxChange(index)} + /> +
+
+ + +
+
+ ); +}; + +export default TeamEmployeeList; diff --git a/src/components/Project/Team/Teams.jsx b/src/components/Project/Team/Teams.jsx index f684347a..0d5d501d 100644 --- a/src/components/Project/Team/Teams.jsx +++ b/src/components/Project/Team/Teams.jsx @@ -24,24 +24,20 @@ import GlobalModel from "../../common/GlobalModel"; import TeamAssignToProject from "./TeamAssignToProject"; const Teams = () => { - const projectId = useSelectedProject(); + const selectedProject = useSelectedProject(); const dispatch = useDispatch(); - const [AssigTeam,setAssignTeam] = useState(false) - const { data, loading } = useMaster(); - const [isModalOpen, setIsModelOpen] = useState(false); - const [error, setError] = useState(""); - const [empJobRoles, setEmpJobRoles] = useState(null); + const [AssigTeam, setAssignTeam] = useState(false); const [employees, setEmployees] = useState([]); const [filteredEmployees, setFilteredEmployees] = useState([]); - const [removingEmployeeId, setRemovingEmployeeId] = useState(null); - const [assignedLoading, setAssignedLoading] = useState(false); - const [activeEmployee, setActiveEmployee] = useState(true); + const [selectedEmployee, setSelectedEmployee] = useState(null); const [deleteEmployee, setDeleteEmplyee] = useState(null); const [searchTerm, setSearchTerm] = useState(""); // State for search term const [selectedService, setSelectedService] = useState(null); + const [activeEmployee,setActiveEmployee] = useState(false) const { data: assignedServices, isLoading: servicesLoading } = - useProjectAssignedServices(projectId); + useProjectAssignedServices(selectedProject); + const {data:empJobRoles,loading} = useMaster() const handleToggleActive = (e) => setActiveEmployee(e.target.checked); const handleServiceChange = (e) => { @@ -56,7 +52,7 @@ const Teams = () => { projectEmployees, loading: employeeLodaing, refetch, - } = useEmployeesByProjectAllocated(projectId, selectedService); + } = useEmployeesByProjectAllocated(selectedProject, selectedService,null,activeEmployee); const { mutate: submitAllocations, isPending, @@ -65,7 +61,6 @@ const Teams = () => { } = useManageProjectAllocation({ onSuccessCallback: () => { setRemovingEmployeeId(null); - setAssignedLoading(false); setDeleteEmplyee(null); closeDeleteModal(); }, @@ -74,44 +69,20 @@ const Teams = () => { }, }); - const removeAllocation = (item) => { - setRemovingEmployeeId(item.id); + const handleDelete = (employee) => { + let payload = [ + { + employeeId: employee.employeeId, + jobRoleId: employee.jobRoleId, + projectId: selectedProject, + serviceId: selectedService, + status: false, + }, + ]; - submitAllocations({ - items: [ - { - empID: item.employeeId, - jobRoleId: item.jobRoleId, - projectId: projectId, - status: false, - }, - ], - added: false, - }); + submitAllocations({payload:payload}); }; - - const handleEmpAlicationFormSubmit = (allocaionObj) => { - let items = allocaionObj.map((item) => { - return { - empID: item.empID, - jobRoleId: item.jobRoleId, - projectId: projectId, - status: true, - }; - }); - - submitAllocations({ items, added: true }); - - setActiveEmployee(true); - setFilteredEmployees(employees.filter((emp) => emp.isActive)); - - const dropdown = document.querySelector( - 'select[name="DataTables_Table_0_length"]' - ); - if (dropdown) dropdown.value = "true"; - }; - - const getRole = (jobRoleId) => { + const getJobRole = (jobRoleId) => { if (loading) return "Loading..."; if (!Array.isArray(empJobRoles)) return "Unassigned"; if (!jobRoleId) return "Unassigned"; @@ -119,165 +90,47 @@ const Teams = () => { const role = empJobRoles.find((b) => b.id == jobRoleId); return role ? role.name : "Unassigned"; }; - const openModel = () => { - setIsModelOpen(true); - }; + // const employeeHandler = useCallback( + // (msg) => { + // if (filteredEmployees.some((emp) => emp.employeeId == msg.employeeId)) { + // refetch(); + // } + // }, + // [filteredEmployees, refetch] + // ); - const onModelClose = () => { - setIsModelOpen(false); - const modalElement = document.getElementById("user-model"); - if (modalElement) { - modalElement.classList.remove("show"); - modalElement.style.display = "none"; - document.body.classList.remove("modal-open"); - document.querySelector(".modal-backdrop").remove(); - } - const modalBackdropElement = document.querySelector(".modal-backdrop"); - if (modalBackdropElement) { - modalBackdropElement.remove(); - } - document.body.style.overflow = "auto"; - }; - - useEffect(() => { - dispatch(changeMaster("Job Role")); - }, [dispatch]); - - useEffect(() => { - if (projectEmployees) { - setEmployees(projectEmployees); - const filtered = projectEmployees.filter((emp) => emp.isActive); - setFilteredEmployees(filtered); - } - }, [projectEmployees, employeeLodaing]); - - useEffect(() => { - if (data) { - setEmpJobRoles(data); - } - }, [data]); - const filterAndSearchEmployees = useCallback(() => { - const statusFiltered = employees.filter((emp) => - activeEmployee ? emp.isActive : !emp.isActive - ); - - if (searchTerm === "") { - setFilteredEmployees(statusFiltered); - return; - } - - const lowercasedSearchTerm = searchTerm.toLowerCase(); - - const searchedAndFiltered = statusFiltered.filter((item) => { - const fullName = - `${item.firstName} ${item.middleName} ${item.lastName}`.toLowerCase(); - const roleName = getRole(item.jobRoleId).toLowerCase(); - const orgName = (item.organizationName || "").toLowerCase(); - const serviceName = (item.serviceName || "").toLowerCase(); - - return ( - fullName.includes(lowercasedSearchTerm) || - roleName.includes(lowercasedSearchTerm) || - orgName.includes(lowercasedSearchTerm) || - serviceName.includes(lowercasedSearchTerm) - ); - }); - - setFilteredEmployees(searchedAndFiltered); - }, [employees, activeEmployee, searchTerm, getRole]); - - useEffect(() => { - filterAndSearchEmployees(); - }, [employees, activeEmployee, searchTerm, filterAndSearchEmployees]); - - const handleFilterEmployee = (e) => { - const filterValue = e.target.value; - setActiveEmployee(filterValue === "true"); - setSearchTerm(""); - }; - - const handleSearch = (e) => { - setSearchTerm(e.target.value); - }; - - const deleteModalOpen = (item) => { - setDeleteEmplyee(item); - setIsDeleteModal(true); - }; - const closeDeleteModal = () => setIsDeleteModal(false); - - const handler = useCallback( - (msg) => { - if (msg.projectIds.some((item) => item === projectId)) { - refetch(); - } - }, - [projectId, refetch] - ); - - useEffect(() => { - eventBus.on("assign_project_all", handler); - return () => eventBus.off("assign_project_all", handler); - }, [handler]); - - const employeeHandler = useCallback( - (msg) => { - if (filteredEmployees.some((item) => item.employeeId == msg.employeeId)) { - refetch(); - } - }, - [filteredEmployees, refetch] - ); - - useEffect(() => { - eventBus.on("employee", employeeHandler); - return () => eventBus.off("employee", employeeHandler); - }, [employeeHandler]); + // useEffect(() => { + // eventBus.on("employee", employeeHandler); + // return () => eventBus.off("employee", employeeHandler); + // }, [employeeHandler]); return ( <> - - - {AssigTeam && ( - setAssignTeam(false)}> - + setAssignTeam(false)} + > + setAssignTeam(false)} /> )} - {IsDeleteModal && ( - removeAllocation(deleteEmployee)} - onClose={closeDeleteModal} - loading={isPending} - /> - )} + handleDelete(selectedEmployee)} + onClose={() => setSelectedEmployee(null)} + />
-
-
+
+ {/*
{!servicesLoading && assignedServices?.length > 0 && (assignedServices.length > 1 ? ( @@ -306,9 +159,9 @@ const Teams = () => { ) : (
{assignedServices[0].name}
))} -
+
*/}
-
+
{ {activeEmployee ? "Active Employees" : "Inactive Employees"}
-
- x`` +
{HasAssignUserPermission && (
{employeeLodaing &&

Loading..

} - {!employeeLodaing && - filteredEmployees && - filteredEmployees.length > 0 && ( - - - - - - - - {!activeEmployee && } - - - - - - {filteredEmployees && - filteredEmployees.map((item) => ( - - - - - - - - {!activeEmployee && ( - + + + + + + + + + + ))} + +
-
Name
-
ServicesOrganizationAssigned DateRelease DateProject RoleActions
- - {item.serviceName || "N/A"}{item.organizationName || "N/A"} - {moment(item.allocationDate).format("DD-MMM-YYYY")} - - {item.reAllocationDate - ? moment(item.reAllocationDate).format( - "DD-MMM-YYYY" + {projectEmployees && projectEmployees.length > 0 && ( + + + + + + + + + + + + + + {projectEmployees && + projectEmployees.map((emp) => ( + + - )} - - - - ))} - -
+
Name
+
ServicesOrganizationAssigned DateRelease DateProject RoleActions
+ - - {getRole(item.jobRoleId)} - - - {item.isActive ? ( - - ) : ( - Not in project - )} -
- )} - {!employeeLodaing && filteredEmployees.length === 0 && ( + + {emp.firstName} + {emp.lastName} + + + + +
{emp.serviceName || "N/A"}{emp.organizationName || "N/A"} + {moment(emp.allocationDate).format("DD-MMM-YYYY")} + + {emp.reAllocationDate + ? moment(emp.reAllocationDate).format("DD-MMM-YYYY") + : "Present"} + + + {getJobRole(emp.jobRoleId)} + + + {emp.isActive ? ( + + ) : ( + Not in project + )} +
+ )} + {/* {!employeeLodaing && filteredEmployees.length === 0 && (
{activeEmployee ? "No active employees assigned to the project" : "No inactive employees assigned to the project"}
- )} + )} */}
diff --git a/src/hooks/useOrganization.js b/src/hooks/useOrganization.js index dea19874..d6c93693 100644 --- a/src/hooks/useOrganization.js +++ b/src/hooks/useOrganization.js @@ -35,6 +35,8 @@ export const useOrganizationModal = () => { }; }; +// ================================Query============================================================= + export const useOrganizationBySPRID = (sprid) => { return useQuery({ queryKey: ["organization by", sprid], @@ -76,6 +78,25 @@ export const useOrganizationsList = ( }); }; +export const useOrganizationEmployees = ( + projectId, + organizationId, + searchString +) => { + return useQuery({ + queryKey: ["OrgEmployees", projectId, organizationId, searchString], + queryFn: async () => + await OrganizationRepository.getOrganizationEmployees( + projectId, + organizationId, + searchString + ), + enabled: !!projectId , + }); +}; + +// =================================Mutation======================================================== + export const useCreateOrganization = (onSuccessCallback) => { const useClient = useQueryClient(); return useMutation({ @@ -103,11 +124,11 @@ export const useAssignOrgToProject = (onSuccessCallback) => { mutationFn: async (payload) => await OrganizationRepository.assignOrganizationToProject(payload), onSuccess: (_, variables) => { - const {projectId} = variables + const { projectId } = variables; useClient.invalidateQueries({ queryKey: ["projectAssignedOrganiztions"], }); - useClient.invalidateQueries({ + useClient.invalidateQueries({ queryKey: ["projectAssignedOrganization", projectId], }); showToast("Organization successfully", "success"); diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index bdbd01c6..9225b0c4 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -54,7 +54,7 @@ export const useProjects = () => { export const useEmployeesByProjectAllocated = ( projectId, serviceId, - organizationId, + organizationId,emloyeeeStatus ) => { const { data = [], @@ -62,12 +62,12 @@ export const useEmployeesByProjectAllocated = ( refetch, error, } = useQuery({ - queryKey: ["empListByProjectAllocated", projectId, serviceId,organizationId], + queryKey: ["empListByProjectAllocated", projectId, serviceId,organizationId,emloyeeeStatus], queryFn: async () => { const res = await ProjectRepository.getProjectAllocation( - projectId, + projectId, serviceId, organizationId, - serviceId + emloyeeeStatus ); return res?.data || res; }, @@ -413,8 +413,8 @@ export const useManageProjectAllocation = ({ const queryClient = useQueryClient(); const { mutate, isPending, isSuccess, isError } = useMutation({ - mutationFn: async ({ items }) => { - const response = await ProjectRepository.manageProjectAllocation(items); + mutationFn: async ({payload}) => { + const response = await ProjectRepository.manageProjectAllocation(payload); return response.data; }, onSuccess: (data, variables, context) => { diff --git a/src/repositories/OrganizationRespository.jsx b/src/repositories/OrganizationRespository.jsx index 4b4ad1b0..28bbf3ce 100644 --- a/src/repositories/OrganizationRespository.jsx +++ b/src/repositories/OrganizationRespository.jsx @@ -10,11 +10,33 @@ const OrganizationRepository = { ); }, - getOrganizationBySPRID :(sprid)=>api.get(`/api/Organization/list?sprid=${sprid}`), + getOrganizationBySPRID: (sprid) => + api.get(`/api/Organization/list?sprid=${sprid}`), - assignOrganizationToProject:(data)=>api.post(`/api/Organization/assign/project`,data), + assignOrganizationToProject: (data) => + api.post(`/api/Organization/assign/project`, data), - assignOrganizationToTenanat:(organizationId)=>api.post(`/api/Organization/assign/tenant/${organizationId}`) + assignOrganizationToTenanat: (organizationId) => + api.post(`/api/Organization/assign/tenant/${organizationId}`), + + getOrganizationEmployees: (projectId, organizationId, searchString) => { + let url = `/api/Employee/list/organizations/${projectId}`; + const queryParams = []; + + if (organizationId) { + queryParams.push(`organizationId=${organizationId}`); + } + + if (searchString) { + queryParams.push(`searchString=${encodeURIComponent(searchString)}`); + } + + if (queryParams.length > 0) { + url += `?${queryParams.join("&")}`; + } + + return api.get(url); + }, }; export default OrganizationRepository; diff --git a/src/repositories/ProjectRepository.jsx b/src/repositories/ProjectRepository.jsx index b1b0727f..43a1ee66 100644 --- a/src/repositories/ProjectRepository.jsx +++ b/src/repositories/ProjectRepository.jsx @@ -5,19 +5,21 @@ const ProjectRepository = { getProjectByprojectId: (projetid) => api.get(`/api/project/details/${projetid}`), - getProjectAllocation: (projectId, organizationId, serviceId) => { - let url = `/api/project/allocation/${projectId}`; + getProjectAllocation: (projectId, serviceId, organizationId, employeeStatus) => { + let url = `/api/project/allocation/${projectId}`; - const params = []; - if (organizationId) params.push(`organizationId=${organizationId}`); - if (serviceId) params.push(`serviceId=${serviceId}`); + const params = []; + if (organizationId) params.push(`organizationId=${organizationId}`); + if (serviceId) params.push(`serviceId=${serviceId}`); + if (employeeStatus !== undefined) params.push(`includeInactive=${employeeStatus}`); - if (params.length > 0) { - url += `?${params.join("&")}`; - } + if (params.length > 0) { + url += `?${params.join("&")}`; + } + + return api.get(url); +}, - return api.get(url); - }, getEmployeesByProject: (projectId) => api.get(`/api/Project/employees/get/${projectId}`), From 265c74f07992f881803db4afe5c6031d70db51cf Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 10:12:34 +0530 Subject: [PATCH 38/57] added filter and sorted employee list - Team --- src/components/Project/Team/Teams.jsx | 313 ++++++++++++++------------ 1 file changed, 174 insertions(+), 139 deletions(-) diff --git a/src/components/Project/Team/Teams.jsx b/src/components/Project/Team/Teams.jsx index 0d5d501d..e9802765 100644 --- a/src/components/Project/Team/Teams.jsx +++ b/src/components/Project/Team/Teams.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useCallback } from "react"; +import React, { useState, useEffect, useCallback, useMemo } from "react"; import MapUsers from "../MapUsers"; import { Link, NavLink, useNavigate, useParams } from "react-router-dom"; @@ -28,16 +28,15 @@ const Teams = () => { const dispatch = useDispatch(); const [AssigTeam, setAssignTeam] = useState(false); const [employees, setEmployees] = useState([]); - const [filteredEmployees, setFilteredEmployees] = useState([]); const [selectedEmployee, setSelectedEmployee] = useState(null); const [deleteEmployee, setDeleteEmplyee] = useState(null); const [searchTerm, setSearchTerm] = useState(""); // State for search term const [selectedService, setSelectedService] = useState(null); - const [activeEmployee,setActiveEmployee] = useState(false) + const [activeEmployee, setActiveEmployee] = useState(false); const { data: assignedServices, isLoading: servicesLoading } = useProjectAssignedServices(selectedProject); - const {data:empJobRoles,loading} = useMaster() + const { data: empJobRoles, loading } = useMaster(); const handleToggleActive = (e) => setActiveEmployee(e.target.checked); const handleServiceChange = (e) => { @@ -52,7 +51,12 @@ const Teams = () => { projectEmployees, loading: employeeLodaing, refetch, - } = useEmployeesByProjectAllocated(selectedProject, selectedService,null,activeEmployee); + } = useEmployeesByProjectAllocated( + selectedProject, + selectedService, + null, + activeEmployee + ); const { mutate: submitAllocations, isPending, @@ -80,9 +84,9 @@ const Teams = () => { }, ]; - submitAllocations({payload:payload}); + submitAllocations({ payload: payload }); }; - const getJobRole = (jobRoleId) => { + const getJobRole = (jobRoleId) => { if (loading) return "Loading..."; if (!Array.isArray(empJobRoles)) return "Unassigned"; if (!jobRoleId) return "Unassigned"; @@ -90,19 +94,36 @@ const Teams = () => { const role = empJobRoles.find((b) => b.id == jobRoleId); return role ? role.name : "Unassigned"; }; - // const employeeHandler = useCallback( - // (msg) => { - // if (filteredEmployees.some((emp) => emp.employeeId == msg.employeeId)) { - // refetch(); - // } - // }, - // [filteredEmployees, refetch] - // ); - // useEffect(() => { - // eventBus.on("employee", employeeHandler); - // return () => eventBus.off("employee", employeeHandler); - // }, [employeeHandler]); + const filteredEmployees = useMemo(() => { + if (!projectEmployees || !searchTerm?.trim()) return projectEmployees; + + const lower = searchTerm.toLowerCase(); + + return projectEmployees?.filter((emp) => { + const fullName = `${emp.firstName ?? ""} ${ + emp.lastName ?? "" + }`.toLowerCase(); + + const joberole = getJobRole(emp?.jobRoleId)?.toLowerCase(); + + return fullName?.includes(lower) || joberole?.includes(lower); + }); + }, [projectEmployees, searchTerm]); + const handleSearch = (e) => setSearchTerm(e.target.value); + const employeeHandler = useCallback( + (msg) => { + if (filteredEmployees.some((emp) => emp.employeeId == msg.employeeId)) { + refetch(); + } + }, + [filteredEmployees, refetch] + ); + + useEffect(() => { + eventBus.on("employee", employeeHandler); + return () => eventBus.off("employee", employeeHandler); + }, [employeeHandler]); return ( <> @@ -112,7 +133,7 @@ const Teams = () => { isOpen={AssigTeam} closeModal={() => setAssignTeam(false)} > - setAssignTeam(false)} /> + setAssignTeam(false)} /> )} @@ -128,79 +149,85 @@ const Teams = () => {
-
-
- {/*
- {!servicesLoading && - assignedServices?.length > 0 && - (assignedServices.length > 1 ? ( - - ) : ( -
{assignedServices[0].name}
- ))} -
*/} +
+
+
+
+ {!servicesLoading && ( + <> + {(!assignedServices || assignedServices.length === 0) && ( + + Not Service Assigned + + )} + + {assignedServices?.length === 1 && ( + + {assignedServices[0].name} + + )} + + {assignedServices?.length > 1 && ( + + )} + + )} +
+ +
+ + +
+
-
-
- - -
-
- -
+ +
+ + {HasAssignUserPermission && ( )}
+
{employeeLodaing &&

Loading..

} {projectEmployees && projectEmployees.length > 0 && ( @@ -213,72 +240,80 @@ const Teams = () => { Services Organization Assigned Date - Release Date + {activeEmployee && Release Date} Project Role Actions - {projectEmployees && - projectEmployees.map((emp) => ( - - -
- - - + - {emp.serviceName || "N/A"} - {emp.organizationName || "N/A"} + {emp.serviceName || "N/A"} + {emp.organizationName || "N/A"} - - {moment(emp.allocationDate).format("DD-MMM-YYYY")} - - - {emp.reAllocationDate - ? moment(emp.reAllocationDate).format("DD-MMM-YYYY") - : "Present"} - - - - {getJobRole(emp.jobRoleId)} - - - - {emp.isActive ? ( - - ) : ( - Not in project + + {moment(emp.allocationDate).format("DD-MMM-YYYY")} + + {activeEmployee && ( + + {emp.reAllocationDate + ? moment(emp.reAllocationDate).format( + "DD-MMM-YYYY" + ) + : "Present"} + )} - - - ))} + + + {getJobRole(emp.jobRoleId)} + + + + {emp.isActive ? ( + + ) : ( + Not in project + )} + + + ))} )} From ca8a41bb63cdb13c6845afe1b7b42201b8b4b953 Mon Sep 17 00:00:00 2001 From: "pramod.mahajan" Date: Sat, 27 Sep 2025 11:21:41 +0530 Subject: [PATCH 39/57] added style classes for footer --- public/assets/vendor/css/core.css | 2 +- .../Project/Team/TeamAssignToProject.jsx | 2 +- .../Project/Team/TeamEmployeeList.jsx | 10 +++---- src/components/Project/Team/Teams.jsx | 30 +++++++++---------- src/hooks/useProjects.js | 2 +- 5 files changed, 23 insertions(+), 23 deletions(-) diff --git a/public/assets/vendor/css/core.css b/public/assets/vendor/css/core.css index 5acc33c5..48163eb5 100644 --- a/public/assets/vendor/css/core.css +++ b/public/assets/vendor/css/core.css @@ -18613,7 +18613,7 @@ li:not(:first-child) .dropdown-item, min-height: 70vh !important; } -.modal-min-h-{ +.modal-min-h{ min-height: 60vh !important; } diff --git a/src/components/Project/Team/TeamAssignToProject.jsx b/src/components/Project/Team/TeamAssignToProject.jsx index 0bab7756..3a1bb856 100644 --- a/src/components/Project/Team/TeamAssignToProject.jsx +++ b/src/components/Project/Team/TeamAssignToProject.jsx @@ -66,7 +66,7 @@ const TeamAssignToProject = ({ closeModal }) => {
-
+
{ status: true, })); - handleAssignEmployee({ payload }); + handleAssignEmployee({ payload,actionType:"assign"} ); setEmployees((prev) => prev.map((emp) => ({ @@ -154,9 +154,9 @@ if (employees.length === 0) { return ( -
- - +
+
+ @@ -231,7 +231,7 @@ if (employees.length === 0) { ))}
Employee Service
-
+
-
+
{employeeLodaing &&

Loading..

} {projectEmployees && projectEmployees.length > 0 && ( @@ -317,13 +315,15 @@ const Teams = () => {
)} - {/* {!employeeLodaing && filteredEmployees.length === 0 && ( -
- {activeEmployee - ? "No active employees assigned to the project" - : "No inactive employees assigned to the project"} + {!employeeLodaing && filteredEmployees.length === 0 && ( +
+

+ {!activeEmployee + ? "No active employees assigned to the project" + : "No inactive employees assigned to the project"} +

- )} */} + )}
diff --git a/src/hooks/useProjects.js b/src/hooks/useProjects.js index 9225b0c4..a0116a62 100644 --- a/src/hooks/useProjects.js +++ b/src/hooks/useProjects.js @@ -422,7 +422,7 @@ export const useManageProjectAllocation = ({ queryKey: ["empListByProjectAllocated"], }); queryClient.removeQueries({ queryKey: ["projectEmployees"] }); - if (variables?.added) { + if (variables.actionType === "assign") { showToast("Employee Assigned Successfully", "success"); } else { showToast("Removed Employee Successfully", "success"); From ae66cb370555118753b18d59d772883003e3268c Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Thu, 25 Sep 2025 16:12:44 +0530 Subject: [PATCH 40/57] Change the label in Assign Organization. --- src/components/Organization/AssignOrg.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Organization/AssignOrg.jsx b/src/components/Organization/AssignOrg.jsx index c460de9b..e30a417c 100644 --- a/src/components/Organization/AssignOrg.jsx +++ b/src/components/Organization/AssignOrg.jsx @@ -248,7 +248,7 @@ const AssignOrg = ({ setStep }) => { ? "Please wait..." : flowType === "default" ? "Assign Organization" - : "Assign Project"} + : "Assign to Project"}
From ddfe09b570975680d5e99c7329a6cc5496e2876e Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Sat, 27 Sep 2025 14:44:43 +0530 Subject: [PATCH 41/57] =?UTF-8?q?Tenant=20Creation=20=E2=80=93=20Logo=20re?= =?UTF-8?q?moved=20when=20navigating=20back=20from=20second=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Tenant/LogoUpload.jsx | 16 ++++++++++------ src/components/Tenant/OrganizationInfo.jsx | 9 ++++++++- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/src/components/Tenant/LogoUpload.jsx b/src/components/Tenant/LogoUpload.jsx index aa6fbc12..9da2f998 100644 --- a/src/components/Tenant/LogoUpload.jsx +++ b/src/components/Tenant/LogoUpload.jsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useEffect, useState } from "react"; import { useFormContext } from "react-hook-form"; const toBase64 = (file) => @@ -10,11 +10,15 @@ const toBase64 = (file) => }); export const LogoUpload = ({ preview, setPreview, fileName, setFileName }) => { - const { - register, - setValue, - formState: { errors }, - } = useFormContext(); + const { register, setValue, watch, formState: { errors } } = useFormContext(); + const logoImage = watch("logoImage"); + + // Sync preview when the form value changes + useEffect(() => { + if (logoImage && !preview) { + setPreview(logoImage); // Use base64 as preview + } + }, [logoImage, preview, setPreview]); const handleUpload = async (e) => { const file = e.target.files?.[0]; diff --git a/src/components/Tenant/OrganizationInfo.jsx b/src/components/Tenant/OrganizationInfo.jsx index b12c55b2..1e166423 100644 --- a/src/components/Tenant/OrganizationInfo.jsx +++ b/src/components/Tenant/OrganizationInfo.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import { useFormContext, Controller } from "react-hook-form"; import Label from "../common/Label"; import DatePicker from "../common/DatePicker"; @@ -57,6 +57,13 @@ const OrganizationInfo = ({ onNext, onPrev, onSubmitTenant }) => { } }; + useEffect(() => { + const logoImage = getValues("logoImage"); + if (logoImage) { + setLogoPreview(logoImage); + setLogoName("Uploaded Logo"); + } +}, [getValues]); return ( From 0d6708619f9a5404e5978c449784a8232f935a7a Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Sat, 27 Sep 2025 14:31:46 +0530 Subject: [PATCH 42/57] =?UTF-8?q?Switch=20Tenant=20&=20Goto=20workspace=20?= =?UTF-8?q?option=20label=20should=20be=20=E2=80=9CSwitch=20Workspace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Layout/Header.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index c02d40db..50280bb1 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -401,7 +401,7 @@ const Header = () => { className="dropdown-item cusor-pointer" > - Workspace + Switch Workspace
  • From a873ace1095e00fac3f1c0bf5d3129206c8d5064 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Sat, 27 Sep 2025 14:24:05 +0530 Subject: [PATCH 43/57] =?UTF-8?q?Tenant=20Creation=20=E2=80=93=20Error=20b?= =?UTF-8?q?anner=20for=20plan=20selection/currency=20should=20not=20be=20s?= =?UTF-8?q?hown?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Tenant/SubScription.jsx | 2 +- src/components/Tenant/TenantSchema.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Tenant/SubScription.jsx b/src/components/Tenant/SubScription.jsx index f63fd911..adb97c70 100644 --- a/src/components/Tenant/SubScription.jsx +++ b/src/components/Tenant/SubScription.jsx @@ -224,7 +224,7 @@ const SubScription = ({ onSubmitSubScription, onNext }) => {
  • {Object.keys(errors).length > 0 && ( -