@pagamio/frontend-commons-lib 0.8.208 → 0.8.209

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ import { Checkbox, Label, TextInput } from 'flowbite-react';
4
4
  import { HiMail } from 'react-icons/hi';
5
5
  import { useEffect, useRef, useState } from 'react';
6
6
  import { Modal, Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '../../components';
7
- import { exportToCsv, exportToPdf, exportToXlsx, generateExportAsBase64, sendExportEmail } from './exportUtils';
7
+ import { exportToCsv, exportToPdf, exportToXlsx, generateExportAsFile, sendExportEmail } from './exportUtils';
8
8
  const ExportDropdown = ({ data, columns, containerClassName, buttonClassName, extraOptions = [], pdfOptions, xlsxOptions, csvOptions, exportAll = false, fetchData, enableEmailExport = false, emailExportApiUrl, onEmailExportSuccess, onEmailExportError, }) => {
9
9
  const [exportType, setExportType] = useState(null);
10
10
  const [excludedColumns, setExcludedColumns] = useState(['action']);
@@ -78,27 +78,18 @@ const ExportDropdown = ({ data, columns, containerClassName, buttonClassName, ex
78
78
  }
79
79
  };
80
80
  const handleEmailModalSubmit = async () => {
81
- console.log('handleEmailModalSubmit called');
82
- console.log('emailAddress:', emailAddress);
83
- console.log('pendingExportData:', pendingExportData);
84
- console.log('pendingEmailFormat:', pendingEmailFormat);
85
81
  if (!emailAddress.trim() || !validateEmail(emailAddress)) {
86
82
  setEmailError(emailAddress.trim() ? 'Please enter a valid email address' : 'Email address is required');
87
83
  return;
88
84
  }
89
85
  if (!pendingExportData || !pendingEmailFormat) {
90
- console.log('Missing pendingExportData or pendingEmailFormat');
91
86
  return;
92
87
  }
93
88
  setEmailError('');
94
89
  setIsExporting(true);
95
90
  try {
96
- console.log('Generating export as base64...');
97
- const base64File = await generateExportAsBase64(pendingExportData.data, pendingExportData.columns, pendingEmailFormat, { pdfOptions, xlsxOptions, csvOptions });
98
- console.log('Base64 file generated, length:', base64File.length);
99
- console.log('Sending email to:', emailAddress);
100
- const result = await sendExportEmail(emailAddress, base64File, emailExportApiUrl);
101
- console.log('Email sent successfully:', result);
91
+ const exportFile = await generateExportAsFile(pendingExportData.data, pendingExportData.columns, pendingEmailFormat, { pdfOptions, xlsxOptions, csvOptions });
92
+ const result = await sendExportEmail(emailAddress, exportFile, emailExportApiUrl);
102
93
  onEmailExportSuccess?.(result || 'Export sent successfully to your email');
103
94
  setEmailAddress('');
104
95
  setShowEmailModal(false);
@@ -40,18 +40,20 @@ export declare const exportToCsv: <T extends Record<string, any>>(data: T[], col
40
40
  export declare const exportToXlsx: <T extends Record<string, any>>(data: T[], columns: MRT_ColumnDef<T>[], options?: XlsxExportOptions) => void;
41
41
  export declare const exportToPdf: <T extends Record<string, any>>(data: T[], columns: MRT_ColumnDef<T>[], options?: PdfExportOptions) => Promise<void>;
42
42
  /**
43
- * Sends an export file to the user via email
43
+ * Sends an export file to the user via email using multipart/form-data
44
+ * Automatically includes Authorization header from cookies if available
44
45
  * @param email - The recipient email address
45
- * @param file - Base64 encoded file string
46
+ * @param file - The File object to send
46
47
  * @param baseUrl - The API base URL (optional, defaults to /api/v1)
48
+ * @param additionalHeaders - Optional additional headers to merge with auth headers
47
49
  */
48
- export declare const sendExportEmail: (email: string, file: string, baseUrl?: string) => Promise<string>;
50
+ export declare const sendExportEmail: (email: string, file: File, baseUrl?: string, additionalHeaders?: HeadersInit) => Promise<string>;
49
51
  /**
50
- * Generates export file as base64 string for email sending
52
+ * Generates export as a File object for email sending
51
53
  */
52
- export declare const generateExportAsBase64: <T extends Record<string, any>>(data: T[], columns: MRT_ColumnDef<T>[], format: "csv" | "xlsx" | "pdf", options?: {
54
+ export declare const generateExportAsFile: <T extends Record<string, any>>(data: T[], columns: MRT_ColumnDef<T>[], format: "csv" | "xlsx" | "pdf", options?: {
53
55
  pdfOptions?: PdfExportOptions;
54
56
  xlsxOptions?: XlsxExportOptions;
55
57
  csvOptions?: CsvExportOptions;
56
- }) => Promise<string>;
58
+ }) => Promise<File>;
57
59
  export {};
@@ -781,28 +781,55 @@ export const exportToPdf = async (data, columns, options = {}) => {
781
781
  doc.save(filename);
782
782
  };
783
783
  /**
784
- * Sends an export file to the user via email
784
+ * Helper to get authentication headers from browser cookies
785
+ * This reads the accessToken cookie that's set by TokenManager
786
+ */
787
+ const getAuthHeaders = () => {
788
+ if (typeof document === 'undefined')
789
+ return {};
790
+ // Read accessToken from cookies (matches TokenManager cookie name)
791
+ const cookies = document.cookie.split(';');
792
+ const accessTokenCookie = cookies.find((cookie) => cookie.trim().startsWith('accessToken='));
793
+ if (!accessTokenCookie)
794
+ return {};
795
+ const token = accessTokenCookie.split('=')[1];
796
+ return {
797
+ Authorization: `Bearer ${token}`,
798
+ };
799
+ };
800
+ /**
801
+ * Sends an export file to the user via email using multipart/form-data
802
+ * Automatically includes Authorization header from cookies if available
785
803
  * @param email - The recipient email address
786
- * @param file - Base64 encoded file string
804
+ * @param file - The File object to send
787
805
  * @param baseUrl - The API base URL (optional, defaults to /api/v1)
806
+ * @param additionalHeaders - Optional additional headers to merge with auth headers
788
807
  */
789
- export const sendExportEmail = async (email, file, baseUrl = '/api/v1') => {
808
+ export const sendExportEmail = async (email, file, baseUrl = '/api/v1', additionalHeaders) => {
809
+ const formData = new FormData();
810
+ formData.append('file', file);
811
+ // Build headers: start with auth, then merge additional headers if provided
812
+ const authHeaders = getAuthHeaders();
813
+ const headers = additionalHeaders ? { ...authHeaders, ...additionalHeaders } : authHeaders;
814
+ // Note: Do NOT set Content-Type here - browser will set it automatically with boundary for multipart/form-data
790
815
  const response = await fetch(`${baseUrl}/exports/send-email?email=${encodeURIComponent(email)}`, {
791
816
  method: 'POST',
792
- headers: {
793
- 'Content-Type': 'application/json',
794
- },
795
- body: JSON.stringify({ file }),
817
+ headers,
818
+ body: formData,
796
819
  });
797
820
  if (!response.ok) {
798
- throw new Error(`Failed to send email: ${response.statusText}`);
821
+ const errorText = await response.text().catch(() => response.statusText);
822
+ throw new Error(`Failed to send email: ${errorText}`);
799
823
  }
800
824
  return response.text();
801
825
  };
802
826
  /**
803
- * Generates export file as base64 string for email sending
827
+ * Generates export as a File object for email sending
804
828
  */
805
- export const generateExportAsBase64 = async (data, columns, format, options) => {
829
+ export const generateExportAsFile = async (data, columns, format, options) => {
830
+ const timestamp = new Date().toISOString().split('T')[0];
831
+ const title = options?.pdfOptions?.title || options?.xlsxOptions?.title || 'export';
832
+ const baseFilename = `${title.toLowerCase().replaceAll(/\s+/g, '_')}_${timestamp}`;
806
833
  if (format === 'csv') {
807
834
  const visibleColumns = columns.filter((col) => col.accessorKey !== 'action');
808
835
  const headers = visibleColumns.map((col) => String(col.header ?? col.accessorKey));
@@ -812,7 +839,8 @@ export const generateExportAsBase64 = async (data, columns, format, options) =>
812
839
  return value !== null && value !== undefined ? String(value) : '';
813
840
  }));
814
841
  const csvContent = Papa.unparse({ fields: headers, data: rows });
815
- return btoa(csvContent);
842
+ const blob = new Blob([csvContent], { type: 'text/csv;charset=utf-8;' });
843
+ return new File([blob], `${baseFilename}.csv`, { type: 'text/csv' });
816
844
  }
817
845
  if (format === 'xlsx') {
818
846
  const visibleColumns = columns.filter((col) => col.accessorKey !== 'action');
@@ -824,8 +852,13 @@ export const generateExportAsBase64 = async (data, columns, format, options) =>
824
852
  const worksheet = XLSX.utils.aoa_to_sheet([headers, ...rows]);
825
853
  const workbook = XLSX.utils.book_new();
826
854
  XLSX.utils.book_append_sheet(workbook, worksheet, options?.xlsxOptions?.sheetName || 'Data');
827
- const xlsxData = XLSX.write(workbook, { bookType: 'xlsx', type: 'base64' });
828
- return xlsxData;
855
+ const xlsxBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
856
+ const blob = new Blob([xlsxBuffer], {
857
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
858
+ });
859
+ return new File([blob], `${baseFilename}.xlsx`, {
860
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
861
+ });
829
862
  }
830
863
  if (format === 'pdf') {
831
864
  const doc = new jsPDF();
@@ -841,7 +874,8 @@ export const generateExportAsBase64 = async (data, columns, format, options) =>
841
874
  body: rows,
842
875
  startY: 20,
843
876
  });
844
- return doc.output('datauristring').split(',')[1];
877
+ const pdfBlob = doc.output('blob');
878
+ return new File([pdfBlob], `${baseFilename}.pdf`, { type: 'application/pdf' });
845
879
  }
846
880
  throw new Error(`Unsupported format: ${format}`);
847
881
  };
@@ -1,5 +1,5 @@
1
1
  export { default } from './data-table/index';
2
2
  export * from './data-table/types';
3
3
  export * from './data-table/pdfExportUtils';
4
- export { sendExportEmail, generateExportAsBase64 } from './data-table/exportUtils';
4
+ export { sendExportEmail, generateExportAsFile } from './data-table/exportUtils';
5
5
  export * from './utils';
@@ -1,5 +1,5 @@
1
1
  export { default } from './data-table/index';
2
2
  export * from './data-table/types';
3
3
  export * from './data-table/pdfExportUtils';
4
- export { sendExportEmail, generateExportAsBase64 } from './data-table/exportUtils';
4
+ export { sendExportEmail, generateExportAsFile } from './data-table/exportUtils';
5
5
  export * from './utils';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pagamio/frontend-commons-lib",
3
3
  "description": "Pagamio library for Frontend reusable components like the form engine and table container",
4
- "version": "0.8.208",
4
+ "version": "0.8.209",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "provenance": false