WEB-573: refactor password preferences UI to card-based layout
- Convert horizontal radio layout to vertical card-based blocks - Add heading labels (Basic, Standard, Strong) for each policy option - Position radio buttons and headings on same row with space-between - Increase padding and spacing for improved visual clarity - Maintain full-width blocks with consistent vertical stacking - Preserve all existing text content and functionality - Add trackBy function to *ngFor for performance optimization - Use translation keys for password labels instead of hardcoded array - Map labels based on preference ID for robustness
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<!--
|
||||
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/.
|
||||
@@ -10,14 +9,25 @@
|
||||
<mat-card>
|
||||
<form [formGroup]="passwordPreferencesForm" (ngSubmit)="submit()">
|
||||
<mat-card-content>
|
||||
<mat-radio-group class="layout-column gap-10px radio-group-spacing" formControlName="validationPolicyId">
|
||||
@for (passwordPreference of passwordPreferencesData; track passwordPreference) {
|
||||
<mat-radio-button [value]="passwordPreference.id">
|
||||
<span class="description-wrap">{{
|
||||
passwordPreference.description | translateKey: 'passwordPreferences'
|
||||
}}</span>
|
||||
</mat-radio-button>
|
||||
}
|
||||
<mat-radio-group class="password-cards-container" formControlName="validationPolicyId">
|
||||
<div
|
||||
*ngFor="let passwordPreference of passwordPreferencesData; trackBy: trackByPasswordPreference"
|
||||
class="password-card"
|
||||
[class.selected]="passwordPreferencesForm.get('validationPolicyId')?.value === passwordPreference.id"
|
||||
>
|
||||
<div class="card-header">
|
||||
<div class="card-heading">
|
||||
{{ getPasswordLabel(passwordPreference) | translate }}
|
||||
</div>
|
||||
<mat-radio-button [value]="passwordPreference.id"></mat-radio-button>
|
||||
</div>
|
||||
|
||||
<div class="card-description">
|
||||
<span class="description-wrap">
|
||||
{{ passwordPreference.description | translateKey: 'passwordPreferences' }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</mat-radio-group>
|
||||
</mat-card-content>
|
||||
|
||||
|
||||
@@ -10,14 +10,44 @@
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.radio-group-spacing {
|
||||
.password-cards-container {
|
||||
display: flex;
|
||||
gap: 2rem;
|
||||
flex-direction: row;
|
||||
flex-direction: column;
|
||||
gap: 1.5rem;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (width <= 768px) {
|
||||
.radio-group-spacing {
|
||||
flex-direction: column;
|
||||
.password-card {
|
||||
width: 100%;
|
||||
border: 1px solid rgb(0 0 0 / 12%);
|
||||
border-radius: 4px;
|
||||
padding: 1.5rem;
|
||||
box-sizing: border-box;
|
||||
|
||||
&.selected {
|
||||
border-color: currentcolor;
|
||||
border-width: 2px;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-heading {
|
||||
font-weight: 500;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.card-description {
|
||||
padding-left: 0;
|
||||
|
||||
.description-wrap {
|
||||
white-space: normal;
|
||||
display: block;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,31 @@ export class PasswordPreferencesComponent implements OnInit {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TrackBy function for ngFor optimization.
|
||||
* @param index Index of the item.
|
||||
* @param item Password preference item.
|
||||
* @returns Unique identifier for the item.
|
||||
*/
|
||||
trackByPasswordPreference(index: number, item: any): any {
|
||||
return item.id || index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the password preference label based on ID.
|
||||
* @param preference Password preference object.
|
||||
* @returns Translation key for the password preference label.
|
||||
*/
|
||||
getPasswordLabel(preference: any): string {
|
||||
// Map based on ID to ensure robustness
|
||||
const labelMap: { [key: number]: string } = {
|
||||
1: 'labels.inputs.Basic',
|
||||
2: 'labels.inputs.Standard',
|
||||
3: 'labels.inputs.Strong'
|
||||
};
|
||||
return labelMap[preference.id] || 'labels.inputs.Unknown';
|
||||
}
|
||||
|
||||
/**
|
||||
* Submits the password preferences form and updates password preferences,
|
||||
* if successful redirects to organization view.
|
||||
|
||||
Reference in New Issue
Block a user