diff --git a/src/components/Directory/NoteCardDirectoryEditable.jsx b/src/components/Directory/NoteCardDirectoryEditable.jsx
new file mode 100644
index 00000000..e6991c2e
--- /dev/null
+++ b/src/components/Directory/NoteCardDirectoryEditable.jsx
@@ -0,0 +1,256 @@
+import React, { useState } from "react";
+import ReactQuill from "react-quill";
+import moment from "moment";
+import Avatar from "../common/Avatar";
+import { DirectoryRepository } from "../../repositories/DirectoryRepository";
+import showToast from "../../services/toastService";
+import { cacheData, getCachedData } from "../../slices/apiDataManager";
+import ConfirmModal from "../common/ConfirmModal"; // Make sure path is correct
+import "../common/TextEditor/Editor.css";
+import ProfileContactDirectory from "./ProfileContactDirectory";
+import GlobalModel from "../common/GlobalModel";
+
+const NoteCardDirectoryEditable = ({
+ noteItem,
+ contactId,
+ onNoteUpdate,
+ onNoteDelete,
+}) => {
+ const [editing, setEditing] = useState(false);
+ const [editorValue, setEditorValue] = useState(noteItem.note);
+ const [isLoading, setIsLoading] = useState(false);
+ const [isDeleting, setIsDeleting] = useState(false);
+ const [isRestoring, setIsRestoring] = useState(false);
+ const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
+ const [open_contact, setOpen_contact] = useState(null);
+ const [isOpenModalNote, setIsOpenModalNote] = useState(false);
+
+ const handleUpdateNote = async () => {
+ try {
+ setIsLoading(true);
+ const payload = {
+ id: noteItem.id,
+ note: editorValue,
+ contactId,
+ };
+ const response = await DirectoryRepository.UpdateNote(noteItem.id, payload);
+
+ const cachedContactProfile = getCachedData("Contact Profile");
+ if (cachedContactProfile?.contactId === contactId) {
+ const updatedCache = {
+ ...cachedContactProfile,
+ data: {
+ ...cachedContactProfile.data,
+ notes: cachedContactProfile.data.notes.map((note) =>
+ note.id === noteItem.id ? response.data : note
+ ),
+ },
+ };
+ cacheData("Contact Profile", updatedCache);
+ }
+
+ onNoteUpdate?.(response.data);
+ setEditing(false);
+ showToast("Note updated successfully", "success");
+ } catch (error) {
+ showToast("Failed to update note", "error");
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const suspendEmployee = async () => {
+ try {
+ setIsDeleting(true);
+ await DirectoryRepository.DeleteNote(noteItem.id, false);
+ onNoteDelete?.(noteItem.id);
+ setIsDeleteModalOpen(false);
+ showToast("Note deleted successfully", "success");
+ } catch (error) {
+ showToast("Failed to delete note", "error");
+ } finally {
+ setIsDeleting(false);
+ }
+ };
+
+ const contactProfile = (contactId) => {
+ DirectoryRepository.GetContactProfile(contactId).then((res) => {
+ setOpen_contact(res?.data);
+ setIsOpenModalNote(true);
+ });
+ };
+
+ const handleRestore = async () => {
+ try {
+ setIsRestoring(true);
+ await DirectoryRepository.DeleteNote(noteItem.id, true);
+ onNoteDelete?.(noteItem.id);
+ showToast("Note restored successfully", "success");
+ } catch (error) {
+ showToast("Failed to restore note", "error");
+ } finally {
+ setIsRestoring(false);
+ }
+ };
+
+ return (
+ <>
+
+ {isOpenModalNote && (
+ {
+ setOpen_contact(null);
+ setIsOpenModalNote(false);
+ }}
+ size="xl"
+ >
+ {open_contact && (
+ setIsOpenModalNote(false)}
+ />
+ )}
+
+ )}
+
+ {/* Header */}
+
+
+
+
+
+
contactProfile(noteItem.contactId)}>
+
+ {noteItem?.contactName}
+ ( {noteItem?.organizationName})
+
+
+
+
+
+
+
+
+
+
+ by {noteItem?.createdBy?.firstName} {noteItem?.createdBy?.lastName}
+
+ on {moment
+ .utc(noteItem?.createdAt)
+ .add(5, "hours")
+ .add(30, "minutes")
+ .format("MMMM DD, YYYY [at] hh:mm A")}
+
+
+
+
+
+
+
+ {/* Action Icons */}
+
+ {noteItem.isActive ? (
+ <>
+
setEditing(true)}
+ title="Edit"
+ >
+ {!isDeleting ? (
+
setIsDeleteModalOpen(true)}
+ title="Delete"
+ >
+ ) : (
+
+ )}
+ >
+ ) : isRestoring ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {/* Editor or Content */}
+ {editing ? (
+ <>
+
+
+ setEditing(false)}
+ >
+ Cancel
+
+
+ {isLoading ? "Saving..." : "Submit"}
+
+
+ >
+ ) : (
+
+ )}
+
+
+ {/* Delete Confirm Modal */}
+ {isDeleteModalOpen && (
+
+ setIsDeleteModalOpen(false)}
+ loading={isDeleting}
+ paramData={noteItem}
+ />
+
+ )}
+ >
+ );
+};
+
+export default NoteCardDirectoryEditable;
diff --git a/src/components/Directory/NotesCardViewDirectory.jsx b/src/components/Directory/NotesCardViewDirectory.jsx
new file mode 100644
index 00000000..be207ffd
--- /dev/null
+++ b/src/components/Directory/NotesCardViewDirectory.jsx
@@ -0,0 +1,173 @@
+import React, { useEffect, useState, useMemo } from "react";
+import { DirectoryRepository } from "../../repositories/DirectoryRepository";
+import NoteCardDirectoryEditable from "./NoteCardDirectoryEditable";
+
+const NotesCardViewDirectory = ({ notes, setNotesForFilter, searchText, filterAppliedNotes }) => {
+ const [allNotes, setAllNotes] = useState([]);
+ const [filteredNotes, setFilteredNotes] = useState([]);
+ const [loading, setLoading] = useState(true);
+ const [currentPage, setCurrentPage] = useState(1);
+ const [totalPages, setTotalPages] = useState(1);
+ const [selectedCreators, setSelectedCreators] = useState([]);
+ const [selectedOrgs, setSelectedOrgs] = useState([]);
+ const pageSize = 20;
+
+ useEffect(() => {
+ fetchNotes();
+ }, []);
+
+ const fetchNotes = async () => {
+ setLoading(true);
+ try {
+ const response = await DirectoryRepository.GetNotes(1000, 1);
+ const fetchedNotes = response.data?.data || [];
+ 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) {
+ console.error("Failed to fetch notes:", error);
+ } finally {
+ setLoading(false);
+ }
+ };
+
+
+
+ const applyCombinedFilter = () => {
+ const lowerSearch = searchText?.toLowerCase() || "";
+
+ 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 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);
+ setCurrentPage(1);
+ setTotalPages(Math.ceil(filtered.length / pageSize));
+ };
+
+ useEffect(() => {
+ applyCombinedFilter();
+ }, [searchText, allNotes]);
+
+ useEffect(() => {
+ setFilteredNotes(filterAppliedNotes);
+ }, [filterAppliedNotes])
+
+ const currentItems = useMemo(() => {
+ const startIndex = (currentPage - 1) * pageSize;
+ return filteredNotes.slice(startIndex, startIndex + pageSize);
+ }, [filteredNotes, currentPage]);
+
+ const handlePageClick = (page) => {
+ if (page !== currentPage) {
+ setCurrentPage(page);
+ }
+ };
+
+ if (loading) return Loading notes...
;
+
+ if (!filteredNotes.length) return No matching notes found
;
+
+
+ return (
+
+ {/* Filter Dropdown */}
+
+
+
+ {/* Notes List */}
+
+ {currentItems.map((noteItem) => (
+ {
+ setAllNotes((prevNotes) =>
+ prevNotes.map((n) => (n.id === updatedNote.id ? updatedNote : n))
+ );
+ }}
+ onNoteDelete={() => fetchNotes()}
+ />
+ ))}
+
+
+ {/* Pagination */}
+ {totalPages > 1 && (
+
+
+
+
+ {[...Array(totalPages)].map((_, i) => {
+ const page = i + 1;
+ return (
+
+ );
+ })}
+
+
+
+
+ )}
+
+ );
+};
+
+export default NotesCardViewDirectory;
diff --git a/src/pages/Directory/Directory.jsx b/src/pages/Directory/Directory.jsx
index af6535b8..ca95181f 100644
--- a/src/pages/Directory/Directory.jsx
+++ b/src/pages/Directory/Directory.jsx
@@ -20,6 +20,7 @@ import DirectoryPageHeader from "./DirectoryPageHeader";
import ManageBucket from "../../components/Directory/ManageBucket";
import { useFab } from "../../Context/FabContext";
import { DireProvider, useDir } from "../../Context/DireContext";
+import NotesCardViewDirectory from "../../components/Directory/NotesCardViewDirectory";
const Directory = ({ IsPage = true, prefernceContacts }) => {
const [projectPrefernce, setPerfence] = useState(null);
@@ -31,11 +32,17 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
const [ContactList, setContactList] = useState([]);
const [contactCategories, setContactCategories] = useState([]);
const [searchText, setSearchText] = useState("");
- const [listView, setListView] = useState(false);
+ const [viewType, setViewType] = useState("notes");
const [selectedBucketIds, setSelectedBucketIds] = useState([]);
const [deleteContact, setDeleteContact] = useState(null);
const [IsDeleting, setDeleting] = useState(false);
const [openBucketModal, setOpenBucketModal] = useState(false);
+ const [notes, setNotes] = useState([]);
+ const [filterAppliedNotes, setFilterAppliedNotes] = useState([]);
+ // const [selectedOrgs, setSelectedOrgs] = useState([]);
+
+ // ✅ Changed to an array for multiple selections
+ const [selectedNoteNames, setSelectedNoteNames] = useState([]);
const [tempSelectedBucketIds, setTempSelectedBucketIds] = useState([]);
const [tempSelectedCategoryIds, setTempSelectedCategoryIds] = useState([]);
@@ -71,8 +78,6 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
setIsOpenModal(false);
}
- // cacheData("Contacts", {data:updatedContacts,isActive:IsActive});
- // setContactList(updatedContacts);
refetch(IsActive, prefernceContacts);
refetchBucket();
} catch (error) {
@@ -249,6 +254,7 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
return () => setActions([]);
}, [IsPage, buckets]);
+
useEffect(() => {
setPerfence(prefernceContacts);
}, [prefernceContacts]);
@@ -326,14 +332,14 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
)}
-
+
{
IsActive={IsActive}
setOpenBucketModal={setOpenBucketModal}
contactsToExport={contacts}
+ notesToExport={notes}
+ selectedNoteNames={selectedNoteNames}
+ setSelectedNoteNames={setSelectedNoteNames}
+ notesForFilter={notes}
+ setFilterAppliedNotes={setFilterAppliedNotes}
/>
-
- {/* Messages when listView is false */}
- {!listView && (
-
- {loading &&
Loading...
}
- {!loading && contacts?.length === 0 && (
+
+ {(viewType === "card" || viewType === "list" || viewType === "notes") && (
+
+ {!loading && (viewType === "card" || viewType === "list") && contacts?.length === 0 && (
No contact found
)}
- {!loading && contacts?.length > 0 && currentItems.length === 0 && (
-
No matching contact found
- )}
+ {!loading &&
+ (viewType === "card" || viewType === "list") &&
+ contacts?.length > 0 &&
+ currentItems.length === 0 && (
+
No matching contact found
+ )}
)}
- {/* Table view (listView === true) */}
-
- {listView ? (
+ {viewType === "list" && (
- {loading && (
-
- |
- {" "}
- Loading... {" "}
- |
-
- )}
-
- {!loading && contacts?.length === 0 && (
-
- |
- No contact found
- |
-
- )}
-
- {!loading &&
- currentItems.length === 0 &&
- contacts?.length > 0 && (
-
- |
- No matching contact found
- |
-
- )}
-
{!loading &&
currentItems.map((contact) => (
{
- ) : (
-
+ )}
+
+ {viewType === "card" && (
+
{!loading &&
currentItems.map((contact) => (
{
)}
+ {viewType === "notes" && (
+
+
+
+ )}
+
{/* Pagination */}
{!loading &&
+ viewType !== "notes" &&
contacts?.length > 0 &&
currentItems.length > ITEMS_PER_PAGE && (