feat(expense): add member self-service My Reimbursements page
Standalone Angular component (Kendo Grid + ExpenseFormDialog) that lets any logged-in user list, create, submit, and delete their own draft reimbursements, with optional receipt upload. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
+52
@@ -0,0 +1,52 @@
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { GridModule } from '@progress/kendo-angular-grid';
|
||||
import { ButtonsModule } from '@progress/kendo-angular-buttons';
|
||||
import { ExpenseApiService } from '../../services/expense-api.service';
|
||||
import { ExpenseFormDialogComponent, ExpenseFormResult } from '../../components/expense-form-dialog/expense-form-dialog.component';
|
||||
import { ExpenseListItemDto } from '../../models/expense.model';
|
||||
import { switchMap, of } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'app-my-reimbursements-page',
|
||||
standalone: true,
|
||||
imports: [CommonModule, GridModule, ButtonsModule, ExpenseFormDialogComponent],
|
||||
templateUrl: './my-reimbursements-page.component.html',
|
||||
styleUrls: ['./my-reimbursements-page.component.scss'],
|
||||
})
|
||||
export class MyReimbursementsPageComponent implements OnInit {
|
||||
rows: ExpenseListItemDto[] = [];
|
||||
loading = false;
|
||||
dialogOpen = false;
|
||||
|
||||
constructor(private api: ExpenseApiService) {}
|
||||
|
||||
ngOnInit(): void { this.load(); }
|
||||
|
||||
load(): void {
|
||||
this.loading = true;
|
||||
this.api.getMine().subscribe({
|
||||
next: r => { this.rows = r.items; this.loading = false; },
|
||||
error: () => { this.loading = false; },
|
||||
});
|
||||
}
|
||||
|
||||
openNew(): void { this.dialogOpen = true; }
|
||||
|
||||
onSave(result: ExpenseFormResult): void {
|
||||
this.api.create(result.request).pipe(
|
||||
switchMap(created => result.receipt
|
||||
? this.api.uploadReceipt(created.id, result.receipt).pipe(switchMap(() => of(created)))
|
||||
: of(created)),
|
||||
).subscribe(() => { this.dialogOpen = false; this.load(); });
|
||||
}
|
||||
|
||||
submit(row: ExpenseListItemDto): void { this.api.submit(row.id).subscribe(() => this.load()); }
|
||||
remove(row: ExpenseListItemDto): void { this.api.delete(row.id).subscribe(() => this.load()); }
|
||||
|
||||
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<string, string>)[status] ?? '';
|
||||
}
|
||||
receiptUrl(id: number): string { return this.api.receiptUrl(id); }
|
||||
}
|
||||
Reference in New Issue
Block a user