Skip to content

Commit 79b1cef

Browse files
committed
update
1 parent 9652679 commit 79b1cef

5 files changed

Lines changed: 80 additions & 35 deletions

File tree

src/app/component/settings-components/download-component/download-component.component.ts

Lines changed: 68 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ import { FormModelComponent } from '../../form-model/form-model.component';
77
import jsPDF from 'jspdf';
88
import autoTable from 'jspdf-autotable';
99
import * 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
})
2422
export 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
}

src/app/features/add-expense/add-expense.component.html

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
<!-- Amount -->
88
<div class="relative w-full md:w-1/2">
99
<label class="block mb-1 font-medium">Amount</label>
10-
<input #amountInput type="number" min="0" formControlName="amount" class="w-full p-2 rounded border"
10+
<input #amountInput type="number" min="0" max="100000000" formControlName="amount"
11+
class="w-full p-2 rounded border"
1112
style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"
1213
step="0.01" />
1314

@@ -16,6 +17,11 @@
1617
*ngIf="expenseForm.get('amount')?.touched && expenseForm.get('amount')?.errors?.['min']">
1718
Amount cannot be negative
1819
</div>
20+
21+
<div class="error-message text-[var(--color-error)]"
22+
*ngIf="expenseForm.get('amount')?.touched && expenseForm.get('amount')?.errors?.['max']">
23+
Amount cannot exceed 100,000,000
24+
</div>
1925
<div class="error-message text-[var(--color-error)]"
2026
*ngIf="expenseForm.get('amount')?.touched && expenseForm.get('amount')?.errors?.['required']">
2127
Amount is required

src/app/features/add-expense/add-expense.component.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export class AddExpenseComponent implements OnInit {
7777
*/
7878
createForm(): FormGroup {
7979
return this.fb.group({
80-
amount: ['', [Validators.required, Validators.min(0)]],
80+
amount: ['', [Validators.required, Validators.min(0), Validators.max(100000000)]],
8181
category_id: ['', Validators.required],
8282
date: ['', Validators.required],
8383
time: ['', Validators.required],

src/app/features/list-expenses/list-expenses.component.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ export class ListExpensesComponent implements OnInit {
295295
this.expenseService.update(expense_id, newData);
296296
this.toastService.show('Expense updated successfully', 'success');
297297
this.isEditOpen = false;
298+
this.searchData(this.searchQuery);
298299
}
299300

300301
/**
@@ -306,6 +307,7 @@ export class ListExpensesComponent implements OnInit {
306307
this.expenseService.delete(id);
307308
this.toastService.show("Expense deleted successfully", 'success');
308309
this.closeModal();
310+
this.searchData(this.searchQuery);
309311
}
310312
}
311313

src/app/features/settings/settings.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ <h2 class="text-xl font-semibold text-[var(--color-accent)] mb-3">🧩 Categorie
6464
<app-form-model *ngIf="showAddCategoryModal" [label]="'Add Category'" (close)="closeCategoryModal()">
6565
<form [formGroup]="addCategoryForm" (ngSubmit)="addCategory()">
6666
<label class="block mb-1 font-medium">Name</label>
67-
<input formControlName="name" type="text" autocomplete="off" placeholder="Category Name"
67+
<input maxlength="20" formControlName="name" type="text" autocomplete="off" placeholder="Category Name"
6868
class="w-full p-2 rounded border"
6969
style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"
7070
step="0.01" />
@@ -77,7 +77,7 @@ <h2 class="text-xl font-semibold text-[var(--color-accent)] mb-3">🧩 Categorie
7777
Category name already exists
7878
</div>
7979
<label class="block mb-1 font-medium">Icon</label>
80-
<input formControlName="icon" type="text" placeholder="Icon (e.g., 🍕)" class="w-full p-2 rounded border"
80+
<input maxlength="3" formControlName="icon" type="text" placeholder="Icon (e.g., 🍕)" class="w-full p-2 rounded border"
8181
style="background-color: var(--input-bg); color: var(--input-text); border-color: var(--input-border);"
8282
step="0.01" />
8383

0 commit comments

Comments
 (0)