diff --git a/src/components/Directory/NoteCardDirectoryEditable.jsx b/src/components/Directory/NoteCardDirectoryEditable.jsx index 4f5bf410..6a6f9be7 100644 --- a/src/components/Directory/NoteCardDirectoryEditable.jsx +++ b/src/components/Directory/NoteCardDirectoryEditable.jsx @@ -5,6 +5,7 @@ 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"; const NoteCardDirectoryEditable = ({ @@ -18,6 +19,7 @@ const NoteCardDirectoryEditable = ({ const [isLoading, setIsLoading] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const [isRestoring, setIsRestoring] = useState(false); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); const handleUpdateNote = async () => { try { @@ -29,7 +31,6 @@ const NoteCardDirectoryEditable = ({ }; const response = await DirectoryRepository.UpdateNote(noteItem.id, payload); - // Optional cache update const cachedContactProfile = getCachedData("Contact Profile"); if (cachedContactProfile?.contactId === contactId) { const updatedCache = { @@ -43,8 +44,7 @@ const NoteCardDirectoryEditable = ({ }; cacheData("Contact Profile", updatedCache); } - - // Notify parent + onNoteUpdate?.(response.data); setEditing(false); showToast("Note updated successfully", "success"); @@ -55,111 +55,156 @@ const NoteCardDirectoryEditable = ({ } }; - const handleDeleteOrRestore = async (shouldRestore) => { + const suspendEmployee = async () => { try { - shouldRestore ? setIsRestoring(true) : setIsDeleting(true); - await DirectoryRepository.DeleteNote(noteItem.id, shouldRestore); + setIsDeleting(true); + await DirectoryRepository.DeleteNote(noteItem.id, false); onNoteDelete?.(noteItem.id); - showToast(`Note ${shouldRestore ? "restored" : "deleted"} successfully`, "success"); + setIsDeleteModalOpen(false); + showToast("Note deleted successfully", "success"); } catch (error) { - showToast("Failed to process note", "error"); + showToast("Failed to delete note", "error"); } finally { setIsDeleting(false); + } + }; + + 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 ( -
- {/* Header */} -
-
- -
- - {noteItem?.createdBy?.firstName} {noteItem?.createdBy?.lastName} - - - {moment - .utc(noteItem?.createdAt) - .add(5, "hours") - .add(30, "minutes") - .format("MMMM DD, YYYY [at] hh:mm A")} - + <> +
+ {/* Header */} +
+
+ +
+ + {noteItem?.createdBy?.firstName} {noteItem?.createdBy?.lastName} + + + {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 ? ( + + {/* Action Icons */} +
+ {noteItem.isActive ? ( + <> handleDeleteOrRestore(false)} - title="Delete" + className="bx bxs-edit bx-sm me-2 text-primary cursor-pointer" + onClick={() => setEditing(true)} + title="Edit" > - ) : ( -
- )} - - ) : isRestoring ? ( - - ) : ( - handleDeleteOrRestore(true)} - title="Restore" - > - )} -
-
-
- {/* Editor or Content */} - {editing ? ( - <> - -
- setEditing(false)} - > - Cancel - - - {isLoading ? "Saving..." : "Submit"} - + {!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; \ No newline at end of file +export default NoteCardDirectoryEditable; diff --git a/src/components/Directory/NotesCardViewDirectory.jsx b/src/components/Directory/NotesCardViewDirectory.jsx index 7f5e53f3..943ea3a2 100644 --- a/src/components/Directory/NotesCardViewDirectory.jsx +++ b/src/components/Directory/NotesCardViewDirectory.jsx @@ -2,7 +2,7 @@ import React, { useEffect, useState, useMemo } from "react"; import { DirectoryRepository } from "../../repositories/DirectoryRepository"; import NoteCardDirectoryEditable from "./NoteCardDirectoryEditable"; -const NotesCardViewDirectory = ({ notes, setNotes, searchText }) => { +const NotesCardViewDirectory = ({ notes, setNotes, searchText, selectedNoteNames }) => { // ✅ Changed to array const [allNotes, setAllNotes] = useState([]); const [filteredNotes, setFilteredNotes] = useState([]); const [loading, setLoading] = useState(true); @@ -13,7 +13,7 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText }) => { const fetchNotes = async () => { setLoading(true); try { - const response = await DirectoryRepository.GetNotes(1000, 1); // fetch all for search + const response = await DirectoryRepository.GetNotes(1000, 1); const fetchedNotes = response.data?.data || []; setAllNotes(fetchedNotes); } catch (error) { @@ -27,26 +27,33 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText }) => { fetchNotes(); }, []); - // Search + update pagination + exportable data useEffect(() => { const lowerSearch = searchText?.toLowerCase() || ""; + const filtered = allNotes.filter((noteItem) => { const plainNote = noteItem?.note?.replace(/<[^>]+>/g, "").toLowerCase(); - const fullName = `${noteItem?.contact?.firstName || ""} ${noteItem?.contact?.lastName || ""}`.toLowerCase(); + const fullName = `${noteItem?.createdBy?.firstName || ""} ${noteItem?.createdBy?.lastName || ""}`.trim(); // Get full name + const lowerFullName = fullName.toLowerCase(); // Convert to lowercase for comparison const createdDate = new Date(noteItem?.createdAt).toLocaleDateString("en-IN").toLowerCase(); - return ( + const matchesSearch = plainNote.includes(lowerSearch) || - fullName.includes(lowerSearch) || - createdDate.includes(lowerSearch) - ); + lowerFullName.includes(lowerSearch) || + createdDate.includes(lowerSearch); + + // ✅ Filter logic for multiple selected names + const matchesNameFilter = + selectedNoteNames.length === 0 || // If no names are selected, all notes pass this filter + selectedNoteNames.includes(fullName); // Check if the note's creator is in the selected names array + + return matchesSearch && matchesNameFilter; }); setFilteredNotes(filtered); - setNotes(filtered); // for export + setNotes(filtered); setCurrentPage(1); setTotalPages(Math.ceil(filtered.length / pageSize)); - }, [searchText, allNotes]); + }, [searchText, allNotes, selectedNoteNames]); // ✅ Add selectedNoteNames to dependencies const currentItems = useMemo(() => { const startIndex = (currentPage - 1) * pageSize; @@ -70,14 +77,8 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText }) => { return (
-
+
{currentItems.map((noteItem) => ( { ); }} onNoteDelete={() => { - fetchNotes(); // refresh after delete + fetchNotes(); }} /> ))}
- {/* Pagination */} {totalPages > 1 && (
@@ -137,4 +137,4 @@ const NotesCardViewDirectory = ({ notes, setNotes, searchText }) => { ); }; -export default NotesCardViewDirectory; +export default NotesCardViewDirectory; \ No newline at end of file diff --git a/src/pages/Directory/Directory.jsx b/src/pages/Directory/Directory.jsx index e0bd2598..f9c4e8ab 100644 --- a/src/pages/Directory/Directory.jsx +++ b/src/pages/Directory/Directory.jsx @@ -39,6 +39,9 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { const [openBucketModal, setOpenBucketModal] = useState(false); const [notes, setNotes] = useState([]); + // ✅ Changed to an array for multiple selections + const [selectedNoteNames, setSelectedNoteNames] = useState([]); + const [tempSelectedBucketIds, setTempSelectedBucketIds] = useState([]); const [tempSelectedCategoryIds, setTempSelectedCategoryIds] = useState([]); const { setActions } = useFab(); @@ -73,8 +76,6 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { setIsOpenModal(false); } - // cacheData("Contacts", {data:updatedContacts,isActive:IsActive}); - // setContactList(updatedContacts); refetch(IsActive, prefernceContacts); refetchBucket(); } catch (error) { @@ -251,6 +252,7 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { return () => setActions([]); }, [IsPage, buckets]); + useEffect(() => { setPerfence(prefernceContacts); }, [prefernceContacts]); @@ -328,7 +330,7 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { )} -
+
{ setOpenBucketModal={setOpenBucketModal} contactsToExport={contacts} notesToExport={notes} + selectedNoteNames={selectedNoteNames} + setSelectedNoteNames={setSelectedNoteNames} />
-
- {/* Common empty/loading messages */} +
{(viewType === "card" || viewType === "list" || viewType === "notes") && (
- {/* {loading &&

Loading...

} */} - - {/* Notes View */} - {/* {!loading && viewType === "notes" && notes?.length > 0 && ( -

No matching note found

- )} */} - - {/* Contact (card/list) View */} {!loading && (viewType === "card" || viewType === "list") && contacts?.length === 0 && (

No contact found

)} @@ -376,7 +371,6 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
)} - {/* List View */} {viewType === "list" && (
@@ -400,9 +394,8 @@ const Directory = ({ IsPage = true, prefernceContacts }) => {
)} - {/* Card View */} {viewType === "card" && ( -
+
{!loading && currentItems.map((contact) => (
{
)} - {/* Notes View */} {viewType === "notes" && (
)} @@ -478,7 +471,6 @@ const Directory = ({ IsPage = true, prefernceContacts }) => { )}
-
); }; diff --git a/src/pages/Directory/DirectoryPageHeader.jsx b/src/pages/Directory/DirectoryPageHeader.jsx index ae577e98..7f8e4e07 100644 --- a/src/pages/Directory/DirectoryPageHeader.jsx +++ b/src/pages/Directory/DirectoryPageHeader.jsx @@ -18,10 +18,43 @@ const DirectoryPageHeader = ({ loading, IsActive, contactsToExport, - notesToExport, // ✅ Add this prop + notesToExport, + selectedNoteNames, // ✅ Changed to array + setSelectedNoteNames, // ✅ Changed to array }) => { const [filtered, setFiltered] = useState(0); + const [noteCreators, setNoteCreators] = useState([]); + + useEffect(() => { + setFiltered(tempSelectedBucketIds?.length + tempSelectedCategoryIds?.length); + }, [tempSelectedBucketIds, tempSelectedCategoryIds]); + + useEffect(() => { + if (viewType === "notes" && notesToExport && notesToExport.length > 0) { + const uniqueNames = [...new Set(notesToExport.map(note => { + const firstName = note.createdBy?.firstName || ""; + const lastName = note.createdBy?.lastName || ""; + return `${firstName} ${lastName}`.trim(); + }).filter(name => name !== ""))]; + setNoteCreators(uniqueNames.sort()); // Sort names for consistent display + } else { + setNoteCreators([]); + setSelectedNoteNames([]); // Reset to empty array for multiple selection + } + }, [notesToExport, viewType, setSelectedNoteNames]); // Add setSelectedNoteNames to dependencies + + // ✅ New handler for multiple name selections + const handleToggleNoteName = (name) => { + setSelectedNoteNames(prevSelectedNames => { + if (prevSelectedNames.includes(name)) { + return prevSelectedNames.filter(n => n !== name); + } else { + return [...prevSelectedNames, name]; + } + }); + }; + const handleExport = (type) => { let dataToExport = []; @@ -39,14 +72,14 @@ const DirectoryPageHeader = ({ const cleanNoteText = (html) => { if (!html) return ""; - const stripped = html.replace(/<[^>]+>/g, ""); // remove HTML tags + const stripped = html.replace(/<[^>]+>/g, ""); const decoded = decodeHtmlEntities(stripped); - return decoded.replace(/\u00A0/g, " ").replace(/\s+/g, " ").trim(); // fix non-breaking space + return decoded.replace(/\u00A0/g, " ").replace(/\s+/g, " ").trim(); }; const cleanName = (name) => { if (!name) return ""; - return name.replace(/\u00A0/g, " ").replace(/\s+/g, " ").trim(); // sanitize name + return name.replace(/\u00A0/g, " ").replace(/\s+/g, " ").trim(); }; dataToExport = notesToExport.map(note => ({ @@ -105,13 +138,8 @@ const DirectoryPageHeader = ({ } }; - useEffect(() => { - setFiltered(tempSelectedBucketIds?.length + tempSelectedCategoryIds?.length); - }, [tempSelectedBucketIds, tempSelectedCategoryIds]); - return ( <> - {/* Top Tabs */}
    @@ -138,21 +166,68 @@ const DirectoryPageHeader = ({

- {/* Controls: Search, Filter, View, Toggle, Export */}
-
+
- {/* Search */} setSearchText(e.target.value)} style={{ width: "200px" }} /> - {/* View Toggle Buttons - only for list/card */} + {/* Name Filter Dropdown - now with checkboxes */} + {viewType === "notes" && noteCreators.length > 0 && ( +
+ + +
+ )} + + {(viewType === "card" || viewType === "list") && (
@@ -174,75 +249,76 @@ const DirectoryPageHeader = ({
)} - {/* Filter */} -
- + {viewType !== "notes" && ( +
+ -
    -

    Filter by

    +
      +

      Filter by

      - {/* Buckets */} -
      -

      Buckets

      -
      - {filteredBuckets.map(({ id, name }) => ( -
      - handleTempBucketChange(id)} - /> - +
      +
      +

      Buckets

      +
      + {filteredBuckets.map(({ id, name }) => ( +
      + handleTempBucketChange(id)} + /> + +
      + ))}
      - ))} -
      -
      - {/* Categories */} -
      -

      Categories

      -
      - {filteredCategories.map(({ id, name }) => ( -
      - handleTempCategoryChange(id)} - /> - +
      +
      +

      Categories

      +
      + {filteredCategories.map(({ id, name }) => ( +
      + handleTempCategoryChange(id)} + /> + +
      + ))}
      - ))} +
      -
      -
      - - -
      -
    -
+
+ + +
+ +
+ )} +
-
- {/* Show Inactive Toggle - only for list/card */} +
{(viewType === "list" || viewType === "card") && (
); }; -export default DirectoryPageHeader; - - - +export default DirectoryPageHeader; \ No newline at end of file