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' }); + } }