feat(expense): add finance expenses overview + review page
This commit is contained in:
@@ -0,0 +1,149 @@
|
||||
<div class="page">
|
||||
<header class="page-header">
|
||||
<h2>Expenses</h2>
|
||||
</header>
|
||||
|
||||
<!-- Filter toolbar -->
|
||||
<div class="flex flex-wrap gap-3 items-end mb-4">
|
||||
<label class="flex flex-col gap-1">
|
||||
Search
|
||||
<kendo-textbox placeholder="Search description / vendor / member"
|
||||
[(ngModel)]="filter.search"
|
||||
(keydown.enter)="applyFilter()">
|
||||
</kendo-textbox>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-1">
|
||||
Ministry
|
||||
<kendo-dropdownlist
|
||||
[data]="ministries"
|
||||
textField="name_en"
|
||||
valueField="id"
|
||||
[valuePrimitive]="true"
|
||||
[(ngModel)]="filter.ministryId"
|
||||
[defaultItem]="{ id: null, name_en: 'All Ministries' }">
|
||||
</kendo-dropdownlist>
|
||||
</label>
|
||||
|
||||
<label class="flex flex-col gap-1">
|
||||
Status
|
||||
<kendo-dropdownlist
|
||||
[data]="statuses"
|
||||
[(ngModel)]="filter.status"
|
||||
[defaultItem]="null">
|
||||
</kendo-dropdownlist>
|
||||
</label>
|
||||
|
||||
<button kendoButton (click)="applyFilter()">Apply</button>
|
||||
|
||||
<div class="ml-auto flex gap-2">
|
||||
<button kendoButton themeColor="primary" (click)="vendorDialogOpen = true">+ Vendor Payment</button>
|
||||
<button kendoButton themeColor="primary" (click)="reimbDialogOpen = true">+ Reimbursement</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Main grid -->
|
||||
<kendo-grid
|
||||
[data]="rows"
|
||||
[loading]="loading"
|
||||
[pageable]="true"
|
||||
[skip]="skip"
|
||||
[pageSize]="pageSize"
|
||||
[total]="total"
|
||||
(pageChange)="onPageChange($event)">
|
||||
|
||||
<kendo-grid-column field="expenseDate" title="Date" [width]="110"></kendo-grid-column>
|
||||
|
||||
<kendo-grid-column field="type" title="Type" [width]="140"></kendo-grid-column>
|
||||
|
||||
<kendo-grid-column field="description" title="Description"></kendo-grid-column>
|
||||
|
||||
<kendo-grid-column field="ministryName" title="Ministry" [width]="140"></kendo-grid-column>
|
||||
|
||||
<kendo-grid-column title="Category" [width]="180">
|
||||
<ng-template kendoGridCellTemplate let-dataItem>
|
||||
{{ dataItem.categoryGroupName }} / {{ dataItem.subCategoryName }}
|
||||
</ng-template>
|
||||
</kendo-grid-column>
|
||||
|
||||
<kendo-grid-column title="Payee" [width]="150">
|
||||
<ng-template kendoGridCellTemplate let-dataItem>
|
||||
{{ dataItem.vendorName || dataItem.memberName || '—' }}
|
||||
</ng-template>
|
||||
</kendo-grid-column>
|
||||
|
||||
<kendo-grid-column field="amount" title="Amount" [width]="110" format="c2"></kendo-grid-column>
|
||||
|
||||
<kendo-grid-column title="Status" [width]="140">
|
||||
<ng-template kendoGridCellTemplate let-dataItem>
|
||||
<span [class]="statusClass(dataItem.status)">{{ dataItem.status }}</span>
|
||||
</ng-template>
|
||||
</kendo-grid-column>
|
||||
|
||||
<kendo-grid-column title="Actions" [width]="240">
|
||||
<ng-template kendoGridCellTemplate let-dataItem>
|
||||
<ng-container *ngIf="canApproveOrReject(dataItem)">
|
||||
<button kendoButton themeColor="success" fillMode="flat" (click)="approve(dataItem)">Approve</button>
|
||||
<button kendoButton themeColor="error" fillMode="flat" (click)="openReject(dataItem)">Reject</button>
|
||||
</ng-container>
|
||||
<button *ngIf="canPay(dataItem)" kendoButton themeColor="primary" fillMode="flat"
|
||||
(click)="openPay(dataItem)">Pay</button>
|
||||
<button *ngIf="dataItem.hasReceipt" kendoButton fillMode="flat"
|
||||
(click)="openReceipt(dataItem.id)" class="receipt-link">Receipt</button>
|
||||
</ng-template>
|
||||
</kendo-grid-column>
|
||||
|
||||
</kendo-grid>
|
||||
|
||||
<!-- Vendor Payment dialog -->
|
||||
<app-expense-form-dialog
|
||||
*ngIf="vendorDialogOpen"
|
||||
mode="vendor"
|
||||
title="Vendor Payment"
|
||||
(save)="onVendorSave($event)"
|
||||
(cancel)="vendorDialogOpen = false">
|
||||
</app-expense-form-dialog>
|
||||
|
||||
<!-- Reimbursement (on behalf) dialog -->
|
||||
<app-expense-form-dialog
|
||||
*ngIf="reimbDialogOpen"
|
||||
mode="reimbursement"
|
||||
[allowMemberPick]="true"
|
||||
title="Reimbursement (on behalf)"
|
||||
(save)="onReimbSave($event)"
|
||||
(cancel)="reimbDialogOpen = false">
|
||||
</app-expense-form-dialog>
|
||||
|
||||
<!-- Mark Paid dialog -->
|
||||
<kendo-dialog *ngIf="payRow" title="Mark Paid" [width]="400" (close)="payRow = null">
|
||||
<div class="grid grid-cols-1 gap-3 p-2">
|
||||
<label class="flex flex-col gap-1">
|
||||
Check #
|
||||
<kendo-textbox [(ngModel)]="payCheckNumber" placeholder="Optional"></kendo-textbox>
|
||||
</label>
|
||||
<label class="flex flex-col gap-1">
|
||||
Payment Date
|
||||
<kendo-datepicker [(ngModel)]="payDate"></kendo-datepicker>
|
||||
</label>
|
||||
</div>
|
||||
<kendo-dialog-actions>
|
||||
<button kendoButton (click)="payRow = null">Cancel</button>
|
||||
<button kendoButton themeColor="primary" (click)="confirmPay()">Confirm</button>
|
||||
</kendo-dialog-actions>
|
||||
</kendo-dialog>
|
||||
|
||||
<!-- Reject dialog -->
|
||||
<kendo-dialog *ngIf="rejectRow" title="Reject Expense" [width]="400" (close)="rejectRow = null">
|
||||
<div class="grid grid-cols-1 gap-3 p-2">
|
||||
<label class="flex flex-col gap-1">
|
||||
Review Notes
|
||||
<kendo-textbox [(ngModel)]="rejectNotes" placeholder="Optional notes for submitter"></kendo-textbox>
|
||||
</label>
|
||||
</div>
|
||||
<kendo-dialog-actions>
|
||||
<button kendoButton (click)="rejectRow = null">Cancel</button>
|
||||
<button kendoButton themeColor="error" (click)="confirmReject()">Reject</button>
|
||||
</kendo-dialog-actions>
|
||||
</kendo-dialog>
|
||||
|
||||
</div>
|
||||
Reference in New Issue
Block a user