Compare commits

..

No commits in common. "main" and "Kartik_Bug#1773" have entirely different histories.

17 changed files with 267 additions and 561 deletions

View File

@ -89,7 +89,7 @@
); );
--bs-root-font-size: 16px; --bs-root-font-size: 16px;
--bs-body-font-family: var(--bs-font-sans-serif); --bs-body-font-family: var(--bs-font-sans-serif);
--bs-body-font-size: 0.85rem; --bs-body-font-size: 0.875rem;
--bs-body-font-weight: 400; --bs-body-font-weight: 400;
--bs-body-line-height: 1.375; --bs-body-line-height: 1.375;
--bs-body-color: #646e78; --bs-body-color: #646e78;
@ -9060,7 +9060,7 @@ img[data-app-light-img][data-app-dark-img] {
} }
.table th { .table th {
color: var(--bs-heading-color); color: var(--bs-heading-color);
font-size: 0.8025rem; font-size: 0.8125rem;
letter-spacing: 0.2px; letter-spacing: 0.2px;
text-transform: uppercase; text-transform: uppercase;
} }
@ -20345,7 +20345,7 @@ li:not(:first-child) .dropdown-item,
} }
.fs-6 { .fs-6 {
font-size: 0.8375rem !important; font-size: 0.9375rem !important;
} }
.fs-tiny { .fs-tiny {

View File

@ -1,80 +1,54 @@
import { useState } from "react"; import { useState } from "react";
const PreviewDocument = ({ imageUrl }) => { const PreviewDocument = ({ imageUrl }) => {
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [rotation, setRotation] = useState(0); const [rotation, setRotation] = useState(0);
const [scale, setScale] = useState(1);
const zoomIn = () => setScale((prev) => Math.min(prev + 0.2, 3));
const zoomOut = () => setScale((prev) => Math.max(prev - 0.2, 0.4));
const resetAll = () => {
setRotation(0);
setScale(1);
};
return ( return (
<> <>
<div className="d-flex justify-content-start gap-3 mb-2"> <div className="d-flex justify-content-start">
<i <i
className="bx bx-rotate-right cursor-pointer fs-4" className="bx bx-rotate-right cursor-pointer"
title="Rotate"
onClick={() => setRotation((prev) => prev + 90)} onClick={() => setRotation((prev) => prev + 90)}
></i> ></i>
</div>
<div
className="position-relative d-flex flex-column justify-content-center align-items-center"
style={{ minHeight: "80vh" }}
>
{loading && (
<div className="text-secondary text-center mb-2">Loading...</div>
)}
<i <div className="mb-3 d-flex justify-content-center align-items-center">
className="bx bx-zoom-in cursor-pointer fs-4" <img
title="Zoom In" src={imageUrl}
onClick={zoomIn} alt="Full View"
></i> className="img-fluid"
style={{
<i maxHeight: "80vh",
className="bx bx-zoom-out cursor-pointer fs-4" objectFit: "contain",
title="Zoom Out" display: loading ? "none" : "block",
onClick={zoomOut} transform: `rotate(${rotation}deg)`,
></i> transition: "transform 0.3s ease",
}}
onLoad={() => setLoading(false)}
/>
</div> </div>
<div <div className="position-absolute bottom-0 start-0 justify-content-center gap-2">
className="position-relative d-flex flex-column justify-content-center align-items-center overflow-hidden" <button
style={{ minHeight: "80vh" }} className="btn btn-outline-secondary"
> onClick={() => setRotation(0)}
{loading && ( title="Reset Rotation"
<div className="text-secondary text-center mb-2"> >
Loading... <i className="bx bx-reset"></i> Reset
</div> </button>
)}
<div className="mb-3 d-flex justify-content-center align-items-center">
<img
src={imageUrl}
alt="Full View"
className="img-fluid"
style={{
maxHeight: "80vh",
objectFit: "contain",
display: loading ? "none" : "block",
transform: `rotate(${rotation}deg) scale(${scale})`,
transition: "transform 0.3s ease",
cursor: "grab",
}}
onLoad={() => setLoading(false)}
/>
</div>
<div className="position-absolute bottom-0 start-0 m-2">
<button
className="btn btn-outline-secondary"
onClick={resetAll}
>
<i className="bx bx-reset"></i> Reset
</button>
</div>
</div> </div>
</> </div>
</>
); );
}; };
export default PreviewDocument; export default PreviewDocument;

View File

@ -25,7 +25,9 @@ const Sidebar = () => {
/> />
</span> */} </span> */}
<small className="app-brand-link fw-bold navbar-brand text-green fs-6"> <small
className="app-brand-link fw-bold navbar-brand text-green fs-6"
>
<span className="app-brand-logo demo"> <span className="app-brand-logo demo">
<img src="/img/brand/marco.png" width="50" /> <img src="/img/brand/marco.png" width="50" />
</span> </span>
@ -35,7 +37,8 @@ const Sidebar = () => {
</small> </small>
</Link> </Link>
<small className="layout-menu-toggle menu-link text-large ms-auto"> <small className="layout-menu-toggle menu-link text-large ms-auto">
<i className="bx bx-chevron-left bx-sm d-flex align-items-center justify-content-center"></i> <i className="bx bx-chevron-left bx-sm d-flex align-items-center justify-content-center"></i>
</small> </small>
</div> </div>
@ -58,7 +61,7 @@ const Sidebar = () => {
</> </>
)} )}
{data && {data &&
data?.data?.map((section) => ( data?.data.map((section) => (
<React.Fragment <React.Fragment
key={section.id || section.header || section.items[0]?.id} key={section.id || section.header || section.items[0]?.id}
> >

View File

@ -8,12 +8,7 @@ const ProjectCardView = ({ data, currentPage, totalPages, paginate }) => {
return ( return (
<div className="row page-min-h"> <div className="row page-min-h">
{data?.length === 0 && ( {data?.length === 0 && (
<div <p className="text-center text-muted">No projects found.</p>
className="col-12 d-flex justify-content-center align-items-center"
style={{ minHeight: "250px" }}
>
<p className="text-center text-muted m-0">No Infra projects found.</p>
</div>
)} )}
{data?.map((project) => ( {data?.map((project) => (

View File

@ -126,8 +126,8 @@ const ProjectListView = ({ data, currentPage, totalPages, paginate }) => {
return ( return (
<div className="card page-min-h py-4 px-6 shadow-sm"> <div className="card page-min-h py-4 px-6 shadow-sm">
<div className="table-responsive text-nowrap"> <div className="table-responsive text-nowrap page-min-h">
<table className="table table-hover align-middle m-0"> <table className="table table-hover align-middle m-0">
<thead className="border-bottom "> <thead className="border-bottom ">
<tr> <tr>
{projectColumns.map((col) => ( {projectColumns.map((col) => (
@ -143,94 +143,77 @@ const ProjectListView = ({ data, currentPage, totalPages, paginate }) => {
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{data?.length > 0 ? ( {data?.map((project) => (
data?.map((project) => ( <tr key={project.id}>
<tr key={project.id}> {projectColumns.map((col) => (
{projectColumns.map((col) => (
<td
key={col.key}
colSpan={col.colSpan}
className={`${col.className} py-5`}
style={{ paddingTop: "20px", paddingBottom: "20px" }}
>
{col.getValue
? col.getValue(project)
: project[col.key] || "N/A"}
</td>
))}
<td <td
className={`mx-2 ${canManageProject ? "d-sm-table-cell" : "d-none" key={col.key}
}`} colSpan={col.colSpan}
className={`${col.className} py-5`}
style={{ paddingTop: "20px", paddingBottom: "20px" }}
> >
<div className="dropdown z-2"> {col.getValue
<button ? col.getValue(project)
type="button" : project[col.key] || "N/A"}
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>
<a
aria-label="click to View details"
className="dropdown-item cursor-pointer"
>
<i className="bx bx-detail me-2"></i>
<span className="align-left">View details</span>
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setMangeProject({
isOpen: true,
Project: project.id,
})
}
>
<i className="bx bx-pencil me-2"></i>
<span className="align-left">Modify</span>
</a>
</li>
<li onClick={() => handleViewActivities(project.id)}>
<a className="dropdown-item cursor-pointer">
<i className="bx bx-task me-2"></i>
<span className="align-left">Activities</span>
</a>
</li>
</ul>
</div>
</td> </td>
</tr> ))}
))
) : (
<tr
className="no-hover"
style={{
pointerEvents: "none",
backgroundColor: "transparent",
}}
>
<td <td
colSpan={projectColumns.length + 1} className={`mx-2 ${
className="text-center align-middle" canManageProject ? "d-sm-table-cell" : "d-none"
style={{ height: "300px", borderBottom: "none" }} }`}
> >
No Infra projects available <div className="dropdown z-2">
<button
type="button"
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>
<a
aria-label="click to View details"
className="dropdown-item cursor-pointer"
>
<i className="bx bx-detail me-2"></i>
<span className="align-left">View details</span>
</a>
</li>
<li>
<a
className="dropdown-item cursor-pointer"
onClick={() =>
setMangeProject({
isOpen: true,
Project: project.id,
})
}
>
<i className="bx bx-pencil me-2"></i>
<span className="align-left">Modify</span>
</a>
</li>
<li onClick={() => handleViewActivities(project.id)}>
<a className="dropdown-item cursor-pointer">
<i className="bx bx-task me-2"></i>
<span className="align-left">Activities</span>
</a>
</li>
</ul>
</div>
</td> </td>
</tr> </tr>
)} ))}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -10,13 +10,11 @@ import {
import useMaster, { useServices } from "../../../hooks/masterHook/useMaster"; import useMaster, { useServices } from "../../../hooks/masterHook/useMaster";
import showToast from "../../../services/toastService"; import showToast from "../../../services/toastService";
import { useOrganizationEmployees } from "../../../hooks/useOrganization"; import { useOrganizationEmployees } from "../../../hooks/useOrganization";
import { useDispatch } from "react-redux";
import { changeMaster } from "../../../slices/localVariablesSlice";
const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => { const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
const selectedProject = useSelectedProject(); const selectedProject = useSelectedProject();
const debounceSearchTerm = useDebounce(searchTerm, 500); const debounceSearchTerm = useDebounce(searchTerm, 500);
const dispatch = useDispatch();
const { const {
data: employeesData = [], data: employeesData = [],
isLoading, isLoading,
@ -47,7 +45,6 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
}); });
useEffect(() => { useEffect(() => {
dispatch(changeMaster("Job Role"));
if (employeesData?.data?.length > 0) { if (employeesData?.data?.length > 0) {
const available = employeesData.data.filter((emp) => { const available = employeesData.data.filter((emp) => {
const projEmp = projectEmployees.find((pe) => pe.employeeId === emp.id); const projEmp = projectEmployees.find((pe) => pe.employeeId === emp.id);
@ -122,7 +119,7 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
status: true, status: true,
})); }));
handleAssignEmployee({ payload, actionType: "assign" }); handleAssignEmployee({ payload,actionType:"assign"} );
setEmployees((prev) => setEmployees((prev) =>
prev.map((emp) => ({ prev.map((emp) => ({
@ -135,26 +132,26 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
); );
}; };
if (isLoading) { if (isLoading) {
return (<div className="page-min-h d-flex justify-content-center align-items-center "><p className="text-muted">Loading employees...</p></div>); return ( <div className="page-min-h d-flex justify-content-center align-items-center "><p className="text-muted">Loading employees...</p></div>) ;
} }
if (isError) { if (isError) {
return ( return (
<div className="page-min-h d-flex justify-content-center align-items-center "> <div className="page-min-h d-flex justify-content-center align-items-center ">
{error?.status === 400 ? ( {error?.status === 400 ? (
<p className="m-0">Enter employee you want to find.</p> <p className="m-0">Enter employee you want to find.</p>
) : ( ) : (
<p className="m-0 dange-text">Something went wrong. Please try again later.</p> <p className="m-0 dange-text">Something went wrong. Please try again later.</p>
)} )}
</div> </div>
); );
} }
if (employees.length === 0) { if (employees.length === 0) {
return (<div className="page-min-h d-flex justify-content-center align-items-center "><p className="text-muted">No available employees to assign.</p></div>); return(<div className="page-min-h d-flex justify-content-center align-items-center "><p className="text-muted">No available employees to assign.</p></div>) ;
} }
return ( return (
@ -186,8 +183,9 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
onChange={(e) => onChange={(e) =>
handleSelectChange(index, "serviceId", e.target.value) handleSelectChange(index, "serviceId", e.target.value)
} }
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${emp.errors.serviceId ? "is-invalid" : "" className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
}`} emp.errors.serviceId ? "is-invalid" : ""
}`}
> >
<option value="">Select Service</option> <option value="">Select Service</option>
{services?.map((s) => ( {services?.map((s) => (
@ -207,8 +205,9 @@ const TeamEmployeeList = ({ organizationId, searchTerm, closeModal }) => {
onChange={(e) => onChange={(e) =>
handleSelectChange(index, "jobRole", e.target.value) handleSelectChange(index, "jobRole", e.target.value)
} }
className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${emp.errors.jobRole ? "is-invalid" : "" className={`form-select form-select-sm w-auto border-none rounded-0 py-1 px-auto ${
}`} emp.errors.jobRole ? "is-invalid" : ""
}`}
> >
<option value="">Select Job Role</option> <option value="">Select Job Role</option>
{jobRoles?.map((r) => ( {jobRoles?.map((r) => (

View File

@ -66,10 +66,19 @@ const Jobs = () => {
<div className="col-12 col-md-6 text-start"> <div className="col-12 col-md-6 text-start">
<button <button
type="button" type="button"
className={`btn btn-sm ${showArchive ? "btn-secondary" : "btn-outline-secondary"}`} className={`btn btn-sm ${showArchive ? "btn-primary" : "btn-outline-secondary"}`}
onClick={() => setShowArchive(!showArchive)} onClick={() => setShowArchive(!showArchive)}
style={{ fontSize: "13px" }}
> >
<i className="bx bx-archive bx-sm me-1 mt-1"></i> Archived {showArchive ? (
<>
<i className="bx bx-list-ul me-1 mt-1"></i> Show Active
</>
) : (
<>
<i className="bx bx-archive me-1 mt-1"></i> Show Archived
</>
)}
</button> </button>
</div> </div>

View File

@ -26,6 +26,13 @@ const ServiceProjectCard = ({ project, isCore = true }) => {
const ManageProject = useHasUserPermission(MANAGE_PROJECT); const ManageProject = useHasUserPermission(MANAGE_PROJECT);
const { setMangeProject, setManageServiceProject } = useProjectContext(); const { setMangeProject, setManageServiceProject } = useProjectContext();
const getProgress = (planned, completed) => {
return (completed * 100) / planned + "%";
};
const getProgressInNumber = (planned, completed) => {
return (completed * 100) / planned;
};
const handleClose = () => setShowModal(false); const handleClose = () => setShowModal(false);
const handleViewProject = () => { const handleViewProject = () => {
@ -36,6 +43,10 @@ const ServiceProjectCard = ({ project, isCore = true }) => {
navigate(`/service-projects/${project.id}`); navigate(`/service-projects/${project.id}`);
} }
}; };
const handleViewActivities = () => {
dispatch(setProjectId(project.id));
navigate(`/activities/records?project=${project.id}`);
};
const handleManage = () => { const handleManage = () => {
if (isCore) { if (isCore) {
setMangeProject({ setMangeProject({
@ -57,8 +68,6 @@ const ServiceProjectCard = ({ project, isCore = true }) => {
DeleteProject(projectId, false); DeleteProject(projectId, false);
}; };
return ( return (
<> <>
<ConfirmModal <ConfirmModal
@ -89,7 +98,7 @@ const ServiceProjectCard = ({ project, isCore = true }) => {
> >
{project?.shortName ? project?.shortName : project?.name} {project?.shortName ? project?.shortName : project?.name}
</h5> </h5>
<div className="client-info text-body text-start"> <div className="client-info text-body">
<span>{project?.shortName ? project?.name : ""}</span> <span>{project?.shortName ? project?.name : ""}</span>
</div> </div>
</div> </div>
@ -129,6 +138,14 @@ const ServiceProjectCard = ({ project, isCore = true }) => {
<span className="align-left">Modify</span> <span className="align-left">Modify</span>
</a> </a>
</li> </li>
{isCore && (
<li onClick={handleViewActivities}>
<a className="dropdown-item">
<i className="bx bx-task me-2"></i>
<span className="align-left">Activities</span>
</a>
</li>
)}
{!isCore && ( {!isCore && (
<li <li
onClick={() => onClick={() =>

View File

@ -1,209 +0,0 @@
import React, { useState } from "react";
import { MANAGE_PROJECT, PROJECT_STATUS } from "../../../utils/constants";
import { useProjects } from "../../../hooks/useProjects";
import { formatNumber, formatUTCToLocalTime } from "../../../utils/dateUtils";
import ProgressBar from "../../common/ProgressBar";
import {
getProjectStatusColor,
getProjectStatusName,
} from "../../../utils/projectStatus";
import { useDispatch } from "react-redux";
import { setProjectId } from "../../../slices/localVariablesSlice";
import { useNavigate } from "react-router-dom";
import { useHasUserPermission } from "../../../hooks/useHasUserPermission";
import { useProjectContext } from "../../../pages/project/ProjectPage";
import usePagination from "../../../hooks/usePagination";
import Pagination from "../../common/Pagination";
const ServiceProjectList = ({
data,
currentPage,
totalPages,
paginate,
isCore = true,
}) => {
const dispatch = useDispatch();
const navigate = useNavigate();
const { setMangeProject, setManageServiceProject } = useProjectContext();
const handleClose = () => setShowModal(false);
// check Permissions
const canManageProject = useHasUserPermission(MANAGE_PROJECT);
const projectColumns = [
{
key: "projectName",
label: "Project Name",
className: "text-start py-3",
getValue: (p) => (
<div
className="text-primary cursor-pointer fw-bold py-3"
onClick={() => {
dispatch(setProjectId(p.id));
navigate(`/service-projects/${p.id}`);
}}
>
{p.shortName ? `${p.name} (${p.shortName})` : p.name}
</div>
),
},
{
key: "client.contactPerson",
label: "Contact Person",
className: "text-start small",
getValue: (p) => p.client?.contactPerson || "N/A",
},
{
key: "assignedDate",
label: "Assign Date",
className: "text-center small",
getValue: (p) => formatUTCToLocalTime(p.assignedDate),
},
{
key: "status",
label: "Status",
className: "text-center small",
getValue: (p) => (
<span className={`badge ${getProjectStatusColor(p.status?.id)}`}>
{p.status?.status}
</span>
),
},
];
const handleViewProject = (p) => {
if (isCore) {
dispatch(setProjectId(p.id));
navigate(`/projects/details`);
} else {
navigate(`/service-projects/${p.id}`);
}
};
const handleManage = (p) => {
if (isCore) {
setMangeProject({
isOpen: true,
Project: p.id,
});
} else {
setManageServiceProject({
isOpen: true,
project: p.id,
});
}
};
return (
<div>
<div className="card page-min-h py-4 px-6 shadow-sm">
<div className="table-responsive text-nowrap page-min-h">
<table className="table table-hover align-middle m-0">
<thead className="border-bottom ">
<tr>
{projectColumns.map((col) => (
<th
key={col.key}
colSpan={col.colSpan}
className={`${col.className} table_header_border`}
>
{col.label}
</th>
))}
<th className="text-center py-3">Action</th>
</tr>
</thead>
<tbody>
{data?.length > 0 ? (
data.map((project) => (
<tr key={project.id}>
{projectColumns.map((col) => (
<td
key={col.key}
colSpan={col.colSpan}
className={`${col.className} py-5`}
style={{ paddingTop: "20px", paddingBottom: "20px" }}
>
{col.getValue
? col.getValue(project)
: project[col.key] || "N/A"}
</td>
))}
<td
className={`mx-2 ${
canManageProject ? "d-sm-table-cell" : "d-none"
}`}
>
<div className="dropdown z-2">
<button
type="button"
className="btn btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0"
data-bs-toggle="dropdown"
aria-expanded="false"
>
<i
className="bx bx-dots-vertical-rounded bx-sm text-muted"
data-bs-toggle="tooltip"
data-bs-offset="0,8"
data-bs-placement="top"
data-bs-custom-class="tooltip-dark"
title="More Action"
></i>
</button>
<ul className="dropdown-menu dropdown-menu-end">
<li>
<a
aria-label="click to View details"
className="dropdown-item"
onClick={() => handleViewProject(project)}
>
<i className="bx bx-detail me-2"></i>
<span className="align-left">View details</span>
</a>
</li>
<li>
<a
className="dropdown-item"
onClick={() => handleManage(project)}
>
<i className="bx bx-pencil me-2"></i>
<span className="align-left">Modify</span>
</a>
</li>
</ul>
</div>
</td>
</tr>
))
) : (
<tr
className="no-hover"
style={{
pointerEvents: "none",
backgroundColor: "transparent",
}}
>
<td
colSpan={projectColumns.length + 1}
className="text-center align-middle"
style={{ height: "300px", borderBottom: "none" }}
>
No Service projects available
</td>
</tr>
)}
</tbody>
</table>
</div>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
paginate={paginate}
/>
</div>
</div>
);
};
export default ServiceProjectList;

View File

@ -20,15 +20,14 @@ export const useCurrentService = () => {
// ------------------------------Query------------------- // ------------------------------Query-------------------
export const useProjects = (pageSize, pageNumber,searchString) => { export const useProjects = (pageSize, pageNumber) => {
const loggedUser = useSelector((store) => store.globalVariables.loginUser); const loggedUser = useSelector((store) => store.globalVariables.loginUser);
return useQuery({ return useQuery({
queryKey: ["ProjectsList", pageSize, pageNumber,searchString], queryKey: ["ProjectsList", pageSize, pageNumber],
queryFn: async () => { queryFn: async () => {
const response = await ProjectRepository.getProjectList( const response = await ProjectRepository.getProjectList(
pageSize, pageSize,
pageNumber, pageNumber
searchString,
); );
return response?.data; return response?.data;
}, },

View File

@ -8,14 +8,13 @@ import { ServiceProjectRepository } from "../repositories/ServiceProjectReposito
import showToast from "../services/toastService"; import showToast from "../services/toastService";
//#region Service Project //#region Service Project
export const useServiceProjects = (pageSize, pageNumber, searchString) => { export const useServiceProjects = (pageSize, pageNumber) => {
return useQuery({ return useQuery({
queryKey: ["serviceProjects", pageSize, pageNumber, searchString], queryKey: ["serviceProjects", pageSize, pageNumber],
queryFn: async () => { queryFn: async () => {
const response = await ServiceProjectRepository.GetServiceProjects( const response = await ServiceProjectRepository.GetServiceProjects(
pageSize, pageSize,
pageNumber, pageNumber
searchString
); );
return response.data; return response.data;
}, },
@ -60,8 +59,8 @@ export const useCreateServiceProject = (onSuccessCallback) => {
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to delete task", "Failed to delete task",
"error" "error"
); );
}, },
@ -85,8 +84,8 @@ export const useUpdateServiceProject = (onSuccessCallback) => {
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to update project", "Failed to update project",
"error" "error"
); );
}, },
@ -111,8 +110,8 @@ export const useActiveInActiveServiceProject = (onSuccessCallback) => {
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to update project", "Failed to update project",
"error" "error"
); );
}, },
@ -139,8 +138,8 @@ export const useAllocationServiceProjectTeam = (onSuccessCallback) => {
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to update project", "Failed to update project",
"error" "error"
); );
}, },
@ -149,6 +148,11 @@ export const useAllocationServiceProjectTeam = (onSuccessCallback) => {
//#endregion //#endregion
//#region Service Jobs //#region Service Jobs
export const useServiceProjectJobs = ( export const useServiceProjectJobs = (
@ -159,14 +163,7 @@ export const useServiceProjectJobs = (
isArchive isArchive
) => { ) => {
return useQuery({ return useQuery({
queryKey: [ queryKey: ["serviceProjectJobs", pageSize, pageNumber, isActive, project, isArchive],
"serviceProjectJobs",
pageSize,
pageNumber,
isActive,
project,
isArchive,
],
queryFn: async () => { queryFn: async () => {
const resp = await ServiceProjectRepository.GetJobList( const resp = await ServiceProjectRepository.GetJobList(
pageSize, pageSize,
@ -233,8 +230,8 @@ export const useAddCommentJob = (onSuccessCallback) => {
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to update project", "Failed to update project",
"error" "error"
); );
}, },
@ -257,8 +254,8 @@ export const useCreateServiceProjectJob = (onSuccessCallback) => {
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to update project", "Failed to update project",
"error" "error"
); );
}, },
@ -287,19 +284,27 @@ export const useUpdateServiceProjectJob = (onSuccessCallback) => {
} }
}, },
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to update project", "Failed to update project",
"error" "error"
); );
}, },
}); });
}; };
//#endregion //#endregion
//#region Branch //#region Branch
export const useBranches = ( export const useBranches = (
projectId, projectId,
@ -338,8 +343,8 @@ export const useBranchTypes = () => {
const resp = await ServiceProjectRepository.GetBranchTypeList(); const resp = await ServiceProjectRepository.GetBranchTypeList();
return resp.data; return resp.data;
}, },
}); })
}; }
export const useBranchDetails = (id) => { export const useBranchDetails = (id) => {
return useQuery({ return useQuery({
@ -348,9 +353,9 @@ export const useBranchDetails = (id) => {
const resp = await ServiceProjectRepository.GetBranchDetail(id); const resp = await ServiceProjectRepository.GetBranchDetail(id);
return resp.data; return resp.data;
}, },
enabled: !!id, enabled: !!id
}); })
}; }
export const useCreateBranch = (onSuccessCallBack) => { export const useCreateBranch = (onSuccessCallBack) => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -396,6 +401,7 @@ export const useUpdateBranch = (onSuccessCallBack) => {
}); });
}; };
export const useDeleteBranch = () => { export const useDeleteBranch = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
@ -405,17 +411,14 @@ export const useDeleteBranch = () => {
onSuccess: (_, variable) => { onSuccess: (_, variable) => {
queryClient.invalidateQueries({ queryKey: ["branches"] }); queryClient.invalidateQueries({ queryKey: ["branches"] });
showToast( showToast(`Branch ${variable.isActive ? "restored" : "deleted"} successfully`, "success");
`Branch ${variable.isActive ? "restored" : "deleted"} successfully`,
"success"
);
}, },
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
error.message || error.message ||
"Failed to delete branch", "Failed to delete branch",
"error" "error"
); );
}, },

View File

@ -11,18 +11,14 @@ import GlobalModel from "../../components/common/GlobalModel";
import ManageServiceProject from "../../components/ServiceProject/ManageServiceProject"; import ManageServiceProject from "../../components/ServiceProject/ManageServiceProject";
import { SpinnerLoader } from "../../components/common/Loader"; import { SpinnerLoader } from "../../components/common/Loader";
import ServiceProjectCard from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectCard"; import ServiceProjectCard from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectCard";
import ServiceProjectList from "../../components/ServiceProject/ServiceProjectTeam/ServiceProjectList";
import { useDebounce } from "../../utils/appUtils";
const ServiceProjectDisplay = ({ listView, selectedStatuses, searchTerm }) => { const ServiceProjectDisplay = ({ listView ,selectedStatuses }) => {
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
const { manageServiceProject, setManageServiceProject } = useProjectContext(); const { manageServiceProject, setManageServiceProject } = useProjectContext();
const debouncedSearch = useDebounce(searchTerm, 500);
const { data, isLoading, isError, error } = useServiceProjects( const { data, isLoading, isError, error } = useServiceProjects(
ITEMS_PER_PAGE, ITEMS_PER_PAGE,
currentPage, currentPage
debouncedSearch
); );
const paginate = (page) => { const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) { if (page >= 1 && page <= (data?.totalPages ?? 1)) {
@ -51,20 +47,15 @@ const ServiceProjectDisplay = ({ listView, selectedStatuses, searchTerm }) => {
</div> </div>
); );
return ( return (
<div className=""> <div className="row">
<div className="row">
{listView ? ( {listView ? (
<ServiceProjectList data={filteredProjects} <p>List</p>
currentPage={currentPage} ) : (
totalPages={data?.totalPages}
paginate={paginate}
isCore={false} />
) : filteredProjects?.length > 0 ? (
filteredProjects?.map((project) => ( filteredProjects?.map((project) => (
<ServiceProjectCard key={project.id} project={project} isCore={false} /> <ServiceProjectCard key={project.id} project={project} isCore={false} />
)) ))
):(<div className="d-flex justify-content-center align-items-center page-min-h "><p>No Service projects available</p></div>)} )}
<div className="col-12 d-flex justify-content-start mt-3"> <div className="col-12 d-flex justify-content-start mt-3">
<Pagination <Pagination
@ -91,7 +82,6 @@ const ServiceProjectDisplay = ({ listView, selectedStatuses, searchTerm }) => {
</GlobalModel> </GlobalModel>
)} )}
</div> </div>
</div>
); );
}; };

View File

@ -1,4 +1,4 @@
import React, { createContext, useContext, useEffect, useRef, useState } from "react"; import React, { createContext, useContext, useEffect, useState } from "react";
import Breadcrumb from "../../components/common/Breadcrumb"; import Breadcrumb from "../../components/common/Breadcrumb";
import { import {
ITEMS_PER_PAGE, ITEMS_PER_PAGE,
@ -36,17 +36,15 @@ const ProjectPage = () => {
isOpen: false, isOpen: false,
project: null, project: null,
}); });
const dropdownRef = useRef(null);
const [projectList, setProjectList] = useState([]); const [projectList, setProjectList] = useState([]);
const [listView, setListView] = useState(false); const [listView, setListView] = useState(false);
const [searchTerm, setSearchTerm] = useState(""); const [searchTerm, setSearchTerm] = useState("");
const [coreProjects, setCoreProjects] = useState(() => { const [coreProjects, setCoreProjects] = useState(() => {
const storedValue = sessionStorage.getItem("whichProjectDisplay"); const storedValue = sessionStorage.getItem('whichProjectDisplay');
return storedValue === "true"; return storedValue === 'true';
}); });
const HasManageProject = useHasUserPermission(MANAGE_PROJECT); const HasManageProject = useHasUserPermission(MANAGE_PROJECT);
const [currentPage, setCurrentPage] = useState(1);
const [open, setOpen] = useState(false);
const [selectedStatuses, setSelectedStatuses] = useState( const [selectedStatuses, setSelectedStatuses] = useState(
PROJECT_STATUS.map((s) => s.id) PROJECT_STATUS.map((s) => s.id)
@ -66,20 +64,12 @@ const ProjectPage = () => {
manageServiceProject, manageServiceProject,
}; };
const handleToggleProject = (value) => { const handleToggleProject = (value) => {
setCoreProjects(value); setCoreProjects(value);
sessionStorage.setItem("whichProjectDisplay", String(value)); sessionStorage.setItem("whichProjectDisplay", String(value));
}; };
useEffect(() => {
const handleClickOutside = (event) => {
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
setOpen(false);
}
};
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
return ( return (
<ProjectContext.Provider value={contextDispatcher}> <ProjectContext.Provider value={contextDispatcher}>
@ -115,11 +105,14 @@ const ProjectPage = () => {
> >
Infra Project Infra Project
</button> </button>
</div> </div>
</div> </div>
{/* RIGHT SIDE — SEARCH + CARD/LIST + DROPDOWN */} {/* RIGHT SIDE — SEARCH + CARD/LIST + DROPDOWN */}
<div className="d-flex flex-wrap align-items-center justify-content-end"> <div className="d-flex flex-wrap align-items-center justify-content-end">
{/* Search */} {/* Search */}
<div className="me-2" style={{ minWidth: "200px" }}> <div className="me-2" style={{ minWidth: "200px" }}>
<input <input
@ -138,8 +131,7 @@ const ProjectPage = () => {
<div className="d-flex gap-2"> <div className="d-flex gap-2">
<button <button
type="button" type="button"
className={`btn btn-sm p-1 ${!listView ? "btn-primary" : "btn-outline-primary" className={`btn btn-sm p-1 ${!listView ? "btn-primary" : "btn-outline-primary"}`}
}`}
onClick={() => setListView(false)} onClick={() => setListView(false)}
title="Card View" title="Card View"
> >
@ -148,8 +140,7 @@ const ProjectPage = () => {
<button <button
type="button" type="button"
className={`btn btn-sm p-1 ${listView ? "btn-primary" : "btn-outline-primary" className={`btn btn-sm p-1 ${listView ? "btn-primary" : "btn-outline-primary"}`}
}`}
onClick={() => setListView(true)} onClick={() => setListView(true)}
title="List View" title="List View"
> >
@ -158,74 +149,41 @@ const ProjectPage = () => {
</div> </div>
{/* Dropdown Filter */} {/* Dropdown Filter */}
<div className="dropdown me-2 position-relative"> <div className="dropdown me-2">
<div <a
className="cursor-pointer p-1" className="dropdown-toggle hide-arrow cursor-pointer p-1"
onClick={() => setOpen(!open)} data-bs-toggle="dropdown"
aria-expanded="false"
title="Filter"
> >
<i <i className="bx bx-slider-alt fs-5"></i>
className={`bx bx-slider-alt fs-5 ${selectedStatuses.length !== PROJECT_STATUS.length ? "text-primary" : "" </a>
}`}
></i>
{selectedStatuses.length !== PROJECT_STATUS.length && ( <ul className="dropdown-menu p-2 text-capitalize">
<span className="badge bg-warning text-white rounded-pill position-absolute" {PROJECT_STATUS.map(({ id, label }) => (
style={{ <li key={id}>
top: "-4px", <div className="form-check">
right: "-4px", <input
fontSize: "0.6rem", className="form-check-input"
padding: "2px 5px", type="checkbox"
}} checked={selectedStatuses.includes(id)}
> onChange={() => handleStatusChange(id)}
{PROJECT_STATUS.length - selectedStatuses.length} />
</span> <label className="form-check-label">{label}</label>
)} </div>
</div> </li>
))}
{open && ( </ul>
<ul
ref={dropdownRef}
className="dropdown-menu show p-2 text-capitalize"
onMouseDown={(e) => e.stopPropagation()} // IMPORTANT
>
{PROJECT_STATUS.map(({ id, label }) => (
<li key={id}>
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
checked={selectedStatuses.includes(id)}
onClick={(e) => e.stopPropagation()} // IMPORTANT
onChange={() => handleStatusChange(id)}
/>
<label
className="form-check-label"
onClick={(e) => e.stopPropagation()} // OPTIONAL
>
{label}
</label>
</div>
</li>
))}
</ul>
)}
</div> </div>
{HasManageProject && ( {HasManageProject && (
<button <button
type="button" type="button"
className="btn btn-primary btn-sm d-flex align-items-center my-2" className="btn btn-primary btn-sm d-flex align-items-center my-2"
onClick={ onClick={() =>
() => coreProjects
coreProjects ? setMangeProject({ isOpen: true, Project: null }) // Organization Project Infra
? setMangeProject({ isOpen: true, Project: null }) // Organization Project Infra : setManageServiceProject({ isOpen: true, Project: null }) // Service Project
: setManageServiceProject({
isOpen: true,
Project: null,
}) // Service Project
} }
> >
<i className="bx bx-plus-circle me-2"></i> <i className="bx bx-plus-circle me-2"></i>
@ -237,22 +195,11 @@ const ProjectPage = () => {
</div> </div>
</div> </div>
{coreProjects ? ( {coreProjects ? <ProjectsDisplay listView={listView}
<ProjectsDisplay searchTerm={searchTerm}
listView={listView} selectedStatuses={selectedStatuses}
searchTerm={searchTerm} handleStatusChange={handleStatusChange} /> : <ServiceProjectDisplay listView={listView}
selectedStatuses={selectedStatuses} selectedStatuses={selectedStatuses} />}
handleStatusChange={handleStatusChange}
currentPage={currentPage}
setCurrentPage={setCurrentPage}
/>
) : (
<ServiceProjectDisplay
listView={listView}
searchTerm={searchTerm}
selectedStatuses={selectedStatuses}
/>
)}
</div> </div>
</ProjectContext.Provider> </ProjectContext.Provider>
); );

View File

@ -10,7 +10,6 @@ import { useServiceProjects } from "../../hooks/useServiceProject";
import { ITEMS_PER_PAGE, PROJECT_STATUS } from "../../utils/constants"; import { ITEMS_PER_PAGE, PROJECT_STATUS } from "../../utils/constants";
import usePagination from "../../hooks/usePagination"; import usePagination from "../../hooks/usePagination";
import ManageProjectInfo from "../../components/Project/ManageProjectInfo"; import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
import { useDebounce } from "../../utils/appUtils";
const ProjectsDisplay = ({ const ProjectsDisplay = ({
listView, listView,
@ -27,8 +26,8 @@ const ProjectsDisplay = ({
} = useProjectContext(); } = useProjectContext();
const [projectList, setProjectList] = useState([]); const [projectList, setProjectList] = useState([]);
const debouncedSearch = useDebounce(searchTerm, 500);
const { data, isLoading, isError, error } = useProjects(ITEMS_PER_PAGE, 1, debouncedSearch); const { data, isLoading, isError, error } = useProjects(ITEMS_PER_PAGE, 1);
const filteredProjects = const filteredProjects =
data?.data?.filter((project) => { data?.data?.filter((project) => {
@ -99,7 +98,7 @@ const ProjectsDisplay = ({
); );
return ( return (
<div className=""> <div className="row">
{listView ? ( {listView ? (
<ProjectListView <ProjectListView
data={projectList} data={projectList}

View File

@ -2,8 +2,8 @@ import { api } from "../utils/axiosClient";
const ProjectRepository = { const ProjectRepository = {
getProjectList: (pageSize, pageNumber,searchString) => getProjectList: (pageSize, pageNumber) =>
api.get(`/api/project/list?pageSize=${pageSize}&pageNumber=${pageNumber}&searchString=${searchString}`), api.get(`/api/project/list?pageSize=${pageSize}&pageNumber=${pageNumber}`),
getProjectByprojectId: (projetid) => getProjectByprojectId: (projetid) =>
api.get(`/api/project/details/${projetid}`), api.get(`/api/project/details/${projetid}`),

View File

@ -4,9 +4,9 @@ import { api } from "../utils/axiosClient";
export const ServiceProjectRepository = { export const ServiceProjectRepository = {
//#region Service Project //#region Service Project
CreateServiceProject: (data) => api.post("/api/ServiceProject/create", data), CreateServiceProject: (data) => api.post("/api/ServiceProject/create", data),
GetServiceProjects: (pageSize, pageNumber,searchString) => GetServiceProjects: (pageSize, pageNumber) =>
api.get( api.get(
`/api/ServiceProject/list?pageSize=${pageSize}&pageNumber=${pageNumber}&searchString=${searchString}` `/api/ServiceProject/list?pageSize=${pageSize}&pageNumber=${pageNumber}`
), ),
GetServiceProject: (id) => api.get(`/api/ServiceProject/details/${id}`), GetServiceProject: (id) => api.get(`/api/ServiceProject/details/${id}`),
UpdateServiceProject: (id, data) => UpdateServiceProject: (id, data) =>

View File

@ -150,9 +150,6 @@ export function startSignalR(loggedUser) {
queryClient.invalidateQueries(["serviceProjects"]); queryClient.invalidateQueries(["serviceProjects"]);
queryClient.invalidateQueries(["serviceProject"]); queryClient.invalidateQueries(["serviceProject"]);
} }
if (keyword === "Project_Branch") {
queryClient.invalidateQueries(["branches"]);
}
if (keyword === "Service_Project_Allocation") { if (keyword === "Service_Project_Allocation") {
queryClient.invalidateQueries(["serviceProjectTeam"]); queryClient.invalidateQueries(["serviceProjectTeam"]);