diff --git a/APP/src/app/features/expense/pages/expenses-page/expenses-page.component.html b/APP/src/app/features/expense/pages/expenses-page/expenses-page.component.html index bd2b652..5bd6884 100644 --- a/APP/src/app/features/expense/pages/expenses-page/expenses-page.component.html +++ b/APP/src/app/features/expense/pages/expenses-page/expenses-page.component.html @@ -30,20 +30,24 @@ - + + + + +
+
+
+
{{ dataItem.expenseDate }}
+
{{ dataItem.amount | currency }}
+
+ +
+
{{ dataItem.ministryName }}
+
+ {{ dataItem.primaryCategoryName }} +{{ dataItem.lineCount - 1 }} +
+
+ +
{{ dataItem.description }}
+ +
+ Payee / 收款人 + + {{ dataItem.vendorName }} + + + {{ dataItem.memberNickName }} + {{ dataItem.memberName }} + + + + +
+ +
+ Check # / 支票號 + {{ dataItem.checkNumber }} +
+ +
+ {{ dataItem.status }} +
✓ Approved by {{ dataItem.reviewedByName }}
{{ dataItem.reviewedAt | date:'yyyy-MM-dd HH:mm' }}
+
+ ✗ Rejected by {{ dataItem.reviewedByName }}
{{ dataItem.reviewedAt | date:'yyyy-MM-dd HH:mm' }} +
{{ dataItem.reviewNotes }}
+
+
+ +
+ + + + +
+
+ +
No expenses / 無支出資料
+ +
+ + {{ page }} / {{ totalPages }} + +
+
; @@ -79,6 +85,52 @@ export class ExpensesPageComponent implements OnInit { this.load(); } + // ── Mobile pager (the Kendo grid pager is desktop-only) ─────────────────────── + get totalPages(): number { return Math.max(1, Math.ceil(this.total / this.pageSize)); } + + prevPage(): void { + if (this.page <= 1) return; + this.page--; + this.load(); + } + + nextPage(): void { + if (this.page >= this.totalPages) return; + this.page++; + this.load(); + } + + // ── Row interaction: right-click opens the per-row action menu ──────────────── + onCellClick(event: CellClickEvent): void { + if (event.type !== 'contextmenu') return; + event.originalEvent.preventDefault(); + const items = this.buildMenuItems(event.dataItem); + if (items.length === 0) return; + this.contextRow = event.dataItem; + this.rowMenuItems = items; + this.rowMenu.show({ left: event.originalEvent.pageX, top: event.originalEvent.pageY }); + } + + onRowMenuSelect(event: ContextMenuSelectEvent): void { + const row = this.contextRow; + if (!row) return; + switch (event.item.text) { + case 'Edit': this.openEdit(row); break; + case 'Review': this.openReview(row); break; + case 'Pay': this.openPay(row); break; + case 'Receipt': this.openReceipt(row.id); break; + } + } + + private buildMenuItems(row: ExpenseListItemDto): { text: string }[] { + const items: { text: string }[] = []; + if (this.canEdit(row)) items.push({ text: 'Edit' }); + if (this.canApproveOrReject(row)) items.push({ text: 'Review' }); + if (this.canPay(row)) items.push({ text: 'Pay' }); + if (row.hasReceipt) items.push({ text: 'Receipt' }); + return items; + } + onVendorSave(result: ExpenseFormResult): void { this.api.create(result.request).subscribe(() => { this.vendorDialogOpen = false; this.load(); }); }