Merge branch 'vikas_78_infra_ui_enhancement' into Feature_Task_Management

This commit is contained in:
Vikas Nale 2025-04-16 16:11:55 +05:30
commit 51ac6cf65f
10 changed files with 292 additions and 258 deletions

View File

@ -128,3 +128,14 @@
text-align: center; text-align: center;
margin-top: 1rem; margin-top: 1rem;
} }
.infra-activity-table-header {
border-top: 0;
text-transform: capitalize !important;
}
.infra-activity-table-header-first {
border-top: 0;
text-transform: capitalize !important;
text-align: left;
padding-left: 60px;
}

View File

@ -2,47 +2,49 @@ import React from "react";
const EmployeeNav = ({ onPillClick, activePill }) => { const EmployeeNav = ({ onPillClick, activePill }) => {
return ( return (
<div className="col-md-12"> <div className="nav-align-top ">
<div className="nav-align-top"> <ul className="nav nav-tabs">
<ul className="nav nav-pills flex-column flex-sm-row mb-6"> <li className="nav-item">
<li className="nav-item"> <a
<a className={`nav-link ${activePill === "account" ? "active" : ""}`}
className={`nav-link ${activePill === "account" ? "active" : ""}`} href="#"
href="#" onClick={(e) => {
onClick={(e) => { e.preventDefault(); // Prevent page reload
e.preventDefault(); // Prevent page reload onPillClick("account");
onPillClick("account"); }}
}} >
> <i className="bx bx-user bx-sm me-1_5"></i> Account
<i className="bx bx-user bx-sm me-1_5"></i> Account </a>
</a> </li>
</li> <li className="nav-item">
<li className="nav-item"> <a
<a className={`nav-link ${
className={`nav-link ${activePill === "attendance" ? "active" : ""}`} activePill === "attendance" ? "active" : ""
href="#" }`}
onClick={(e) => { href="#"
e.preventDefault(); // Prevent page reload onClick={(e) => {
onPillClick("attendance"); e.preventDefault(); // Prevent page reload
}} onPillClick("attendance");
> }}
<i className="bx bx-group bx-sm me-1_5"></i> Attendances >
</a> <i className="bx bx-group bx-sm me-1_5"></i> Attendances
</li> </a>
<li className="nav-item"> </li>
<a <li className="nav-item">
className={`nav-link ${activePill === "activities" ? "active" : ""}`} <a
href="#" className={`nav-link ${
onClick={(e) => { activePill === "activities" ? "active" : ""
e.preventDefault(); // Prevent page reload }`}
onPillClick("activities"); href="#"
}} onClick={(e) => {
> e.preventDefault(); // Prevent page reload
<i className="bx bx-grid-alt bx-sm me-1_5"></i> Activities onPillClick("activities");
</a> }}
</li> >
</ul> <i className="bx bx-grid-alt bx-sm me-1_5"></i> Activities
</div> </a>
</li>
</ul>
</div> </div>
); );
}; };

View File

@ -1,31 +1,38 @@
import React from "react"; import React from "react";
const Building = ( {building, toggleBuilding, expandedBuildings, getContent} ) => const Building = ({
{ building,
toggleBuilding,
expandedBuildings,
getContent,
}) => {
return (
<React.Fragment key={building.id}>
<tr className="overflow-auto">
<td
colSpan="4"
className="text-start"
style={{
background: "#fafafa",
cursor: "pointer",
textAlign: "center!important",
}}
onClick={() => toggleBuilding(building.id)}
>
<div className="row table-responsive">
<h6 style={{ marginBottom: "0px", fontSize: "14px" }}>
{building.name} &nbsp;
{expandedBuildings.includes(building.id) ? (
<i className="bx bx-chevron-down"></i>
) : (
<i className="bx bx-chevron-right"></i>
)}
</h6>
</div>
</td>
</tr>
return ( {expandedBuildings.includes(building.id) && getContent(building)}
<React.Fragment key={building.id}> </React.Fragment>
<tr className="overflow-auto"> );
<td };
colSpan="4" export default Building;
className="text-start"
style={{ background: "#f0f0f0", cursor: "pointer" }}
onClick={() => toggleBuilding(building.id)}
>
<div className="row table-responsive">
<h6 style={{ marginBottom: "0px", fontSize:"14px" }}>
{building.name} &nbsp;
{expandedBuildings.includes(building.id) ? (
<i className="bx bx-chevron-down"></i>
) : (
<i className="bx bx-chevron-right"></i>
)}
</h6>
</div>
</td>
</tr>
{expandedBuildings.includes(building.id) && getContent(building)}
</React.Fragment>
);
};
export default Building

