In Directory changing filter logic and adding popup.
This commit is contained in:
parent
abc8bd8629
commit
deb7100899
@ -7,6 +7,8 @@ import showToast from "../../services/toastService";
|
|||||||
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
import { cacheData, getCachedData } from "../../slices/apiDataManager";
|
||||||
import ConfirmModal from "../common/ConfirmModal"; // Make sure path is correct
|
import ConfirmModal from "../common/ConfirmModal"; // Make sure path is correct
|
||||||
import "../common/TextEditor/Editor.css";
|
import "../common/TextEditor/Editor.css";
|
||||||
|
import ProfileContactDirectory from "./ProfileContactDirectory";
|
||||||
|
import GlobalModel from "../common/GlobalModel";
|
||||||
|
|
||||||
const NoteCardDirectoryEditable = ({
|
const NoteCardDirectoryEditable = ({
|
||||||
noteItem,
|
noteItem,
|
||||||
@ -20,6 +22,8 @@ const NoteCardDirectoryEditable = ({
|
|||||||
const [isDeleting, setIsDeleting] = useState(false);
|
const [isDeleting, setIsDeleting] = useState(false);
|
||||||
const [isRestoring, setIsRestoring] = useState(false);
|
const [isRestoring, setIsRestoring] = useState(false);
|
||||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||||
|
const [open_contact, setOpen_contact] = useState(null);
|
||||||
|
const [isOpenModalNote, setIsOpenModalNote] = useState(false);
|
||||||
|
|
||||||
const handleUpdateNote = async () => {
|
const handleUpdateNote = async () => {
|
||||||
try {
|
try {
|
||||||
@ -69,6 +73,13 @@ const NoteCardDirectoryEditable = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const contactProfile = (contactId) => {
|
||||||
|
DirectoryRepository.GetContactProfile(contactId).then((res) => {
|
||||||
|
setOpen_contact(res?.data);
|
||||||
|
setIsOpenModalNote(true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const handleRestore = async () => {
|
const handleRestore = async () => {
|
||||||
try {
|
try {
|
||||||
setIsRestoring(true);
|
setIsRestoring(true);
|
||||||
@ -84,6 +95,25 @@ const NoteCardDirectoryEditable = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
||||||
|
{isOpenModalNote && (
|
||||||
|
<GlobalModel
|
||||||
|
isOpen={isOpenModalNote}
|
||||||
|
closeModal={() => {
|
||||||
|
setOpen_contact(null);
|
||||||
|
setIsOpenModalNote(false);
|
||||||
|
}}
|
||||||
|
size="xl"
|
||||||
|
>
|
||||||
|
{open_contact && (
|
||||||
|
<ProfileContactDirectory
|
||||||
|
contact={open_contact}
|
||||||
|
setOpen_contact={setOpen_contact}
|
||||||
|
closeModal={() => setIsOpenModalNote(false)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</GlobalModel>
|
||||||
|
)}
|
||||||
<div
|
<div
|
||||||
className="card p-1 shadow-sm border-1 mb-4 rounded"
|
className="card p-1 shadow-sm border-1 mb-4 rounded"
|
||||||
style={{
|
style={{
|
||||||
@ -95,23 +125,39 @@ const NoteCardDirectoryEditable = ({
|
|||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||||
<div className="d-flex align-items-center">
|
<div className="d-flex align-items-center">
|
||||||
|
|
||||||
<Avatar
|
<Avatar
|
||||||
size="xs"
|
size="xs"
|
||||||
firstName={noteItem?.createdBy?.firstName}
|
firstName={noteItem?.createdBy?.firstName}
|
||||||
lastName={noteItem?.createdBy?.lastName}
|
lastName={noteItem?.createdBy?.lastName}
|
||||||
className="m-0"
|
className="m-0"
|
||||||
/>
|
/>
|
||||||
<div className="d-flex flex-column ms-0">
|
<div>
|
||||||
<span className="fw-semibold small">
|
<div className="d-flex ms-0 align-middle cursor-pointer" onClick={() =>contactProfile(noteItem.contactId)}>
|
||||||
{noteItem?.createdBy?.firstName} {noteItem?.createdBy?.lastName}
|
<span>
|
||||||
</span>
|
<span className="fw-bold "> {noteItem?.contactName} </span> <span className="text-muted font-weight-normal">
|
||||||
<span className="text-muted" style={{ fontSize: "10px" }}>
|
( {noteItem?.organizationName})
|
||||||
{moment
|
</span>
|
||||||
.utc(noteItem?.createdAt)
|
</span>
|
||||||
.add(5, "hours")
|
|
||||||
.add(30, "minutes")
|
</div>
|
||||||
.format("MMMM DD, YYYY [at] hh:mm A")}
|
<div className="d-flex ms-0 align-middle">
|
||||||
</span>
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div className="d-flex ms-0 mt-2">
|
||||||
|
<span className="text-muted">
|
||||||
|
by <span className="fw-bold "> {noteItem?.createdBy?.firstName} {noteItem?.createdBy?.lastName} </span>
|
||||||
|
<span className="text-muted">
|
||||||
|
on {moment
|
||||||
|
.utc(noteItem?.createdAt)
|
||||||
|
.add(5, "hours")
|
||||||
|
.add(30, "minutes")
|
||||||
|
.format("MMMM DD, YYYY [at] hh:mm A")}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -2,20 +2,39 @@ import React, { useEffect, useState, useMemo } from "react";
|
|||||||
import { DirectoryRepository } from "../../repositories/DirectoryRepository";
|
import { DirectoryRepository } from "../../repositories/DirectoryRepository";
|
||||||
import NoteCardDirectoryEditable from "./NoteCardDirectoryEditable";
|
import NoteCardDirectoryEditable from "./NoteCardDirectoryEditable";
|
||||||
|
|
||||||
const NotesCardViewDirectory = ({ notes, setNotes, searchText, selectedNoteNames }) => { // ✅ Changed to array
|
const NotesCardViewDirectory = ({ notes, setNotesForFilter, searchText, filterAppliedNotes }) => {
|
||||||
const [allNotes, setAllNotes] = useState([]);
|
const [allNotes, setAllNotes] = useState([]);
|
||||||
const [filteredNotes, setFilteredNotes] = useState([]);
|
const [filteredNotes, setFilteredNotes] = useState([]);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [totalPages, setTotalPages] = useState(1);
|
const [totalPages, setTotalPages] = useState(1);
|
||||||
|
const [selectedCreators, setSelectedCreators] = useState([]);
|
||||||
|
const [selectedOrgs, setSelectedOrgs] = useState([]);
|
||||||
const pageSize = 20;
|
const pageSize = 20;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchNotes();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const fetchNotes = async () => {
|
const fetchNotes = async () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await DirectoryRepository.GetNotes(1000, 1);
|
const response = await DirectoryRepository.GetNotes(1000, 1);
|
||||||
const fetchedNotes = response.data?.data || [];
|
const fetchedNotes = response.data?.data || [];
|
||||||
setAllNotes(fetchedNotes);
|
setAllNotes(fetchedNotes);
|
||||||
|
setNotesForFilter(fetchedNotes)
|
||||||
|
|
||||||
|
const creatorsSet = new Set();
|
||||||
|
const orgsSet = new Set();
|
||||||
|
|
||||||
|
fetchedNotes.forEach((note) => {
|
||||||
|
const creator = `${note.createdBy?.firstName || ""} ${note.createdBy?.lastName || ""}`.trim();
|
||||||
|
if (creator) creatorsSet.add(creator);
|
||||||
|
|
||||||
|
const org = note.organizationName;
|
||||||
|
if (org) orgsSet.add(org);
|
||||||
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Failed to fetch notes:", error);
|
console.error("Failed to fetch notes:", error);
|
||||||
} finally {
|
} finally {
|
||||||
@ -23,52 +42,51 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText, selectedNoteNames
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
fetchNotes();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
|
const applyCombinedFilter = () => {
|
||||||
const lowerSearch = searchText?.toLowerCase() || "";
|
const lowerSearch = searchText?.toLowerCase() || "";
|
||||||
|
|
||||||
const filtered = allNotes.filter((noteItem) => {
|
const filtered = allNotes.filter((noteItem) => {
|
||||||
|
const creator = `${noteItem.createdBy?.firstName || ""} ${noteItem.createdBy?.lastName || ""}`.trim();
|
||||||
|
const org = noteItem.organizationName;
|
||||||
|
|
||||||
|
const matchesCreator = selectedCreators.length === 0 || selectedCreators.includes(creator);
|
||||||
|
const matchesOrg = selectedOrgs.length === 0 || selectedOrgs.includes(org);
|
||||||
|
|
||||||
const plainNote = noteItem?.note?.replace(/<[^>]+>/g, "").toLowerCase();
|
const plainNote = noteItem?.note?.replace(/<[^>]+>/g, "").toLowerCase();
|
||||||
const fullName = `${noteItem?.createdBy?.firstName || ""} ${noteItem?.createdBy?.lastName || ""}`.trim();
|
|
||||||
const lowerFullName = fullName.toLowerCase();
|
|
||||||
const createdDate = new Date(noteItem?.createdAt).toLocaleDateString("en-IN").toLowerCase();
|
|
||||||
|
|
||||||
// ✅ Collect all string values in the note object to search through
|
|
||||||
const stringValues = [];
|
const stringValues = [];
|
||||||
|
|
||||||
const extractStrings = (obj) => {
|
const extractStrings = (obj) => {
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (!obj.hasOwnProperty(key)) continue;
|
|
||||||
const value = obj[key];
|
const value = obj[key];
|
||||||
if (typeof value === "string") {
|
if (typeof value === "string") {
|
||||||
stringValues.push(value.toLowerCase());
|
stringValues.push(value.toLowerCase());
|
||||||
} else if (typeof value === "object" && value !== null) {
|
} else if (typeof value === "object" && value !== null) {
|
||||||
extractStrings(value); // Recursively extract from nested objects
|
extractStrings(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
extractStrings(noteItem);
|
extractStrings(noteItem);
|
||||||
// Add manually stripped note, full name, date, etc.
|
stringValues.push(plainNote, creator.toLowerCase());
|
||||||
stringValues.push(plainNote, lowerFullName, createdDate);
|
|
||||||
|
|
||||||
const matchesSearch = stringValues.some((val) => val.includes(lowerSearch));
|
const matchesSearch = stringValues.some((val) => val.includes(lowerSearch));
|
||||||
|
|
||||||
const matchesNameFilter =
|
return matchesCreator && matchesOrg && matchesSearch;
|
||||||
selectedNoteNames.length === 0 || selectedNoteNames.includes(fullName);
|
|
||||||
|
|
||||||
return matchesSearch && matchesNameFilter;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
setFilteredNotes(filtered);
|
setFilteredNotes(filtered);
|
||||||
setNotes(filtered);
|
|
||||||
setCurrentPage(1);
|
setCurrentPage(1);
|
||||||
setTotalPages(Math.ceil(filtered.length / pageSize));
|
setTotalPages(Math.ceil(filtered.length / pageSize));
|
||||||
}, [searchText, allNotes, selectedNoteNames]);
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
applyCombinedFilter();
|
||||||
|
}, [searchText, allNotes]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setFilteredNotes(filterAppliedNotes);
|
||||||
|
}, [filterAppliedNotes])
|
||||||
|
|
||||||
const currentItems = useMemo(() => {
|
const currentItems = useMemo(() => {
|
||||||
const startIndex = (currentPage - 1) * pageSize;
|
const startIndex = (currentPage - 1) * pageSize;
|
||||||
@ -81,19 +99,19 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText, selectedNoteNames
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
if (loading) return <p className="mt-10 text-center">Loading notes...</p>;
|
||||||
return <p className="mt-10 text-center">Loading notes...</p>;
|
|
||||||
}
|
if (!filteredNotes.length) return <p className="mt-10 text-center">No matching notes found</p>;
|
||||||
|
|
||||||
if (!filteredNotes.length) {
|
|
||||||
return <p className="mt-10 text-center">No matching notes found</p>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="w-100 h-100 ">
|
||||||
className="w-100 h-100"
|
{/* Filter Dropdown */}
|
||||||
>
|
<div className="dropdown mb-3 ms-2">
|
||||||
<div className="d-flex flex-column text-start mt-4" style={{ gap: "0rem", minHeight: "100%" }}>
|
</div>
|
||||||
|
|
||||||
|
{/* Notes List */}
|
||||||
|
<div className="d-flex flex-column text-start" style={{ gap: "0rem", minHeight: "100%" }}>
|
||||||
{currentItems.map((noteItem) => (
|
{currentItems.map((noteItem) => (
|
||||||
<NoteCardDirectoryEditable
|
<NoteCardDirectoryEditable
|
||||||
key={noteItem.id}
|
key={noteItem.id}
|
||||||
@ -104,13 +122,12 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText, selectedNoteNames
|
|||||||
prevNotes.map((n) => (n.id === updatedNote.id ? updatedNote : n))
|
prevNotes.map((n) => (n.id === updatedNote.id ? updatedNote : n))
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
onNoteDelete={() => {
|
onNoteDelete={() => fetchNotes()}
|
||||||
fetchNotes();
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Pagination */}
|
||||||
{totalPages > 1 && (
|
{totalPages > 1 && (
|
||||||
<div className="d-flex justify-content-end mt-3 me-3">
|
<div className="d-flex justify-content-end mt-3 me-3">
|
||||||
<div className="d-flex align-items-center gap-2">
|
<div className="d-flex align-items-center gap-2">
|
||||||
@ -128,7 +145,8 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText, selectedNoteNames
|
|||||||
return (
|
return (
|
||||||
<button
|
<button
|
||||||
key={page}
|
key={page}
|
||||||
className={`btn btn-sm rounded-circle border ${page === currentPage ? "btn-primary text-white" : "btn-outline-primary"}`}
|
className={`btn btn-sm rounded-circle border ${page === currentPage ? "btn-primary text-white" : "btn-outline-primary"
|
||||||
|
}`}
|
||||||
style={{ width: "32px", height: "32px", padding: 0 }}
|
style={{ width: "32px", height: "32px", padding: 0 }}
|
||||||
onClick={() => handlePageClick(page)}
|
onClick={() => handlePageClick(page)}
|
||||||
>
|
>
|
||||||
@ -152,4 +170,4 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText, selectedNoteNames
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default NotesCardViewDirectory;
|
export default NotesCardViewDirectory;
|
||||||
|
@ -38,6 +38,8 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
|
|||||||
const [IsDeleting, setDeleting] = useState(false);
|
const [IsDeleting, setDeleting] = useState(false);
|
||||||
const [openBucketModal, setOpenBucketModal] = useState(false);
|
const [openBucketModal, setOpenBucketModal] = useState(false);
|
||||||
const [notes, setNotes] = useState([]);
|
const [notes, setNotes] = useState([]);
|
||||||
|
const [filterAppliedNotes, setFilterAppliedNotes] = useState([]);
|
||||||
|
// const [selectedOrgs, setSelectedOrgs] = useState([]);
|
||||||
|
|
||||||
// ✅ Changed to an array for multiple selections
|
// ✅ Changed to an array for multiple selections
|
||||||
const [selectedNoteNames, setSelectedNoteNames] = useState([]);
|
const [selectedNoteNames, setSelectedNoteNames] = useState([]);
|
||||||
@ -353,6 +355,8 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
|
|||||||
notesToExport={notes}
|
notesToExport={notes}
|
||||||
selectedNoteNames={selectedNoteNames}
|
selectedNoteNames={selectedNoteNames}
|
||||||
setSelectedNoteNames={setSelectedNoteNames}
|
setSelectedNoteNames={setSelectedNoteNames}
|
||||||
|
notesForFilter={notes}
|
||||||
|
setFilterAppliedNotes={setFilterAppliedNotes}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -421,9 +425,10 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
|
|||||||
<div className="mt-0">
|
<div className="mt-0">
|
||||||
<NotesCardViewDirectory
|
<NotesCardViewDirectory
|
||||||
notes={notes}
|
notes={notes}
|
||||||
setNotes={setNotes}
|
setNotesForFilter={setNotes}
|
||||||
searchText={searchText}
|
searchText={searchText}
|
||||||
selectedNoteNames={selectedNoteNames}
|
setIsOpenModalNote={setIsOpenModalNote}
|
||||||
|
filterAppliedNotes={filterAppliedNotes}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
@ -19,12 +19,19 @@ const DirectoryPageHeader = ({
|
|||||||
IsActive,
|
IsActive,
|
||||||
contactsToExport,
|
contactsToExport,
|
||||||
notesToExport,
|
notesToExport,
|
||||||
selectedNoteNames, // ✅ Changed to array
|
selectedNoteNames,
|
||||||
setSelectedNoteNames, // ✅ Changed to array
|
setSelectedNoteNames,
|
||||||
|
notesForFilter,
|
||||||
|
setFilterAppliedNotes
|
||||||
}) => {
|
}) => {
|
||||||
const [filtered, setFiltered] = useState(0);
|
const [filtered, setFiltered] = useState(0);
|
||||||
|
const [filteredNotes, setFilteredNotes] = useState([]);
|
||||||
const [noteCreators, setNoteCreators] = useState([]);
|
const [noteCreators, setNoteCreators] = useState([]);
|
||||||
|
const [allCreators, setAllCreators] = useState([]);
|
||||||
|
const [allOrganizations, setAllOrganizations] = useState([]);
|
||||||
|
const [filteredOrganizations, setFilteredOrganizations] = useState([]);
|
||||||
|
const [selectedCreators, setSelectedCreators] = useState([]); // Corrected to setSelectedCreators
|
||||||
|
const [selectedOrgs, setSelectedOrgs] = useState([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setFiltered(tempSelectedBucketIds?.length + tempSelectedCategoryIds?.length);
|
setFiltered(tempSelectedBucketIds?.length + tempSelectedCategoryIds?.length);
|
||||||
@ -55,6 +62,23 @@ const DirectoryPageHeader = ({
|
|||||||
}
|
}
|
||||||
}, [viewType]);
|
}, [viewType]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const creatorsSet = new Set();
|
||||||
|
const orgsSet = new Set();
|
||||||
|
|
||||||
|
notesForFilter.forEach((note) => {
|
||||||
|
const creator = `${note.createdBy?.firstName || ""} ${note.createdBy?.lastName || ""}`.trim();
|
||||||
|
if (creator) creatorsSet.add(creator);
|
||||||
|
|
||||||
|
const org = note.organizationName;
|
||||||
|
if (org) orgsSet.add(org);
|
||||||
|
});
|
||||||
|
|
||||||
|
setAllCreators([...creatorsSet].sort());
|
||||||
|
setAllOrganizations([...orgsSet].sort());
|
||||||
|
setFilteredOrganizations([...orgsSet].sort());
|
||||||
|
}, [notesForFilter])
|
||||||
|
|
||||||
|
|
||||||
const handleToggleNoteName = (name) => {
|
const handleToggleNoteName = (name) => {
|
||||||
setSelectedNoteNames(prevSelectedNames => {
|
setSelectedNoteNames(prevSelectedNames => {
|
||||||
@ -66,6 +90,45 @@ const DirectoryPageHeader = ({
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateFilteredOrganizations = () => {
|
||||||
|
if (selectedCreators.length === 0) {
|
||||||
|
setFilteredOrganizations(allOrganizations);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filteredOrgsSet = new Set();
|
||||||
|
notesForFilter.forEach((note) => {
|
||||||
|
const creator = `${note.createdBy?.firstName || ""} ${note.createdBy?.lastName || ""}`.trim();
|
||||||
|
if (selectedCreators.includes(creator)) {
|
||||||
|
if (note.organizationName) {
|
||||||
|
filteredOrgsSet.add(note.organizationName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setFilteredOrganizations([...filteredOrgsSet].sort());
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToggleCreator = (name) => {
|
||||||
|
const updated = selectedCreators.includes(name)
|
||||||
|
? selectedCreators.filter((n) => n !== name)
|
||||||
|
: [...selectedCreators, name];
|
||||||
|
|
||||||
|
setSelectedCreators(updated);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleToggleOrg = (name) => {
|
||||||
|
const updated = selectedOrgs.includes(name)
|
||||||
|
? selectedOrgs.filter((n) => n !== name)
|
||||||
|
: [...selectedOrgs, name];
|
||||||
|
|
||||||
|
setSelectedOrgs(updated);
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
updateFilteredOrganizations();
|
||||||
|
}, [selectedCreators]);
|
||||||
|
|
||||||
const handleExport = (type) => {
|
const handleExport = (type) => {
|
||||||
let dataToExport = [];
|
let dataToExport = [];
|
||||||
|
|
||||||
@ -149,6 +212,41 @@ const DirectoryPageHeader = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const applyCombinedFilter = () => {
|
||||||
|
const lowerSearch = searchText?.toLowerCase() || "";
|
||||||
|
|
||||||
|
const filtered = notesForFilter.filter((noteItem) => {
|
||||||
|
const creator = `${noteItem.createdBy?.firstName || ""} ${noteItem.createdBy?.lastName || ""}`.trim();
|
||||||
|
const org = noteItem.organizationName;
|
||||||
|
|
||||||
|
const matchesCreator = selectedCreators.length === 0 || selectedCreators.includes(creator);
|
||||||
|
const matchesOrg = selectedOrgs.length === 0 || selectedOrgs.includes(org);
|
||||||
|
|
||||||
|
const plainNote = noteItem?.note?.replace(/<[^>]+>/g, "").toLowerCase();
|
||||||
|
|
||||||
|
const stringValues = [];
|
||||||
|
const extractStrings = (obj) => {
|
||||||
|
for (const key in obj) {
|
||||||
|
const value = obj[key];
|
||||||
|
if (typeof value === "string") {
|
||||||
|
stringValues.push(value.toLowerCase());
|
||||||
|
} else if (typeof value === "object" && value !== null) {
|
||||||
|
extractStrings(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
extractStrings(noteItem);
|
||||||
|
stringValues.push(plainNote, creator.toLowerCase());
|
||||||
|
|
||||||
|
const matchesSearch = stringValues.some((val) => val.includes(lowerSearch));
|
||||||
|
|
||||||
|
return matchesCreator && matchesOrg && matchesSearch;
|
||||||
|
});
|
||||||
|
|
||||||
|
setFilteredNotes(filtered);
|
||||||
|
setFilterAppliedNotes(filtered);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="row mx-0 px-0 align-items-center mt-0">
|
<div className="row mx-0 px-0 align-items-center mt-0">
|
||||||
@ -189,52 +287,72 @@ const DirectoryPageHeader = ({
|
|||||||
style={{ width: "200px" }}
|
style={{ width: "200px" }}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Name Filter Dropdown - now with checkboxes */}
|
{/* Moved the "Filter by" dropdown to be triggered by the funnel icon */}
|
||||||
{viewType === "notes" && noteCreators.length > 0 && (
|
{viewType === "notes" && (
|
||||||
<div className="dropdown" style={{ width: "fit-content" }}>
|
<div className="dropdown" style={{ width: "500px" }}>
|
||||||
<button
|
<a
|
||||||
className="btn btn-sm btn-outline-secondary dropdown-toggle" // Added btn-sm for small size
|
className="dropdown-toggle hide-arrow cursor-pointer d-flex align-items-center position-relative"
|
||||||
type="button"
|
|
||||||
id="notesNameFilterDropdown"
|
|
||||||
data-bs-toggle="dropdown"
|
data-bs-toggle="dropdown"
|
||||||
aria-expanded="false"
|
aria-expanded="false"
|
||||||
>
|
>
|
||||||
{selectedNoteNames.length > 0
|
<i className={`fa-solid fa-filter ms-1 fs-5 ${selectedCreators.length > 0 || selectedOrgs.length > 0 ? "text-primary" : "text-muted"}`}></i>
|
||||||
? `Names (${selectedNoteNames.length})`
|
{(selectedCreators.length > 0 || selectedOrgs.length > 0) && (
|
||||||
: "Filter by Name"}
|
<span className="position-absolute top-0 start-100 translate-middle badge rounded-pill bg-warning" style={{ fontSize: "0.4rem" }}>
|
||||||
</button>
|
{selectedCreators.length + selectedOrgs.length}
|
||||||
<ul className="dropdown-menu p-2" aria-labelledby="notesNameFilterDropdown">
|
</span>
|
||||||
{/* Option to clear all selections */}
|
)}
|
||||||
<li>
|
</a>
|
||||||
<a
|
|
||||||
className={`dropdown-item ${selectedNoteNames.length === 0 ? "active" : ""}`}
|
<div className="dropdown-menu p-3" style={{ width: "300px", maxHeight: "400px", overflowY: "auto" }}>
|
||||||
href="#"
|
<p className="text-muted mb-1">Created By</p>
|
||||||
onClick={(e) => {
|
{allCreators.map((name, idx) => (
|
||||||
e.preventDefault();
|
<div className="form-check mb-1" key={`creator-${idx}`}>
|
||||||
setSelectedNoteNames([]); // Clear all
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id={`creator-${idx}`}
|
||||||
|
checked={selectedCreators.includes(name)}
|
||||||
|
onChange={() => handleToggleCreator(name)}
|
||||||
|
/>
|
||||||
|
<label className="form-check-label" htmlFor={`creator-${idx}`}>
|
||||||
|
{name}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<p className="text-muted mt-3 mb-1">Organization</p>
|
||||||
|
{filteredOrganizations.map((org, idx) => (
|
||||||
|
<div className="form-check mb-1" key={`org-${idx}`}>
|
||||||
|
<input
|
||||||
|
className="form-check-input"
|
||||||
|
type="checkbox"
|
||||||
|
id={`org-${idx}`}
|
||||||
|
checked={selectedOrgs.includes(org)}
|
||||||
|
onChange={() => handleToggleOrg(org)}
|
||||||
|
/>
|
||||||
|
<label className="form-check-label" htmlFor={`org-${idx}`}>
|
||||||
|
{org}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
|
<div className="d-flex justify-content-between mt-2">
|
||||||
|
<button
|
||||||
|
className="btn btn-sm btn-outline-danger"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedCreators([]);
|
||||||
|
setSelectedOrgs([]);
|
||||||
|
setFilteredOrganizations(allOrganizations);
|
||||||
|
applyCombinedFilter();
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Clear
|
Clear
|
||||||
</a>
|
</button>
|
||||||
</li>
|
<button className="btn btn-sm btn-primary" onClick={applyCombinedFilter}>
|
||||||
<li><hr className="dropdown-divider" /></li>
|
Apply Filter
|
||||||
{noteCreators.map((name, index) => (
|
</button>
|
||||||
<li key={index}>
|
</div>
|
||||||
<div className="form-check dropdown-item p-0">
|
</div>
|
||||||
<input
|
|
||||||
className="form-check-input ms-2"
|
|
||||||
type="checkbox"
|
|
||||||
id={`name-filter-${index}`}
|
|
||||||
checked={selectedNoteNames.includes(name)}
|
|
||||||
onChange={() => handleToggleNoteName(name)}
|
|
||||||
/>
|
|
||||||
<label className="form-check-label ms-2" htmlFor={`name-filter-${index}`}>
|
|
||||||
{name}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
))}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user