add side-by-side filtering UI for Buckets and Categories with flex layout

This commit is contained in:
Pramod Mahajan 2025-05-21 18:18:49 +05:30
parent cb5cacaee0
commit 228c178acf

View File

@ -23,14 +23,13 @@ const Directory = () => {
const [ContactList, setContactList] = useState([]);
const [contactCategories, setContactCategories] = useState([]);
const [searchText, setSearchText] = useState("");
const [ listView, setListView ] = useState( false );
const [listView, setListView] = useState(false);
const [selectedBucketIds, setSelectedBucketIds] = useState([]);
const { contacts, loading } = useDirectory();
const { contactCategory, loading: contactCategoryLoading } =
useContactCategory();
const {buckets} = useBuckets()
const { buckets } = useBuckets();
const submitContact = async (data) => {
try {
let response;
@ -88,35 +87,35 @@ const Directory = () => {
};
const handleBucketChange = (id) => {
setSelectedBucketIds((prev) =>
prev.includes(id) ? prev.filter((bid) => bid !== id) : [...prev, id]
setSelectedBucketIds((prev) =>
prev.includes(id) ? prev.filter((bid) => bid !== id) : [...prev, id]
);
};
const usedBucketIds = [
...new Set(contacts.flatMap((c) => c.bucketIds || [])),
];
const filteredBuckets = buckets.filter((bucket) =>
usedBucketIds.includes(bucket.id)
);
};
const usedBucketIds = [
...new Set(contacts.flatMap((c) => c.bucketIds || [])),
];
const filteredBuckets = buckets.filter((bucket) =>
usedBucketIds.includes(bucket.id)
);
const filteredContacts = useMemo(() => {
return ContactList.filter((c) => {
const matchesSearch =
c.name.toLowerCase().includes(searchText.toLowerCase()) ||
c.organization.toLowerCase().includes(searchText.toLowerCase());
const filteredContacts = useMemo(() => {
return ContactList.filter((c) => {
const matchesSearch =
c.name.toLowerCase().includes(searchText.toLowerCase()) ||
c.organization.toLowerCase().includes(searchText.toLowerCase());
const matchesCategory =
selectedCategoryIds.length === 0 ||
selectedCategoryIds.includes(c.contactCategory?.id);
const matchesCategory =
selectedCategoryIds.length === 0 ||
selectedCategoryIds.includes(c.contactCategory?.id);
const matchesBucket =
selectedBucketIds.length === 0 ||
(c.bucketIds || []).some((id) => selectedBucketIds.includes(id));
const matchesBucket =
selectedBucketIds.length === 0 ||
(c.bucketIds || []).some((id) => selectedBucketIds.includes(id));
return matchesSearch && matchesCategory && matchesBucket;
}).sort((a, b) => a.name.localeCompare(b.name));
}, [ContactList, searchText, selectedCategoryIds, selectedBucketIds]);
return matchesSearch && matchesCategory && matchesBucket;
}).sort((a, b) => a.name.localeCompare(b.name));
}, [ContactList, searchText, selectedCategoryIds, selectedBucketIds]);
const { currentPage, totalPages, currentItems, paginate } = usePagination(
filteredContacts,
@ -153,10 +152,9 @@ const filteredContacts = useMemo(() => {
{isOpenModal && (
<GlobalModel
isOpen={isOpenModal}
closeModal={() =>
{
setSelectedContact(null)
setIsOpenModal(false)
closeModal={() => {
setSelectedContact(null);
setIsOpenModal(false);
}}
size="lg"
>
@ -166,14 +164,19 @@ const filteredContacts = useMemo(() => {
{isOpenModalNote && (
<GlobalModel
isOpen={isOpenModalNote}
closeModal={() =>
{
setOpen_contact(null)
setIsOpenModalNote(false)
closeModal={() => {
setOpen_contact(null);
setIsOpenModalNote(false);
}}
size="lg"
>
{open_contact && <ProfileContactDirectory contact={open_contact} setOpen_contact={setOpen_contact} closeModal={ () => setIsOpenModalNote(false)} />}
{open_contact && (
<ProfileContactDirectory
contact={open_contact}
setOpen_contact={setOpen_contact}
closeModal={() => setIsOpenModalNote(false)}
/>
)}
</GlobalModel>
)}
<div className="card p-2">
@ -224,49 +227,57 @@ const filteredContacts = useMemo(() => {
>
<i className="bx bx-filter bx-lg ms-1"></i>
</a>
<ul className="dropdown-menu p-2 text-capitalize">
<p className="small-text mb-1">Apply Bucket Filter</p>
{filteredBuckets.map(({ id, name }) => (
<li key={id}>
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
id={`bucket-${id}`}
checked={selectedBucketIds.includes(id)}
onChange={() => handleBucketChange(id)}
/>
<label className="form-check-label" htmlFor={`bucket-${id}`}>
{name}
</label>
</div>
</li>
) )}
<hr className="my-2" />
<p className="small-text mb-1">Apply Category Filter</p>
{filteredCategories.map(({ id, name }) => (
<li key={id}>
<div className="form-check">
<input
className="form-check-input"
type="checkbox"
id={`cat-${id}`}
checked={selectedCategoryIds.includes(id)}
onChange={() => handleCategoryChange(id)}
/>
<label className="form-check-label" htmlFor={`cat-${id}`}>
{name}
</label>
</div>
</li>
))}
</ul>
<ul
className="dropdown-menu p-2 text-capitalize"
style={{ Width: "100%" }}
>
<p className="small-text text-muted m-0">Filter by</p>
<div className="d-flex gap-4 justify-content-between">
{/* Bucket Filter */}
<div className="flex-fill ">
<p className="small-text mb-2 ">Buckets</p>
{filteredBuckets.map(({ id, name }) => (
<div className="form-check mb-1" key={id}>
<input
className="form-check-input "
type="checkbox"
id={`bucket-${id}`}
checked={selectedBucketIds.includes(id)}
onChange={() => handleBucketChange(id)}
/>
<label
className="form-check-label text-nowrap"
htmlFor={`bucket-${id}`}
>
{name}
</label>
</div>
))}
</div>
{/* Category Filter */}
<div className="flex-fill">
<p className="small-text mb-2">Categories</p>
{filteredCategories.map(({ id, name }) => (
<div className="form-check mb-1" key={id}>
<input
className="form-check-input"
type="checkbox"
id={`cat-${id}`}
checked={selectedCategoryIds.includes(id)}
onChange={() => handleCategoryChange(id)}
/>
<label
className="form-check-label text-nowrap"
htmlFor={`cat-${id}`}
>
{name}
</label>
</div>
))}
</div>
</div>
</ul>
</div>
</div>
@ -282,6 +293,7 @@ const filteredContacts = useMemo(() => {
</div>
</div>
{!listView && loading && <p>Loading...</p>}
{!listView && !loading && currentItems.length == 0 && <p>Not Found Contact</p>}
{listView ? (
<div className="table-responsive text-nowrap py-2 ">
<table className="table px-2">
@ -332,9 +344,6 @@ const filteredContacts = useMemo(() => {
</th>
<th className="mx-2">Category</th>
<th
// className={`mx-2 ${
// HasManageProject ? "d-sm-table-cell" : "d-none"
// }`}
>
Action
</th>
@ -347,12 +356,12 @@ const filteredContacts = useMemo(() => {
</tr>
)}
{!loading &&
contacts.length == 0 &&
ContatList.length === 0 && (
currentItems.length === 0 && (
<tr>
<td colSpan={10}>No Contact Found</td>
<td colSpan={10}>Not Found Contact</td>
</tr>
)}
{!loading &&
currentItems.map((contact) => (
<ListViewDirectory