Implemented signalR for create and update project

This commit is contained in:
ashutosh.nehete 2025-06-12 19:39:56 +05:30
parent a0e4b1c5ed
commit ed2fa2d5b0
8 changed files with 160 additions and 72 deletions

View File

@ -1,9 +1,13 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import moment from "moment"; import moment from "moment";
import { getProjectStatusName } from "../../utils/projectStatus"; import { getProjectStatusName } from "../../utils/projectStatus";
const AboutProject = ({ data }) => { const AboutProject = ({ data }) => {
const [CurrentProject, setCurrentProject] = useState(data); const [CurrentProject, setCurrentProject] = useState(data);
useEffect(() => {
setCurrentProject(data);
}, [data]);
return ( return (
<> <>
{data && ( {data && (

View File

@ -12,10 +12,15 @@ const ProjectBanner = ({ project_data }) => {
const [showModal, setShowModal] = useState(false); const [showModal, setShowModal] = useState(false);
const manageProject = useHasUserPermission(MANAGE_PROJECT); const manageProject = useHasUserPermission(MANAGE_PROJECT);
const [CurrentProject, setCurrentProject] = useState(project_data); const [CurrentProject, setCurrentProject] = useState(project_data);
if (project_data == null) { if (project_data == null) {
return <span>incomplete project information</span>; return <span>incomplete project information</span>;
} }
useEffect(() => {
setCurrentProject(project_data);
}, [project_data]);
const handleShow = () => setShowModal(true); const handleShow = () => setShowModal(true);
const handleClose = () => setShowModal(false); const handleClose = () => setShowModal(false);

View File

@ -1,4 +1,4 @@
import React, { useState } from "react"; import React, { useEffect, useState } from "react";
import moment from "moment"; import moment from "moment";
import { getDateDifferenceInDays } from "../../utils/dateUtils"; import { getDateDifferenceInDays } from "../../utils/dateUtils";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
@ -22,6 +22,9 @@ const ProjectCard = ({ projectData, recall }) => {
const ManageProject = useHasUserPermission(MANAGE_PROJECT); const ManageProject = useHasUserPermission(MANAGE_PROJECT);
const [modifyProjectLoading, setMdifyProjectLoading] = useState(false); const [modifyProjectLoading, setMdifyProjectLoading] = useState(false);
useEffect(()=>{
setProjectInfo(projectData);
},[projectData])
const handleShow = async () => { const handleShow = async () => {
try { try {
setMdifyProjectLoading(true); setMdifyProjectLoading(true);

View File

@ -58,7 +58,7 @@ const AttendancePage = () => {
setAttendances(updatedAttendance); setAttendances(updatedAttendance);
} }
}, },
[selectedProject, attrecall] [selectedProject,attendances]
); );
const getRole = (roleId) => { const getRole = (roleId) => {

View File

@ -1,5 +1,5 @@
import { useParams } from "react-router-dom"; 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 ActivityTimeline from "../../components/Project/ActivityTimeline";
import ProjectOverview from "../../components/Project/ProjectOverview"; import ProjectOverview from "../../components/Project/ProjectOverview";
@ -11,7 +11,7 @@ import ProjectInfra from "../../components/Project/ProjectInfra";
import Loader from "../../components/common/Loader"; import Loader from "../../components/common/Loader";
import WorkPlan from "../../components/Project/WorkPlan"; import WorkPlan from "../../components/Project/WorkPlan";
import Breadcrumb from "../../components/common/Breadcrumb"; 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 ProjectRepository from "../../repositories/ProjectRepository";
import { ActivityeRepository } from "../../repositories/MastersRepository"; import { ActivityeRepository } from "../../repositories/MastersRepository";
import "./ProjectDetails.css"; import "./ProjectDetails.css";
@ -23,6 +23,7 @@ import { useDispatch } from "react-redux";
import { setProjectId } from "../../slices/localVariablesSlice"; import { setProjectId } from "../../slices/localVariablesSlice";
import { ComingSoonPage } from "../Misc/ComingSoonPage"; import { ComingSoonPage } from "../Misc/ComingSoonPage";
import Directory from "../Directory/Directory"; import Directory from "../Directory/Directory";
import eventBus from "../../services/eventBus";
const ProjectDetails = () => { const ProjectDetails = () => {
let { projectId } = useParams(); let { projectId } = useParams();
@ -121,7 +122,7 @@ const ProjectDetails = () => {
case "directory": { case "directory": {
return ( return (
<div className="row"> <div className="row">
<Directory IsPage={ false} prefernceContacts={projectDetails.id} /> <Directory IsPage={false} prefernceContacts={projectDetails.id} />
</div> </div>
); );
} }
@ -137,6 +138,31 @@ const ProjectDetails = () => {
setProjectDetails(projects_Details); setProjectDetails(projects_Details);
}, [projects_Details, projectId]); }, [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 ( return (
<> <>
{} {}

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect, useCallback } from "react";
import ProjectCard from "../../components/Project/ProjectCard"; import ProjectCard from "../../components/Project/ProjectCard";
import ManageProjectInfo from "../../components/Project/ManageProjectInfo"; import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
import Breadcrumb from "../../components/common/Breadcrumb"; import Breadcrumb from "../../components/common/Breadcrumb";
@ -11,6 +11,8 @@ import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { useProfile } from "../../hooks/useProfile"; import { useProfile } from "../../hooks/useProfile";
import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants"; import { ITEMS_PER_PAGE, MANAGE_PROJECT } from "../../utils/constants";
import ProjectListView from "./ProjectListView"; import ProjectListView from "./ProjectListView";
import eventBus from "../../services/eventBus";
import { clearApiCacheKey } from "../../slices/apiCacheSlice";
const ProjectList = () => { const ProjectList = () => {
const { profile: loginUser } = useProfile(); const { profile: loginUser } = useProfile();
@ -37,7 +39,7 @@ const ProjectList = () => {
const handleShow = () => setShowModal(true); const handleShow = () => setShowModal(true);
const handleClose = () => setShowModal(false); const handleClose = () => setShowModal(false);
const sortingProject = (projects) =>{ const sortingProject = (projects) => {
if (!loading && Array.isArray(projects)) { if (!loading && Array.isArray(projects)) {
const grouped = {}; const grouped = {};
projects.forEach((project) => { projects.forEach((project) => {
@ -56,10 +58,10 @@ const ProjectList = () => {
setProjectList(sortedGrouped); setProjectList(sortedGrouped);
} }
} };
useEffect(() => { useEffect(() => {
sortingProject(projects) sortingProject(projects);
}, [projects, loginUser?.projects, loading]); }, [projects, loginUser?.projects, loading]);
useEffect(() => { useEffect(() => {
@ -70,16 +72,16 @@ const ProjectList = () => {
} }
}, [loginUser, HasManageProjectPermission]); }, [loginUser, HasManageProjectPermission]);
const handleSubmitForm = (newProject,setloading,reset) => { const handleSubmitForm = (newProject, setloading, reset) => {
ProjectRepository.manageProject(newProject) ProjectRepository.manageProject(newProject)
.then((response) => { .then((response) => {
const cachedProjects = getCachedData("projectslist") || []; const cachedProjects = getCachedData("projectslist") || [];
const updatedProjects = [...cachedProjects, response.data]; const updatedProjects = [...cachedProjects, response.data];
cacheData("projectslist", updatedProjects); cacheData("projectslist", updatedProjects);
setProjectList( ( prev ) => [ ...prev, response.data ] ); setProjectList((prev) => [...prev, response.data]);
setloading( false ) setloading(false);
reset() reset();
sortingProject(getCachedData("projectslist")) sortingProject(getCachedData("projectslist"));
showToast("Project Created successfully.", "success"); showToast("Project Created successfully.", "success");
setShowModal(false); setShowModal(false);
}) })
@ -123,7 +125,7 @@ const ProjectList = () => {
indexOfLastItem indexOfLastItem
); );
const totalPages = Math.ceil(filteredProjects.length / itemsPerPage); const totalPages = Math.ceil(filteredProjects.length / itemsPerPage);
console.log("filtter", currentItems);
useEffect(() => { useEffect(() => {
const tooltipTriggerList = Array.from( const tooltipTriggerList = Array.from(
document.querySelectorAll('[data-bs-toggle="tooltip"]') document.querySelectorAll('[data-bs-toggle="tooltip"]')
@ -131,6 +133,31 @@ const ProjectList = () => {
tooltipTriggerList.forEach((el) => new bootstrap.Tooltip(el)); 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 ( return (
<> <>
<div <div
@ -200,49 +227,48 @@ const ProjectList = () => {
<i className="bx bx-list-ul bx-sm"></i> <i className="bx bx-list-ul bx-sm"></i>
</button> </button>
</div> </div>
<div className="dropdown ms-3"> <div className="dropdown ms-3">
<a <a
className="dropdown-toggle hide-arrow cursor-pointer" className="dropdown-toggle hide-arrow cursor-pointer"
data-bs-toggle="dropdown" data-bs-toggle="dropdown"
aria-expanded="false" aria-expanded="false"
> >
<i className="bx bx-filter bx-lg"></i> <i className="bx bx-filter bx-lg"></i>
</a> </a>
<ul className="dropdown-menu p-2 text-capitalize"> <ul className="dropdown-menu p-2 text-capitalize">
{[ {[
{ {
id: "b74da4c2-d07e-46f2-9919-e75e49b12731", id: "b74da4c2-d07e-46f2-9919-e75e49b12731",
label: "Active", label: "Active",
}, },
{ {
id: "603e994b-a27f-4e5d-a251-f3d69b0498ba", id: "603e994b-a27f-4e5d-a251-f3d69b0498ba",
label: "On Hold", label: "On Hold",
}, },
{ {
id: "ef1c356e-0fe0-42df-a5d3-8daee355492d", id: "ef1c356e-0fe0-42df-a5d3-8daee355492d",
label: "Inactive", label: "Inactive",
}, },
{ {
id: "33deaef9-9af1-4f2a-b443-681ea0d04f81", id: "33deaef9-9af1-4f2a-b443-681ea0d04f81",
label: "Completed", label: "Completed",
}, },
].map(({ id, label }) => ( ].map(({ id, label }) => (
<li key={id}> <li key={id}>
<div className="form-check"> <div className="form-check">
<input <input
className="form-check-input " className="form-check-input "
type="checkbox" type="checkbox"
checked={selectedStatuses.includes(id)} checked={selectedStatuses.includes(id)}
onChange={() => handleStatusChange(id)} onChange={() => handleStatusChange(id)}
/> />
<label className="form-check-label">{label}</label> <label className="form-check-label">{label}</label>
</div> </div>
</li> </li>
))} ))}
</ul> </ul>
</div> </div>
</div> </div>
<div> <div>
@ -341,7 +367,11 @@ const ProjectList = () => {
</tr> </tr>
) : ( ) : (
currentItems.map((project) => ( currentItems.map((project) => (
<ProjectListView key={project.id} projectData={project} recall={sortingProject} /> <ProjectListView
key={project.id}
projectData={project}
recall={sortingProject}
/>
)) ))
)} )}
</tbody> </tbody>
@ -349,7 +379,11 @@ const ProjectList = () => {
</div> </div>
) : ( ) : (
currentItems.map((project) => ( currentItems.map((project) => (
<ProjectCard key={project.id} projectData={project} recall={sortingProject} /> <ProjectCard
key={project.id}
projectData={project}
recall={sortingProject}
/>
)) ))
)} )}
</div> </div>

View File

@ -22,6 +22,10 @@ const ProjectListView = ({ projectData, recall }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const ManageProject = useHasUserPermission(MANAGE_PROJECT); const ManageProject = useHasUserPermission(MANAGE_PROJECT);
useEffect(()=>{
setProjectInfo(projectData);
},[projectData])
const handleShow = async () => { const handleShow = async () => {
try { try {
const response = await ProjectRepository.getProjectByprojectId( const response = await ProjectRepository.getProjectByprojectId(

View File

@ -3,6 +3,7 @@ import { clearCacheKey, getCachedData } from "../slices/apiDataManager";
import showToast from "./toastService"; import showToast from "./toastService";
import eventBus from "./eventBus"; import eventBus from "./eventBus";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { clearApiCacheKey } from "../slices/apiCacheSlice";
const base_Url = process.env.VITE_BASE_URL; const base_Url = process.env.VITE_BASE_URL;
// const base_Url = "https://devapi.marcoaiot.com"; // const base_Url = "https://devapi.marcoaiot.com";
let connection = null; let connection = null;
@ -25,19 +26,30 @@ export function startSignalR(loggedUser) {
) )
.toISOString() .toISOString()
.split("T")[0]; .split("T")[0];
connection.on("Attendance", (data) => { connection.on("NotificationEventHandler", (data) => {
const checkIn = data.response.checkInTime.substring(0, 10); // console.log("Notification received:", data);
if (data.loginUserId != loggedUser?.employeeInfo.id) { if (data.keyword == "Attendance") {
if (today === checkIn) { const checkIn = data.response.checkInTime.substring(0, 10);
eventBus.emit("attendance", data); if (data.loginUserId != loggedUser?.employeeInfo.id) {
} if (today === checkIn) {
var onlyDate = Number(checkIn.substring(8, 10)); eventBus.emit("attendance", data);
}
var onlyDate = Number(checkIn.substring(8, 10));
var afterTwoDay = checkIn.substring(0, 8) + (onlyDate + 2).toString().padStart(2, "0");; var afterTwoDay =
if(afterTwoDay <= today && (data.response.activity == 4 || data.response.activity == 5)){ checkIn.substring(0, 8) + (onlyDate + 2).toString().padStart(2, "0");
eventBus.emit("regularization", data); 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);
} }
}); });