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
|
Copyright since 2025 Mifos Initiative
|
||||||
|
|
||||||
This Source Code Form is subject to the terms of the Mozilla Public
|
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
|
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/.
|
file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
@@ -10,14 +9,25 @@
|
|||||||
<mat-card>
|
<mat-card>
|
||||||
<form [formGroup]="passwordPreferencesForm" (ngSubmit)="submit()">
|
<form [formGroup]="passwordPreferencesForm" (ngSubmit)="submit()">
|
||||||
<mat-card-content>
|
<mat-card-content>
|
||||||
<mat-radio-group class="layout-column gap-10px radio-group-spacing" formControlName="validationPolicyId">
|
<mat-radio-group class="password-cards-container" formControlName="validationPolicyId">
|
||||||
@for (passwordPreference of passwordPreferencesData; track passwordPreference) {
|
<div
|
||||||
<mat-radio-button [value]="passwordPreference.id">
|
*ngFor="let passwordPreference of passwordPreferencesData; trackBy: trackByPasswordPreference"
|
||||||
<span class="description-wrap">{{
|
class="password-card"
|
||||||
passwordPreference.description | translateKey: 'passwordPreferences'
|
[class.selected]="passwordPreferencesForm.get('validationPolicyId')?.value === passwordPreference.id"
|
||||||
}}</span>
|
>
|
||||||
</mat-radio-button>
|
<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-radio-group>
|
||||||
</mat-card-content>
|
</mat-card-content>
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,44 @@
|
|||||||
white-space: normal;
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
.radio-group-spacing {
|
.password-cards-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 2rem;
|
flex-direction: column;
|
||||||
flex-direction: row;
|
gap: 1.5rem;
|
||||||
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (width <= 768px) {
|
.password-card {
|
||||||
.radio-group-spacing {
|
width: 100%;
|
||||||
flex-direction: column;
|
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,
|
* Submits the password preferences form and updates password preferences,
|
||||||
* if successful redirects to organization view.
|
* if successful redirects to organization view.
|
||||||
|
|||||||
Reference in New Issue
Block a user