Change the UI for Employee and Directory and adding export functionality.
This commit is contained in:
parent
3072354ed2
commit
7e4a8157bf
@ -19,7 +19,7 @@ import BucketList from "../../components/Directory/BucketList";
|
|||||||
import { MainDirectoryPageSkeleton } from "../../components/Directory/DirectoryPageSkeleton";
|
import { MainDirectoryPageSkeleton } from "../../components/Directory/DirectoryPageSkeleton";
|
||||||
import ContactProfile from "../../components/Directory/ContactProfile";
|
import ContactProfile from "../../components/Directory/ContactProfile";
|
||||||
import GlobalModel from "../../components/common/GlobalModel";
|
import GlobalModel from "../../components/common/GlobalModel";
|
||||||
import { exportToCSV } from "../../utils/exportUtils";
|
import { exportToCSV,exportToExcel,exportToPDF1,exportToPDF,printTable } from "../../utils/tableExportUtils";
|
||||||
import ConfirmModal from "../../components/common/ConfirmModal";
|
import ConfirmModal from "../../components/common/ConfirmModal";
|
||||||
import { useSelectedProject } from "../../slices/apiDataManager";
|
import { useSelectedProject } from "../../slices/apiDataManager";
|
||||||
|
|
||||||
@ -64,11 +64,49 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
const [ContactData, setContactData] = useState([]);
|
const [ContactData, setContactData] = useState([]);
|
||||||
|
|
||||||
const handleExport = (type) => {
|
const handleExport = (type) => {
|
||||||
if (activeTab === "notes" && type === "csv") {
|
let exportData = activeTab === "notes" ? notesData : ContactData;
|
||||||
exportToCSV(notesData, "notes.csv");
|
if (!exportData?.length) return;
|
||||||
}
|
|
||||||
if (activeTab === "contacts" && type === "csv") {
|
switch (type) {
|
||||||
exportToCSV(ContactData, "contact.csv");
|
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");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -133,7 +171,7 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
]}
|
]}
|
||||||
></Breadcrumb>
|
></Breadcrumb>
|
||||||
)}
|
)}
|
||||||
<div className="card">
|
<div className="card ">
|
||||||
<div className="d-flex-row px-2">
|
<div className="d-flex-row px-2">
|
||||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||||
<ul className="nav nav-tabs">
|
<ul className="nav nav-tabs">
|
||||||
@ -158,13 +196,11 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mb-1 px-md-2 px-0 py-3">
|
<div className="mb-1 px-2 py-3">
|
||||||
<div className="row">
|
<div className="d-flex align-items-center justify-content-between">
|
||||||
<div className="col-12 col-md-10 mb-2">
|
<div className="d-flex align-items-center gap-3">
|
||||||
{activeTab === "notes" && (
|
{activeTab === "notes" && (
|
||||||
<div className="col-8 col-md-3">
|
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
@ -172,13 +208,11 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
value={searchNote}
|
value={searchNote}
|
||||||
onChange={(e) => setSearchNote(e.target.value)}
|
onChange={(e) => setSearchNote(e.target.value)}
|
||||||
/>
|
/>
|
||||||
</div>
|
)}
|
||||||
)}
|
|
||||||
|
|
||||||
{activeTab === "contacts" && (
|
{activeTab === "contacts" && (
|
||||||
<div className="d-flex align-items-center gap-3">
|
<div className="d-flex align-items-center gap-3">
|
||||||
<div className="col-12 col-md-8 d-flex flex-row gap-2">
|
<div className="d-flex gap-2 align-items-center">
|
||||||
<div className="col-7 col-md-4">
|
|
||||||
<input
|
<input
|
||||||
type="search"
|
type="search"
|
||||||
className="form-control form-control-sm"
|
className="form-control form-control-sm"
|
||||||
@ -186,61 +220,117 @@ export default function DirectoryPage({ IsPage = true, projectId = null }) {
|
|||||||
value={searchContact}
|
value={searchContact}
|
||||||
onChange={(e) => setsearchContact(e.target.value)}
|
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>
|
||||||
<button
|
</div>
|
||||||
className={`btn btn-sm p-1 ${gridView ? " btn-primary" : " btn-outline-primary"
|
)}
|
||||||
|
</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)}
|
onClick={() => setGridView(true)}
|
||||||
>
|
>
|
||||||
<i className="bx bx-grid-alt"></i>
|
<i className="bx bx-grid-alt"></i> Card View
|
||||||
</button>
|
</span>
|
||||||
<button
|
</li>
|
||||||
className={`btn btn-sm p-1 ${!gridView ? "btn-primary" : "btn-outline-primary"
|
|
||||||
|
<li className={` d-block d-md-none ${activeTab === "contacts" ? "d-block" : "d-none"}`}>
|
||||||
|
<span
|
||||||
|
className={`dropdown-item ${!gridView ? "text-primary" : ""
|
||||||
}`}
|
}`}
|
||||||
onClick={() => setGridView(false)}
|
onClick={() => setGridView(false)}
|
||||||
>
|
>
|
||||||
<i className="bx bx-list-ul"></i>
|
<i className="bx bx-list-ul"></i> List View
|
||||||
</button>
|
</span>
|
||||||
|
|
||||||
<div className="form-check form-switch d-flex align-items-end d-none d-md-flex">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="form-check-input"
|
|
||||||
role="switch"
|
|
||||||
id="inactiveEmployeesCheckbox"
|
|
||||||
checked={showActive}
|
|
||||||
onChange={(e) => setShowActive(e.target.checked)}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
className="form-check-label ms-2"
|
|
||||||
htmlFor="inactiveEmployeesCheckbox"
|
|
||||||
>
|
|
||||||
{showActive ? "Active" : "In-active"} Contacts
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="col-12 col-md-2 d-flex justify-content-end align-items-center gap-2">
|
|
||||||
<div className=" btn-group">
|
|
||||||
<button
|
|
||||||
className="btn btn-sm btn-label-secondary dropdown-toggle"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<i className="bx bx-export me-2 bx-sm"></i>Export
|
|
||||||
</button>
|
|
||||||
<ul className="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="dropdown-item cursor-pointer"
|
|
||||||
onClick={() => handleExport("csv")}
|
|
||||||
>
|
|
||||||
<i className="bx bx-file me-1"></i> CSV
|
|
||||||
</a>
|
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
{/* Divider */}
|
||||||
|
{activeTab === "contacts" && (
|
||||||
|
<li>
|
||||||
|
<hr className="dropdown-divider" />
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -298,31 +298,7 @@ const EmployeeList = () => {
|
|||||||
<div className="d-flex flex-wrap align-items-center justify-content-between gap-3 mb-3">
|
<div className="d-flex flex-wrap align-items-center justify-content-between gap-3 mb-3">
|
||||||
{/* Switches: All Employees + Inactive */}
|
{/* Switches: All Employees + Inactive */}
|
||||||
<div className="d-flex flex-wrap align-items-center gap-3">
|
<div className="d-flex flex-wrap align-items-center gap-3">
|
||||||
{/* All Employees Switch */}
|
{/* {showAllEmployees && ( */}
|
||||||
|
|
||||||
{/* Show Inactive Employees Switch */}
|
|
||||||
|
|
||||||
<div className="form-check form-switch text-start">
|
|
||||||
<input
|
|
||||||
type="checkbox"
|
|
||||||
className="form-check-input"
|
|
||||||
role="switch"
|
|
||||||
id="inactiveEmployeesCheckbox"
|
|
||||||
checked={showInactive}
|
|
||||||
onChange={(e) => setShowInactive(e.target.checked)}
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
className="form-check-label ms-0"
|
|
||||||
htmlFor="inactiveEmployeesCheckbox"
|
|
||||||
>
|
|
||||||
{showInactive ? "Hide In-active Employees":"Show In-active Employees"}
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Right side: Search + Export + Add Employee */}
|
|
||||||
<div className="d-flex flex-wrap align-items-center justify-content-start justify-content-md-end gap-3 flex-grow-1">
|
|
||||||
{/* Search Input - ALWAYS ENABLED */}
|
|
||||||
<div className="dataTables_filter">
|
<div className="dataTables_filter">
|
||||||
<label className="mb-0">
|
<label className="mb-0">
|
||||||
<input
|
<input
|
||||||
@ -336,57 +312,11 @@ const EmployeeList = () => {
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Export Dropdown */}
|
{/* )} */}
|
||||||
<div className="dropdown">
|
</div>
|
||||||
<button
|
|
||||||
aria-label="Click me"
|
|
||||||
className="btn btn-sm btn-label-secondary dropdown-toggle"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
>
|
|
||||||
<i className="bx bx-export me-2 bx-sm"></i>Export
|
|
||||||
</button>
|
|
||||||
<ul className="dropdown-menu">
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
onClick={() => handleExport("print")}
|
|
||||||
>
|
|
||||||
<i className="bx bx-printer me-1"></i> Print
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
onClick={() => handleExport("csv")}
|
|
||||||
>
|
|
||||||
<i className="bx bx-file me-1"></i> CSV
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
onClick={() => handleExport("excel")}
|
|
||||||
>
|
|
||||||
<i className="bx bxs-file-export me-1"></i> Excel
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
className="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
onClick={() => handleExport("pdf")}
|
|
||||||
>
|
|
||||||
<i className="bx bxs-file-pdf me-1"></i> PDF
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
{/* Right side: Search + Add Employee + Options */}
|
||||||
|
<div className="d-flex flex-wrap align-items-center justify-content-end flex-grow-1">
|
||||||
{/* Add Employee Button */}
|
{/* Add Employee Button */}
|
||||||
{Manage_Employee && (
|
{Manage_Employee && (
|
||||||
<button
|
<button
|
||||||
@ -400,7 +330,60 @@ const EmployeeList = () => {
|
|||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 3-Dots Dropdown (New Combined Menu) */}
|
||||||
|
<div className="dropdown">
|
||||||
|
<button
|
||||||
|
className="btn btn-icon me-1"
|
||||||
|
type="button"
|
||||||
|
data-bs-toggle="dropdown"
|
||||||
|
aria-expanded="false"
|
||||||
|
>
|
||||||
|
<i className="bx bx-dots-vertical-rounded bx-md"></i>
|
||||||
|
</button>
|
||||||
|
<ul className="dropdown-menu dropdown-menu-end shadow-sm " style={{ minWidth: "220px" }}>
|
||||||
|
|
||||||
|
<li className="dropdown-item d-flex align-items-center justify-content-between">
|
||||||
|
<div className="form-check form-switch mb-0">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
className="form-check-input"
|
||||||
|
role="switch"
|
||||||
|
id="inactiveEmployeesCheckboxMenu"
|
||||||
|
checked={showInactive}
|
||||||
|
onChange={(e) => setShowInactive(e.target.checked)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<span className="ms-0">Show Inactive Employees</span>
|
||||||
|
</li>
|
||||||
|
|
||||||
|
|
||||||
|
<li><hr className="dropdown-divider" /></li>
|
||||||
|
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("print")}>
|
||||||
|
<i className="bx bx-printer me-2"></i> Print
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("csv")}>
|
||||||
|
<i className="bx bx-file me-2"></i> CSV
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("excel")}>
|
||||||
|
<i className="bx bxs-file-export me-2"></i> Excel
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<button className="dropdown-item" onClick={() => handleExport("pdf")}>
|
||||||
|
<i className="bx bxs-file-pdf me-2"></i> PDF
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<table
|
<table
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
// utils/exportUtils.js
|
|
||||||
export const exportToCSV = (data, filename = "export.csv") => {
|
|
||||||
if (!data || data.length === 0) return;
|
|
||||||
|
|
||||||
const headers = Object.keys(data[0]);
|
|
||||||
const csvRows = [];
|
|
||||||
|
|
||||||
// Add headers
|
|
||||||
csvRows.push(headers.join(","));
|
|
||||||
|
|
||||||
// Add values
|
|
||||||
data.forEach(row => {
|
|
||||||
const values = headers.map(header => `"${row[header] ?? ""}"`);
|
|
||||||
csvRows.push(values.join(","));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create CSV Blob
|
|
||||||
const csvContent = csvRows.join("\n");
|
|
||||||
const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
|
|
||||||
|
|
||||||
// Create download link
|
|
||||||
const link = document.createElement("a");
|
|
||||||
const url = URL.createObjectURL(blob);
|
|
||||||
link.setAttribute("href", url);
|
|
||||||
link.setAttribute("download", filename);
|
|
||||||
link.click();
|
|
||||||
};
|
|
||||||
Loading…
x
Reference in New Issue
Block a user