refactored notes list
This commit is contained in:
parent
b113675cc2
commit
e454917763
@ -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});
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -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: [],
|
||||
};
|
||||
|
@ -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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
<span className="text-muted">
|
||||
on {moment
|
||||
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("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>
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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) => {
|
||||
|
@ -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>
|
||||
|
76
src/pages/Directory/NoteFilterPanel.jsx
Normal file
76
src/pages/Directory/NoteFilterPanel.jsx
Normal 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;
|
@ -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;
|
||||
|
@ -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")
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user