View File

@ -1,29 +1,31 @@
import React from "react"; import React from "react";
import WorkArea from "./WorkArea"; import WorkArea from "./WorkArea";
const Floor = ( {floor, workAreas,forBuilding} ) => const Floor = ({ floor, workAreas, forBuilding }) => {
{ return (
<React.Fragment key={floor.id}>
return ( {workAreas && workAreas.length > 0 ? (
<React.Fragment key={floor.id}> workAreas.map((workArea) => (
{workAreas && workAreas.length > 0 ? ( <WorkArea
workAreas.map((workArea) => ( forBuilding={forBuilding}
<WorkArea forBuilding={forBuilding} key={workArea.id} workArea={workArea} floor={floor} /> key={workArea.id}
)) workArea={workArea}
) : ( floor={floor}
<tr> />
<td colSpan="4" className="text-start"> ))
<div className="row ps-2"> ) : (
<div className="col-6"> <tr>
<h6> <td colSpan="4" className="text-start">
<span>{floor.floorName} &nbsp;</span> <div className="row ps-2">
</h6> <div className="col-6">
</div> <h6 className="infra-floor-lable">
<span>{floor.floorName} &nbsp;</span>
</h6>
</div> </div>
</td> </div>
</tr> </td>
)} </tr>
)}
</React.Fragment> </React.Fragment>
); );
}; };
export default Floor export default Floor;

View File

@ -73,7 +73,6 @@ const InfraTable = ({ buildings }) => {
} }
}; };
const handleClearComplete = () => { const handleClearComplete = () => {
setClearTrigger(false); setClearTrigger(false);
}; };
@ -91,8 +90,11 @@ const InfraTable = ({ buildings }) => {
) : ( ) : (
<tr> <tr>
<td colSpan="4"> <td colSpan="4">
<div className="alert alert-warning text-center mb-0" role="alert"> <div className=" mb-0" role="alert">
<p>No floors have been added yet. Please add floors to start managing your building.</p> <p className="fw-semibold">
No floors have been added yet. Start by adding floors to manage
this building.
</p>
<button <button
type="button" type="button"
className="btn btn-xs btn-primary" className="btn btn-xs btn-primary"
@ -111,7 +113,7 @@ const InfraTable = ({ buildings }) => {
}, [buildings]); }, [buildings]);
return ( return (
<div > <div>
{projectBuilding && projectBuilding.length > 0 && ( {projectBuilding && projectBuilding.length > 0 && (
<table className="table table-bordered"> <table className="table table-bordered">
<tbody> <tbody>
@ -138,7 +140,7 @@ const InfraTable = ({ buildings }) => {
> >
<FloorModel <FloorModel
project={{ project={{
buildings: [selectedBuilding] buildings: [selectedBuilding],
}} }}
onClose={() => setShowFloorModal(false)} onClose={() => setShowFloorModal(false)}
onSubmit={handleFloorSubmit} onSubmit={handleFloorSubmit}
@ -146,7 +148,6 @@ const InfraTable = ({ buildings }) => {
/> />
</div> </div>
)} )}
</div> </div>
); );
}; };

View File

