WEB-401:redesign page layout and remove absolute positioning (#2947)

Added @if guards before accessing nested properties to prevent runtime errors if the API returns incomplete data
This commit is contained in:
Shubham Kumar
2025-12-30 20:24:02 -06:00
committed by GitHub
parent 299bd1a2eb
commit 400edb27d9
4 changed files with 186 additions and 174 deletions
@@ -1,32 +1,41 @@
<!-- Version 1 --> <div class="container m-b-20 layout-row align-end">
<button
mat-raised-button
color="primary"
*mifosxHasPermission="'READ_STANDINGINSTRUCTION'"
(click)="filterStandingInstructions()"
>
{{ 'labels.buttons.Filter' | translate | titlecase }}
</button>
</div>
<mat-card class="container"> <mat-card class="container">
<div class="layout-row-wrap gap-2px responsive-column"> <mat-card-content>
<div class="layout-row-wrap gap-20px responsive-column">
@if (isFromClient) { @if (isFromClient) {
<div class="flex-fill"> <div class="flex-100 layout-row align-items-center gap-20px">
<span class="flex-40"> <div>
<h3 class="mat-h3">{{ 'labels.heading.Client Type' | translate }}</h3> <h3 class="mat-h3 no-m-b">{{ 'labels.heading.Client Type' | translate }}</h3>
</span> </div>
<span class="client-Name"> <div>
<h3 class="mat-h3">{{ clientName }}</h3> <h3 class="mat-h3 no-m-b">{{ clientName }}</h3>
</span> </div>
</div> </div>
} }
@if (!isFromClient) { @if (!isFromClient) {
<div class="flex-fill"> <mat-form-field class="flex-48">
<mat-form-field class="flex-30"> <mat-label>{{ 'labels.text.ClientName' | translate }}</mat-label>
<input matInput placeholder="{{ 'labels.text.ClientName' | translate }}" [formControl]="clientNameControl" /> <input matInput [formControl]="clientNameControl" />
</mat-form-field> </mat-form-field>
<mat-form-field class="flex-30">
<input matInput placeholder="{{ 'labels.text.FromClientId' | translate }}" [formControl]="fromClientId" /> <mat-form-field class="flex-48">
<mat-label>{{ 'labels.text.FromClientId' | translate }}</mat-label>
<input matInput [formControl]="fromClientId" />
</mat-form-field> </mat-form-field>
</div>
} }
<mat-divider [inset]="true"></mat-divider> <mat-form-field class="flex-48">
<mat-form-field class="type-field">
<mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Type' | translate }}</mat-label>
<mat-select [formControl]="transferType"> <mat-select [formControl]="transferType">
@for (transferTypeData of transferTypeDatas; track transferTypeData) { @for (transferTypeData of transferTypeDatas; track transferTypeData) {
@@ -37,69 +46,85 @@
</mat-select> </mat-select>
</mat-form-field> </mat-form-field>
<mat-form-field class="account-Id-field"> <mat-form-field class="flex-48">
<input matInput placeholder="{{ 'labels.text.FromAccountId' | translate }}" [formControl]="fromAccountId" /> <mat-label>{{ 'labels.text.FromAccountId' | translate }}</mat-label>
<input matInput [formControl]="fromAccountId" />
</mat-form-field> </mat-form-field>
<button
mat-raised-button
color="primary"
*mifosxHasPermission="'READ_STANDINGINSTRUCTION'"
(click)="filterStandingInstructions()"
class="filter-button"
>
{{ 'labels.buttons.Filter' | translate | titlecase }}
</button>
</div> </div>
</mat-card-content>
<table mat-table #instructionsTable [dataSource]="dataSource"> <div class="table-container">
<table
mat-table
#instructionsTable
[dataSource]="dataSource"
class="bordered-table"
[attr.aria-label]="'labels.heading.List Standing Instructions' | translate"
>
<ng-container matColumnDef="client"> <ng-container matColumnDef="client">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Client' | translate }}</th>
<td mat-cell *matCellDef="let instruction"> <td mat-cell *matCellDef="let instruction">
@if (instruction?.fromClient) {
{{ instruction.fromClient.displayName }}-{{ instruction.fromClient.id }} {{ instruction.fromClient.displayName }}-{{ instruction.fromClient.id }}
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="fromAccount"> <ng-container matColumnDef="fromAccount">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.From Account' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.From Account' | translate }}</th>
<td mat-cell *matCellDef="let instruction"> <td mat-cell *matCellDef="let instruction">
@if (instruction?.fromAccount && instruction?.fromAccountType) {
{{ instruction.fromAccount.accountNo }} ({{ instruction.fromAccountType.value }}) {{ instruction.fromAccount.accountNo }} ({{ instruction.fromAccountType.value }})
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="beneficiary"> <ng-container matColumnDef="beneficiary">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Beneficiary' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Beneficiary' | translate }}</th>
<td mat-cell *matCellDef="let instruction">{{ instruction.toClient.displayName }}</td> <td mat-cell *matCellDef="let instruction">
@if (instruction?.toClient) {
{{ instruction.toClient.displayName }}
}
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="toAccount"> <ng-container matColumnDef="toAccount">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.To Account' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.To Account' | translate }}</th>
<td mat-cell *matCellDef="let instruction"> <td mat-cell *matCellDef="let instruction">
@if (instruction?.toAccount && instruction?.toAccountType) {
{{ instruction.toAccount.accountNo }} ({{ instruction.toAccountType.value }}) {{ instruction.toAccount.accountNo }} ({{ instruction.toAccountType.value }})
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="amount"> <ng-container matColumnDef="amount">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Amount' | translate }}</th>
<td mat-cell *matCellDef="let instruction">{{ instruction.instructionType.value }}/{{ instruction.amount }}</td> <td mat-cell *matCellDef="let instruction">
@if (instruction?.instructionType && instruction?.amount !== undefined) {
{{ instruction.instructionType.value }}/{{ instruction.amount }}
}
</td>
</ng-container> </ng-container>
<ng-container matColumnDef="validity"> <ng-container matColumnDef="validity">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Validity' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Validity' | translate }}</th>
<td mat-cell *matCellDef="let instruction"> <td mat-cell *matCellDef="let instruction">
@if (instruction?.validFrom && instruction?.validTill) {
{{ instruction.validFrom | dateFormat }} to {{ instruction.validTill | dateFormat }} {{ instruction.validFrom | dateFormat }} to {{ instruction.validTill | dateFormat }}
}
</td> </td>
</ng-container> </ng-container>
<ng-container matColumnDef="actions"> <ng-container matColumnDef="actions">
<th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th> <th mat-header-cell *matHeaderCellDef>{{ 'labels.inputs.Actions' | translate }}</th>
<td mat-cell *matCellDef="let instruction"> <td mat-cell *matCellDef="let instruction">
@if (instruction.status.value !== 'Deleted') { @if (instruction?.status?.value !== 'Deleted') {
<span> <span>
<button <button
class="account-action-button" class="account-action-button"
mat-raised-button mat-raised-button
color="primary" color="primary"
[attr.aria-label]="'tooltips.Edit Standing Instruction' | translate"
matTooltip="{{ 'tooltips.Edit Standing Instruction' | translate }}" matTooltip="{{ 'tooltips.Edit Standing Instruction' | translate }}"
*mifosxHasPermission="'UPDATE_STANDINGINSTRUCTION'" *mifosxHasPermission="'UPDATE_STANDINGINSTRUCTION'"
[routerLink]="['../', instruction.id, 'edit']" [routerLink]="['../', instruction.id, 'edit']"
@@ -108,12 +133,13 @@
</button> </button>
</span> </span>
} }
@if (instruction.status.value !== 'Deleted') { @if (instruction?.status?.value !== 'Deleted') {
<span> <span>
<button <button
class="account-action-button" class="account-action-button"
mat-raised-button mat-raised-button
color="warn" color="warn"
[attr.aria-label]="'tooltips.Delete Standing Instruction' | translate"
matTooltip="{{ 'tooltips.Delete Standing Instruction' | translate }}" matTooltip="{{ 'tooltips.Delete Standing Instruction' | translate }}"
*mifosxHasPermission="'DELETE_STANDINGINSTRUCTION'" *mifosxHasPermission="'DELETE_STANDINGINSTRUCTION'"
(click)="deleteStandingInstruction(instruction.id)" (click)="deleteStandingInstruction(instruction.id)"
@@ -126,6 +152,7 @@
class="account-action-button" class="account-action-button"
mat-raised-button mat-raised-button
color="primary" color="primary"
[attr.aria-label]="'tooltips.View Standing Instruction' | translate"
matTooltip="{{ 'tooltips.View Standing Instruction' | translate }}" matTooltip="{{ 'tooltips.View Standing Instruction' | translate }}"
*mifosxHasPermission="'READ_STANDINGINSTRUCTION'" *mifosxHasPermission="'READ_STANDINGINSTRUCTION'"
[routerLink]="['../', instruction.id, 'view']" [routerLink]="['../', instruction.id, 'view']"
@@ -140,4 +167,5 @@
</table> </table>
<mat-paginator [pageSize]="10" [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator> <mat-paginator [pageSize]="10" [pageSizeOptions]="[10, 25, 50, 100]" showFirstLastButtons></mat-paginator>
</div>
</mat-card> </mat-card>
@@ -1,23 +1,29 @@
// Version 1
.container { .container {
.filter-button { .table-container {
height: 2.5rem; margin-top: 1rem;
margin-top: 2rem;
}
}
table { .bordered-table {
width: 100%; width: 100%;
.account-action-button { .account-action-button {
min-width: 26px; min-width: 1.625rem;
padding: 0 6px; padding: 0 0.375rem;
margin: 4px; margin: 0.25rem;
line-height: 25px; line-height: 1.5625rem;
}
th,
td {
padding: 0.75rem 1rem;
}
th {
font-weight: 500;
} }
} }
.mat-divider { mat-paginator {
border-top-color: #fff; border-top: 1px solid #e0e0e0;
}
}
} }
@@ -24,7 +24,6 @@ import { AccountTransfersService } from '../account-transfers.service';
/** Dialog Components */ /** Dialog Components */
import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component'; import { DeleteDialogComponent } from 'app/shared/delete-dialog/delete-dialog.component';
import { SettingsService } from 'app/settings/settings.service'; import { SettingsService } from 'app/settings/settings.service';
import { MatDivider } from '@angular/material/divider';
import { MatTooltip } from '@angular/material/tooltip'; import { MatTooltip } from '@angular/material/tooltip';
import { DateFormatPipe } from '../../pipes/date-format.pipe'; import { DateFormatPipe } from '../../pipes/date-format.pipe';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module'; import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
@@ -38,7 +37,6 @@ import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
styleUrls: ['./list-standing-instructions.component.scss'], styleUrls: ['./list-standing-instructions.component.scss'],
imports: [ imports: [
...STANDALONE_SHARED_IMPORTS, ...STANDALONE_SHARED_IMPORTS,
MatDivider,
MatTable, MatTable,
MatColumnDef, MatColumnDef,
MatHeaderCellDef, MatHeaderCellDef,
-20
View File
@@ -651,23 +651,3 @@ mat-card {
overflow: hidden; overflow: hidden;
} }
} }
//Re-positions account type field on list standing instructions page
.type-field {
position: absolute;
left: 1px;
top: 20px;
}
//Re-positions account id field on list standing instructions page
.account-Id-field {
position: absolute;
left: 490px;
top: 20px;
}
//Re-positions client name label on list standing instructions page
.client-Name {
position: absolute;
left: 490px;
}