@@ -7,8 +7,6 @@ import { FormModelComponent } from '../../form-model/form-model.component';
77import jsPDF from 'jspdf' ;
88import autoTable from 'jspdf-autotable' ;
99import * as XLSX from 'xlsx' ;
10- import { saveAs } from 'file-saver' ;
11-
1210
1311@Component ( {
1412 selector : 'app-download-component' ,
@@ -22,7 +20,6 @@ import { saveAs } from 'file-saver';
2220 styleUrl : './download-component.component.css'
2321} )
2422export class DownloadComponentComponent {
25-
2623 constructor (
2724 private expenseService : ExpenseService ,
2825 private fb : FormBuilder ,
@@ -32,9 +29,7 @@ export class DownloadComponentComponent {
3229 }
3330
3431 downloadDataForm ! : FormGroup ;
35-
3632 showDownloadDataModal = false ;
37-
3833 today : string ;
3934
4035 ngOnInit ( ) : void {
@@ -53,13 +48,9 @@ export class DownloadComponentComponent {
5348 this . confirmAndDownload ( ) ;
5449 }
5550
56- /**
57- * Confirm and download all expenses as a JSON file.
58- */
5951 confirmAndDownload ( ) : void {
6052 const { fromDate, toDate, fileType } = this . downloadDataForm . value ;
6153
62- // Guard: Ensure toDate >= fromDate
6354 if ( new Date ( toDate ) < new Date ( fromDate ) ) {
6455 this . toastService . show ( 'Invalid date range: To Date must be after From Date' , 'error' , 3000 ) ;
6556 return ;
@@ -91,17 +82,15 @@ export class DownloadComponentComponent {
9182 const jsonData = JSON . stringify ( filteredData , null , 2 ) ;
9283 this . triggerDownload ( jsonData , 'application/json' , 'json' ) ;
9384 this . closeDownloadDataModal ( ) ;
94- this . toastService . show ( 'Data downloaded successfully!' , 'success' , 3000 ) ;
85+ this . toastService . show ( 'JSON downloaded successfully!' , 'success' , 3000 ) ;
9586 return ;
96- }
97- else if ( fileType === 'PDF' ) {
98- this . exportToPDF ( filteredData ) ;
87+ } else if ( fileType === 'PDF' ) {
88+ this . exportToPDF ( filteredData , fromDate , toDate ) ;
9989 this . closeDownloadDataModal ( ) ;
10090 this . toastService . show ( 'PDF downloaded successfully!' , 'success' , 3000 ) ;
10191 return ;
102- }
103- else if ( fileType === 'EXCEL' ) {
104- this . exportToExcel ( filteredData ) ;
92+ } else if ( fileType === 'EXCEL' ) {
93+ this . exportToExcel ( filteredData , fromDate , toDate ) ;
10594 this . closeDownloadDataModal ( ) ;
10695 this . toastService . show ( 'Excel downloaded successfully!' , 'success' , 3000 ) ;
10796 return ;
@@ -120,7 +109,12 @@ export class DownloadComponentComponent {
120109 const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, '-' ) ;
121110 link . download = `expenses-${ timestamp } .${ extension } ` ;
122111
112+ // append -> safer across browsers
113+ document . body . appendChild ( link ) ;
123114 link . click ( ) ;
115+ document . body . removeChild ( link ) ;
116+
117+ // ensure revoke
124118 window . URL . revokeObjectURL ( url ) ;
125119 }
126120
@@ -135,19 +129,27 @@ export class DownloadComponentComponent {
135129 this . showDownloadDataModal = true ;
136130 }
137131
138-
139132 closeDownloadDataModal ( ) : void {
140133 this . showDownloadDataModal = false ;
141134 }
142135
136+ // PDF export
137+ private exportToPDF ( data : any [ ] , fromDate : string , toDate : string ) : void {
138+ const doc = new jsPDF ( 'p' , 'pt' , 'a4' ) ;
139+ const pageWidth = doc . internal . pageSize . getWidth ( ) ;
143140
144- private exportToPDF ( data : any [ ] ) : void {
145- const doc = new jsPDF ( ) ;
141+ // Title + metadata
142+ const title = 'Expenses Report' ;
143+ const sub = `From: ${ fromDate } — To: ${ toDate } ` ;
144+ doc . setFontSize ( 14 ) ;
145+ doc . text ( title , pageWidth / 2 , 40 , { align : 'center' } ) ;
146+ doc . setFontSize ( 10 ) ;
147+ doc . text ( sub , pageWidth / 2 , 58 , { align : 'center' } ) ;
146148
147- // Table headers
148- const headers = [ 'Index' , 'Amount' , 'Date' , 'Time' , 'Location' , 'Note' , 'Payment Mode' , 'Category ', 'Extra Spending' ] ;
149+ // Table headers (order matches rows below)
150+ const headers = [ 'Index' , 'Category' , ' Amount', 'Date' , 'Time' , 'Location' , 'Note' , 'Payment Mode' , 'Extra Spending' ] ;
149151
150- // Table rows with index
152+ // Rows
151153 const rows = data . map ( ( exp , i ) => [
152154 i + 1 ,
153155 exp . category_name ,
@@ -161,18 +163,41 @@ export class DownloadComponentComponent {
161163 ] ) ;
162164
163165 autoTable ( doc , {
166+ startY : 70 ,
164167 head : [ headers ] ,
165168 body : rows ,
166- styles : { fontSize : 8 } ,
167- headStyles : { fillColor : [ 52 , 152 , 219 ] } , // nice blue
169+ styles : { fontSize : 9 , overflow : 'linebreak' } ,
170+ headStyles : {
171+ fillColor : [ 143 , 145 , 234 ] ,
172+ halign : 'center' ,
173+ valign : 'middle' ,
174+ fontStyle : 'bold' ,
175+ textColor : 255
176+ } ,
177+ margin : { left : 30 , right : 30 } ,
178+ bodyStyles : {
179+ halign : 'center'
180+ } ,
181+ columnStyles : {
182+ 0 : { cellWidth : 33 } , // Index
183+ 1 : { cellWidth : 58 , halign : 'left' } , // Category
184+ 2 : { cellWidth : 46 } , // Amount
185+ 3 : { cellWidth : 55 } , // Date
186+ 4 : { cellWidth : 50 } , // Time
187+ 5 : { cellWidth : 85 , halign : 'left' } , // Location
188+ 6 : { cellWidth : 110 , halign : 'left' } , // Note (wraps)
189+ 7 : { cellWidth : 48 } , // Payment Mode
190+ 8 : { cellWidth : 50 } // Extra Spending
191+ }
168192 } ) ;
193+
194+ // file name
169195 const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, '-' ) ;
170196 doc . save ( `expenses-${ timestamp } .pdf` ) ;
171197 }
172198
173-
174- private exportToExcel ( data : any [ ] ) : void {
175- // Add index column
199+ // Excel export (no file-saver)
200+ private exportToExcel ( data : any [ ] , fromDate : string , toDate : string ) : void {
176201 const excelData = data . map ( ( exp , i ) => ( {
177202 'Index' : i + 1 ,
178203 Category : exp . category_name ,
@@ -185,14 +210,26 @@ export class DownloadComponentComponent {
185210 'Extra Spending' : exp . isExtraSpending ? 'Yes' : 'No'
186211 } ) ) ;
187212
188- const worksheet = XLSX . utils . json_to_sheet ( excelData ) ;
189213 const workbook = XLSX . utils . book_new ( ) ;
214+
215+ // ---- Build AoA (Array of Arrays)----
216+ const aoa : any [ ] [ ] = [ ] ;
217+
218+ if ( excelData . length > 0 ) {
219+ const headerRow = Object . keys ( excelData [ 0 ] ) ;
220+ aoa . push ( headerRow ) ;
221+
222+ for ( const row of excelData ) {
223+ aoa . push ( Object . values ( row ) ) ; // ✅ avoids TS index errors
224+ }
225+ }
226+
227+ const worksheet = XLSX . utils . aoa_to_sheet ( aoa ) ;
190228 XLSX . utils . book_append_sheet ( workbook , worksheet , 'Expenses' ) ;
191229
192- const excelBuffer : any = XLSX . write ( workbook , { bookType : 'xlsx' , type : 'array' } ) ;
193- const blob = new Blob ( [ excelBuffer ] , { type : 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' } ) ;
194230 const timestamp = new Date ( ) . toISOString ( ) . replace ( / [: .] / g, '-' ) ;
195- saveAs ( blob , `expenses-${ timestamp } .xlsx` ) ;
231+ const filename = `expenses-${ timestamp } .xlsx` ;
232+ XLSX . writeFile ( workbook , filename ) ;
196233 }
197234
198235}
0 commit comments