@ -1,22 +1,25 @@
import React,{useEffect} from "react"; import React, { useEffect } from "react";
import WorkItem from "./WorkItem"; import WorkItem from "./WorkItem";
const WorkArea = ( {workArea, floor, forBuilding} ) => const WorkArea = ({ workArea, floor, forBuilding }) => {
{ useEffect(() => {}, [workArea]);
useEffect(() => {
}, [workArea]);
return ( return (
<React.Fragment key={workArea.id}> <React.Fragment key={workArea.id}>
<tr> <tr>
<td colSpan="4" className="text-start table-cell"> <td colSpan="4" className="text-start table-cell">
<div className="row ps-2"> <div className="row ps-2">
<div className="col-6"> <div className="col-6">
<h6> <div className="row">
<span> <div className="col">
{floor.floorName} - {workArea.areaName} &nbsp; {" "}
</span> <span className="fw-semibold">Floor:&nbsp;</span>{" "}
</h6> <span class="fw-normal">{floor.floorName}</span>
</div>
<div className="col">
<span className="ms-10 fw-semibold">Work Area:&nbsp;</span>{" "}
<span class=" fw-normal">{workArea.areaName}</span>
</div>
</div>
</div> </div>
</div> </div>
</td> </td>
@ -28,21 +31,35 @@ const WorkArea = ( {workArea, floor, forBuilding} ) =>
<table className="table mx-1"> <table className="table mx-1">
<thead> <thead>
<tr> <tr>
<th>Activity</th> <th className="infra-activity-table-header-first">
Activity
</th>
{/* for mobile view */} {/* for mobile view */}
<th className="d-sm-none d-sm-table-cell">Status</th> <th className="infra-activity-table-header d-sm-none d-sm-table-cell">
Status
</th>
{/* for greather than mobile view ************* */} {/* for greather than mobile view ************* */}
<th className="d-none d-md-table-cell">Planned</th> <th className="infra-activity-table-header d-none d-md-table-cell">
<th className="d-none d-md-table-cell">Completed</th> Planned
</th>
<th className="infra-activity-table-header d-none d-md-table-cell">
Completed
</th>
{/* ************************** */} {/* ************************** */}
<th>Progress</th> <th className="infra-activity-table-header">Progress</th>
<th>Actions</th> <th className="infra-activity-table-header">Actions</th>
</tr> </tr>
</thead> </thead>
<tbody className="table-border-bottom-0"> <tbody className="table-border-bottom-0">
{workArea?.workItems && workArea.workItems.length > 0 ? ( {workArea?.workItems && workArea.workItems.length > 0 ? (
workArea.workItems.map((workItem) => ( workArea.workItems.map((workItem) => (
<WorkItem key={workItem.workItemId} workItem={workItem} forBuilding={forBuilding} forFloor={floor} forWorkArea={workArea} /> <WorkItem
key={workItem.workItemId}
workItem={workItem}
forBuilding={forBuilding}
forFloor={floor}
forWorkArea={workArea}
/>
)) ))
) : ( ) : (
<tr> <tr>

View File

@ -20,8 +20,6 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
setItemName(""); setItemName("");
}; };
useEffect(() => { useEffect(() => {
setNewWorkItem(workItem); setNewWorkItem(workItem);
}, [workItem]); // This hook will run whenever the workItem prop changes }, [workItem]); // This hook will run whenever the workItem prop changes
@ -33,8 +31,6 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
workItem, workItem,
}; };
const hasWorkItem = NewWorkItem && NewWorkItem; const hasWorkItem = NewWorkItem && NewWorkItem;
return ( return (
@ -51,7 +47,7 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
<tr> <tr>
<td className="text-start table-cell-small"> <td className="text-start table-cell-small">
<i className="bx bx-right-arrow-alt"></i> <i className="bx bx-right-arrow-alt"></i>
<span className="fw-medium"> <span className="fw-light">
{hasWorkItem {hasWorkItem
? NewWorkItem?.workItem?.activityMaster?.activityName || ? NewWorkItem?.workItem?.activityMaster?.activityName ||
workItem.activityMaster?.activityName workItem.activityMaster?.activityName
@ -91,7 +87,7 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
NewWorkItem?.workItem?.completedWork || NewWorkItem?.workItem?.completedWork ||
workItem?.completedWork workItem?.completedWork
), ),
height: "10px", height: "5px",
}} }}
aria-valuenow={ aria-valuenow={
hasWorkItem hasWorkItem
@ -120,7 +116,9 @@ const WorkItem = ({ workItem, forBuilding, forFloor, forWorkArea }) => {
data-bs-target="#project-modal" data-bs-target="#project-modal"
onClick={openModal} onClick={openModal}
> >
<span className="badge badge-md bg-label-primary me-1">Assign</span> <span className="badge badge-md bg-label-primary me-1">
Assign
</span>
</button> </button>
)} )}
<button <button

View File

@ -1,53 +1,51 @@
import React from "react"; import React from "react";
import {hasUserPermission} from "../../utils/authUtils"; import { hasUserPermission } from "../../utils/authUtils";
import {useHasUserPermission} from "../../hooks/useHasUserPermission"; import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { VIEW_PROJECT_INFRA} from "../../utils/constants"; import { VIEW_PROJECT_INFRA } from "../../utils/constants";
const ProjectNav = ( {onPillClick, activePill} ) => const ProjectNav = ({ onPillClick, activePill }) => {
{ const HasViewInfraStructure = useHasUserPermission(VIEW_PROJECT_INFRA);
const HasViewInfraStructure = useHasUserPermission( VIEW_PROJECT_INFRA )
return ( return (
<div className="col-md-12"> <div className="nav-align-top">
<div className="nav-align-top"> <ul className="nav nav-tabs ">
<ul className="nav nav-pills flex-column flex-sm-row mb-6"> <li className="nav-item">
<li className="nav-item"> <a
<a className={`nav-link ${activePill === "profile" ? "active" : ""}`}
className={`nav-link ${activePill === "profile" ? "active" : ""}`} href="#"
href="#" onClick={(e) => {
onClick={(e) => { e.preventDefault(); // Prevent page reload
e.preventDefault(); // Prevent page reload onPillClick("profile");
onPillClick("profile"); }}
}} >
> <i className="bx bx-user bx-sm me-1_5"></i> Profile
<i className="bx bx-user bx-sm me-1_5"></i> Profile </a>
</a> </li>
</li> <li className="nav-item">
<li className="nav-item"> <a
<a className={`nav-link ${activePill === "teams" ? "active" : ""}`}
className={`nav-link ${activePill === "teams" ? "active" : ""}`} href="#"
href="#" onClick={(e) => {
onClick={(e) => { e.preventDefault(); // Prevent page reload
e.preventDefault(); // Prevent page reload onPillClick("teams");
onPillClick("teams"); }}
}} >
> <i className="bx bx-group bx-sm me-1_5"></i> Teams
<i className="bx bx-group bx-sm me-1_5"></i> Teams </a>
</a> </li>
</li> <li className={`nav-item ${!HasViewInfraStructure && "d-none"} `}>
<li className={`nav-item ${!HasViewInfraStructure && 'd-none'} `}> <a
<a className={`nav-link ${activePill === "infra" ? "active" : ""}`}
className={`nav-link ${activePill === "infra" ? "active" : ""}`} href="#"
href="#" onClick={(e) => {
onClick={(e) => { e.preventDefault(); // Prevent page reload
e.preventDefault(); // Prevent page reload onPillClick("infra");
onPillClick("infra"); }}
}} >
> <i className="bx bx-grid-alt bx-sm me-1_5"></i> Infrastructure
<i className="bx bx-grid-alt bx-sm me-1_5"></i> Infrastructure </a>
</a> </li>
</li> {/* <li className="nav-item">
{/* <li className="nav-item">
<a <a
className={`nav-link ${ className={`nav-link ${
activePill === "workplan" ? "active" : "" activePill === "workplan" ? "active" : ""
@ -61,36 +59,33 @@ const ProjectNav = ( {onPillClick, activePill} ) =>
<i className="bx bx-link bx-sm me-1_5"></i> Work Plan <i className="bx bx-link bx-sm me-1_5"></i> Work Plan
</a> </a>
</li> */} </li> */}
<li className="nav-item"> <li className="nav-item">
<a <a
className={`nav-link ${ className={`nav-link ${
activePill === "imagegallary" ? "active" : "" activePill === "imagegallary" ? "active" : ""
}`} }`}
href="#" href="#"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); // Prevent page reload e.preventDefault(); // Prevent page reload
onPillClick("imagegallary"); onPillClick("imagegallary");
}} }}
> >
<i className="bx bx-link bx-sm me-1_5"></i> Image Gallary <i className="bx bx-link bx-sm me-1_5"></i> Image Gallary
</a> </a>
</li> </li>
<li className="nav-item"> <li className="nav-item">
<a <a
className={`nav-link ${ className={`nav-link ${activePill === "directory" ? "active" : ""}`}
activePill === "directory" ? "active" : "" href="#"
}`} onClick={(e) => {
href="#" e.preventDefault(); // Prevent page reload
onClick={(e) => { onPillClick("directory");
e.preventDefault(); // Prevent page reload }}
onPillClick("directory"); >
}} <i className="bx bx-link bx-sm me-1_5"></i> Directory
> </a>
<i className="bx bx-link bx-sm me-1_5"></i> Directory </li>
</a> </ul>
</li>
</ul>
</div>
</div> </div>
); );
}; };

