Add cards in project list and diectory list views

This commit is contained in:
Vikas Nale 2025-06-20 14:33:51 +05:30
parent 6f88980986
commit 8ac9c64bf4
5 changed files with 289 additions and 259 deletions

View File

@ -213,3 +213,8 @@
.ql-editor { .ql-editor {
max-height: 200px; max-height: 200px;
} }
/* Remove Table Header Top Line */
thead tr {
border-top: 1px solid white;
}

View File

@ -326,26 +326,29 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
</GlobalModel> </GlobalModel>
)} )}
<div className="card p-2 card-minHeight"> <div className="card p-0 mb-2 ">
<DirectoryPageHeader <div className="card-body p-1 pb-0">
searchText={searchText} <DirectoryPageHeader
setSearchText={setSearchText} searchText={searchText}
setIsActive={setIsActive} setSearchText={setSearchText}
listView={listView} setIsActive={setIsActive}
setListView={setListView} listView={listView}
filteredBuckets={filteredBuckets} setListView={setListView}
tempSelectedBucketIds={tempSelectedBucketIds} filteredBuckets={filteredBuckets}
handleTempBucketChange={handleTempBucketChange} tempSelectedBucketIds={tempSelectedBucketIds}
filteredCategories={filteredCategories} handleTempBucketChange={handleTempBucketChange}
tempSelectedCategoryIds={tempSelectedCategoryIds} filteredCategories={filteredCategories}
handleTempCategoryChange={handleTempCategoryChange} tempSelectedCategoryIds={tempSelectedCategoryIds}
clearFilter={clearFilter} handleTempCategoryChange={handleTempCategoryChange}
applyFilter={applyFilter} clearFilter={clearFilter}
loading={loading} applyFilter={applyFilter}
IsActive={IsActive} loading={loading}
setOpenBucketModal={setOpenBucketModal} IsActive={IsActive}
/> setOpenBucketModal={setOpenBucketModal}
/>
</div>
</div>
<div className="card-minHeight">
{/* Messages when listView is false */} {/* Messages when listView is false */}
{!listView && ( {!listView && (
<div className="d-flex flex-column justify-content-center align-items-center text-center "> <div className="d-flex flex-column justify-content-center align-items-center text-center ">
@ -360,48 +363,55 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
)} )}
{/* Table view (listView === true) */} {/* Table view (listView === true) */}
{listView ? ( {listView ? (
<DirectoryListTableHeader> <div className="card cursor-pointer mt-5">
{loading && ( <div className="card-body p-2 pb-1">
<tr> <DirectoryListTableHeader>
<td colSpan={10}> {loading && (
{" "} <tr>
<p className="mt-10">Loading...</p>{" "} <td colSpan={10}>
</td> {" "}
</tr> <p className="mt-10">Loading...</p>{" "}
)} </td>
</tr>
)}
{!loading && contacts?.length === 0 && ( {!loading && contacts?.length === 0 && (
<tr> <tr>
<td colSpan={10}> <td colSpan={10}>
<p className="mt-10">No contact found</p> <p className="mt-10">No contact found</p>
</td> </td>
</tr> </tr>
)} )}
{!loading && currentItems.length === 0 && contacts?.length > 0 && ( {!loading &&
<tr> currentItems.length === 0 &&
<td colSpan={10}> contacts?.length > 0 && (
<p className="mt-10">No matching contact found</p> <tr>
</td> <td colSpan={10}>
</tr> <p className="mt-10">No matching contact found</p>
)} </td>
</tr>
)}
{!loading && {!loading &&
currentItems.map((contact) => ( currentItems.map((contact) => (
<ListViewDirectory <ListViewDirectory
key={contact.id} key={contact.id}
IsActive={IsActive} IsActive={IsActive}
contact={contact} contact={contact}
setSelectedContact={setSelectedContact} setSelectedContact={setSelectedContact}
setIsOpenModal={setIsOpenModal} setIsOpenModal={setIsOpenModal}
setOpen_contact={setOpen_contact} setOpen_contact={setOpen_contact}
setIsOpenModalNote={setIsOpenModalNote} setIsOpenModalNote={setIsOpenModalNote}
IsDeleted={setDeleteContact} IsDeleted={setDeleteContact}
restore={handleDeleteContact} restore={handleDeleteContact}
/> />
))} ))}
</DirectoryListTableHeader> </DirectoryListTableHeader>
</div>
</div>
) : ( ) : (
<div className="row mt-5"> <div className="row mt-5">
{!loading && {!loading &&

View File

@ -32,16 +32,16 @@ const DirectoryPageHeader = ({
<div className="col-12 col-md-6 mb-2 px-1 d-flex align-items-center gap-4 "> <div className="col-12 col-md-6 mb-2 px-1 d-flex align-items-center gap-4 ">
<input <input
type="search" type="search"
className="form-control me-2" className="form-control"
placeholder="Search Contact..." placeholder="Search Contact..."
value={searchText} value={searchText}
onChange={(e) => setSearchText(e.target.value)} onChange={(e) => setSearchText(e.target.value)}
style={{ width: "200px" }} style={{ width: "200px" }}
/> />
<div className="d-flex gap-2 "> <div className="d-flex gap-2">
<button <button
type="button" type="button"
className={`btn btn-xs ${ className={`btn btn-sm p-1 ${
!listView ? "btn-primary" : "btn-outline-primary" !listView ? "btn-primary" : "btn-outline-primary"
}`} }`}
onClick={() => setListView(false)} onClick={() => setListView(false)}
@ -55,7 +55,7 @@ const DirectoryPageHeader = ({
</button> </button>
<button <button
type="button" type="button"
className={`btn btn-xs ${ className={`btn btn-sm p-1 ${
listView ? "btn-primary" : "btn-outline-primary" listView ? "btn-primary" : "btn-outline-primary"
}`} }`}
onClick={() => setListView(true)} onClick={() => setListView(true)}

View File

@ -37,7 +37,7 @@ const ProjectList = () => {
const handleShow = () => setShowModal(true); const handleShow = () => setShowModal(true);
const handleClose = () => setShowModal(false); const handleClose = () => setShowModal(false);
const sortingProject = (projects) =>{ const sortingProject = (projects) => {
if (!loading && Array.isArray(projects)) { if (!loading && Array.isArray(projects)) {
const grouped = {}; const grouped = {};
projects.forEach((project) => { projects.forEach((project) => {
@ -56,10 +56,10 @@ const ProjectList = () => {
setProjectList(sortedGrouped); setProjectList(sortedGrouped);
} }
} };
useEffect(() => { useEffect(() => {
sortingProject(projects) sortingProject(projects);
}, [projects, loginUser?.projects, loading]); }, [projects, loginUser?.projects, loading]);
useEffect(() => { useEffect(() => {
@ -70,16 +70,16 @@ const ProjectList = () => {
} }
}, [loginUser, HasManageProjectPermission]); }, [loginUser, HasManageProjectPermission]);
const handleSubmitForm = (newProject,setloading,reset) => { const handleSubmitForm = (newProject, setloading, reset) => {
ProjectRepository.manageProject(newProject) ProjectRepository.manageProject(newProject)
.then((response) => { .then((response) => {
const cachedProjects = getCachedData("projectslist") || []; const cachedProjects = getCachedData("projectslist") || [];
const updatedProjects = [...cachedProjects, response.data]; const updatedProjects = [...cachedProjects, response.data];
cacheData("projectslist", updatedProjects); cacheData("projectslist", updatedProjects);
setProjectList( ( prev ) => [ ...prev, response.data ] ); setProjectList((prev) => [...prev, response.data]);
setloading( false ) setloading(false);
reset() reset();
sortingProject(getCachedData("projectslist")) sortingProject(getCachedData("projectslist"));
showToast("Project Created successfully.", "success"); showToast("Project Created successfully.", "success");
setShowModal(false); setShowModal(false);
}) })
@ -123,7 +123,7 @@ const ProjectList = () => {
indexOfLastItem indexOfLastItem
); );
const totalPages = Math.ceil(filteredProjects.length / itemsPerPage); const totalPages = Math.ceil(filteredProjects.length / itemsPerPage);
useEffect(() => { useEffect(() => {
const tooltipTriggerList = Array.from( const tooltipTriggerList = Array.from(
document.querySelectorAll('[data-bs-toggle="tooltip"]') document.querySelectorAll('[data-bs-toggle="tooltip"]')
@ -154,205 +154,220 @@ const ProjectList = () => {
{ label: "Projects", link: null }, { 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 flex-wrap justify-content-between align-items-start mb-4"> <div className="d-flex gap-2 mb-2">
<div className="d-flex flex-wrap align-items-start"> <button
<div className="flex-grow-1 me-2 mb-2"> type="button"
<input className={`btn btn-sm p-1 ${
type="search" !listView ? "btn-primary" : "btn-outline-primary"
className="form-control form-control-sm" }`}
placeholder="Search projects..." onClick={() => setListView(false)}
value={searchTerm} data-bs-toggle="tooltip"
onChange={(e) => { data-bs-custom-class="tooltip"
setSearchTerm(e.target.value); title="Card View"
setCurrentPage(1); >
}} <i className="bx bx-grid-alt fs-5"></i>
/> </button>
</div> <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="d-flex gap-2 mb-2"> <div className="dropdown ms-3 mt-1">
<button <a
type="button" className="dropdown-toggle hide-arrow cursor-pointer p-1 mt-3 "
className={`btn btn-sm ${ data-bs-toggle="dropdown"
!listView ? "btn-primary" : "btn-outline-primary" aria-expanded="false"
}`} data-bs-custom-class="tooltip"
onClick={() => setListView(false)} title="Filter"
data-bs-toggle="tooltip" >
data-bs-offset="0,8" <i className="fa-solid fa-filter fs-4"></i>
data-bs-placement="top" </a>
data-bs-custom-class="tooltip" <ul className="dropdown-menu p-2 text-capitalize">
title="Card View" {[
> {
<i className="bx bx-grid-alt bx-sm"></i> id: "b74da4c2-d07e-46f2-9919-e75e49b12731",
</button> label: "Active",
<button },
type="button" {
className={`btn btn-sm ${ id: "603e994b-a27f-4e5d-a251-f3d69b0498ba",
listView ? "btn-primary" : "btn-outline-primary" label: "On Hold",
}`} },
onClick={() => setListView(true)} {
data-bs-toggle="tooltip" id: "ef1c356e-0fe0-42df-a5d3-8daee355492d",
data-bs-offset="0,8" label: "Inactive",
data-bs-placement="top" },
data-bs-custom-class="tooltip" {
title="List View" id: "33deaef9-9af1-4f2a-b443-681ea0d04f81",
> label: "Completed",
<i className="bx bx-list-ul bx-sm"></i> },
</button> ].map(({ id, label }) => (
</div> <li key={id}>
<div className="form-check">
<div className="dropdown ms-3"> <input
<a className="form-check-input "
className="dropdown-toggle hide-arrow cursor-pointer" type="checkbox"
data-bs-toggle="dropdown" checked={selectedStatuses.includes(id)}
aria-expanded="false" onChange={() => handleStatusChange(id)}
> />
<i className="bx bx-filter bx-lg"></i> <label className="form-check-label">{label}</label>
</a> </div>
<ul className="dropdown-menu p-2 text-capitalize"> </li>
{[ ))}
{ </ul>
id: "b74da4c2-d07e-46f2-9919-e75e49b12731", </div>
label: "Active",
},
{
id: "603e994b-a27f-4e5d-a251-f3d69b0498ba",
label: "On Hold",
},
{
id: "ef1c356e-0fe0-42df-a5d3-8daee355492d",
label: "Inactive",
},
{
id: "33deaef9-9af1-4f2a-b443-681ea0d04f81",
label: "Completed",
},
].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>
<div> <div>
<button <button
type="button" type="button"
className={`btn btn-sm btn-primary ${ data-bs-toggle="tooltip"
!HasManageProject && "d-none" data-bs-offset="0,8"
}`} data-bs-placement="top"
onClick={handleShow} data-bs-custom-class="tooltip"
> title="Add New Project"
<i className="bx bx-plus-circle me-2"></i> className={`p-1 me-2 bg-primary rounded-circle ${
Create New Project !HasManageProject && "d-none"
</button> }`}
onClick={handleShow}
>
<i className="bx bx-plus fs-4 text-white"></i>
</button>
</div>
</div>
</div> </div>
</div> </div>
{loading && <p className="text-center">Loading...</p>} {loading && <p className="text-center">Loading...</p>}
{!loading && filteredProjects.length === 0 && !listView && ( {!loading && filteredProjects.length === 0 && !listView && (
<p className="text-center text-muted">No projects found.</p> <p className="text-center text-muted">No projects found.</p>
)} )}
<div className="row"> {listView ? (
{listView ? ( <div className="card cursor-pointer">
<div className="table-responsive text-nowrap py-2 "> <div className="card-body p-2">
<table className="table px-2"> <div className="table-responsive text-nowrap py-2 ">
<thead> <table className="table m-3">
<tr> <thead>
<th className="text-start" colSpan={5}>
Project Name
</th>
<th className="mx-2 text-start">Contact Person</th>
<th className="mx-2">START DATE</th>
<th className="mx-2">DEADLINE</th>
<th className="mx-2">Task</th>
<th className="mx-2">Progress</th>
<th className="mx-2">
<div className="dropdown">
<a
className="dropdown-toggle hide-arrow cursor-pointer"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Status <i className="bx bx-filter bx-sm"></i>
</a>
<ul className="dropdown-menu p-2 text-capitalize">
{[
{
id: "b74da4c2-d07e-46f2-9919-e75e49b12731",
label: "Active",
},
{
id: "603e994b-a27f-4e5d-a251-f3d69b0498ba",
label: "On Hold",
},
{
id: "ef1c356e-0fe0-42df-a5d3-8daee355492d",
label: "Inactive",
},
{
id: "33deaef9-9af1-4f2a-b443-681ea0d04f81",
label: "Completed",
},
].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>
</th>
<th
className={`mx-2 ${
HasManageProject ? "d-sm-table-cell" : "d-none"
}`}
>
Action
</th>
</tr>
</thead>
<tbody className="table-border-bottom-0 overflow-auto ">
{currentItems.length === 0 ? (
<tr> <tr>
<td colSpan="12" className="text-center py-4"> <th className="text-start" colSpan={5}>
No projects found Project Name
</td> </th>
<th className="mx-2 text-start">Contact Person</th>
<th className="mx-2">START DATE</th>
<th className="mx-2">DEADLINE</th>
<th className="mx-2">Task</th>
<th className="mx-2">Progress</th>
<th className="mx-2">
<div className="dropdown">
<a
className="dropdown-toggle hide-arrow cursor-pointer"
data-bs-toggle="dropdown"
aria-expanded="false"
>
Status <i className="bx bx-filter bx-sm"></i>
</a>
<ul className="dropdown-menu p-2 text-capitalize">
{[
{
id: "b74da4c2-d07e-46f2-9919-e75e49b12731",
label: "Active",
},
{
id: "603e994b-a27f-4e5d-a251-f3d69b0498ba",
label: "On Hold",
},
{
id: "ef1c356e-0fe0-42df-a5d3-8daee355492d",
label: "Inactive",
},
{
id: "33deaef9-9af1-4f2a-b443-681ea0d04f81",
label: "Completed",
},
].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>
</th>
<th
className={`mx-2 ${
HasManageProject ? "d-sm-table-cell" : "d-none"
}`}
>
Action
</th>
</tr> </tr>
) : ( </thead>
currentItems.map((project) => ( <tbody className="table-border-bottom-0 overflow-auto ">
<ProjectListView key={project.id} projectData={project} recall={sortingProject} /> {currentItems.length === 0 ? (
)) <tr>
)} <td colSpan="12" className="text-center py-4">
</tbody> No projects found
</table> </td>
</div> </tr>
) : ( ) : (
currentItems.map((project) => ( currentItems.map((project) => (
<ProjectCard key={project.id} projectData={project} recall={sortingProject} /> <ProjectListView
)) key={project.id}
)} projectData={project}
</div> recall={sortingProject}
/>
))
)}
</tbody>
</table>
</div>{" "}
</div>{" "}
</div>
) : (
<div className="row">
{currentItems.map((project) => (
<ProjectCard
key={project.id}
projectData={project}
recall={sortingProject}
/>
))}
</div>
)}
{!loading && totalPages > 1 && ( {!loading && totalPages > 1 && (
<nav> <nav>

View File

@ -107,14 +107,14 @@ const ProjectListView = ({ projectData, recall }) => {
<tr className="py-8"> <tr className="py-8">
<td className="text-start" colSpan={5}> <td className="text-start" colSpan={5}>
<strong <span
className="text-primary cursor-pointer" className="text-primary cursor-pointer"
onClick={() => navigate(`/projects/${projectInfo.id}`)} onClick={() => navigate(`/projects/${projectInfo.id}`)}
> >
{projectInfo.shortName {projectInfo.shortName
? `${projectInfo.name} (${projectInfo.shortName})` ? `${projectInfo.name} (${projectInfo.shortName})`
: projectInfo.name} : projectInfo.name}
</strong> </span>
</td> </td>
<td className="text-start small">{projectInfo.contactPerson}</td> <td className="text-start small">{projectInfo.contactPerson}</td>
<td className="small text-center"> <td className="small text-center">