From fb1dec6c9343a71e068bc4a6c2ed4ef49e9d741e Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Wed, 2 Jul 2025 15:06:01 +0530 Subject: [PATCH 01/90] Added new field in contact create and update form --- .../Directory/CardViewDirectory.jsx | 10 + src/components/Directory/DirectorySchema.js | 1 + src/components/Directory/ManageDirectory.jsx | 89 +++++++- .../Directory/ProfileContactDirectory.jsx | 3 +- src/components/Directory/UpdateContact.jsx | 199 +++++++++++++----- src/hooks/useDirectory.js | 33 +++ src/repositories/DirectoryRepository.jsx | 1 + 7 files changed, 273 insertions(+), 63 deletions(-) diff --git a/src/components/Directory/CardViewDirectory.jsx b/src/components/Directory/CardViewDirectory.jsx index 9be8663a..0d955810 100644 --- a/src/components/Directory/CardViewDirectory.jsx +++ b/src/components/Directory/CardViewDirectory.jsx @@ -123,6 +123,16 @@ const CardViewDirectory = ({ }} >
+ {contact.designation && ( + + )} {contact.contactEmails[0] && ( - {errors.bucketIds && ( - - {errors.bucketIds.message} - - )} + {errors.bucketIds && ( + + {errors.bucketIds.message} + + )} diff --git a/src/components/Directory/ProfileContactDirectory.jsx b/src/components/Directory/ProfileContactDirectory.jsx index 256e2ea8..41e1dadd 100644 --- a/src/components/Directory/ProfileContactDirectory.jsx +++ b/src/components/Directory/ProfileContactDirectory.jsx @@ -47,7 +47,8 @@ const ProfileContactDirectory = ({ contact, setOpen_contact, closeModal }) => {
{contact?.name} - {contactProfile?.tags?.map((tag) => tag.name).join(" | ")} + {/* {contactProfile?.tags?.map((tag) => tag.name).join(" | ")} */} + {contactProfile?.designation}
diff --git a/src/components/Directory/UpdateContact.jsx b/src/components/Directory/UpdateContact.jsx index 985229a3..9d74fc35 100644 --- a/src/components/Directory/UpdateContact.jsx +++ b/src/components/Directory/UpdateContact.jsx @@ -14,7 +14,11 @@ import useMaster, { } from "../../hooks/masterHook/useMaster"; import { useDispatch, useSelector } from "react-redux"; import { changeMaster } from "../../slices/localVariablesSlice"; -import { useBuckets, useOrganization } from "../../hooks/useDirectory"; +import { + useBuckets, + useDesignation, + useOrganization, +} from "../../hooks/useDirectory"; import { useProjects } from "../../hooks/useProjects"; import SelectMultiple from "../common/SelectMultiple"; import { ContactSchema } from "./DirectorySchema"; @@ -32,10 +36,13 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => { const { contactCategory, loading: contactCategoryLoading } = useContactCategory(); const { contactTags, loading: Tagloading } = useContactTags(); - const [ IsSubmitting, setSubmitting ] = useState( false ); + const [IsSubmitting, setSubmitting] = useState(false); const [isInitialized, setIsInitialized] = useState(false); const dispatch = useDispatch(); - const {organizationList} = useOrganization() + const { organizationList } = useOrganization(); + const { designationList } = useDesignation(); + const [showSuggestions, setShowSuggestions] = useState(false); + const [filteredDesignationList, setFilteredDesignationList] = useState([]); const methods = useForm({ resolver: zodResolver(ContactSchema), @@ -45,6 +52,7 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => { contactCategoryId: null, address: "", description: "", + designation: "", projectIds: [], contactEmails: [], contactPhones: [], @@ -95,6 +103,24 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => { } }; + // handle logic when input of desgination is changed + const handleDesignationChange = (e) => { + const val = e.target.value; + + const matches = designationList.filter((org) => + org.toLowerCase().includes(val.toLowerCase()) + ); + setFilteredDesignationList(matches); + setShowSuggestions(true); + setTimeout(() => setShowSuggestions(false), 5000); + }; + + // handle logic when designation is selected + const handleSelectDesignation = (val) => { + setShowSuggestions(false); + setValue("designation", val); + }; + const watchBucketIds = watch("bucketIds"); const toggleBucketId = (id) => { @@ -113,33 +139,28 @@ const UpdateContact = ({ submitContact, existingContact, onCLosed }) => { }; const onSubmit = async (data) => { -const cleaned = { - ...data, - contactEmails: (data.contactEmails || []) - .filter((e) => e.emailAddress?.trim() !== "") - .map((email, index) => { - const existingEmail = existingContact.contactEmails?.[index]; - return existingEmail - ? { ...email, id: existingEmail.id } - : email; - }), - contactPhones: (data.contactPhones || []) - .filter((p) => p.phoneNumber?.trim() !== "") - .map((phone, index) => { - const existingPhone = existingContact.contactPhones?.[index]; - return existingPhone - ? { ...phone, id: existingPhone.id } - : phone; - }), -}; + const cleaned = { + ...data, + contactEmails: (data.contactEmails || []) + .filter((e) => e.emailAddress?.trim() !== "") + .map((email, index) => { + const existingEmail = existingContact.contactEmails?.[index]; + return existingEmail ? { ...email, id: existingEmail.id } : email; + }), + contactPhones: (data.contactPhones || []) + .filter((p) => p.phoneNumber?.trim() !== "") + .map((phone, index) => { + const existingPhone = existingContact.contactPhones?.[index]; + return existingPhone ? { ...phone, id: existingPhone.id } : phone; + }), + }; -setSubmitting(true); -await submitContact({ ...cleaned, id: existingContact.id }); + setSubmitting(true); + await submitContact({ ...cleaned, id: existingContact.id }); setSubmitting(false); - }; - const orgValue = watch("organization") + const orgValue = watch("organization"); const handleClosed = () => { onCLosed(); }; @@ -149,7 +170,7 @@ await submitContact({ ...cleaned, id: existingContact.id }); typeof existingContact === "object" && !Array.isArray(existingContact); - if (!isInitialized &&isValidContact && TagsData) { + if (!isInitialized && isValidContact && TagsData) { reset({ name: existingContact.name || "", organization: existingContact.organization || "", @@ -158,24 +179,30 @@ await submitContact({ ...cleaned, id: existingContact.id }); contactCategoryId: existingContact.contactCategory?.id || null, address: existingContact.address || "", description: existingContact.description || "", + designation: existingContact.designation || "", projectIds: existingContact.projectIds || null, tags: existingContact.tags || [], bucketIds: existingContact.bucketIds || [], - } ); - - if (!existingContact.contactPhones || existingContact.contactPhones.length === 0) { - appendPhone({ label: "Office", phoneNumber: "" }); + }); + + if ( + !existingContact.contactPhones || + existingContact.contactPhones.length === 0 + ) { + appendPhone({ label: "Office", phoneNumber: "" }); + } + + if ( + !existingContact.contactEmails || + existingContact.contactEmails.length === 0 + ) { + appendEmail({ label: "Work", emailAddress: "" }); + } + setIsInitialized(true); } - if (!existingContact.contactEmails || existingContact.contactEmails.length === 0) { - appendEmail({ label: "Work", emailAddress: "" }); - } - setIsInitialized(true) - } - // return()=> reset() - }, [ existingContact, buckets, projects ] ); - + }, [existingContact, buckets, projects]); return ( @@ -195,15 +222,14 @@ await submitContact({ ...cleaned, id: existingContact.id }); )} -
setValue("organization", val)} - error={errors.organization?.message} - /> + organizationList={organizationList} + value={getValues("organization") || ""} + onChange={(val) => setValue("organization", val)} + error={errors.organization?.message} + /> {errors.organization && ( {errors.organization.message} @@ -211,6 +237,55 @@ await submitContact({ ...cleaned, id: existingContact.id }); )}
+
+
+ + + {showSuggestions && filteredDesignationList.length > 0 && ( +
    + {filteredDesignationList.map((designation) => ( +
  • handleSelectDesignation(designation)} + onMouseEnter={(e) => + (e.currentTarget.style.backgroundColor = "#f8f9fa") + } + onMouseLeave={(e) => + (e.currentTarget.style.backgroundColor = "transparent") + } + > + {designation} +
  • + ))} +
+ )} + {errors.designation && ( + + {errors.designation.message} + + )} +
+
{emailFields.map((field, index) => ( @@ -247,11 +322,13 @@ await submitContact({ ...cleaned, id: existingContact.id }); //
{errors.contactEmails?.[index]?.emailAddress && ( @@ -310,7 +389,10 @@ await submitContact({ ...cleaned, id: existingContact.id }); // onClick={handleAddPhone} // style={{ width: "24px", height: "24px" }} // > - + ) : ( //
{errors.contactPhones?.[index]?.phoneNumber && ( @@ -348,7 +433,7 @@ await submitContact({ ...cleaned, id: existingContact.id }); ) : ( <> - {contactCategory?.map((cate) => ( @@ -387,7 +472,7 @@ await submitContact({ ...cleaned, id: existingContact.id }); )}
-
+
    @@ -445,7 +530,11 @@ await submitContact({ ...cleaned, id: existingContact.id });
-
{/* Organization */} -
+

Organization

From 633e60d141c9896c2d440fd1eab1df0be2109d5c Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Tue, 22 Jul 2025 11:35:15 +0530 Subject: [PATCH 03/90] At the time of Regularize status code 400 were shown now solved we add error message in popup "Checkout time must be later than check-in time". --- .../Activities/CheckCheckOutForm.jsx | 78 ++++++++++++++----- 1 file changed, 58 insertions(+), 20 deletions(-) diff --git a/src/components/Activities/CheckCheckOutForm.jsx b/src/components/Activities/CheckCheckOutForm.jsx index fe12730b..016f088f 100644 --- a/src/components/Activities/CheckCheckOutForm.jsx +++ b/src/components/Activities/CheckCheckOutForm.jsx @@ -10,10 +10,43 @@ import showToast from "../../services/toastService"; import { checkIfCurrentDate } from "../../utils/dateUtils"; -const schema = z.object({ - markTime: z.string().nonempty({ message: "Time is required" }), - description: z.string().max(200, "description should less than 200 chracters").optional() -}); +// const schema = z.object({ +// markTime: z.string().nonempty({ message: "Time is required" }), +// description: z.string().max(200, "description should less than 200 chracters").optional() +// }); + +const createSchema = (modeldata) => { + return z + .object({ + markTime: z.string().nonempty({ message: "Time is required" }), + description: z + .string() + .max(200, "Description should be less than 200 characters") + .optional(), + }) + .refine((data) => { + if (modeldata?.checkInTime && !modeldata?.checkOutTime) { + const checkIn = new Date(modeldata.checkInTime); + const [time, modifier] = data.markTime.split(" "); + const [hourStr, minuteStr] = time.split(":"); + let hour = parseInt(hourStr, 10); + const minute = parseInt(minuteStr, 10); + + if (modifier === "PM" && hour !== 12) hour += 12; + if (modifier === "AM" && hour === 12) hour = 0; + + const checkOut = new Date(checkIn); + checkOut.setHours(hour, minute, 0, 0); + + return checkOut > checkIn; + } + return true; + }, { + message: "Checkout time must be later than check-in time", + path: ["markTime"], + }); +}; + const CheckCheckOutmodel = ({ modeldata, closeModal, handleSubmitForm, }) => { @@ -31,16 +64,28 @@ const CheckCheckOutmodel = ({ modeldata, closeModal, handleSubmitForm, }) => { return `${day}-${month}-${year}`; }; + // const { + // register, + // handleSubmit, + // formState: { errors }, + // reset, + // setValue, + // } = useForm({ + // resolver: zodResolver(schema), + // mode: "onChange" + // }); + const { - register, - handleSubmit, - formState: { errors }, - reset, - setValue, - } = useForm({ - resolver: zodResolver(schema), - mode: "onChange" - }); + register, + handleSubmit, + formState: { errors }, + reset, + setValue, +} = useForm({ + resolver: zodResolver(createSchema(modeldata)), + mode: "onChange", +}); + const onSubmit = (data) => { let record = { ...data, date: new Date().toLocaleDateString(), latitude: coords.latitude, longitude: coords.longitude, employeeId: modeldata.employeeId, action: modeldata.action, id: modeldata?.id || null } @@ -48,8 +93,6 @@ const CheckCheckOutmodel = ({ modeldata, closeModal, handleSubmitForm, }) => { handleSubmitForm(record) } else { - // if ( modeldata?.currentDate && checkIfCurrentDate( modeldata?.currentDate ) ) - // { dispatch(markAttendance(record)) .unwrap() .then((data) => { @@ -61,11 +104,6 @@ const CheckCheckOutmodel = ({ modeldata, closeModal, handleSubmitForm, }) => { showToast(error, "error"); }); - - // } else - // { - // let formData = {...data, date: new Date().toLocaleDateString(),latitude:coords.latitude,longitude:coords.longitude,employeeId:modeldata.employeeId,projectId:projectId,action:modeldata.action,id:modeldata?.id || null} - // } } closeModal() From b5cf52ba4944d84689a34675ef36d56891792296 Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Tue, 22 Jul 2025 12:05:47 +0530 Subject: [PATCH 04/90] Changes in Employee list and Update and Create pupup of employee change "Role" to "Official designation". --- src/components/Employee/ManageEmployee.jsx | 2 +- src/pages/employee/EmployeeList.jsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Employee/ManageEmployee.jsx b/src/components/Employee/ManageEmployee.jsx index a377ea14..1f1b3a62 100644 --- a/src/components/Employee/ManageEmployee.jsx +++ b/src/components/Employee/ManageEmployee.jsx @@ -469,7 +469,7 @@ const { mutate: updateEmployee, isPending } = useUpdateEmployee();
-
Role
+
Official Designation
dispatch(setProjectId(e.target.value))} - aria-label="" - > - {!projectLoading && - projects - ?.filter((project) => - loginUser?.projects?.map(String).includes(project.id) - ) - .map((project) => ( - - ))} - {projectLoading && ( - - )} - - - )} +
    +
    +
  • + +
  • +
  • + +
  • +
  • + +
  • +
    + {/* Search Box remains here */} +
    + setSearchQuery(e.target.value)} + // style={{ width: "200px", height: "30px" }} // Optional: further reduce width/height + />
    -
*/} -
    -
  • - -
  • -
  • - -
  • -
  • - -
{activeTab === "all" && ( <> {!attLoading && (
- -
+ +
)} - {!attLoading && filteredAttendance?.length === 0 && ( + {!attLoading && filteredAndSearchedTodayAttendance()?.length === 0 && (

{" "} - {ShowPending + {showPending ? "No Pending Available" : "No Employee assigned yet."}{" "}

@@ -294,18 +310,21 @@ const AttendancePage = () => { handleModalData={handleModalData} projectId={selectedProject} setshowOnlyCheckout={setShowPending} - showOnlyCheckout={ShowPending} + showOnlyCheckout={showPending} + searchQuery={searchQuery} // Pass search query to AttendanceLog />
)} {activeTab === "regularization" && DoRegularized && (
- +
)} - {attLoading && Loading..} {!attLoading && !attendances && Not Found}
@@ -314,4 +333,4 @@ const AttendancePage = () => { ); }; -export default AttendancePage; +export default AttendancePage; \ No newline at end of file From a021a7b7b24492d7173e7b0e444105f71914fe3d Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Tue, 22 Jul 2025 17:13:45 +0530 Subject: [PATCH 06/90] Adding In-built scrollbar in Select Teams dropdown and Employees. --- src/components/Project/AssignTask.jsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/components/Project/AssignTask.jsx b/src/components/Project/AssignTask.jsx index 2ab01e6e..0d922c29 100644 --- a/src/components/Project/AssignTask.jsx +++ b/src/components/Project/AssignTask.jsx @@ -201,7 +201,11 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { -
    + {/* --- Scrollbar applied here --- */} +
-
+
{selectedRole !== "" && (
{employeeLoading ? ( @@ -396,7 +403,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { className="bi bi-info-circle" viewBox="0 0 16 16" > - +
@@ -542,4 +549,4 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { ); }; -export default AssignTask; +export default AssignTask; \ No newline at end of file From 9dbec1e1371d4cde5f5ec560d02126d7c173db0d Mon Sep 17 00:00:00 2001 From: Kartik Sharma Date: Wed, 23 Jul 2025 10:52:30 +0530 Subject: [PATCH 07/90] Project dropdown should only include active ,On Hold and In Progress Projects only show in Projects page. --- src/components/Layout/Header.jsx | 75 +++++++++++++++++++------------- 1 file changed, 45 insertions(+), 30 deletions(-) diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 38966865..7a1a6aaa 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -1,4 +1,3 @@ - import getGreetingMessage from "../../utils/greetingHandler"; import { cacheData, @@ -28,9 +27,19 @@ const Header = () => { const navigate = useNavigate(); const HasManageProjectPermission = useHasUserPermission(MANAGE_PROJECT); - const isDirectoryPath = /^\/directory$/.test(location.pathname); - const isProjectPath = /^\/projects$/.test(location.pathname); - const isDashboard = /^\/dashboard$/.test(location.pathname) || /^\/$/.test(location.pathname) ; + const isDirectoryPath = /^\/directory$/.test(location.pathname); + const isProjectPath = /^\/projects$/.test(location.pathname); + const isDashboard = + /^\/dashboard$/.test(location.pathname) || /^\/$/.test(location.pathname); + + // Define the specific project status IDs you want to filter by + const allowedProjectStatusIds = [ + "603e994b-a27f-4e5d-a251-f3d69b0498ba", // On Hold + "cdad86aa-8a56-4ff4-b633-9c629057dfef", // In Progress + "ef1c356e-0fe0-42df-a5d3-8daee355492d", // Inactive + "b74da4c2-d07e-46f2-9919-e75e49b12731", // Active + ]; + const getRole = (roles, joRoleId) => { if (!Array.isArray(roles)) return "User"; let role = roles.find((role) => role.id === joRoleId); @@ -38,7 +47,7 @@ const Header = () => { }; const handleLogout = (e) => { - e.preventDefault(); + e.preventDefault(); logout(); }; @@ -83,11 +92,20 @@ const Header = () => { (store) => store.localVariables.projectId ); + // Conditional filtering for projectsForDropdown + // If on Dashboard, show all projects. Otherwise, filter by allowedProjectStatusIds. + const projectsForDropdown = isDashboard + ? projectNames // On dashboard, show all projects + : projectNames?.filter(project => + allowedProjectStatusIds.includes(project.projectStatusId) + ); + // Determine the display text for the project dropdown let displayText = "All Projects"; if (selectedProject === null) { displayText = "All Projects"; } else if (selectedProject) { + // Find the selected project from the full projectNames list const selectedProjectObj = projectNames?.find( (p) => p?.id === selectedProject ); @@ -101,22 +119,20 @@ const Header = () => { useEffect(() => { if ( - projectNames && + projectNames && // Use the original projectNames for initial setting to ensure all are considered for initial state if needed projectNames.length > 0 && selectedProject === undefined && !getCachedData("hasReceived") ) { - if(isDashboard){ - dispatch(setProjectId(null)); - }else{ - dispatch(setProjectId(projectNames[0]?.id)); + if (isDashboard) { + dispatch(setProjectId(null)); // Always set to null for "All Projects" on Dashboard initial load + } else { + // If not dashboard, set to the first project that matches the allowed statuses if available + const firstAllowedProject = projectNames.find(project => allowedProjectStatusIds.includes(project.projectStatusId)); + dispatch(setProjectId(firstAllowedProject?.id || null)); // Fallback to null if no allowed projects } } - }, [projectNames, selectedProject, dispatch]); - - - /** Check if current page is project details page or directory page */ - // const isProjectPath = /^\/projects\/[a-f0-9-]{36}$/.test(location.pathname); + }, [projectNames, selectedProject, dispatch, isDashboard]); const handler = useCallback( @@ -160,15 +176,16 @@ const Header = () => { }; }, [handler, newProjectHandler]); - const handleProjectChange =(project)=>{ - if(isProjectPath){ - dispatch(setProjectId(project)) - navigate("/projects/details") - } else{ - dispatch(setProjectId(project)) - } - } + const handleProjectChange = (project) => { + dispatch(setProjectId(project)); // Always set the projectId + if (isProjectPath && project !== null) { + navigate("/projects/details"); // Navigate only if on /projects and a specific project is selected + } + // No navigation if on dashboard or if "All Projects" is selected + }; + + console.log("Kartik sharma", projectNames); return (
+
+ {selectedRoles?.length > 0 && ( +
+ {employeeLoading ? ( +
+

Loading employees...

+
+ ) : filteredEmployees?.length > 0 ? ( + filteredEmployees.map((emp) => { + const jobRole = jobRoleData?.find( + (role) => role?.id === emp?.jobRoleId + ); -
-
- {selectedRole !== "" && ( -
- {employeeLoading ? ( -
-

Loading employees...

-
- ) : filteredEmployees?.length > 0 ? ( - filteredEmployees.map((emp) => { - const jobRole = jobRoleData?.find( - (role) => role?.id === emp?.jobRoleId - ); - - return ( -
-
- ( - { - handleCheckboxChange(e, emp); - }} - /> + return ( +
+
+ ( + { + handleCheckboxChange(e, emp); + }} + /> + )} + /> +
+

+ {emp.firstName} {emp.lastName} +

+ + {loading ? ( + + + + ) : ( + jobRole?.name || "Unknown Role" )} - /> -
-

- {emp.firstName} {emp.lastName} -

- - {loading ? ( - - - - ) : ( - jobRole?.name || "Unknown Role" - )} - -
+
- ); - }) - ) : ( -
-

- No employees found for the selected role. -

-
- )} -
- )} -
+
+ ); + }) + ) : ( +
+

+ No employees found for the selected role(s). +

+
+ )} +
+ )}
{ className="bi bi-info-circle" viewBox="0 0 16 16" > - +
@@ -439,11 +585,11 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { id="defaultFormControlInput" aria-describedby="defaultFormControlHelp" /> - - { - assignData?.workItem?.workItem?.activityMaster + + { + assignData?.workItem?.activityMaster ?.unitOfMeasurement - } + }
{ className="position-absolute bg-white border p-2 rounded shadow" style={{ zIndex: 10, marginLeft: "10px" }} > - {/* Add your help content here */}

Enter the target value for today's task.

@@ -501,7 +646,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => { @@ -512,7 +657,7 @@ const AssignTask = ({ assignData, onClose, setAssigned }) => {