View File

@ -3,12 +3,13 @@ import React from "react";
const Loader = () => { const Loader = () => {
return ( return (
<div className="demo-inline-spacing"> <div className="demo-inline-spacing">
<div className="spinner-grow" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div className="spinner-grow text-primary" role="status"> <div className="spinner-grow text-primary" role="status">
<span className="visually-hidden">Loading...</span> <span className="visually-hidden">Loading...</span>
</div> </div>
{/* <div className="spinner-grow" role="status">
<span className="visually-hidden">Loading...</span>
</div>
<div className="spinner-grow text-secondary" role="status"> <div className="spinner-grow text-secondary" role="status">
<span className="visually-hidden">Loading...</span> <span className="visually-hidden">Loading...</span>
</div> </div>
@ -23,13 +24,13 @@ const Loader = () => {
</div> </div>
<div className="spinner-grow text-info" role="status"> <div className="spinner-grow text-info" role="status">
<span className="visually-hidden">Loading...</span> <span className="visually-hidden">Loading...</span>
</div> </div> */}
<div className="spinner-grow text-light" role="status"> <div className="spinner-grow text-light" role="status">
<span className="visually-hidden">Loading...</span> <span className="visually-hidden">Loading...</span>
</div> </div>
<div className="spinner-grow text-dark" role="status"> {/* <div className="spinner-grow text-dark" role="status">
<span className="visually-hidden">Loading...</span> <span className="visually-hidden">Loading...</span>
</div> </div> */}
</div> </div>
); );
}; };

