refactored notes list

This commit is contained in:
pramod mahajan 2025-09-10 02:41:59 +05:30
parent b113675cc2
commit e454917763
10 changed files with 501 additions and 249 deletions

View File

@ -16,7 +16,7 @@ const CardViewContact = ({
IsDeleted,
restore,
}) => {
const { data , setManageContact} = useDirectoryContext();
const { data , setManageContact,setContactOpen} = useDirectoryContext();
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const {mutate:ActiveInActive,isPending} = useActiveInActiveContact()
@ -61,8 +61,7 @@ const {dirActions} = useDir()
}`}
onClick={() => {
if (IsActive) {
setIsOpenModalNote(true);
setOpen_contact(contact);
setContactOpen({contact:contact,Open:true});
}
}}
>

View File

@ -89,3 +89,13 @@ export const defaultContactFilter = {
buckets: [],
contactCategories: [],
};
export const notesFilter = z.object({
createdBy: z.array(z.string()).optional(),
organizations: z.array(z.string()).optional(),
});
export const defaultNotesFilter = {
createdBy: [],
organizations: [],
};

View File

@ -6,16 +6,20 @@ import { useActiveInActiveContact } from "../../hooks/useDirectory";
import ConfirmModal from "../common/ConfirmModal";
const ListViewContact = ({ data, Pagination }) => {
const { showActive, setManageContact } = useDirectoryContext();
const [deleteContact,setDeleteContact] = useState({contactId:null,Open:false})
const [activeContact,setActiveContact] = useState(null)
const { showActive, setManageContact, setContactOpen } =
useDirectoryContext();
const [deleteContact, setDeleteContact] = useState({
contactId: null,
Open: false,
});
const [activeContact, setActiveContact] = useState(null);
const contactList = [
{
key: "name",
label: "Name",
getValue: (e) => (
<div className="d-flex align-items-center">
<div className="d-flex align-items-center ps-1">
<Avatar
size="xs"
classAvatar="m-0"
@ -30,7 +34,7 @@ const ListViewContact = ({ data, Pagination }) => {
</span>
</div>
),
align: "text-start",
align: "text-center",
},
{
key: "email",
@ -73,7 +77,9 @@ const ListViewContact = ({ data, Pagination }) => {
},
];
const { mutate: ActiveInActive, isPending } = useActiveInActiveContact(()=>setDeleteContact({contactId:null,Open:false}));
const { mutate: ActiveInActive, isPending } = useActiveInActiveContact(() =>
setDeleteContact({ contactId: null, Open: false })
);
const handleActiveInactive = (contactId) => {
ActiveInActive({ contactId: contactId, contactStatus: !showActive });
@ -81,103 +87,126 @@ const ListViewContact = ({ data, Pagination }) => {
return (
<>
{deleteContact.Open && (
<div
className={`modal fade show`}
tabIndex="-1"
role="dialog"
style={{
display: "block",
backgroundColor: "rgba(0,0,0,0.5)",
}}
aria-hidden="false"
>
<ConfirmModal
type="delete"
header="Delete Contact"
message="Are you sure you want delete?"
onSubmit={handleActiveInactive}
onClose={() => setDeleteContact({contactId:null,Open:false})}
loading={isPending}
paramData={deleteContact.contactId}
/>
</div>
)}
<div className="card ">
<div className="card-datatable table-responsive" id="horizontal-example">
<div className="dataTables_wrapper no-footer ">
<table className="table border-top dataTable text-nowrap">
<thead>
<tr className="shadow-sm">
{contactList?.map((col) => (
<th key={col.key} className={col.align}>
{col.label}
{deleteContact.Open && (
<div
className={`modal fade show`}
tabIndex="-1"
role="dialog"
style={{
display: "block",
backgroundColor: "rgba(0,0,0,0.5)",
}}
aria-hidden="false"
>
<ConfirmModal
type="delete"
header="Delete Contact"
message="Are you sure you want delete?"
onSubmit={handleActiveInactive}
onClose={() => setDeleteContact({ contactId: null, Open: false })}
loading={isPending}
paramData={deleteContact.contactId}
/>
</div>
)}
<div className="card ">
<div
className="card-datatable table-responsive"
id="horizontal-example"
>
<div className="dataTables_wrapper no-footer ">
<table className="table border-top dataTable text-nowrap">
<thead>
<tr className="shadow-sm ">
{contactList?.map((col) => (
<th key={col.key} className={col.align}>
{col.label}
</th>
))}
<th className="sticky-action-column bg-white text-center">
Action
</th>
))}
<th className="sticky-action-column bg-white text-center">
Action
</th>
</tr>
</thead>
<tbody>
{Array.isArray(data) && data.length > 0 ? (
data.map((row, i) => (
<tr
key={i}
style={{ background: `${!showActive ? "#f8f6f6" : ""}` }}
>
{contactList.map((col) => (
<td key={col.key} className={col.align}>
{col.getValue(row)}
</td>
))}
<td className="text-center">
{showActive ? (
<div className="d-flex justify-content-center gap-2">
<i className="bx bx-show text-primary cursor-pointer"></i>
</tr>
</thead>
<tbody>
{Array.isArray(data) && data.length > 0 ? (
data.map((row, i) => (
<tr
key={i}
style={{ background: `${!showActive ? "#f8f6f6" : ""}` }}
>
{contactList.map((col) => (
<td key={col.key} className={col.align}>
{col.getValue(row)}
</td>
))}
<td className="text-center">
{showActive ? (
<div className="d-flex justify-content-center gap-2">
<i
className="bx bx-show text-primary cursor-pointer"
onClick={() =>
setContactOpen({ contact: row, Open: true })
}
></i>
<i
className="bx bx-edit text-secondary cursor-pointer"
onClick={() =>
setManageContact({
isOpen: true,
contactId: row.id,
})
}
></i>
<i
className="bx bx-trash text-danger cursor-pointer"
onClick={() =>
setDeleteContact({
contactId: row.id,
Open: true,
})
}
></i>
</div>
) : (
<i
className="bx bx-edit text-secondary cursor-pointer"
onClick={() =>
setManageContact({
isOpen: true,
contactId: row.id,
})
}
className={`bx ${
isPending && activeContact === row.id
? "bx-loader-alt bx-spin"
: "bx-recycle"
} me-1 text-primary cursor-pointer`}
title="Restore"
onClick={() => {
setActiveContact(row.id);
handleActiveInactive(row.id);
}}
></i>
<i className="bx bx-trash text-danger cursor-pointer" onClick={()=>setDeleteContact({contactId:row.id,Open:true})}></i>
</div>
) : (
<i
className={`bx ${
isPending && activeContact === row.id ? "bx-loader-alt bx-spin" : "bx-recycle"
} me-1 text-primary cursor-pointer`}
title="Restore"
onClick={() => {
setActiveContact(row.id)
handleActiveInactive(row.id)
}}
></i>
)}
)}
</td>
</tr>
))
) : (
<tr>
<td
colSpan={contactList.length + 1}
className="text-center"
>
No contacts found
</td>
</tr>
))
) : (
<tr>
<td colSpan={contactList.length + 1} className="text-center">
No contacts found
</td>
</tr>
)}
</tbody>
</table>
{Pagination && (
<div className="d-flex justify-content-start p-3">{Pagination}</div>
)}
)}
</tbody>
</table>
{Pagination && (
<div className="d-flex justify-content-start p-3">
{Pagination}
</div>
)}
</div>
</div>
</div>
</div>
</>
);
};

View File

@ -9,6 +9,7 @@ import ConfirmModal from "../common/ConfirmModal"; // Make sure path is correct
import "../common/TextEditor/Editor.css";
import ProfileContactDirectory from "./ProfileContactDirectory";
import GlobalModel from "../common/GlobalModel";
import { useActiveInActiveNote, useUpdateNote } from "../../hooks/useDirectory";
const NoteCardDirectoryEditable = ({
noteItem,
@ -25,59 +26,31 @@ const NoteCardDirectoryEditable = ({
const [open_contact, setOpen_contact] = useState(null);
const [isOpenModalNote, setIsOpenModalNote] = useState(false);
const { mutate: UpdateNote, isPending: isUpatingNote } = useUpdateNote(() =>
setEditing(false)
);
const { mutate: ActiveInactive, isPending: isUpdatingStatus } =
useActiveInActiveNote(() => setIsDeleteModalOpen(false));
const handleUpdateNote = async () => {
try {
setIsLoading(true);
const payload = {
id: noteItem.id,
note: editorValue,
contactId,
};
const response = await DirectoryRepository.UpdateNote(noteItem.id, payload);
const payload = {
id: noteItem.id,
note: editorValue,
contactId,
};
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);
}
UpdateNote({ noteId: noteItem.id, notePayload: payload });
};
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 ActiveInActive = (noteItem) => {
ActiveInactive({ noteId: noteItem.id, noteStatus: !noteItem.isActive });
};
const contactProfile = (contactId) => {
DirectoryRepository.GetContactProfile(contactId).then((res) => {
setOpen_contact(res?.data);
setIsOpenModalNote(true);
});
setOpen_contact(res?.data);
setIsOpenModalNote(true);
});
};
const handleRestore = async () => {
@ -95,7 +68,6 @@ const NoteCardDirectoryEditable = ({
return (
<>
{isOpenModalNote && (
<GlobalModel
isOpen={isOpenModalNote}
@ -125,7 +97,6 @@ const NoteCardDirectoryEditable = ({
{/* Header */}
<div className="d-flex justify-content-between align-items-center mb-1">
<div className="d-flex align-items-center">
<Avatar
size="xxs"
firstName={noteItem?.createdBy?.firstName}
@ -133,30 +104,36 @@ const NoteCardDirectoryEditable = ({
className="m-0"
/>
<div>
<div className="d-flex ms-0 align-middle cursor-pointer" onClick={() =>contactProfile(noteItem.contactId)}>
<div
className="d-flex ms-0 align-middle cursor-pointer"
onClick={() => contactProfile(noteItem.contactId)}
>
<span>
<span className="fw-bold "> {noteItem?.contactName} </span> <span className="text-muted font-weight-normal">
<span className="fw-bold "> {noteItem?.contactName} </span>{" "}
<span className="text-muted font-weight-normal">
({noteItem?.organizationName})
</span>
</span>
</div>
<div className="d-flex ms-0 align-middle">
</div>
<div className="d-flex ms-0 align-middle"></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>
&nbsp; <span className="text-muted">
on {moment
by{" "}
<span className="fw-bold ">
{" "}
{noteItem?.createdBy?.firstName}{" "}
{noteItem?.createdBy?.lastName}{" "}
</span>
&nbsp;{" "}
<span className="text-muted">
on{" "}
{moment
.utc(noteItem?.createdAt)
.add(5, "hours")
.add(30, "minutes")
.format("DD MMMM, YYYY [at] hh:mm A")}
</span>
</span>
</div>
</div>
</div>
@ -180,12 +157,12 @@ const NoteCardDirectoryEditable = ({
<div className="spinner-border spinner-border-sm text-danger" />
)}
</>
) : isRestoring ? (
) : isUpdatingStatus ? (
<i className="bx bx-loader-alt bx-spin text-primary"></i>
) : (
<i
className="bx bx-recycle me-2 text-primary cursor-pointer"
onClick={handleRestore}
onClick={() => ActiveInActive(noteItem)}
title="Restore"
></i>
)}
@ -214,7 +191,7 @@ const NoteCardDirectoryEditable = ({
className="text-primary cursor-pointer"
onClick={handleUpdateNote}
>
{isLoading ? "Saving..." : "Submit"}
{isUpatingNote ? "Saving..." : "Submit"}
</span>
</div>
</>
@ -242,9 +219,9 @@ const NoteCardDirectoryEditable = ({
type={"delete"}
header={"Delete Note"}
message={"Are you sure you want to delete this note?"}
onSubmit={suspendEmployee}
onSubmit={ActiveInActive}
onClose={() => setIsDeleteModalOpen(false)}
loading={isDeleting}
loading={isUpdatingStatus}
paramData={noteItem}
/>
</div>

View File

@ -116,11 +116,7 @@ const NotesCardViewDirectory = ({
return (
<div className="w-100 h-100 ">
{/* Filter Dropdown */}
<div className="dropdown mb-3 ms-2">
</div>
{/* Notes List */}
<div className="d-flex flex-column text-start" style={{ gap: "0rem", minHeight: "100%" }}>
{currentItems.map((noteItem) => (
<NoteCardDirectoryEditable
@ -132,53 +128,12 @@ const NotesCardViewDirectory = ({
prevNotes.map((n) => (n.id === updatedNote.id ? updatedNote : n))
);
}}
onNoteDelete={() => fetchNotes(projectId)} // reload with projectId
onNoteDelete={() => fetchNotes(projectId)}
/>
))}
</div>
{/* Pagination */}
{totalPages > 1 && (
<div className="d-flex justify-content-end mt-2 align-items-center gap-2"
style={{ marginBottom: '70px' }}>
{/* Previous Button */}
<button
className="btn btn-sm rounded-circle border text-secondary"
onClick={() => handlePageClick(Math.max(1, currentPage - 1))}
disabled={currentPage === 1}
title="Previous"
style={{ width: "30px", height: "30px", padding: 0, fontSize: "0.75rem" }} // Adjusted width, height, and font size
>
«
</button>
{/* Page Number Buttons */}
{[...Array(totalPages)].map((_, i) => {
const page = i + 1;
return (
<button
key={page}
className={`btn rounded-circle border ${page === currentPage ? "btn-primary text-white" : "btn-light text-secondary"}`}
style={{ width: "30px", height: "30px", padding: 0, fontSize: "0.75rem", lineHeight: "1" }} // Adjusted width, height, and font size
onClick={() => handlePageClick(page)}
>
{page}
</button>
);
})}
{/* Next Button */}
<button
className="btn btn-sm rounded-circle border text-secondary"
onClick={() => handlePageClick(Math.min(totalPages, currentPage + 1))}
disabled={currentPage === totalPages}
title="Next"
style={{ width: "30px", height: "30px", padding: 0, fontSize: "0.75rem" }} // Adjusted width, height, and font size
>
»
</button>
</div>
)}
</div>
);
};

View File

@ -307,6 +307,56 @@ export const useContactDetails = (contactId)=>{
}
const cleanNoteFilter = (filter) => {
const cleaned = { ...filter };
["bucketIds", "contactCategories"].forEach((key) => {
if (Array.isArray(cleaned[key]) && cleaned[key].length === 0) {
delete cleaned[key];
}
});
return cleaned;
};
export const useNotes = (
projectId,
pageSize,
pageNumber,
filter,
searchString=""
) => {
return useQuery({
queryKey: [
"Notes",
projectId,
pageSize,
pageNumber,
filter,
searchString,
],
queryFn: async () => {
const cleanedFilter = cleanNoteFilter(filter);
const resp = await DirectoryRepository.GetNotes(
projectId,
pageSize,
pageNumber,
cleanedFilter,
searchString
);
return resp.data;
},
});
};
export const useNoteFilter = ()=>{
return useQuery({
queryKey:["NoteFilter"],
queryFn:async()=> {
const resp = await DirectoryRepository.GetNoteFilter();
return resp.data;
}
})
}
// ---------------------------Mutation------------------------------------------------------------------
@ -438,7 +488,65 @@ export const useActiveInActiveContact =(onSuccessCallBack)=>{
onSuccess: (_, variables) => {
const {contactStatus} = variables;
queryClient.invalidateQueries({queryKey: ["contacts"]})
showToast(`Contact ${contactStatus ? "Active":"In-Active"} Successfully`, "success");
showToast(`Contact ${contactStatus ? "Restored":"Deleted"} Successfully`, "success");
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(
error.response.data.message ||
"Something went wrong.Please try again later.",
"error"
);
}
})
}
export const useCreateNote =()=>{
const queryClient = useQueryClient();
return useMutation({
mutationFn:async(notPayload)=> await DirectoryRepository.CreateNote(notPayload),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({queryKey: ["Notes"]})
showToast(`Note Created Successfully`, "success");
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(
error.response.data.message ||
"Something went wrong.Please try again later.",
"error"
);
}
})
}
export const useUpdateNote =(onSuccessCallBack)=>{
const queryClient = useQueryClient();
return useMutation({
mutationFn:async({noteId,notePayload})=> await DirectoryRepository.UpdateNote(noteId,notePayload),
onSuccess: (_, variables) => {
queryClient.invalidateQueries({queryKey: ["Notes"]})
showToast("Note updated Successfully", "success");
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {
showToast(
error.response.data.message ||
"Something went wrong.Please try again later.",
"error"
);
}
})
}
export const useActiveInActiveNote =(onSuccessCallBack)=>{
const queryClient = useQueryClient();
return useMutation({
mutationFn:async({noteId,noteStatus})=> await DirectoryRepository.DeleteNote(noteId,noteStatus),
onSuccess: (_, variables) => {
const {noteStatus} = variables;
queryClient.invalidateQueries({queryKey: ["Notes"]})
showToast(`Note ${noteStatus ? "Restored":"Deleted"} Successfully`, "success");
if (onSuccessCallBack) onSuccessCallBack();
},
onError: (error) => {

View File

@ -14,6 +14,7 @@ import ManageBucket from "../../components/Directory/ManageBucket";
import ManageBucket1 from "../../components/Directory/ManageBucket1";
import ManageContact from "../../components/Directory/ManageContact";
import BucketList from "../../components/Directory/BucketList";
import ProfileContactDirectory from "../../components/Directory/ProfileContactDirectory";
const NotesPage = lazy(() => import("./NotesPage"));
const ContactsPage = lazy(() => import("./ContactsPage"));
@ -39,9 +40,14 @@ export default function DirectoryPage({ IsPage = true }) {
const [gridView, setGridView] = useState(false);
const [isOpenBucket, setOpenBucket] = useState({});
const [isManageContact, setManageContact] = useState({
isOpen:false,contactId:null
isOpen: false,
contactId: null,
});
const [showActive, setShowActive] = useState(true);
const [contactOpen, setContactOpen] = useState({
contact: null,
Open: false,
});
const { data, isLoading, isError, error } = useBucketList();
@ -66,7 +72,7 @@ export default function DirectoryPage({ IsPage = true }) {
label: "New Contact",
icon: "bx bx-plus-circle",
color: "warning",
onClick: () => setManageContact({isOpen:true,contactId:null}),
onClick: () => setManageContact({ isOpen: true, contactId: null }),
});
}
@ -79,7 +85,8 @@ export default function DirectoryPage({ IsPage = true }) {
showActive,
gridView,
data,
setManageContact
setManageContact,
setContactOpen,
};
if (isLoading) return <div>Loading...</div>;
@ -123,20 +130,18 @@ export default function DirectoryPage({ IsPage = true }) {
<div className="btn-group">
<button
className="btn btn-sm btn-secondary dropdown-toggle"
className="btn btn-sm btn-label-secondary dropdown-toggle"
type="button"
data-bs-toggle="dropdown"
aria-haspopup="true"
aria-expanded="false"
>
<i className="bx bx-download"></i> Export
<i className="bx bx-export me-2 bx-sm"></i>Export
</button>
<ul className="dropdown-menu dropdown-menu-end">
<ul className="dropdown-menu">
<li>
<a className="dropdown-item">Action</a>
</li>
<li>
<a className="dropdown-item">Another action</a>
<a className="dropdown-item" href="#">
<i className="bx bx-file me-1"></i> CSV
</a>
</li>
</ul>
</div>
@ -213,7 +218,9 @@ export default function DirectoryPage({ IsPage = true }) {
</div>
}
>
{activeTab === "notes" && <NotesPage />}
{activeTab === "notes" && (
<NotesPage projectId={null} searchText={searchNote} />
)}
{activeTab === "contacts" && (
<ContactsPage searchText={searchContact} />
)}
@ -230,13 +237,29 @@ export default function DirectoryPage({ IsPage = true }) {
</GlobalModel>
)}
{contactOpen.Open && (
<GlobalModel
size="xl"
isOpen={contactOpen.Open}
closeModal={() => setContactOpen({ contact: null, Open: false })}
>
<ProfileContactDirectory contact={contactOpen.contact} />
</GlobalModel>
)}
{isManageContact.isOpen && (
<GlobalModel
size="lg"
isOpen={isManageContact}
closeModal={() => setManageContact({isOpen:false,contactId:null})}
closeModal={() =>
setManageContact({ isOpen: false, contactId: null })
}
>
<ManageContact contactId={isManageContact.contactId} closeModal={() => setManageContact({isOpen:false,contactId:null})} />
<ManageContact
contactId={isManageContact.contactId}
closeModal={() =>
setManageContact({ isOpen: false, contactId: null })
}
/>
</GlobalModel>
)}
</div>

View File

@ -0,0 +1,76 @@
import { zodResolver } from "@hookform/resolvers/zod";
import React from "react";
import { FormProvider, useForm } from "react-hook-form";
import {
defaultNotesFilter,
notesFilter,
} from "../../components/Directory/DirectorySchema";
import { useContactFilter, useNoteFilter } from "../../hooks/useDirectory";
import { ExpenseFilterSkeleton } from "../../components/Expenses/ExpenseSkeleton";
import SelectMultiple from "../../components/common/SelectMultiple";
const NoteFilterPanel = ({ onApply, clearFilter }) => {
const { data, isError, isLoading, error, isFetched, isFetching } =
useNoteFilter();
const methods = useForm({
resolver: zodResolver(notesFilter),
defaultValues: defaultNotesFilter,
});
const closePanel = () => {
document.querySelector(".offcanvas.show .btn-close")?.click();
};
const { register, handleSubmit, reset, watch } = methods;
const onSubmit = (formData) => {
onApply(formData);
closePanel();
};
const handleClose = () => {
reset(defaultNotesFilter);
closePanel();
};
if (isLoading || isFetching) return <ExpenseFilterSkeleton />;
if (isError && isFetched)
return <div>Something went wrong Here- {error.message} </div>;
return (
<FormProvider {...methods}>
<form onSubmit={handleSubmit(onSubmit)} className="p-2 text-start">
<div className="row g-2">
<SelectMultiple
name="createdBy"
label="Created By :"
options={data.createdBy}
labelKey="name"
valueKey="id"
/>
<SelectMultiple
name="organizations"
label="Organization:"
options={data.organizations}
labelKey={(item) => item.name}
valueKey="id"
/>
</div>
<div className="d-flex justify-content-end py-3 gap-2">
<button
type="button"
className="btn btn-secondary btn-xs"
onClick={handleClose}
>
Clear
</button>
<button type="submit" className="btn btn-primary btn-xs">
Apply
</button>
</div>
</form>
</FormProvider>
);
};
export default NoteFilterPanel;

View File

@ -1,26 +1,81 @@
import React, { useEffect } from 'react'
import { useFab } from '../../Context/FabContext';
import React, { useEffect, useState } from "react";
import { useFab } from "../../Context/FabContext";
import { useNoteFilter, useNotes } from "../../hooks/useDirectory";
import NoteFilterPanel from "./NoteFilterPanel";
import { defaultNotesFilter } from "../../components/Directory/DirectorySchema";
import { ITEMS_PER_PAGE } from "../../utils/constants";
import { useDebounce } from "../../utils/appUtils";
import NoteCardDirectoryEditable from "../../components/Directory/NoteCardDirectoryEditable";
import Pagination from "../../components/common/Pagination";
const NotesPage = () => {
const NotesPage = ({ projectId, searchText }) => {
const [filters, setFilter] = useState(defaultNotesFilter);
const [currentPage, setCurrentPage] = useState(1);
const debouncedSearch = useDebounce(searchText, 500);
const { data, isLoading, isError, error } = useNotes(
projectId,
ITEMS_PER_PAGE,
currentPage,
filters,
debouncedSearch
);
const { setOffcanvasContent, setShowTrigger } = useFab();
const {setOffcanvasContent,setShowTrigger} = useFab()
useEffect(() => {
setShowTrigger(true);
setOffcanvasContent(
"Notes Filters",
<div>hlleo</div>
);
const clearFilter = () => {
setFilter(defaultContactFilter);
};
useEffect(() => {
setShowTrigger(true);
setOffcanvasContent(
"Notes Filters",
<NoteFilterPanel onApply={setFilter} clearFilter={clearFilter} />
);
return () => {
setShowTrigger(false);
setOffcanvasContent("", null);
};
}, []);
const paginate = (page) => {
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
setCurrentPage(page);
}
};
if (isError) return <div>{error.message}</div>;
if (isLoading) return <div>Loading...</div>;
return (
<div className='container'>
<div className="d-flex flex-column text-start mt-5">
{data?.data?.length > 0 ? (
<>
{data.data.map((noteItem) => (
<NoteCardDirectoryEditable
key={noteItem.id}
noteItem={noteItem}
contactId={noteItem.contactId}
/>
))}
<div className="col-12 d-flex justify-content-start mt-3">
<Pagination
currentPage={currentPage}
totalPages={data.totalPages}
onPageChange={paginate}
/>
</div>
</>
) : (
<div className="text-muted text-center mt-4">
{debouncedSearch
? `No notes found matching "${searchText}"`
: Object.keys(filters).some((k) => filters[k] && filters[k].length)
? "No notes found for the applied filters."
: "No notes available."}
</div>
)}
</div>
)
}
);
};
export default NotesPage
export default NotesPage;

View File

@ -3,18 +3,27 @@ import { api } from "../utils/axiosClient";
export const DirectoryRepository = {
GetOrganizations: () => api.get("/api/directory/organization"),
GetDesignations: () => api.get("/api/directory/designations"),
GetContact:(id)=>api.get(`/api/Directory/profile/${id}`),
GetContact: (id) => api.get(`/api/Directory/profile/${id}`),
GetContacts: (isActive, projectId, pageSize, pageNumber, filter, searchString) => {
const payloadJsonString = JSON.stringify(filter);
return api.get(
`/api/Directory/list?active=${isActive}` +
(projectId ? `&projectId=${projectId}` : "") +
`&pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${encodeURIComponent(payloadJsonString)}&searchString=${encodeURIComponent(searchString)}`
);
},
GetContacts: (
isActive,
projectId,
pageSize,
pageNumber,
filter,
searchString
) => {
const payloadJsonString = JSON.stringify(filter);
return api.get(
`/api/Directory/list?active=${isActive}` +
(projectId ? `&projectId=${projectId}` : "") +
`&pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${encodeURIComponent(
payloadJsonString
)}&searchString=${encodeURIComponent(searchString)}`
);
},
GetContactFilter:()=>api.get("/api/directory/contact/filter"),
GetContactFilter: () => api.get("/api/directory/contact/filter"),
CreateContact: (data) => api.post("/api/directory", data),
UpdateContact: (id, data) => api.put(`/api/directory/${id}`, data),
DeleteContact: (id, isActive) =>
@ -36,10 +45,21 @@ export const DirectoryRepository = {
DeleteNote: (id, isActive) =>
api.delete(`/api/directory/note/${id}?active=${isActive}`),
GetNotes: (pageSize, pageNumber, filter, searchString) => {
GetNotes: (
projectId,
pageSize,
pageNumber,
filter,
searchString
) => {
const payloadJsonString = JSON.stringify(filter);
return api.get(
`/api/directory/notes?pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${payloadJsonString}&searchString=${searchString}`
`/api/directory/notes?` +
(projectId ? `projectId=${projectId}` : "&") +
`pageSize=${pageSize}&pageNumber=${pageNumber}&filter=${encodeURIComponent(
payloadJsonString
)}&searchString=${encodeURIComponent(searchString)}`
);
},
GetNoteFilter:()=>api.get("/api/Directory/notes/filter")
};