255 lines
8.2 KiB
JavaScript
255 lines
8.2 KiB
JavaScript
import React, { createContext, useContext, useEffect, useState } from "react";
|
|
import Breadcrumb from "../../components/common/Breadcrumb";
|
|
import {
|
|
ITEMS_PER_PAGE,
|
|
MANAGE_PROJECT,
|
|
PROJECT_STATUS,
|
|
} from "../../utils/constants";
|
|
import ProjectListView from "../../components/Project/ProjectListView";
|
|
import GlobalModel from "../../components/common/GlobalModel";
|
|
import ManageProjectInfo from "../../components/Project/ManageProjectInfo";
|
|
import ProjectCardView from "../../components/Project/ProjectCardView";
|
|
import usePagination from "../../hooks/usePagination";
|
|
import { useProjects } from "../../hooks/useProjects";
|
|
import Loader from "../../components/common/Loader";
|
|
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
|
|
|
const ProjectContext = createContext();
|
|
export const useProjectContext = () => {
|
|
const context = useContext(ProjectContext);
|
|
if (!context) {
|
|
throw new Error("useProjectContext must be used within an ProjectProvider");
|
|
}
|
|
return context;
|
|
};
|
|
|
|
const ProjectPage = () => {
|
|
const [manageProject, setMangeProject] = useState({
|
|
isOpen: false,
|
|
Project: null,
|
|
});
|
|
|
|
const [projectList, setProjectList] = useState([]);
|
|
const [listView, setListView] = useState(false);
|
|
const [searchTerm, setSearchTerm] = useState("");
|
|
const HasManageProject = useHasUserPermission(MANAGE_PROJECT);
|
|
|
|
const [selectedStatuses, setSelectedStatuses] = useState(
|
|
PROJECT_STATUS.map((s) => s.id)
|
|
);
|
|
|
|
const { data, isLoading, isError, error } = useProjects();
|
|
|
|
const contextDispatcher = {
|
|
setMangeProject,
|
|
};
|
|
|
|
const filteredProjects = projectList.filter((project) => {
|
|
const matchesStatus = selectedStatuses.includes(project.projectStatusId);
|
|
const matchesSearch = project.name
|
|
.toLowerCase()
|
|
.includes(searchTerm.toLowerCase());
|
|
return matchesStatus && matchesSearch;
|
|
});
|
|
|
|
const totalPages = Math.ceil(filteredProjects.length / ITEMS_PER_PAGE);
|
|
|
|
const { currentItems, currentPage, paginate, setCurrentPage } = usePagination(
|
|
filteredProjects,
|
|
ITEMS_PER_PAGE
|
|
);
|
|
|
|
const handleStatusChange = (statusId) => {
|
|
setCurrentPage(1);
|
|
setSelectedStatuses((prev) =>
|
|
prev.includes(statusId)
|
|
? prev.filter((id) => id !== statusId)
|
|
: [...prev, statusId]
|
|
);
|
|
};
|
|
|
|
const sortingProject = (projects) => {
|
|
if (!isLoading && Array.isArray(projects)) {
|
|
const grouped = {};
|
|
|
|
projects.forEach((project) => {
|
|
const statusId = project.projectStatusId;
|
|
if (!grouped[statusId]) grouped[statusId] = [];
|
|
grouped[statusId].push(project);
|
|
});
|
|
|
|
const sortedGrouped = selectedStatuses
|
|
.filter((statusId) => grouped[statusId])
|
|
.flatMap((statusId) =>
|
|
grouped[statusId].sort((a, b) =>
|
|
a.name.toLowerCase().localeCompare(b.name.toLowerCase())
|
|
)
|
|
);
|
|
|
|
setProjectList((prev) => {
|
|
const isSame = JSON.stringify(prev) === JSON.stringify(sortedGrouped);
|
|
return isSame ? prev : sortedGrouped;
|
|
});
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (!isLoading && data) {
|
|
sortingProject(data);
|
|
}
|
|
}, [data, isLoading, selectedStatuses]);
|
|
|
|
if (isLoading)
|
|
return (
|
|
<div className="page-min-h">
|
|
<Loader />
|
|
</div>
|
|
);
|
|
if (isError)
|
|
return (
|
|
<div className="page-min-h d-flex justify-content-center align-items-center">
|
|
<p>{error.message}</p>
|
|
</div>
|
|
);
|
|
return (
|
|
<ProjectContext.Provider value={contextDispatcher}>
|
|
<div className="container-fluid">
|
|
<Breadcrumb
|
|
data={[
|
|
{ label: "Home", link: "/dashboard" },
|
|
{ label: "Projects", link: null },
|
|
]}
|
|
/>
|
|
|
|
<div className="card cursor-pointer mb-5">
|
|
<div className="card-body p-2 pb-1">
|
|
<div className="d-flex flex-wrap justify-content-between align-items-start">
|
|
<div className="d-flex flex-wrap align-items-start">
|
|
<div className="flex-grow-1 me-2 mb-2">
|
|
<input
|
|
type="search"
|
|
className="form-control form-control-sm"
|
|
placeholder="Search projects..."
|
|
value={searchTerm}
|
|
onChange={(e) => {
|
|
setSearchTerm(e.target.value);
|
|
setCurrentPage(1);
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
<div className="d-flex gap-2 mb-2">
|
|
<button
|
|
type="button"
|
|
className={`btn btn-sm p-1 ${
|
|
!listView ? "btn-primary" : "btn-outline-primary"
|
|
}`}
|
|
onClick={() => setListView(false)}
|
|
data-bs-toggle="tooltip"
|
|
data-bs-custom-class="tooltip"
|
|
title="Card View"
|
|
>
|
|
<i className="bx bx-grid-alt fs-5"></i>
|
|
</button>
|
|
<button
|
|
type="button"
|
|
className={`btn btn-sm p-1 ${
|
|
listView ? "btn-primary" : "btn-outline-primary"
|
|
}`}
|
|
onClick={() => setListView(true)}
|
|
data-bs-toggle="tooltip"
|
|
data-bs-custom-class="tooltip"
|
|
title="List View"
|
|
>
|
|
<i className="bx bx-list-ul fs-5"></i>
|
|
</button>
|
|
</div>
|
|
|
|
<div className="dropdown mt-1">
|
|
<a
|
|
className="dropdown-toggle hide-arrow cursor-pointer p-1 mt-3 "
|
|
data-bs-toggle="dropdown"
|
|
aria-expanded="false"
|
|
data-bs-custom-class="tooltip"
|
|
title="Filter"
|
|
>
|
|
<i className="bx bx-slider-alt ms-1"></i>
|
|
</a>
|
|
<ul className="dropdown-menu p-2 text-capitalize">
|
|
{PROJECT_STATUS.map(({ id, label }) => (
|
|
<li key={id}>
|
|
<div className="form-check">
|
|
<input
|
|
className="form-check-input "
|
|
type="checkbox"
|
|
checked={selectedStatuses.includes(id)}
|
|
onChange={() => handleStatusChange(id)}
|
|
/>
|
|
<label className="form-check-label">{label}</label>
|
|
</div>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
{/* {HasManageProject && ( */}
|
|
<button
|
|
className="btn btn-sm btn-primary"
|
|
type="button"
|
|
onClick={() =>
|
|
setMangeProject({ isOpen: true, Project: null })
|
|
}
|
|
>
|
|
<i className="bx bx-plus-circle me-2"></i>
|
|
<span className="d-none d-md-inline-block">
|
|
Add New Project
|
|
</span>
|
|
</button>
|
|
{/* )} */}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Project Render here */}
|
|
{listView ? (
|
|
<ProjectListView
|
|
currentItems={currentItems}
|
|
selectedStatuses={selectedStatuses}
|
|
handleStatusChange={handleStatusChange}
|
|
setCurrentPage={setCurrentPage}
|
|
totalPages={totalPages}
|
|
isLoading={isLoading}
|
|
/>
|
|
) : (
|
|
<ProjectCardView
|
|
currentItems={currentItems}
|
|
setCurrentPage={setCurrentPage}
|
|
totalPages={totalPages}
|
|
/>
|
|
)}
|
|
|
|
{/* ------------------ */}
|
|
|
|
{/* Project Manage UPdate or create */}
|
|
|
|
{manageProject.isOpen && (
|
|
<GlobalModel
|
|
isOpen={manageProject.isOpen}
|
|
closeModal={() => setMangeProject({ isOpen: false, Project: null })}
|
|
>
|
|
<ManageProjectInfo
|
|
project={manageProject.Project}
|
|
onClose={() => setMangeProject({ isOpen: false, Project: null })}
|
|
/>
|
|
</GlobalModel>
|
|
)}
|
|
</div>
|
|
</ProjectContext.Provider>
|
|
);
|
|
};
|
|
|
|
export default ProjectPage;
|