425 lines
15 KiB
JavaScript
425 lines
15 KiB
JavaScript
import {
|
|
useState,
|
|
Suspense,
|
|
lazy,
|
|
createContext,
|
|
useContext,
|
|
useEffect,
|
|
} from "react";
|
|
import Breadcrumb from "../../components/common/Breadcrumb";
|
|
import { useFab } from "../../Context/FabContext";
|
|
import {
|
|
useBucketList,
|
|
useBuckets,
|
|
useDeleteBucket,
|
|
} from "../../hooks/useDirectory";
|
|
import ManageBucket1 from "../../components/Directory/ManageBucket1";
|
|
import ManageContact from "../../components/Directory/ManageContact";
|
|
import BucketList from "../../components/Directory/BucketList";
|
|
import { MainDirectoryPageSkeleton } from "../../components/Directory/DirectoryPageSkeleton";
|
|
import ContactProfile from "../../components/Directory/ContactProfile";
|
|
import GlobalModel from "../../components/common/GlobalModel";
|
|
import ConfirmModal from "../../components/common/ConfirmModal";
|
|
import { useSelectedProject } from "../../slices/apiDataManager";
|
|
import {
|
|
exportToCSV,
|
|
exportToExcel,
|
|
exportToPDF,
|
|
exportToPDF1,
|
|
printTable,
|
|
} from "../../utils/tableExportUtils";
|
|
|
|
const NotesPage = lazy(() => import("./NotesPage"));
|
|
const ContactsPage = lazy(() => import("./ContactsPage"));
|
|
|
|
export const DirectoryContext = createContext();
|
|
export const useDirectoryContext = () => {
|
|
const context = useContext(DirectoryContext);
|
|
|
|
if (!context) {
|
|
return (
|
|
<div className="container-fluid">
|
|
<p>Your Action is out of context</p>
|
|
</div>
|
|
);
|
|
}
|
|
return context;
|
|
};
|
|
export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|
const [searchContact, setsearchContact] = useState("");
|
|
const [searchNote, setSearchNote] = useState("");
|
|
const [activeTab, setActiveTab] = useState("notes");
|
|
const { setActions } = useFab();
|
|
const [gridView, setGridView] = useState(true);
|
|
const [isOpenBucket, setOpenBucket] = useState(false);
|
|
const [isManageContact, setManageContact] = useState({
|
|
isOpen: false,
|
|
contactId: null,
|
|
});
|
|
const [deleteBucket, setDeleteBucket] = useState({
|
|
isOpen: false,
|
|
bucketId: null,
|
|
});
|
|
const [showActive, setShowActive] = useState(true);
|
|
const [contactOpen, setContactOpen] = useState({
|
|
contact: null,
|
|
Open: false,
|
|
});
|
|
|
|
const [notesData, setNotesData] = useState([]);
|
|
const [ContactData, setContactData] = useState([]);
|
|
|
|
const handleExport = (type) => {
|
|
let exportData = activeTab === "notes" ? notesData : ContactData;
|
|
if (!exportData?.length) return;
|
|
|
|
switch (type) {
|
|
case "csv":
|
|
exportToCSV(exportData, activeTab === "notes" ? "Notes" : "Contacts");
|
|
break;
|
|
case "excel":
|
|
exportToExcel(exportData, activeTab === "notes" ? "Notes" : "Contacts");
|
|
break;
|
|
case "pdf":
|
|
if (activeTab === "notes") {
|
|
exportToPDF1(exportData, "Notes");
|
|
} else {
|
|
// Columns for Contacts PDF
|
|
const columns = [
|
|
"Email",
|
|
"Phone",
|
|
"Organization",
|
|
"Category",
|
|
"Tags",
|
|
];
|
|
|
|
// Sanitize and trim long text to avoid PDF overflow
|
|
const sanitizedData = exportData.map((item) => ({
|
|
Email: (item.Email || "").slice(0, 40),
|
|
Phone: (item.Phone || "").slice(0, 20),
|
|
Organization: (item.Organization || "").slice(0, 30),
|
|
Category: (item.Category || "").slice(0, 20),
|
|
Tags: (item.Tags || "").slice(0, 40),
|
|
}));
|
|
|
|
// Export with proper spacing
|
|
exportToPDF(sanitizedData, "Contacts", columns, {
|
|
columnWidths: [200, 120, 180, 120, 200], // Adjust widths per column
|
|
fontSizeHeader: 12,
|
|
fontSizeRow: 10,
|
|
rowHeight: 25,
|
|
});
|
|
}
|
|
break;
|
|
default:
|
|
console.warn("Unsupported export type");
|
|
}
|
|
};
|
|
|
|
const { data, isLoading, isError, error } = useBucketList();
|
|
|
|
const handleTabClick = (tab, e) => {
|
|
e.preventDefault();
|
|
setActiveTab(tab);
|
|
};
|
|
|
|
useEffect(() => {
|
|
const actions = [];
|
|
|
|
if (IsPage) {
|
|
actions.push({
|
|
label: "Manage Bucket",
|
|
icon: "fa-solid fa-bucket fs-5",
|
|
color: "primary",
|
|
onClick: () => setOpenBucket(true),
|
|
});
|
|
}
|
|
if (data?.length > 0) {
|
|
actions.push({
|
|
label: "New Contact",
|
|
icon: "bx bx-plus-circle",
|
|
color: "warning",
|
|
onClick: () => setManageContact({ isOpen: true, contactId: null }),
|
|
});
|
|
}
|
|
|
|
setActions(actions);
|
|
|
|
return () => setActions([]);
|
|
}, [IsPage, data]);
|
|
|
|
const contextValues = {
|
|
showActive,
|
|
gridView,
|
|
data,
|
|
setManageContact,
|
|
setContactOpen,
|
|
setDeleteBucket,
|
|
};
|
|
|
|
const { mutate: DeleteBucket, isPending: Deleting } = useDeleteBucket(() => {
|
|
setDeleteBucket({ isOpen: false, bucketId: null });
|
|
});
|
|
const handleDelete = (bucketId) => {
|
|
DeleteBucket(bucketId);
|
|
};
|
|
if (isLoading) return <MainDirectoryPageSkeleton />;
|
|
if (isError) return <div>{error.message}</div>;
|
|
return (
|
|
<>
|
|
<DirectoryContext.Provider value={contextValues}>
|
|
<div className={`${IsPage ? "container-fluid" : ""}`}>
|
|
{IsPage && (
|
|
<Breadcrumb
|
|
data={[
|
|
{ label: "Home", link: "/dashboard" },
|
|
{ label: "Directory", link: null },
|
|
]}
|
|
></Breadcrumb>
|
|
)}
|
|
<div className="card ">
|
|
<div className="d-flex-row px-2">
|
|
<div className="d-flex justify-content-between align-items-center mb-1">
|
|
<ul className="nav nav-tabs">
|
|
<li className="nav-item cursor-pointer">
|
|
<a
|
|
className={`nav-link ${
|
|
activeTab === "notes" ? "active" : ""
|
|
} fs-6`}
|
|
onClick={(e) => handleTabClick("notes", e)}
|
|
>
|
|
<i className="bx bx-notepad bx-sm me-1_5"></i>
|
|
<span className="d-none d-md-inline">Notes</span>
|
|
</a>
|
|
</li>
|
|
<li className="nav-item cursor-pointer">
|
|
<a
|
|
className={`nav-link ${
|
|
activeTab === "contacts" ? "active" : ""
|
|
} fs-6`}
|
|
onClick={(e) => handleTabClick("contacts", e)}
|
|
>
|
|
<i className="bx bxs-contact bx-sm me-1_5"></i>
|
|
<span className="d-none d-md-inline">Contacts</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
|
|
<div className="mb-1 px-2 py-3">
|
|
<div className="d-flex align-items-center justify-content-between">
|
|
<div className="d-flex align-items-center gap-3">
|
|
{activeTab === "notes" && (
|
|
<input
|
|
type="search"
|
|
className="form-control form-control-sm"
|
|
placeholder="Search notes..."
|
|
value={searchNote}
|
|
onChange={(e) => setSearchNote(e.target.value)}
|
|
/>
|
|
)}
|
|
|
|
{activeTab === "contacts" && (
|
|
<div className="d-flex align-items-center gap-3">
|
|
<div className="d-flex gap-2 align-items-center">
|
|
<input
|
|
type="search"
|
|
className="form-control form-control-sm"
|
|
placeholder="Search contacts..."
|
|
value={searchContact}
|
|
onChange={(e) => setsearchContact(e.target.value)}
|
|
/>
|
|
<div className="d-none d-md-flex gap-2">
|
|
{" "}
|
|
<button
|
|
className={`btn btn-sm p-1 ${
|
|
gridView
|
|
? " btn-primary"
|
|
: " btn-outline-primary"
|
|
}`}
|
|
onClick={() => setGridView(true)}
|
|
>
|
|
<i className="bx bx-grid-alt"></i>
|
|
</button>
|
|
<button
|
|
className={`btn btn-sm p-1 ${
|
|
!gridView
|
|
? "btn-primary"
|
|
: "btn-outline-primary"
|
|
}`}
|
|
onClick={() => setGridView(false)}
|
|
>
|
|
<i className="bx bx-list-ul"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
<div className="dropdown z-2">
|
|
<button
|
|
type="button"
|
|
className="btn btn-icon p-0 m-0"
|
|
data-bs-toggle="dropdown"
|
|
aria-expanded="false"
|
|
title="More Actions"
|
|
>
|
|
<i className="bx bx-dots-vertical-rounded text-muted bx-md"></i>
|
|
</button>
|
|
|
|
<ul className="dropdown-menu dropdown-menu-end shadow-sm ">
|
|
{activeTab === "contacts" && (
|
|
<li className="dropdown-item d-flex align-items-center">
|
|
<div className="form-check form-switch mb-0">
|
|
<input
|
|
type="checkbox"
|
|
className="form-check-input"
|
|
role="switch"
|
|
id="inactiveContactsSwitch"
|
|
checked={showActive}
|
|
onChange={(e) =>
|
|
setShowActive(e.target.checked)
|
|
}
|
|
/>
|
|
</div>
|
|
<span>
|
|
{showActive
|
|
? "Active Contacts"
|
|
: "Inactive Contacts"}
|
|
</span>
|
|
</li>
|
|
)}
|
|
<li>
|
|
<button
|
|
className="dropdown-item d-flex align-items-center gap-2"
|
|
onClick={() => handleExport("csv")}
|
|
>
|
|
<i className="bx bx-file"></i>
|
|
<span>Export to CSV</span>
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button
|
|
className="dropdown-item d-flex align-items-center gap-2"
|
|
onClick={() => handleExport("excel")}
|
|
>
|
|
<i className="bx bx-spreadsheet"></i>
|
|
<span>Export to Excel</span>
|
|
</button>
|
|
</li>
|
|
<li>
|
|
<button
|
|
className="dropdown-item d-flex align-items-center gap-2"
|
|
onClick={() => handleExport("pdf")}
|
|
>
|
|
<i className="bx bxs-file-pdf"></i>
|
|
<span>Export to PDF</span>
|
|
</button>
|
|
</li>
|
|
<li className={`d-block d-md-none ${activeTab === "contacts" ? "d-block":"d-none"}`}>
|
|
<span
|
|
className={`dropdown-item ${
|
|
gridView ? " text-primary" : ""
|
|
}`}
|
|
onClick={() => setGridView(true)}
|
|
>
|
|
<i className="bx bx-grid-alt"></i> Card View
|
|
</span>
|
|
</li>
|
|
|
|
<li className={` d-block d-md-none ${activeTab === "contacts" ? "d-block":"d-none"}`}>
|
|
<span
|
|
className={`dropdown-item ${
|
|
!gridView ? "text-primary" : ""
|
|
}`}
|
|
onClick={() => setGridView(false)}
|
|
>
|
|
<i className="bx bx-list-ul"></i> List View
|
|
</span>
|
|
</li>
|
|
|
|
{/* Divider */}
|
|
{activeTab === "contacts" && (
|
|
<li>
|
|
<hr className="dropdown-divider" />
|
|
</li>
|
|
)}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<Suspense fallback={<MainDirectoryPageSkeleton />}>
|
|
{activeTab === "notes" && (
|
|
<NotesPage
|
|
projectId={projectId}
|
|
searchText={searchNote}
|
|
onExport={setNotesData}
|
|
/>
|
|
)}
|
|
{activeTab === "contacts" && (
|
|
<ContactsPage
|
|
projectId={projectId}
|
|
searchText={searchContact}
|
|
onExport={setContactData}
|
|
/>
|
|
)}
|
|
</Suspense>
|
|
</div>
|
|
|
|
{isOpenBucket && (
|
|
<GlobalModel
|
|
size="lg"
|
|
isOpen={isOpenBucket}
|
|
closeModal={() => setOpenBucket(false)}
|
|
>
|
|
<ManageBucket1 closeModal={() => setOpenBucket(false)} />
|
|
</GlobalModel>
|
|
)}
|
|
|
|
{contactOpen.Open && (
|
|
<GlobalModel
|
|
size="xl"
|
|
isOpen={contactOpen.Open}
|
|
closeModal={() => setContactOpen({ contact: null, Open: false })}
|
|
>
|
|
<ContactProfile contactId={contactOpen.contact} />
|
|
</GlobalModel>
|
|
)}
|
|
{isManageContact.isOpen && (
|
|
<GlobalModel
|
|
size="lg"
|
|
isOpen={isManageContact}
|
|
closeModal={() =>
|
|
setManageContact({ isOpen: false, contactId: null })
|
|
}
|
|
>
|
|
<ManageContact
|
|
contactId={isManageContact.contactId}
|
|
closeModal={() =>
|
|
setManageContact({ isOpen: false, contactId: null })
|
|
}
|
|
/>
|
|
</GlobalModel>
|
|
)}
|
|
|
|
{deleteBucket.isOpen && (
|
|
<ConfirmModal
|
|
isOpen={deleteBucket.isOpen}
|
|
type="delete"
|
|
header="Delete Bucket"
|
|
message="Are you sure you want delete?"
|
|
onSubmit={handleDelete}
|
|
onClose={() => setDeleteBucket({ isOpen: false, bucketId: null })}
|
|
loading={Deleting}
|
|
paramData={deleteBucket.bucketId}
|
|
/>
|
|
)}
|
|
</div>
|
|
</DirectoryContext.Provider>
|
|
</>
|
|
);
|
|
}
|