update mobile view.

This commit is contained in:
Chris Chen
2026-06-23 14:15:20 -07:00
parent 9d7c224ad2
commit 68649223d9
11 changed files with 304 additions and 52 deletions
@@ -14,47 +14,104 @@
<button kendoButton (click)="applyFilter()">Apply</button>
</div>
<kendo-grid [data]="{ data: rows, total: total }" [loading]="loading" [pageable]="true" [skip]="skip"
[pageSize]="pageSize" (pageChange)="onPageChange($event)">
<!-- Desktop / tablet: full data grid -->
<div class="hidden md:block">
<kendo-grid [data]="{ data: rows, total: total }" [loading]="loading" [pageable]="true" [skip]="skip"
[pageSize]="pageSize" (pageChange)="onPageChange($event)">
<kendo-grid-column field="checkNumber" title="Check #" [width]="100"></kendo-grid-column>
<kendo-grid-column field="checkDate" title="Date" [width]="110"></kendo-grid-column>
<kendo-grid-column field="payeeName" title="Payee"></kendo-grid-column>
<kendo-grid-column field="amount" title="Amount" [width]="120" format="c2"></kendo-grid-column>
<kendo-grid-column title="Lines" [width]="80">
<ng-template kendoGridCellTemplate let-dataItem>{{ dataItem.lineCount }}</ng-template>
</kendo-grid-column>
<kendo-grid-column title="Status" [width]="110">
<ng-template kendoGridCellTemplate let-dataItem>
<span [class]="statusClass(dataItem.status)">{{ dataItem.status }}</span>
</ng-template>
</kendo-grid-column>
<kendo-grid-column title="Receipt / 簽收" [width]="180">
<ng-template kendoGridCellTemplate let-dataItem>
<ng-container *ngIf="dataItem.signed; else notSigned">
<span class="badge-paid">Signed</span>
<div class="text-xs" style="color:#6b7280;">
{{ dataItem.receiptSignedName }} · {{ dataItem.receiptSignedAt | date:'short' }}
</div>
</ng-container>
<ng-template #notSigned><span style="color:#9ca3af;"></span></ng-template>
</ng-template>
</kendo-grid-column>
<kendo-grid-column field="checkNumber" title="Check #" [width]="100"></kendo-grid-column>
<kendo-grid-column field="checkDate" title="Date" [width]="110"></kendo-grid-column>
<kendo-grid-column field="payeeName" title="Payee"></kendo-grid-column>
<kendo-grid-column field="amount" title="Amount" [width]="120" format="c2"></kendo-grid-column>
<kendo-grid-column title="Lines" [width]="80">
<ng-template kendoGridCellTemplate let-dataItem>{{ dataItem.lineCount }}</ng-template>
</kendo-grid-column>
<kendo-grid-column title="Status" [width]="110">
<ng-template kendoGridCellTemplate let-dataItem>
<span [class]="statusClass(dataItem.status)">{{ dataItem.status }}</span>
</ng-template>
</kendo-grid-column>
<kendo-grid-column title="Receipt / 簽收" [width]="180">
<ng-template kendoGridCellTemplate let-dataItem>
<ng-container *ngIf="dataItem.signed; else notSigned">
<span class="badge-paid">Signed</span>
<div class="text-xs" style="color:#6b7280;">
{{ dataItem.receiptSignedName }} · {{ dataItem.receiptSignedAt | date:'short' }}
</div>
</ng-container>
<ng-template #notSigned><span style="color:#9ca3af;"></span></ng-template>
</ng-template>
</kendo-grid-column>
<kendo-grid-column title="Actions" [width]="600">
<ng-template kendoGridCellTemplate let-dataItem>
<button kendoButton fillMode="flat" (click)="view(dataItem)">View</button>
<button kendoButton fillMode="flat" themeColor="primary" (click)="print(dataItem)">Print</button>
<button *ngIf="canSign(dataItem)" kendoButton fillMode="flat" themeColor="success"
(click)="openSign(dataItem)">簽收</button>
<!-- <button *ngIf="dataItem.signed" kendoButton fillMode="flat" (click)="viewSignature(dataItem)">Signature</button> -->
<button *ngIf="dataItem.signed" kendoButton fillMode="flat" themeColor="primary"
(click)="printReceipt(dataItem)">收據</button>
<button *ngIf="canVoid(dataItem)" kendoButton fillMode="flat" themeColor="error"
(click)="openVoid(dataItem)">Void</button>
</ng-template>
</kendo-grid-column>
</kendo-grid>
<kendo-grid-column title="Actions" [width]="600">
<ng-template kendoGridCellTemplate let-dataItem>
<button kendoButton fillMode="flat" (click)="view(dataItem)">View</button>
<button kendoButton fillMode="flat" themeColor="primary" (click)="print(dataItem)">Print</button>
<button *ngIf="canSign(dataItem)" kendoButton fillMode="flat" themeColor="success"
(click)="openSign(dataItem)">簽收</button>
<!-- <button *ngIf="dataItem.signed" kendoButton fillMode="flat" (click)="viewSignature(dataItem)">Signature</button> -->
<button *ngIf="dataItem.signed" kendoButton fillMode="flat" themeColor="primary"
(click)="printReceipt(dataItem)">收據</button>
<button *ngIf="canVoid(dataItem)" kendoButton fillMode="flat" themeColor="error"
(click)="openVoid(dataItem)">Void</button>
</ng-template>
</kendo-grid-column>
</kendo-grid>
</div>
<!-- Mobile: tappable card list -->
<div class="md:hidden chk-cards">
<div *ngIf="loading" class="chk-empty">Loading…</div>
<div *ngIf="!loading && rows.length === 0" class="chk-empty">No checks found.</div>
<div class="chk-card" *ngFor="let row of rows">
<div class="chk-card__top">
<span class="chk-card__number">Check #{{ row.checkNumber }}</span>
<span class="chk-card__amount">{{ row.amount | currency:'USD':'symbol':'1.2-2' }}</span>
</div>
<div class="chk-card__payee">{{ row.payeeName }}</div>
<dl class="chk-card__meta">
<div>
<dt>Date</dt>
<dd>{{ row.checkDate }}</dd>
</div>
<div>
<dt>Lines</dt>
<dd>{{ row.lineCount }}</dd>
</div>
<div>
<dt>Receipt / 簽收</dt>
<dd>
<ng-container *ngIf="row.signed; else notSignedCard">
{{ row.receiptSignedName }} · {{ row.receiptSignedAt | date:'short' }}
</ng-container>
<ng-template #notSignedCard></ng-template>
</dd>
</div>
</dl>
<div class="chk-card__footer">
<span [class]="statusClass(row.status)">{{ row.status }}</span>
<span *ngIf="row.signed" class="badge-paid">Signed</span>
</div>
<div class="chk-card__actions">
<button kendoButton fillMode="outline" (click)="view(row)">View</button>
<button kendoButton themeColor="primary" (click)="print(row)">Print</button>
<button *ngIf="canSign(row)" kendoButton themeColor="success" (click)="openSign(row)">簽收</button>
<button *ngIf="row.signed" kendoButton fillMode="outline" (click)="printReceipt(row)">收據</button>
<button *ngIf="canVoid(row)" kendoButton themeColor="error" fillMode="outline" (click)="openVoid(row)">Void</button>
</div>
</div>
<div class="chk-pager" *ngIf="!loading && rows.length > 0">
<button kendoButton fillMode="outline" [disabled]="page <= 1" (click)="prevPage()">Prev</button>
<span class="chk-pager__info">Page {{ page }} of {{ totalPages }}</span>
<button kendoButton fillMode="outline" [disabled]="page >= totalPages" (click)="nextPage()">Next</button>
</div>
</div>
<!-- Detail dialog -->
<kendo-dialog *ngIf="detail" title="Check #{{ detail.checkNumber }}" [width]="560" (close)="detail = null">
@@ -24,3 +24,112 @@
background-color: #fee2e2;
color: #991b1b;
}
// Mobile card list
// NOTE: display/flex layout lives on the element via Tailwind (flex flex-col gap-3)
// so the responsive `md:hidden` utility wins on desktop. Setting `display: flex`
// here would override `md:hidden` and leak the card list onto the desktop view.
.chk-empty {
padding: 24px 0;
text-align: center;
color: #6b7280;
}
.chk-card {
border: 1px solid #e5e7eb;
border-radius: 12px;
background: #fff;
padding: 14px 16px;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
&__top {
display: flex;
align-items: baseline;
justify-content: space-between;
gap: 12px;
}
&__number {
font-size: 0.85rem;
font-weight: 600;
color: #374151;
}
&__amount {
font-size: 1.1rem;
font-weight: 700;
color: #111827;
}
&__payee {
margin-top: 4px;
font-weight: 600;
color: #1f2937;
}
&__meta {
margin: 10px 0 0;
display: flex;
flex-direction: column;
gap: 6px;
div {
display: flex;
justify-content: space-between;
gap: 12px;
font-size: 0.85rem;
}
dt {
margin: 0;
color: #6b7280;
}
dd {
margin: 0;
color: #374151;
text-align: right;
}
}
&__footer {
margin-top: 12px;
display: flex;
flex-wrap: wrap;
gap: 8px;
}
&__actions {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid #f3f4f6;
display: flex;
flex-wrap: wrap;
gap: 8px;
// Comfortable tap targets; let buttons share the row evenly
.k-button {
flex: 1 1 auto;
min-height: 40px;
justify-content: center;
}
}
}
.chk-pager {
margin-top: 16px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
&__info {
font-size: 0.85rem;
color: #6b7280;
}
.k-button {
min-height: 40px;
}
}
@@ -49,9 +49,24 @@ export class CheckRegisterPageComponent implements OnInit {
}
get skip(): number { return (this.page - 1) * this.pageSize; }
get totalPages(): number { return Math.max(1, Math.ceil(this.total / this.pageSize)); }
applyFilter(): void { this.page = 1; this.load(); }
onPageChange(e: PageChangeEvent): void { this.page = Math.floor(e.skip / this.pageSize) + 1; this.load(); }
prevPage(): void {
if (this.page > 1) {
this.page--;
this.load();
}
}
nextPage(): void {
if (this.page < this.totalPages) {
this.page++;
this.load();
}
}
view(row: CheckListItemDto): void {
this.api.getCheck(row.id).subscribe(d => (this.detail = d));
}