Compare commits
No commits in common. "main" and "Kartik_Bug#1773" have entirely different histories.
main
...
Kartik_Bug
6
public/assets/vendor/css/core.css
vendored
6
public/assets/vendor/css/core.css
vendored
@ -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 {
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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}
|
||||||
>
|
>
|
||||||
|
|||||||
@ -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) => (
|
||||||
|
|||||||
@ -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>
|
||||||
|
|||||||
@ -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) => (
|
||||||
|
|||||||
@ -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>
|
||||||
|
|
||||||
|
|||||||
@ -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={() =>
|
||||||
|
|||||||
@ -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;
|
|
||||||
@ -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;
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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"
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|||||||
@ -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>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -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}
|
||||||
|
|||||||
@ -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}`),
|
||||||
|
|
||||||
|
|||||||
@ -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) =>
|
||||||
|
|||||||
@ -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"]);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user