added restore feature for contacts
This commit is contained in:
parent
0082a60e02
commit
755ea1b0ee
21
src/Context/DireContext.jsx
Normal file
21
src/Context/DireContext.jsx
Normal file
@ -0,0 +1,21 @@
|
||||
import React, { createContext, useContext, useState } from "react";
|
||||
|
||||
const DireContext = createContext(undefined);
|
||||
|
||||
export const DireProvider = ({ children }) => {
|
||||
const [dirActions, setDirActions] = useState([]);
|
||||
|
||||
return (
|
||||
<DireContext.Provider value={{ dirActions, setDirActions }}>
|
||||
{children}
|
||||
</DireContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useDir = () => {
|
||||
const context = useContext(DireContext);
|
||||
if (!context) {
|
||||
throw new Error("useDir must be used within a <DireProvider>");
|
||||
}
|
||||
return context;
|
||||
};
|
@ -3,6 +3,7 @@ import Avatar from "../common/Avatar";
|
||||
import { getBucketNameById } from "./DirectoryUtils";
|
||||
import { useBuckets } from "../../hooks/useDirectory";
|
||||
import { getPhoneIcon } from "./DirectoryUtils";
|
||||
import { useDir } from "../../Context/DireContext";
|
||||
const CardViewDirectory = ({
|
||||
IsActive,
|
||||
contact,
|
||||
@ -11,8 +12,10 @@ const CardViewDirectory = ({
|
||||
setOpen_contact,
|
||||
setIsOpenModalNote,
|
||||
IsDeleted,
|
||||
restore,
|
||||
}) => {
|
||||
const { buckets } = useBuckets();
|
||||
const { dirActions, setDirActions } = useDir();
|
||||
return (
|
||||
<div
|
||||
className="card text-start border-1"
|
||||
@ -30,54 +33,70 @@ const CardViewDirectory = ({
|
||||
(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""
|
||||
}
|
||||
/>{" "}
|
||||
<span className="text-heading fs-6"> {contact.name}</span>
|
||||
<span className="text-heading fs-6"> {contact.name}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className={`dropdown z-2 ${!IsActive && "d-none"}`}>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted p-0"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end w-auto">
|
||||
<li
|
||||
onClick={() => {
|
||||
setSelectedContact(contact);
|
||||
setIsOpenModal(true);
|
||||
}}
|
||||
{IsActive && (
|
||||
<div className="dropdown z-2">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
|
||||
data-bs-toggle="dropdown"
|
||||
aria-expanded="false"
|
||||
>
|
||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||
<i className="bx bx-edit bx-xs text-primary me-2"></i>
|
||||
<span className="align-left ">Modify</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="dropdown-item px-2 cursor-pointer py-1"
|
||||
onClick={() => IsDeleted(contact.id)}
|
||||
<i
|
||||
className="bx bx-dots-vertical-rounded bx-sm text-muted p-0"
|
||||
data-bs-toggle="tooltip"
|
||||
data-bs-offset="0,8"
|
||||
data-bs-placement="top"
|
||||
data-bs-custom-class="tooltip-dark"
|
||||
title="More Action"
|
||||
></i>
|
||||
</button>
|
||||
<ul className="dropdown-menu dropdown-menu-end w-auto">
|
||||
<li
|
||||
onClick={() => {
|
||||
setSelectedContact(contact);
|
||||
setIsOpenModal(true);
|
||||
}}
|
||||
>
|
||||
<i className="bx bx-trash text-danger bx-xs me-2"></i>
|
||||
<span className="align-left">Delete</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||
<i className="bx bx-edit bx-xs text-primary me-2"></i>
|
||||
<span className="align-left ">Modify</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
className="dropdown-item px-2 cursor-pointer py-1"
|
||||
onClick={() => IsDeleted(contact.id)}
|
||||
>
|
||||
<i className="bx bx-trash text-danger bx-xs me-2"></i>
|
||||
<span className="align-left">Delete</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
)}
|
||||
{!IsActive && (
|
||||
<i
|
||||
className={`bx bx-history ${
|
||||
dirActions.action && dirActions.id === contact.id
|
||||
? "bx-spin"
|
||||
: ""
|
||||
} me-1 text-primary cursor-pointer`}
|
||||
title="Restore"
|
||||
onClick={() => {
|
||||
setDirActions({ action: false, id: contact.id });
|
||||
restore(contact.id);
|
||||
}}
|
||||
></i>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="list-inline m-0 ps-4">
|
||||
<li className="list-inline-item me-1" style={{ fontSize: "10px" }}>
|
||||
<i className="bx bx-building bx-xs"></i>
|
||||
<i className="fa-solid fa-briefcase me-2"></i>
|
||||
</li>
|
||||
<li className="list-inline-item text-small">
|
||||
{contact.organization}
|
||||
@ -97,7 +116,7 @@ const CardViewDirectory = ({
|
||||
{contact.contactEmails[0] && (
|
||||
<ul className="list-inline my-1 ">
|
||||
<li className="list-inline-item me-2">
|
||||
<i className="bx bx-envelope bx-xs"></i>
|
||||
<i className="bx bx-envelope bx-xs"></i>
|
||||
</li>
|
||||
<li className="list-inline-item text-small">
|
||||
{contact.contactEmails[0]?.emailAddress}
|
||||
@ -122,27 +141,28 @@ const CardViewDirectory = ({
|
||||
|
||||
<ul className="list-inline m-0">
|
||||
<li className="list-inline-item me-2 my-1">
|
||||
<i className="fa-solid fa-tag fs-6"></i>
|
||||
|
||||
<i className="fa-solid fa-tag fs-6"></i>
|
||||
</li>
|
||||
<li className="list-inline-item text-small active">
|
||||
{contact.contactCategory.name}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul className="list-inline m-0">
|
||||
{contact.bucketIds?.map((bucketId) => (
|
||||
<li key={bucketId} className="list-inline-item me-1">
|
||||
<span className="badge bg-label-primary rounded-pill d-flex align-items-center gap-1" style={{padding:'0.1rem 0.3rem'}}>
|
||||
<i className="bx bx-pin bx-xs"></i>
|
||||
<span className="small-text">
|
||||
{getBucketNameById(buckets, bucketId)}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
<ul className="list-inline m-0">
|
||||
{contact.bucketIds?.map((bucketId) => (
|
||||
<li key={bucketId} className="list-inline-item me-1">
|
||||
<span
|
||||
className="badge bg-label-primary rounded-pill d-flex align-items-center gap-1"
|
||||
style={{ padding: "0.1rem 0.3rem" }}
|
||||
>
|
||||
<i className="bx bx-pin bx-xs"></i>
|
||||
<span className="small-text">
|
||||
{getBucketNameById(buckets, bucketId)}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,74 +1,128 @@
|
||||
import React from 'react'
|
||||
import Avatar from '../common/Avatar';
|
||||
import { getEmailIcon,getPhoneIcon } from './DirectoryUtils';
|
||||
import React, { useEffect } from "react";
|
||||
import Avatar from "../common/Avatar";
|
||||
import { getEmailIcon, getPhoneIcon } from "./DirectoryUtils";
|
||||
import { useDir } from "../../Context/DireContext";
|
||||
|
||||
const ListViewDirectory = ({
|
||||
IsActive,
|
||||
contact,
|
||||
setSelectedContact,
|
||||
setIsOpenModal,
|
||||
setOpen_contact,
|
||||
setIsOpenModalNote,
|
||||
IsDeleted,
|
||||
restore,
|
||||
}) => {
|
||||
const { dirActions, setDirActions } = useDir();
|
||||
|
||||
const ListViewDirectory = ({IsActive, contact,setSelectedContact,setIsOpenModal,setOpen_contact,setIsOpenModalNote,IsDeleted}) => {
|
||||
return (
|
||||
<tr className={!IsActive ? "bg-light" : ""}>
|
||||
<td className="text-start cursor-pointer" style={{ width: "18%" }} colSpan={2} onClick={() => {
|
||||
if (IsActive) {
|
||||
setIsOpenModalNote(true);
|
||||
setOpen_contact(contact);
|
||||
}
|
||||
}}>
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
classAvatar="m-0"
|
||||
firstName={(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""}
|
||||
lastName={(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""}
|
||||
/>
|
||||
<span className="text-truncate mx-0" style={{ maxWidth: "150px" }}>{contact?.name || ""}</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-2" style={{ width: "20%" }}>
|
||||
<div className="d-flex flex-column align-items-start text-truncate">
|
||||
{contact.contactEmails?.map((email, index) => (
|
||||
<span key={email.id} className="text-truncate">
|
||||
<i className={getEmailIcon(email.label)} style={{ fontSize: "12px" }}></i>
|
||||
<a href={`mailto:${email.emailAddress}`} className="text-decoration-none ms-1">{email.emailAddress}</a>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-2" style={{ width: "20%" }}>
|
||||
<div className="d-flex flex-column align-items-start text-truncate">
|
||||
{contact.contactPhones?.map((phone, index) => (
|
||||
<span key={phone.id}>
|
||||
<i className={getPhoneIcon(phone.label)} style={{ fontSize: "12px" }}></i>
|
||||
<span className="ms-1">{phone.phoneNumber}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td colSpan={2} className="text-start text-truncate px-2" style={{ width: "20%", maxWidth: "200px" }}>
|
||||
{contact.organization}
|
||||
</td>
|
||||
|
||||
<td className="px-2" style={{ width: "10%" }}>
|
||||
<span className="badge badge-outline-secondary">
|
||||
{contact?.contactCategory?.name}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
{IsActive && (
|
||||
<td className="align-middle text-center" style={{ width: "12%" }}>
|
||||
<i className="bx bx-edit bx-sm text-primary cursor-pointer me-2"
|
||||
<tr className={!IsActive ? "bg-light" : ""}>
|
||||
<td
|
||||
className="text-start cursor-pointer"
|
||||
style={{ width: "18%" }}
|
||||
colSpan={2}
|
||||
onClick={() => {
|
||||
setSelectedContact(contact);
|
||||
setIsOpenModal(true);
|
||||
}}></i>
|
||||
<i className="bx bx-trash bx-sm text-danger cursor-pointer"
|
||||
onClick={() => IsDeleted(contact.id)}></i>
|
||||
</td>
|
||||
)}
|
||||
</tr>
|
||||
if (IsActive) {
|
||||
setIsOpenModalNote(true);
|
||||
setOpen_contact(contact);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
classAvatar="m-0"
|
||||
firstName={
|
||||
(contact?.name || "").trim().split(" ")[0]?.charAt(0) || ""
|
||||
}
|
||||
lastName={
|
||||
(contact?.name || "").trim().split(" ")[1]?.charAt(0) || ""
|
||||
}
|
||||
/>
|
||||
<span className="text-truncate mx-0" style={{ maxWidth: "150px" }}>
|
||||
{contact?.name || ""}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-2" style={{ width: "20%" }}>
|
||||
<div className="d-flex flex-column align-items-start text-truncate">
|
||||
{contact.contactEmails?.map((email, index) => (
|
||||
<span key={email.id} className="text-truncate">
|
||||
<i
|
||||
className={getEmailIcon(email.label)}
|
||||
style={{ fontSize: "12px" }}
|
||||
></i>
|
||||
<a
|
||||
href={`mailto:${email.emailAddress}`}
|
||||
className="text-decoration-none ms-1"
|
||||
>
|
||||
{email.emailAddress}
|
||||
</a>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="px-2" style={{ width: "20%" }}>
|
||||
<div className="d-flex flex-column align-items-start text-truncate">
|
||||
{contact.contactPhones?.map((phone, index) => (
|
||||
<span key={phone.id}>
|
||||
<i
|
||||
className={getPhoneIcon(phone.label)}
|
||||
style={{ fontSize: "12px" }}
|
||||
></i>
|
||||
<span className="ms-1">{phone.phoneNumber}</span>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td
|
||||
colSpan={2}
|
||||
className="text-start text-truncate px-2"
|
||||
style={{ width: "20%", maxWidth: "200px" }}
|
||||
>
|
||||
{contact.organization}
|
||||
</td>
|
||||
|
||||
<td className="px-2" style={{ width: "10%" }}>
|
||||
<span className="badge badge-outline-secondary">
|
||||
{contact?.contactCategory?.name}
|
||||
</span>
|
||||
</td>
|
||||
|
||||
<td className="align-middle text-center" style={{ width: "12%" }}>
|
||||
{IsActive && (
|
||||
<>
|
||||
<i
|
||||
className="bx bx-edit bx-sm text-primary cursor-pointer me-2"
|
||||
onClick={() => {
|
||||
setSelectedContact(contact);
|
||||
setIsOpenModal(true);
|
||||
}}
|
||||
></i>
|
||||
<i
|
||||
className="bx bx-trash bx-sm text-danger cursor-pointer"
|
||||
onClick={() => IsDeleted(contact.id)}
|
||||
></i>
|
||||
</>
|
||||
)}
|
||||
{!IsActive && (
|
||||
<i
|
||||
className={`bx bx-history ${
|
||||
dirActions.action && dirActions.id === contact.id ? "bx-spin" : ""
|
||||
} me-1 text-primary cursor-pointer`}
|
||||
title="Restore"
|
||||
onClick={() => {
|
||||
setDirActions({ action: false, id: contact.id });
|
||||
restore(contact.id);
|
||||
}}
|
||||
></i>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListViewDirectory;
|
||||
export default ListViewDirectory;
|
||||
|
@ -19,6 +19,7 @@ import DirectoryListTableHeader from "./DirectoryListTableHeader";
|
||||
import DirectoryPageHeader from "./DirectoryPageHeader";
|
||||
import ManageBucket from "../../components/Directory/ManageBucket";
|
||||
import {useFab} from "../../Context/FabContext";
|
||||
import {DireProvider, useDir} from "../../Context/DireContext";
|
||||
|
||||
const Directory = () =>
|
||||
{
|
||||
@ -33,12 +34,15 @@ const Directory = () =>
|
||||
const [listView, setListView] = useState(false);
|
||||
const [selectedBucketIds, setSelectedBucketIds] = useState([]);
|
||||
const [deleteContact, setDeleteContact] = useState(null);
|
||||
const [ IsDeleting, setIsDeletng ] = useState( false );
|
||||
const [openBucketModal,setOpenBucketModal] = useState(false)
|
||||
const [ IsDeleting, setDeleting ] = useState( false );
|
||||
const [ openBucketModal, setOpenBucketModal ] = useState( false )
|
||||
|
||||
|
||||
const [tempSelectedBucketIds, setTempSelectedBucketIds] = useState([]);
|
||||
const [ tempSelectedCategoryIds, setTempSelectedCategoryIds ] = useState( [] );
|
||||
const {setActions} = useFab()
|
||||
const {setActions} = useFab()
|
||||
const { dirActions, setDirActions } = useDir();
|
||||
|
||||
|
||||
const { contacts, loading , refetch} = useDirectory(IsActive);
|
||||
const { contactCategory, loading: contactCategoryLoading } =
|
||||
@ -78,28 +82,43 @@ const Directory = () =>
|
||||
}
|
||||
};
|
||||
|
||||
const handleDeleteContact = async () => {
|
||||
try {
|
||||
setIsDeletng(true);
|
||||
const contacts_cache = getCachedData("contacts")?.data || [];
|
||||
|
||||
const response = await DirectoryRepository.DeleteContact(deleteContact);
|
||||
const updatedContacts = ContactList.filter( ( c ) => c.id !== deleteContact );
|
||||
setContactList(updatedContacts);
|
||||
cacheData("Contacts", {data:updatedContacts,isActive:IsActive});
|
||||
showToast("Contact deleted successfully", "success");
|
||||
setDeleteContact(null);
|
||||
|
||||
setIsDeletng(false);
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error.response.data.message ||
|
||||
error.message ||
|
||||
"Error occured during API calling";
|
||||
showToast(msg, "error");
|
||||
setIsDeletng(false);
|
||||
const handleDeleteContact = async (overrideId = null) => {
|
||||
try
|
||||
{
|
||||
if (!IsActive) {
|
||||
setDirActions((prev) => ({ ...prev, action: true }));
|
||||
} else {
|
||||
setDeleting(true);
|
||||
}
|
||||
};
|
||||
const id = overrideId || (!IsActive ? dirActions.id : deleteContact);
|
||||
if (!id) {
|
||||
showToast("No contact selected for deletion", "error");
|
||||
return;
|
||||
}
|
||||
|
||||
await DirectoryRepository.DeleteContact(id, !IsActive);
|
||||
|
||||
const updatedContacts = ContactList.filter((c) => c.id !== id);
|
||||
setContactList(updatedContacts);
|
||||
cacheData("Contacts", { data: updatedContacts, isActive: IsActive });
|
||||
|
||||
showToast(`Contact ${IsActive ? "Deleted":"Restored"} successfully`, "success");
|
||||
|
||||
setDeleteContact(null);
|
||||
setDirActions({ action: false, id: null });
|
||||
setDeleting(false);
|
||||
} catch (error) {
|
||||
const msg =
|
||||
error?.response?.data?.message ||
|
||||
error.message ||
|
||||
"Error occurred during API call";
|
||||
showToast(msg, "error");
|
||||
|
||||
setDeleting(false);
|
||||
setDirActions({ action: false, id: null });
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
const closedModel = () => {
|
||||
setIsOpenModal(false);
|
||||
@ -306,7 +325,6 @@ const Directory = () =>
|
||||
applyFilter={applyFilter}
|
||||
loading={loading}
|
||||
IsActive={IsActive}
|
||||
setIsOpenModal={setIsOpenModal}
|
||||
setOpenBucketModal={setOpenBucketModal}
|
||||
/>
|
||||
{!listView && loading && <p>Loading...</p>}
|
||||
@ -314,7 +332,7 @@ const Directory = () =>
|
||||
<p>No Matching Contact Found</p>
|
||||
)}
|
||||
{listView ? (
|
||||
<DirectoryListTableHeader IsActive={IsActive}>
|
||||
<DirectoryListTableHeader>
|
||||
{loading && (
|
||||
<tr>
|
||||
<td colSpan={10}>Loading...</td>
|
||||
@ -336,6 +354,7 @@ const Directory = () =>
|
||||
setOpen_contact={setOpen_contact}
|
||||
setIsOpenModalNote={setIsOpenModalNote}
|
||||
IsDeleted={setDeleteContact}
|
||||
restore={handleDeleteContact}
|
||||
/>
|
||||
))}
|
||||
</DirectoryListTableHeader>
|
||||
@ -354,15 +373,16 @@ const Directory = () =>
|
||||
setOpen_contact={setOpen_contact}
|
||||
setIsOpenModalNote={setIsOpenModalNote}
|
||||
IsDeleted={setDeleteContact}
|
||||
restore={handleDeleteContact}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
{!loading && currentItems < ITEMS_PER_PAGE && (
|
||||
{!loading && currentItems < ITEMS_PER_PAGE && (
|
||||
<nav aria-label="Page ">
|
||||
<ul className="pagination pagination-sm justify-content-end py-1">
|
||||
<li
|
||||
@ -406,7 +426,7 @@ const Directory = () =>
|
||||
</nav>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user