From ed2fa2d5b09cb616b6e47385a3475be6b8c0120a Mon Sep 17 00:00:00 2001 From: "ashutosh.nehete" Date: Thu, 12 Jun 2025 19:39:56 +0530 Subject: [PATCH] Implemented signalR for create and update project --- src/components/Project/AboutProject.jsx | 6 +- src/components/Project/ProjectBanner.jsx | 5 + src/components/Project/ProjectCard.jsx | 5 +- src/pages/Activities/AttendancePage.jsx | 2 +- src/pages/project/ProjectDetails.jsx | 32 ++++- src/pages/project/ProjectList.jsx | 144 ++++++++++++++--------- src/pages/project/ProjectListView.jsx | 4 + src/services/signalRService.js | 34 ++++-- 8 files changed, 160 insertions(+), 72 deletions(-) diff --git a/src/components/Project/AboutProject.jsx b/src/components/Project/AboutProject.jsx index b358e8c8..5802e4d9 100644 --- a/src/components/Project/AboutProject.jsx +++ b/src/components/Project/AboutProject.jsx @@ -1,9 +1,13 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import moment from "moment"; import { getProjectStatusName } from "../../utils/projectStatus"; const AboutProject = ({ data }) => { const [CurrentProject, setCurrentProject] = useState(data); + useEffect(() => { + setCurrentProject(data); + }, [data]); + return ( <> {data && ( diff --git a/src/components/Project/ProjectBanner.jsx b/src/components/Project/ProjectBanner.jsx index cd7c47ce..06ca6619 100644 --- a/src/components/Project/ProjectBanner.jsx +++ b/src/components/Project/ProjectBanner.jsx @@ -12,10 +12,15 @@ const ProjectBanner = ({ project_data }) => { const [showModal, setShowModal] = useState(false); const manageProject = useHasUserPermission(MANAGE_PROJECT); const [CurrentProject, setCurrentProject] = useState(project_data); + if (project_data == null) { return incomplete project information; } + useEffect(() => { + setCurrentProject(project_data); + }, [project_data]); + const handleShow = () => setShowModal(true); const handleClose = () => setShowModal(false); diff --git a/src/components/Project/ProjectCard.jsx b/src/components/Project/ProjectCard.jsx index bac2b23c..a681f250 100644 --- a/src/components/Project/ProjectCard.jsx +++ b/src/components/Project/ProjectCard.jsx @@ -1,4 +1,4 @@ -import React, { useState } from "react"; +import React, { useEffect, useState } from "react"; import moment from "moment"; import { getDateDifferenceInDays } from "../../utils/dateUtils"; import { useNavigate } from "react-router-dom"; @@ -22,6 +22,9 @@ const ProjectCard = ({ projectData, recall }) => { const ManageProject = useHasUserPermission(MANAGE_PROJECT); const [modifyProjectLoading, setMdifyProjectLoading] = useState(false); + useEffect(()=>{ + setProjectInfo(projectData); + },[projectData]) const handleShow = async () => { try { setMdifyProjectLoading(true); diff --git a/src/pages/Activities/AttendancePage.jsx b/src/pages/Activities/AttendancePage.jsx index 434593a1..e3b25493 100644 --- a/src/pages/Activities/AttendancePage.jsx +++ b/src/pages/Activities/AttendancePage.jsx @@ -58,7 +58,7 @@ const AttendancePage = () => { setAttendances(updatedAttendance); } }, - [selectedProject, attrecall] + [selectedProject,attendances] ); const getRole = (roleId) => { diff --git a/src/pages/project/ProjectDetails.jsx b/src/pages/project/ProjectDetails.jsx index 27e13d6a..07d8b316 100644 --- a/src/pages/project/ProjectDetails.jsx +++ b/src/pages/project/ProjectDetails.jsx @@ -1,5 +1,5 @@ import { useParams } from "react-router-dom"; -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import ActivityTimeline from "../../components/Project/ActivityTimeline"; import ProjectOverview from "../../components/Project/ProjectOverview"; @@ -11,7 +11,7 @@ import ProjectInfra from "../../components/Project/ProjectInfra"; import Loader from "../../components/common/Loader"; import WorkPlan from "../../components/Project/WorkPlan"; import Breadcrumb from "../../components/common/Breadcrumb"; -import { cacheData, getCachedData } from "../../slices/apiDataManager"; +import { cacheData, clearCacheKey, getCachedData } from "../../slices/apiDataManager"; import ProjectRepository from "../../repositories/ProjectRepository"; import { ActivityeRepository } from "../../repositories/MastersRepository"; import "./ProjectDetails.css"; @@ -23,6 +23,7 @@ import { useDispatch } from "react-redux"; import { setProjectId } from "../../slices/localVariablesSlice"; import { ComingSoonPage } from "../Misc/ComingSoonPage"; import Directory from "../Directory/Directory"; +import eventBus from "../../services/eventBus"; const ProjectDetails = () => { let { projectId } = useParams(); @@ -121,7 +122,7 @@ const ProjectDetails = () => { case "directory": { return (
- +
); } @@ -137,6 +138,31 @@ const ProjectDetails = () => { setProjectDetails(projects_Details); }, [projects_Details, projectId]); + const handler = useCallback( + (msg) => { + if (msg.keyword === "Update_Project" && project.id === msg.response.id) { + clearCacheKey("projectInfo") + ProjectRepository.getProjectByprojectId(projectId) + .then((response) => { + setProjectDetails(response.data); + setProject(response.data); + cacheData("projectInfo", { projectId, data: response.data }); + setLoading(false); + }) + .catch((error) => { + console.error(error); + setError("Failed to fetch data."); + setLoading(false); + }); + } + }, + [project,handleDataChange] + ); + useEffect(() => { + eventBus.on("project", handler); + return () => eventBus.off("project", handler); + }, [handler]); + return ( <> {} diff --git a/src/pages/project/ProjectList.jsx b/src/pages/project/ProjectList.jsx index 14ddf8df..8f2707c7 100644 --- a/src/pages/project/ProjectList.jsx +++ b/src/pages/project/ProjectList.jsx @@ -1,4 +1,4 @@ -import React, { useState, useEffect } from "react"; +import React, { useState, useEffect, useCallback } from "react"; import ProjectCard from "../../components/Project/ProjectCard"; import ManageProjectInfo from "../../components/Project/ManageProjectInfo"; import Breadcrumb from "../../components/common/Breadcrumb"; @@ -11,6 +11,8 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { useProfile } from "../../hooks/useProfile"; import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants"; import ProjectListView from "./ProjectListView"; +import eventBus from "../../services/eventBus"; +import { clearApiCacheKey } from "../../slices/apiCacheSlice"; const ProjectList = () => { const { profile: loginUser } = useProfile(); @@ -37,7 +39,7 @@ const ProjectList = () => { const handleShow = () => setShowModal(true); const handleClose = () => setShowModal(false); - const sortingProject = (projects) =>{ + const sortingProject = (projects) => { if (!loading && Array.isArray(projects)) { const grouped = {}; projects.forEach((project) => { @@ -56,10 +58,10 @@ const ProjectList = () => { setProjectList(sortedGrouped); } - } + }; useEffect(() => { - sortingProject(projects) + sortingProject(projects); }, [projects, loginUser?.projects, loading]); useEffect(() => { @@ -70,16 +72,16 @@ const ProjectList = () => { } }, [loginUser, HasManageProjectPermission]); - const handleSubmitForm = (newProject,setloading,reset) => { + const handleSubmitForm = (newProject, setloading, reset) => { ProjectRepository.manageProject(newProject) .then((response) => { const cachedProjects = getCachedData("projectslist") || []; const updatedProjects = [...cachedProjects, response.data]; cacheData("projectslist", updatedProjects); - setProjectList( ( prev ) => [ ...prev, response.data ] ); - setloading( false ) - reset() - sortingProject(getCachedData("projectslist")) + setProjectList((prev) => [...prev, response.data]); + setloading(false); + reset(); + sortingProject(getCachedData("projectslist")); showToast("Project Created successfully.", "success"); setShowModal(false); }) @@ -123,7 +125,7 @@ const ProjectList = () => { indexOfLastItem ); const totalPages = Math.ceil(filteredProjects.length / itemsPerPage); - + console.log("filtter", currentItems); useEffect(() => { const tooltipTriggerList = Array.from( document.querySelectorAll('[data-bs-toggle="tooltip"]') @@ -131,6 +133,31 @@ const ProjectList = () => { tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); }, []); + const handler = useCallback( + async (msg) => { + if (HasManageProject && msg.keyword === "Create_Project") { + const updatedProjects = [...projectList, msg.response]; + cacheData("projectslist", updatedProjects); + setProjectList(updatedProjects); + sortingProject(updatedProjects); + } + + if ( + msg.keyword === "Update_Project" && + projectList.some((item) => item.id === msg.response.id) + ) { + await refetch(); + sortingProject(projects); + } + }, + [HasManageProject, projects, sortingProject] + ); + + useEffect(() => { + eventBus.on("project", handler); + return () => eventBus.off("project", handler); + }, [handler]); + return ( <>
{
- -
- - -
- + +
+ + +
@@ -341,7 +367,11 @@ const ProjectList = () => { ) : ( currentItems.map((project) => ( - + )) )} @@ -349,7 +379,11 @@ const ProjectList = () => {
) : ( currentItems.map((project) => ( - + )) )} diff --git a/src/pages/project/ProjectListView.jsx b/src/pages/project/ProjectListView.jsx index 0b2682e0..f16f7292 100644 --- a/src/pages/project/ProjectListView.jsx +++ b/src/pages/project/ProjectListView.jsx @@ -22,6 +22,10 @@ const ProjectListView = ({ projectData, recall }) => { const navigate = useNavigate(); const ManageProject = useHasUserPermission(MANAGE_PROJECT); + useEffect(()=>{ + setProjectInfo(projectData); + },[projectData]) + const handleShow = async () => { try { const response = await ProjectRepository.getProjectByprojectId( diff --git a/src/services/signalRService.js b/src/services/signalRService.js index 2c0d432d..1fd5a5ac 100644 --- a/src/services/signalRService.js +++ b/src/services/signalRService.js @@ -3,6 +3,7 @@ import { clearCacheKey, getCachedData } from "../slices/apiDataManager"; import showToast from "./toastService"; import eventBus from "./eventBus"; import { useSelector } from "react-redux"; +import { clearApiCacheKey } from "../slices/apiCacheSlice"; const base_Url = process.env.VITE_BASE_URL; // const base_Url = "https://devapi.marcoaiot.com"; let connection = null; @@ -25,19 +26,30 @@ export function startSignalR(loggedUser) { ) .toISOString() .split("T")[0]; - connection.on("Attendance", (data) => { - const checkIn = data.response.checkInTime.substring(0, 10); - if (data.loginUserId != loggedUser?.employeeInfo.id) { - if (today === checkIn) { - eventBus.emit("attendance", data); - } - var onlyDate = Number(checkIn.substring(8, 10)); + connection.on("NotificationEventHandler", (data) => { + // console.log("Notification received:", data); + if (data.keyword == "Attendance") { + const checkIn = data.response.checkInTime.substring(0, 10); + if (data.loginUserId != loggedUser?.employeeInfo.id) { + if (today === checkIn) { + eventBus.emit("attendance", data); + } + var onlyDate = Number(checkIn.substring(8, 10)); - var afterTwoDay = checkIn.substring(0, 8) + (onlyDate + 2).toString().padStart(2, "0");; - if(afterTwoDay <= today && (data.response.activity == 4 || data.response.activity == 5)){ - eventBus.emit("regularization", data); + var afterTwoDay = + checkIn.substring(0, 8) + (onlyDate + 2).toString().padStart(2, "0"); + if ( + afterTwoDay <= today && + (data.response.activity == 4 || data.response.activity == 5) + ) { + eventBus.emit("regularization", data); + } + eventBus.emit("attendance_log", data); } - eventBus.emit("attendance_log", data); + } + if (data.keyword == "Create_Project" || data.keyword == "Update_Project") { + clearCacheKey("projectslist"); + eventBus.emit("project", data); } });