Clicking a row navigated away to its own page, costing the checker the list and whatever search they had set up. It now opens in a dialog over the list, the same pattern the Disbursement and Closure Requests tabs already use. ViewCheckerInboxComponent gained the optional MatDialogRef/MAT_DIALOG_DATA pair rather than being duplicated, so the direct route still works for a bookmarked link. Two differences follow from being a dialog: - the list rows carry only summary fields, so the detail (which is what has commandAsJson) is fetched here; by route it stays resolver-provided. The template reads checkerInboxDetail.* unguarded, so it is now behind an @if with a loading state -- otherwise a dialog would render against undefined. - commandAsJson is absent on some entries (a DELETE carries no payload) and JSON.parse(undefined) throws. By route that broke the page; in a dialog it would have left an empty shell open. Parsing is now guarded. The list only reloads when something was actually actioned, since reload() re-navigates the route and would otherwise discard the checker's search on a plain close. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
181 lines
7.9 KiB
HTML
181 lines
7.9 KiB
HTML
<!--
|
|
Copyright since 2025 Mifos Initiative
|
|
|
|
This Source Code Form is subject to the terms of the Mozilla Public
|
|
License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
-->
|
|
|
|
<div class="cv-tab-content">
|
|
<!-- Action bar (search + advanced toggle + Approve/Delete/Reject) — only
|
|
rendered when there's actual data to work with. An empty inbox shows
|
|
the friendly empty-state block below without these controls. -->
|
|
@if (checkerData) {
|
|
<div class="cv-action-bar">
|
|
<mat-form-field appearance="outline" class="cv-search-field">
|
|
<mat-icon matPrefix>search</mat-icon>
|
|
<input
|
|
matInput
|
|
placeholder="{{ 'labels.text.Search by user' | translate }}"
|
|
(keyup)="applyFilter($event.target.value)"
|
|
/>
|
|
</mat-form-field>
|
|
<button
|
|
mat-icon-button
|
|
(click)="toggleAdvancedSearch()"
|
|
class="cv-toggle-btn"
|
|
[attr.aria-label]="
|
|
showAdvancedSearch
|
|
? ('labels.buttons.Hide advanced search' | translate)
|
|
: ('labels.buttons.Show advanced search' | translate)
|
|
"
|
|
[attr.aria-expanded]="showAdvancedSearch"
|
|
>
|
|
<mat-icon>{{ showAdvancedSearch ? 'arrow_drop_up' : 'arrow_drop_down' }}</mat-icon>
|
|
</button>
|
|
<div class="cv-action-buttons">
|
|
<button mat-stroked-button class="cv-btn cv-btn-approve" (click)="approveChecker()">
|
|
<fa-icon icon="check" class="m-r-10"></fa-icon>{{ 'labels.buttons.Approve' | translate }}
|
|
</button>
|
|
<button mat-stroked-button class="cv-btn cv-btn-delete" (click)="deleteChecker()">
|
|
<fa-icon icon="trash" class="m-r-10"></fa-icon>{{ 'labels.buttons.Delete' | translate }}
|
|
</button>
|
|
<button mat-stroked-button class="cv-btn cv-btn-reject" (click)="rejectChecker()">
|
|
<fa-icon icon="times" class="m-r-10"></fa-icon>{{ 'labels.buttons.Reject' | translate }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
@if (showAdvancedSearch) {
|
|
<form [formGroup]="makerCheckerSearchForm">
|
|
<div class="cv-advanced-search">
|
|
<mat-form-field appearance="outline" class="cv-adv-field" (click)="makerDateTimeFromPicker.open()">
|
|
<mat-label>{{ 'labels.inputs.From date' | translate }}</mat-label>
|
|
<input
|
|
matInput
|
|
[min]="minDate"
|
|
[max]="maxDate"
|
|
[matDatepicker]="makerDateTimeFromPicker"
|
|
formControlName="makerDateTimeFrom"
|
|
/>
|
|
<mat-datepicker-toggle matSuffix [for]="makerDateTimeFromPicker"></mat-datepicker-toggle>
|
|
<mat-datepicker #makerDateTimeFromPicker></mat-datepicker>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="cv-adv-field" (click)="makerDateTimetoPicker.open()">
|
|
<mat-label>{{ 'labels.inputs.To date' | translate }}</mat-label>
|
|
<input
|
|
matInput
|
|
[min]="minDate"
|
|
[max]="maxDate"
|
|
[matDatepicker]="makerDateTimetoPicker"
|
|
formControlName="makerDateTimeto"
|
|
/>
|
|
<mat-datepicker-toggle matSuffix [for]="makerDateTimetoPicker"></mat-datepicker-toggle>
|
|
<mat-datepicker #makerDateTimetoPicker></mat-datepicker>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="cv-adv-field">
|
|
<mat-label>{{ 'labels.inputs.Select action' | translate }}</mat-label>
|
|
<mat-select formControlName="actionName">
|
|
@for (action of makerCheckerTemplate.actionNames; track action) {
|
|
<mat-option [value]="action">
|
|
{{ action }}
|
|
</mat-option>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="cv-adv-field">
|
|
<mat-label>{{ 'labels.inputs.Select entity' | translate }}</mat-label>
|
|
<mat-select formControlName="entityName">
|
|
@for (entity of makerCheckerTemplate.entityNames; track entity) {
|
|
<mat-option [value]="entity">
|
|
{{ entity }}
|
|
</mat-option>
|
|
}
|
|
</mat-select>
|
|
</mat-form-field>
|
|
<mat-form-field appearance="outline" class="cv-adv-field">
|
|
<mat-label>{{ 'labels.inputs.Resource id' | translate }}</mat-label>
|
|
<input matInput formControlName="resourceId" />
|
|
</mat-form-field>
|
|
<button mat-flat-button color="primary" class="cv-btn cv-btn-search" type="button" (click)="search()">
|
|
<fa-icon icon="search" class="m-r-10"></fa-icon>{{ 'labels.buttons.Advanced Search' | translate }}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
}
|
|
|
|
@if (checkerData && !noSearchedData) {
|
|
<div class="table-container">
|
|
<table mat-table [dataSource]="dataSource" class="modern-table">
|
|
<ng-container matColumnDef="select">
|
|
<th mat-header-cell *matHeaderCellDef>
|
|
<mat-checkbox
|
|
(change)="$event ? masterToggle() : null"
|
|
[checked]="selection.hasValue() && isAllSelected()"
|
|
[indeterminate]="selection.hasValue() && !isAllSelected()"
|
|
[aria-label]="checkboxLabel()"
|
|
>
|
|
</mat-checkbox>
|
|
</th>
|
|
<td mat-cell *matCellDef="let row">
|
|
<mat-checkbox
|
|
(click)="$event.stopPropagation()"
|
|
(change)="$event ? selection.toggle(row) : null"
|
|
[checked]="selection.isSelected(row)"
|
|
[aria-label]="checkboxLabel(row)"
|
|
>
|
|
</mat-checkbox>
|
|
</td>
|
|
</ng-container>
|
|
<ng-container matColumnDef="id">
|
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.ID' | translate }}</th>
|
|
<td mat-cell *matCellDef="let makerChecker" class="cell-mono">{{ makerChecker.id }}</td>
|
|
</ng-container>
|
|
<ng-container matColumnDef="madeOnDate">
|
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Made on Date' | translate }}</th>
|
|
<td mat-cell *matCellDef="let makerChecker">{{ makerChecker.madeOnDate | dateFormat }}</td>
|
|
</ng-container>
|
|
<ng-container matColumnDef="status">
|
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Status' | translate }}</th>
|
|
<td mat-cell *matCellDef="let makerChecker">
|
|
<span class="status-badge">{{ makerChecker.processingResult }}</span>
|
|
</td>
|
|
</ng-container>
|
|
<ng-container matColumnDef="user">
|
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.User' | translate }}</th>
|
|
<td mat-cell *matCellDef="let makerChecker">{{ makerChecker.maker }}</td>
|
|
</ng-container>
|
|
<ng-container matColumnDef="action">
|
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Action' | translate }}</th>
|
|
<td mat-cell *matCellDef="let makerChecker">{{ makerChecker.actionName }}</td>
|
|
</ng-container>
|
|
<ng-container matColumnDef="entity">
|
|
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Entity' | translate }}</th>
|
|
<td mat-cell *matCellDef="let makerChecker">{{ makerChecker.entityName }}</td>
|
|
</ng-container>
|
|
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
|
<tr mat-row *matRowDef="let row; columns: displayedColumns" (click)="viewEntry(row)" class="table-row"></tr>
|
|
</table>
|
|
</div>
|
|
}
|
|
|
|
@if (checkerData && noSearchedData) {
|
|
<div class="empty-state empty-state--filter">
|
|
<i class="fas fa-inbox empty-icon"></i>
|
|
<p class="empty-title">No matches</p>
|
|
<p class="empty-text">No checker inbox items match the current search. Try clearing the filters.</p>
|
|
</div>
|
|
}
|
|
|
|
@if (!checkerData) {
|
|
<div class="empty-state">
|
|
<i class="fas fa-inbox empty-icon"></i>
|
|
<p class="empty-title">Inbox is empty</p>
|
|
<p class="empty-text">
|
|
Currently no items in the Checker Inbox. New maker actions awaiting review will appear here.
|
|
</p>
|
|
</div>
|
|
}
|
|
</div>
|