diff --git a/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.html b/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.html
index 5231ce1..6e44d17 100644
--- a/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.html
+++ b/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.html
@@ -23,10 +23,10 @@
-
+
- Receipt
+
diff --git a/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.ts b/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.ts
index 071ea41..3338edb 100644
--- a/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.ts
+++ b/APP/src/app/features/expense/pages/my-reimbursements-page/my-reimbursements-page.component.ts
@@ -42,11 +42,21 @@ export class MyReimbursementsPageComponent implements OnInit {
}
submit(row: ExpenseListItemDto): void { this.api.submit(row.id).subscribe(() => this.load()); }
- remove(row: ExpenseListItemDto): void { this.api.delete(row.id).subscribe(() => this.load()); }
+ remove(row: ExpenseListItemDto): void {
+ if (!confirm('Delete this reimbursement?')) return;
+ this.api.delete(row.id).subscribe(() => this.load());
+ }
+
+ openReceipt(id: number): void {
+ this.api.downloadReceipt(id).subscribe(blob => {
+ const url = URL.createObjectURL(blob);
+ window.open(url, '_blank');
+ setTimeout(() => URL.revokeObjectURL(url), 60_000);
+ });
+ }
canEdit(row: ExpenseListItemDto): boolean { return row.status === 'Draft'; }
statusClass(status: string): string {
return ({ Draft: 'badge-draft', PendingApproval: 'badge-pending', Approved: 'badge-approved', Paid: 'badge-paid', Rejected: 'badge-rejected' } as Record)[status] ?? '';
}
- receiptUrl(id: number): string { return this.api.receiptUrl(id); }
}
diff --git a/APP/src/app/features/expense/services/expense-api.service.ts b/APP/src/app/features/expense/services/expense-api.service.ts
index de4c40a..07f2c5a 100644
--- a/APP/src/app/features/expense/services/expense-api.service.ts
+++ b/APP/src/app/features/expense/services/expense-api.service.ts
@@ -41,5 +41,12 @@ export class ExpenseApiService {
const form = new FormData(); form.append('file', file);
return this.http.post(`${this.endpoint}/${id}/receipt`, form);
}
- receiptUrl(id: number): string { return `${this.endpoint}/${id}/receipt`; }
+ /**
+ * Fetches the receipt as a Blob via HttpClient so the auth interceptor attaches
+ * the JWT. A plain / window.open on the API URL would be an unauthenticated
+ * browser navigation and the API's [Authorize] would reject it with 401.
+ */
+ downloadReceipt(id: number): Observable {
+ return this.http.get(`${this.endpoint}/${id}/receipt`, { responseType: 'blob' });
+ }
}