View File

@ -15,52 +15,51 @@ import { cacheData, 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";
import {useEmployeesByProjectAllocated, useProjectDetails} from "../../hooks/useProjects"; import {
import {useDispatch} from "react-redux"; useEmployeesByProjectAllocated,
import {setProjectId} from "../../slices/localVariablesSlice"; useProjectDetails,
} from "../../hooks/useProjects";
import { useDispatch } from "react-redux";
import { setProjectId } from "../../slices/localVariablesSlice";
import { ComingSoonPage } from "../Misc/ComingSoonPage"; import { ComingSoonPage } from "../Misc/ComingSoonPage";
const ProjectDetails = () => { const ProjectDetails = () => {
let {projectId} = useParams(); let { projectId } = useParams();
const {projects_Details,loading:projectLoading,error:ProjectError} = useProjectDetails(projectId) const {
const dispatch = useDispatch() projects_Details,
loading: projectLoading,
error: ProjectError,
} = useProjectDetails(projectId);
const dispatch = useDispatch();
const [project, setProject] = useState(null); const [project, setProject] = useState(null);
const [ projectDetails, setProjectDetails ] = useState( null ); const [projectDetails, setProjectDetails] = useState(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [ error, setError ] = useState( "" ); const [error, setError] = useState("");
const fetchData = async () => { const fetchData = async () => {
const project_cache = getCachedData("projectInfo");
const project_cache = getCachedData("projectInfo"); if (!project_cache || project_cache?.projectId !== projectId) {
if (!project_cache || project_cache?.projectId !== projectId) { ProjectRepository.getProjectByprojectId(projectId)
ProjectRepository.getProjectByprojectId(projectId) .then((response) => {
.then( ( response ) => setProjectDetails(response.data);
{ setProject(response.data);
setProjectDetails( response.data ); cacheData("projectInfo", { projectId, data: response.data });
setProject( response.data ); setLoading(false);
cacheData("projectInfo", {projectId,data: response.data} ); })
setLoading(false) .catch((error) => {
}) console.error(error);
.catch((error) => { setError("Failed to fetch data.");
console.error(error); setLoading(false);
setError( "Failed to fetch data." ); });
setLoading(false) } else {
}); setProjectDetails(project_cache.data);
} else { setProject(project_cache.data);
setProjectDetails( project_cache.data ); setLoading(false);
setProject( project_cache.data ); }
setLoading(false)
}
}; };
const [activePill, setActivePill] = useState("profile"); const [activePill, setActivePill] = useState("profile");
const handlePillClick = (pillKey) => { const handlePillClick = (pillKey) => {
setActivePill(pillKey); setActivePill(pillKey);
}; };
@ -69,19 +68,18 @@ const ProjectDetails = () => {
fetchData(); fetchData();
}; };
const renderContent = () => { const renderContent = () => {
if (projectLoading) return <Loader></Loader>; if (projectLoading) return <Loader></Loader>;
switch (activePill) { switch (activePill) {
case "profile": { case "profile": {
return ( return (
<div className="row"> <div className="row">
<div className="col-xl-4 col-lg-5 col-md-5"> <div className="col-xl-4 col-lg-5 col-md-5 mt-5">
{/* About User */} {/* About User */}
<AboutProject data={projectDetails}></AboutProject> <AboutProject data={projectDetails}></AboutProject>
{/* About User */} {/* About User */}
</div> </div>
<div className="col-xl-4 col-lg-5 col-md-5"> <div className="col-xl-4 col-lg-5 col-md-5 mt-5">
{/* Profile Overview */} {/* Profile Overview */}
<ProjectOverview project={projectId} /> <ProjectOverview project={projectId} />
{/* Profile Overview */} {/* Profile Overview */}
@ -130,15 +128,15 @@ const ProjectDetails = () => {
} }
default: default:
return <ComingSoonPage></ComingSoonPage>; return <ComingSoonPage></ComingSoonPage>;
} }
}; };
useEffect(() => { useEffect(() => {
dispatch(setProjectId(projectId)) dispatch(setProjectId(projectId));
setProject( projects_Details ) setProject(projects_Details);
setProjectDetails(projects_Details) setProjectDetails(projects_Details);
}, [projects_Details,projectId]); }, [projects_Details, projectId]);
return ( return (
<> <>
@ -154,16 +152,18 @@ const ProjectDetails = () => {
<div className="row"> <div className="row">
{projectLoading && <p>Loading....</p>} {projectLoading && <p>Loading....</p>}
{(!projectLoading && project) && <ProjectBanner project_data={project} ></ProjectBanner>} {!projectLoading && project && (
</div> <ProjectBanner project_data={project}></ProjectBanner>
)}
<div className="row">
<ProjectNav <ProjectNav
onPillClick={handlePillClick} onPillClick={handlePillClick}
activePill={activePill} activePill={activePill}
></ProjectNav> ></ProjectNav>
</div> </div>
<div className="row"></div>
{renderContent()} {renderContent()}
</div> </div>
</> </>