diff --git a/src/components/Activities/Attendance.jsx b/src/components/Activities/Attendance.jsx index c46feac3..b7ddfaff 100644 --- a/src/components/Activities/Attendance.jsx +++ b/src/components/Activities/Attendance.jsx @@ -223,50 +223,6 @@ const Attendance = ({ getRole, handleModalData, searchTerm, projectId, organizat )} - - - {!loading && finalFilteredData.length > ITEMS_PER_PAGE && ( - - )} ) : (
)}
+ {!loading && finalFilteredData.length > ITEMS_PER_PAGE && ( + + )} ); }; diff --git a/src/components/Activities/AttendcesLogs.jsx b/src/components/Activities/AttendcesLogs.jsx index 485eca62..4660f956 100644 --- a/src/components/Activities/AttendcesLogs.jsx +++ b/src/components/Activities/AttendcesLogs.jsx @@ -15,6 +15,7 @@ import AttendanceRepository from "../../repositories/AttendanceRepository"; import { useAttendancesLogs } from "../../hooks/useAttendance"; import { queryClient } from "../../layouts/AuthLayout"; import { ITEMS_PER_PAGE } from "../../utils/constants"; +import { useNavigate } from "react-router-dom"; const usePagination = (data, itemsPerPage) => { const [currentPage, setCurrentPage] = useState(1); @@ -44,6 +45,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { const [loading, setLoading] = useState(false); const [showPending, setShowPending] = useState(false); const [isRefreshing, setIsRefreshing] = useState(false); + const navigate = useNavigate(); const today = new Date(); today.setHours(0, 0, 0, 0); @@ -172,7 +174,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { return ( <>
@@ -202,6 +204,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => {
+
{ lastName={attendance.lastName} />
- + + navigate(`/employee/${attendance.employeeId}?for=attendance`) + } + className="text-heading text-truncate cursor-pointer" + > {attendance.firstName} {attendance.lastName} @@ -305,8 +313,7 @@ const AttendanceLog = ({ handleModalData, searchTerm, organizationId }) => { ) : (
- No data available for the selected date range. Please Select - another date. + No attendance record found in selected date range.
)} diff --git a/src/components/Activities/InfraPlanning.jsx b/src/components/Activities/InfraPlanning.jsx index 5dce91c4..e7a4003a 100644 --- a/src/components/Activities/InfraPlanning.jsx +++ b/src/components/Activities/InfraPlanning.jsx @@ -33,7 +33,7 @@ const InfraPlanning = () => { const selectedService = useCurrentService(); const { projectInfra, isLoading, isError, error, isFetched } = - useProjectInfra(selectedProject, selectedService || "" ); + useProjectInfra(selectedProject, selectedService || ""); const canManageInfra = useHasUserPermission(MANAGE_PROJECT_INFRA); const canApproveTask = useHasUserPermission(APPROVE_TASK); @@ -62,9 +62,13 @@ const InfraPlanning = () => { if (isFetched && (!projectInfra || projectInfra.length === 0)) { return ( -
-

No Result Found

+
+

No Result Found

+ ); } diff --git a/src/components/Activities/Regularization.jsx b/src/components/Activities/Regularization.jsx index 0aab7474..954820c2 100644 --- a/src/components/Activities/Regularization.jsx +++ b/src/components/Activities/Regularization.jsx @@ -14,6 +14,7 @@ import { } from "../../slices/apiDataManager"; import { useQueryClient } from "@tanstack/react-query"; import Pagination from "../../components/common/Pagination"; +import { useNavigate } from "react-router-dom"; const Regularization = ({ handleRequest, @@ -26,6 +27,7 @@ const Regularization = ({ // var selectedProject = useSelector((store) => store.localVariables.projectId); const selectedProject = useSelectedProject(); const [regularizesList, setregularizedList] = useState([]); + const navigate = useNavigate(); const { regularizes, loading, error, refetch } = useRegularizationRequests( selectedProject, organizationId, @@ -33,9 +35,9 @@ const Regularization = ({ ); useEffect(() => { - if(!regularizes) return - if(regularizes?.length) { - setregularizedList(regularizes); + if (!regularizes) return + if (regularizes?.length) { + setregularizedList(regularizes); } }, [regularizes]); @@ -102,141 +104,106 @@ const Regularization = ({ }, [employeeHandler]); return ( -
- {loading ? ( -
-

Loading...

-
- ) : currentItems?.length > 0 ? ( - - - - - - - - - - - - - - - {currentItems?.map((att, index) => ( - - + + + + + ))} + +
NameDateOrganization - Check-In - - Check-Out - - Requested By - - Requested At - Action
-
- -
- +
+
+ {loading ? ( +
+

Loading...

+
+ ) : currentItems?.length > 0 ? ( + + + + + + + + + + + + + + + {currentItems?.map((att, index) => ( + + - + + - + - - - - + - - - ))} - -
NameDateOrganization + Check-In + + Check-Out + + Requested By + + Requested At + Action
+ - - {moment(att.checkOutTime).format("DD-MMM-YYYY")}{moment(att.checkOutTime).format("DD-MMM-YYYY")}{att.organizationName || "--"}{att.organizationName || "--"}{convertShortTime(att.checkInTime)} - {att.requestedAt ? convertShortTime(att.checkOutTime) : "--"} - - {att.requestedBy ? ( ):(--)} - {convertShortTime(att.checkInTime)} - {att?.requestedAt ? formatUTCToLocalTime(att.requestedAt,true) : "--"} - - -
- ) : ( -
- - {searchTerm - ? "No results found for your search." - : "No Requests Found !"} - -
- )} - {/* {!loading && totalPages > 1 && ( - - )} */} + {att.requestedAt ? convertShortTime(att.checkOutTime) : "--"} +
+ {att.requestedBy ? () : (--)} + + {att?.requestedAt ? formatUTCToLocalTime(att.requestedAt, true) : "--"} + + +
+ ) : ( +
+ + {searchTerm + ? "No results found for your search." + : "No Requests Found !"} + +
+ )} +
{totalPages > 0 && ( )} +
); }; diff --git a/src/components/DailyProgressRport/TaskReportList.jsx b/src/components/DailyProgressRport/TaskReportList.jsx index 78af8df3..c47f46c4 100644 --- a/src/components/DailyProgressRport/TaskReportList.jsx +++ b/src/components/DailyProgressRport/TaskReportList.jsx @@ -29,7 +29,7 @@ const TaskReportList = () => { const ApprovedTaskRights = useHasUserPermission(APPROVE_TASK); const ReportTaskRights = useHasUserPermission(ASSIGN_REPORT_TASK); - const { service, openModal, closeModal,filter } = useDailyProgrssContext(); + const { service, openModal, closeModal, filter } = useDailyProgrssContext(); const selectedProject = useSelectedProject(); const { projectNames } = useProjectName(); @@ -37,7 +37,7 @@ const TaskReportList = () => { selectedProject, ITEMS_PER_PAGE, currentPage, - service,filter + service, filter ); const ProgrssReportColumn = [ @@ -192,109 +192,114 @@ const TaskReportList = () => { if (isLoading) return ; if (isError) return
Loading....
; return ( -
- - - - - - - - - - - - - {groupedTasks.length === 0 && ( +
+
+
Activity - - Total Pending{" "} - This shows the total pending tasks for each activity on that date.

} - > - -
-
-
- - Reported/Planned{" "} - This shows the reported versus planned tasks for each activity on that date.

} - > - -
-
-
Assign DateTeamActions
+ - + + + + + + - )} - - {groupedTasks.map(({ date, tasks }) => ( - - - + + {groupedTasks.length === 0 && ( + + - {tasks.map((task, idx) => ( - - - - - - - + - ))} - - ))} - -
- No reports available - Activity + + Total Pending{" "} + This shows the total pending tasks for each activity on that date.

} + > + +
+
+
+ + Reported/Planned{" "} + This shows the reported versus planned tasks for each activity on that date.

} + > + +
+
+
Assign DateTeamActions
- {formatUTCToLocalTime(date)} +
+ No reports available
-
- {task.workItem.activityMaster?.activityName || "No Activity Name"} -
-
- {task.workItem.workArea?.floor?.building?.name} ›{" "} - {task.workItem.workArea?.floor?.floorName} ›{" "} - {task.workItem.workArea?.areaName} -
-
- {formatNumber(task.workItem.plannedWork)} - {`${formatNumber(task.completedTask)} / ${formatNumber(task.plannedTask)}`}{formatUTCToLocalTime(task.assignmentDate)}{renderTeamMembers(task, idx)} -
- {ReportTaskRights && !task.reportedDate && ( - - )} - {ApprovedTaskRights && task.reportedDate && !task.approvedBy && ( - - )} - -
+ )} + + {groupedTasks.map(({ date, tasks }) => ( + +
+ {formatUTCToLocalTime(date)}
- {data?.data?.length > 0 && ( - - )} -
+ {tasks.map((task, idx) => ( + + +
+ {task.workItem.activityMaster?.activityName || "No Activity Name"} +
+
+ {task.workItem.workArea?.floor?.building?.name} ›{" "} + {task.workItem.workArea?.floor?.floorName} ›{" "} + {task.workItem.workArea?.areaName} +
+ + + {formatNumber(task.workItem.plannedWork)} + + {`${formatNumber(task.completedTask)} / ${formatNumber(task.plannedTask)}`} + {formatUTCToLocalTime(task.assignmentDate)} + {renderTeamMembers(task, idx)} + +
+ {ReportTaskRights && !task.reportedDate && ( + + )} + {ApprovedTaskRights && task.reportedDate && !task.approvedBy && ( + + )} + +
+ + + ))} + + ))} + + + +
+ { + data?.data?.length > 0 && ( + + ) + } +
); }; diff --git a/src/components/Directory/ListViewContact.jsx b/src/components/Directory/ListViewContact.jsx index 16fd9a14..f4f15531 100644 --- a/src/components/Directory/ListViewContact.jsx +++ b/src/components/Directory/ListViewContact.jsx @@ -160,8 +160,7 @@ const ListViewContact = ({ data, Pagination }) => { ) : ( { )} - {Pagination && ( -
- {Pagination} -
- )} + {Pagination && ( +
+ {Pagination} +
+ )} ); diff --git a/src/components/Employee/handleEmployeeExport.jsx b/src/components/Employee/handleEmployeeExport.jsx new file mode 100644 index 00000000..a4883d4f --- /dev/null +++ b/src/components/Employee/handleEmployeeExport.jsx @@ -0,0 +1,84 @@ +import moment from "moment"; +import { exportToExcel, exportToCSV, exportToPDF, printTable } from "../../utils/tableExportUtils"; + +/** + * Handles export operations for employee data. + * @param {string} type - Export type: 'csv', 'excel', 'pdf', or 'print' + * @param {Array} employeeList - Full employee data array + * @param {Array} filteredData - Filtered employee data (if search applied) + * @param {string} searchText - Current search text (used to decide dataset) + * @param {RefObject} tableRef - Table reference (used for print) + */ +const handleEmployeeExport = (type, employeeList, filteredData, searchText, tableRef) => { + // Export full list (filtered if search applied) + const dataToExport = searchText ? filteredData : employeeList; + + if (!dataToExport || dataToExport.length === 0) return; + + // Map and format employee data for export + const exportData = dataToExport.map((item) => ({ + "First Name": item.firstName || "", + "Middle Name": item.middleName || "", + "Last Name": item.lastName || "", + "Email": item.email || "", + "Gender": item.gender || "", + "Birth Date": item.birthdate + ? moment(item.birthdate).format("DD-MMM-YYYY") + : "", + "Joining Date": item.joiningDate + ? moment(item.joiningDate).format("DD-MMM-YYYY") + : "", + "Permanent Address": item.permanentAddress || "", + "Current Address": item.currentAddress || "", + "Phone Number": item.phoneNumber || "", + "Emergency Phone Number": item.emergencyPhoneNumber || "", + "Emergency Contact Person": item.emergencyContactPerson || "", + "Is Active": item.isActive ? "Active" : "Inactive", + "Job Role": item.jobRole || "", + })); + + switch (type) { + case "csv": + exportToCSV(exportData, "employees"); + break; + + case "excel": + exportToExcel(exportData, "employees"); + break; + + case "pdf": + exportToPDF( + dataToExport.map((item) => ({ + Name: `${item.firstName || ""} ${item.lastName || ""}`.trim(), + Email: item.email || "", + "Phone Number": item.phoneNumber || "", + "Job Role": item.jobRole || "", + "Joining Date": item.joiningDate + ? moment(item.joiningDate).format("DD-MMM-YYYY") + : "", + Gender: item.gender || "", + Status: item.isActive ? "Active" : "Inactive", + })), + "employees", + [ + "Name", + "Email", + "Phone Number", + "Job Role", + "Joining Date", + "Gender", + "Status", + ] + ); + break; + + case "print": + printTable(tableRef.current); + break; + + default: + break; + } +}; + +export default handleEmployeeExport; diff --git a/src/components/Expenses/ExpenseList.jsx b/src/components/Expenses/ExpenseList.jsx index 7cccb5d3..eadd0ac1 100644 --- a/src/components/Expenses/ExpenseList.jsx +++ b/src/components/Expenses/ExpenseList.jsx @@ -141,6 +141,8 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { getValue: (e) => `${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? "" }`.trim() || "N/A", + `${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? "" + }`.trim() || "N/A", customRender: (e) => (
navigate(`/employee/${e.createdBy?.id}`)}> @@ -153,6 +155,8 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { {`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? "" }`.trim() || "N/A"} + {`${e.createdBy?.firstName ?? ""} ${e.createdBy?.lastName ?? "" + }`.trim() || "N/A"}
), @@ -178,6 +182,8 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { {e.status?.name || "Unknown"} diff --git a/src/components/gallary/ImageGalleryListView.jsx b/src/components/gallary/ImageGalleryListView.jsx index 945736e3..6973f6f0 100644 --- a/src/components/gallary/ImageGalleryListView.jsx +++ b/src/components/gallary/ImageGalleryListView.jsx @@ -28,13 +28,21 @@ const ImageGalleryListView = ({filter}) => { } }; - if (!data?.data?.length && !isLoading) { - return ( -

- {selectedProject ? " No images match the selected filters.":"Please Select Project!"} -

- ); - } + if (!data?.data?.length && !isLoading) { + return ( +
+ + {selectedProject + ? "No images match the selected filters." + : "Please Select Project!"} + +
+ ); +} + if (isLoading) { return ( diff --git a/src/pages/Activities/AttendancePage.jsx b/src/pages/Activities/AttendancePage.jsx index 6ea212b7..b2a0c39b 100644 --- a/src/pages/Activities/AttendancePage.jsx +++ b/src/pages/Activities/AttendancePage.jsx @@ -179,44 +179,40 @@ const AttendancePage = () => { {/* Search + Organization filter */} -
- {/* Organization Dropdown */} -
-
- +
+
+
+
- {/* Search Input */} - setSearchTerm(e.target.value)} - /> + setSearchTerm(e.target.value)} + />
- - -
+
@@ -224,7 +220,7 @@ const AttendancePage = () => { {selectedProject ? ( <> {activeTab === "all" && ( -
+
{
)} {activeTab === "logs" && ( -
+
{
)} {activeTab === "regularization" && DoRegularized && ( -
+
{ ]} /> -
+
{data?.length === 0 ? (

Service not assigned

diff --git a/src/pages/employee/EmployeeList.jsx b/src/pages/employee/EmployeeList.jsx index 2018afb2..10db6786 100644 --- a/src/pages/employee/EmployeeList.jsx +++ b/src/pages/employee/EmployeeList.jsx @@ -38,6 +38,7 @@ import usePagination from "../../hooks/usePagination"; import { setProjectId } from "../../slices/localVariablesSlice"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import Pagination from "../../components/common/Pagination"; +import handleEmployeeExport from "../../components/Employee/handleEmployeeExport"; const EmployeeList = () => { const selectedProjectId = useSelector( @@ -134,26 +135,11 @@ const EmployeeList = () => { const tableRef = useRef(null); const handleExport = (type) => { - if (!currentItems || currentItems.length === 0) return; - - switch (type) { - case "csv": - exportToCSV(currentItems, "employees"); - break; - case "excel": - exportToExcel(currentItems, "employees"); - break; - case "pdf": - exportToPDF(currentItems, "employees"); - break; - case "print": - printTable(tableRef.current); - break; - default: - break; - } + handleEmployeeExport(type, employeeList, filteredData, searchText, tableRef); }; + + const handleAllEmployeesToggle = (e) => { const isChecked = e.target.checked; setShowInactive(false); @@ -663,17 +649,16 @@ const EmployeeList = () => { ))} - - - {displayData?.length > 0 && ( + +
+
+ {displayData?.length > 0 && ( )} -
-
) : (
diff --git a/src/utils/tableExportUtils.jsx b/src/utils/tableExportUtils.jsx index 90a1306a..768ff5ac 100644 --- a/src/utils/tableExportUtils.jsx +++ b/src/utils/tableExportUtils.jsx @@ -40,112 +40,57 @@ export const exportToExcel = (data, fileName = "data") => { * @param {Array} data - Array of objects to export * @param {string} fileName - File name for the PDF (optional) */ -export const exportToPDF = async (data, fileName = "data") => { +const sanitizeText = (text) => { + if (!text) return ""; + // Replace all non-ASCII characters with "?" or remove them + return text.replace(/[^\x00-\x7F]/g, "?"); +}; + +export const exportToPDF = async (data, fileName = "data", columns = null, options = {}) => { if (!data || data.length === 0) return; - // Create a new PDF document const pdfDoc = await PDFDocument.create(); + const font = await pdfDoc.embedFont(StandardFonts.Helvetica); - // Set up the font - const font = await pdfDoc.embedFont(StandardFonts.Helvetica); // Use Helvetica font - - // Calculate column widths dynamically based on data content - const headers = Object.keys(data[0]); - const rows = data.map(item => headers.map(header => item[header] || '')); + // Default options + const { + columnWidths = [], // array of widths per column + fontSizeHeader = 12, + fontSizeRow = 10, + rowHeight = 25, + } = options; - const getMaxColumnWidth = (columnIndex) => { - let maxWidth = font.widthOfTextAtSize(headers[columnIndex], 12); - rows.forEach(row => { - const cellText = row[columnIndex].toString(); - maxWidth = Math.max(maxWidth, font.widthOfTextAtSize(cellText, 10)); + const pageWidth = 1000; + const pageHeight = 600; + let page = pdfDoc.addPage([pageWidth, pageHeight]); + const margin = 30; + let y = pageHeight - margin; + + const headers = columns || Object.keys(data[0]); + + // Draw headers + headers.forEach((header, i) => { + const x = margin + (columnWidths[i] ? columnWidths.slice(0, i).reduce((a, b) => a + b, 0) : i * 150); + page.drawText(header, { x, y, font, size: fontSizeHeader }); + }); + y -= rowHeight; + + // Draw rows + data.forEach(row => { + headers.forEach((header, i) => { + const x = margin + (columnWidths[i] ? columnWidths.slice(0, i).reduce((a, b) => a + b, 0) : i * 150); + const text = row[header] || ''; + page.drawText(text, { x, y, font, size: fontSizeRow }); }); - return maxWidth + 10; // Padding for better spacing - }; + y -= rowHeight; - const columnWidths = headers.map((_, index) => getMaxColumnWidth(index)); - const tableX = 30; // X-coordinate for the table start - const rowHeight = 20; // Height of each row (can be adjusted) - const maxPageHeight = 750; // Max available height for content (before a new page is added) - const pageMargin = 30; // Margin from the top of the page - - let tableY = maxPageHeight; // Start Y position for the table - const maxPageWidth = 600; // Max available width for content (before a new page is added) - - // Add the headers and rows to the page - const addHeadersToPage = (page, scaleFactor) => { - let xPosition = tableX; - headers.forEach((header, index) => { - page.drawText(header, { - x: xPosition, - y: tableY, - font, - size: 12 * scaleFactor, // Scale the header font size - color: rgb(0, 0, 0), - }); - xPosition += columnWidths[index] * scaleFactor; // Adjust X position based on scaling - }); - tableY -= rowHeight; // Move down after adding headers - }; - - // Add a new page and reset the table position - const addNewPage = (scaleFactor) => { - const page = pdfDoc.addPage([600, 800]); - tableY = maxPageHeight; // Reset Y position for the new page - addHeadersToPage(page, scaleFactor); // Re-add headers to the new page - return page; - }; - - // Create the first page and add headers - let page = pdfDoc.addPage([600, 800]); - - // Check if the content fits within the page width, scale if necessary - const checkPageWidth = (row) => { - let totalWidth = columnWidths.reduce((acc, width) => acc + width, 0); - let scaleFactor = 1; - if (totalWidth > maxPageWidth) { - scaleFactor = maxPageWidth / totalWidth; // Scale down if necessary + if (y < margin) { + page = pdfDoc.addPage([pageWidth, pageHeight]); + y = pageHeight - margin; } - - return scaleFactor; - }; - - // Function to check for page breaks when adding a new row - const checkPageBreak = () => { - if (tableY - rowHeight < pageMargin) { - page = addNewPage(scaleFactor); // Add a new page if there is no space for the next row - } - }; - - // Add rows to the PDF with pagination and horizontal scaling - rows.forEach(row => { - checkPageBreak(); // Check for page break before adding each row - - const scaleFactor = checkPageWidth(row); // Get the scaling factor for the row - - // Add headers to the first page and each new page with the same scale factor - if (tableY === maxPageHeight) { - addHeadersToPage(page, scaleFactor); // Add headers only on the first page - } - - let xPosition = tableX; - row.forEach((value, index) => { - page.drawText(value.toString(), { - x: xPosition, - y: tableY, - font, - size: 10 * scaleFactor, // Scale the font size - color: rgb(0, 0, 0), - }); - xPosition += columnWidths[index] * scaleFactor; // Adjust X position based on scaling - }); - - tableY -= rowHeight; // Move down to the next row position }); - // Serialize the document to bytes const pdfBytes = await pdfDoc.save(); - - // Trigger a download of the PDF const blob = new Blob([pdfBytes], { type: 'application/pdf' }); const link = document.createElement('a'); link.href = URL.createObjectURL(blob); @@ -153,15 +98,110 @@ export const exportToPDF = async (data, fileName = "data") => { link.click(); }; + + + + +/** + * Export JSON data to PDF in a card-style format + * @param {Array} data - Array of objects to export + * @param {string} fileName - File name for the PDF (optional) + */ +export const exportToPDF1 = async (data, fileName = "data") => { + if (!data || data.length === 0) return; + + const pdfDoc = await PDFDocument.create(); + const font = await pdfDoc.embedFont(StandardFonts.Helvetica); + const boldFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold); + + const pageWidth = 600; + const pageHeight = 800; + const margin = 30; + const cardSpacing = 20; + const cardPadding = 10; + let page = pdfDoc.addPage([pageWidth, pageHeight]); + let y = pageHeight - margin; + + for (const item of data) { + const title = item.ContactName || ""; + const subtitle = `by ${item.CreatedBy || ""} on ${item.CreatedAt || ""}`; + const body = item.Note || ""; + + const cardHeight = 80 + (body.length / 60) * 14; // approximate height for body text + + if (y - cardHeight < margin) { + page = pdfDoc.addPage([pageWidth, pageHeight]); + y = pageHeight - margin; + } + + // Draw card border + page.drawRectangle({ + x: margin, + y: y - cardHeight, + width: pageWidth - 2 * margin, + height: cardHeight, + borderColor: rgb(0.7, 0.7, 0.7), + borderWidth: 1, + color: rgb(1, 1, 1), + }); + + // Draw title + page.drawText(title, { + x: margin + cardPadding, + y: y - 20, + font: boldFont, + size: 12, + color: rgb(0.1, 0.1, 0.1), + }); + + // Draw subtitle + page.drawText(subtitle, { + x: margin + cardPadding, + y: y - 35, + font, + size: 10, + color: rgb(0.4, 0.4, 0.4), + }); + + // Draw body text (wrap manually) + const lines = body.match(/(.|[\r\n]){1,80}/g) || []; + lines.forEach((line, i) => { + page.drawText(line, { + x: margin + cardPadding, + y: y - 50 - i * 12, + font, + size: 10, + color: rgb(0.2, 0.2, 0.2), + }); + }); + + y -= cardHeight + cardSpacing; + } + + const pdfBytes = await pdfDoc.save(); + const blob = new Blob([pdfBytes], { type: 'application/pdf' }); + const link = document.createElement('a'); + link.href = URL.createObjectURL(blob); + link.download = `${fileName}.pdf`; + link.click(); +}; + + + + /** * Print the HTML table by accepting the table element or a reference. * @param {HTMLElement} table - The table element (or ref) to print */ export const printTable = (table) => { if (table) { - const newWindow = window.open("", "", "width=600,height=600"); // Open a new window + const clone = table.cloneNode(true); + // Remove last column (Actions) from all rows + clone.querySelectorAll("tr").forEach((row) => { + row.removeChild(row.lastElementChild); + }); - // Inject styles for the table and body + const newWindow = window.open("", "", "width=600,height=600"); newWindow.document.write("Print Table"); const style = document.createElement('style'); style.innerHTML = ` @@ -171,16 +211,14 @@ export const printTable = (table) => { th { background-color: #f2f2f2; } `; newWindow.document.head.appendChild(style); - + newWindow.document.write(""); - newWindow.document.write(table.outerHTML); // Write the table HTML to the new window + newWindow.document.write(clone.outerHTML); newWindow.document.write(""); - - newWindow.document.close(); // Close the document stream - - // Wait for the document to load before triggering print + newWindow.document.close(); newWindow.onload = () => { - newWindow.print(); // Trigger the print dialog after the content is loaded + newWindow.print(); }; } }; +