feat: redesign all dialogs, file upload, address/nominee/identifier tabs

- File upload: compact dashed zone (54px height), inline icon+filename+change
- Dialogs: no scroll, rounded close button (red on hover), 700px width
- Identifier dialog: 2-column grid, section divider, outline fields
- Nominee: new NomineeDialogComponent popup (replaces page navigation)
- Address tab: card panels, status badges, 3-column grid
- Nominee tab: accordion with badges, 3-column grid
- Shared form dialog: outline fields, fixed close button

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Aditya
2026-03-21 00:09:59 +05:30
co-authored by Claude Opus 4.6
parent b7f612c2f6
commit 97f30ac3c1
17 changed files with 1589 additions and 281 deletions
@@ -0,0 +1,87 @@
/**
* 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/.
*/
import { Component, OnInit, inject } from '@angular/core';
import { UntypedFormGroup, UntypedFormBuilder, Validators } from '@angular/forms';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import { MatCheckbox } from '@angular/material/checkbox';
import { STANDALONE_SHARED_IMPORTS } from 'app/standalone-shared.module';
import { SettingsService } from 'app/settings/settings.service';
import { Dates } from 'app/core/utils/dates';
export interface NomineeDialogData {
title: string;
familyMemberOptions: any;
member?: any;
}
@Component({
selector: 'mifosx-nominee-dialog',
templateUrl: './nominee-dialog.component.html',
styleUrls: ['./nominee-dialog.component.scss'],
imports: [
...STANDALONE_SHARED_IMPORTS,
MatCheckbox
]
})
export class NomineeDialogComponent implements OnInit {
private formBuilder = inject(UntypedFormBuilder);
private dialogRef = inject(MatDialogRef<NomineeDialogComponent>);
private settingsService = inject(SettingsService);
private dateUtils = inject(Dates);
data: NomineeDialogData = inject(MAT_DIALOG_DATA);
nomineeForm: UntypedFormGroup;
maxDate = new Date();
ngOnInit() {
this.maxDate = this.settingsService.businessDate;
this.createForm();
}
createForm() {
const member = this.data.member;
this.nomineeForm = this.formBuilder.group({
nominationType: [member?.nominationType || 'DA1'],
firstName: [member?.firstName || '', Validators.required],
relationshipId: [member?.relationshipId || '', Validators.required],
dateOfBirth: [member?.dateOfBirth ? this.dateUtils.formatDate(member.dateOfBirth, 'yyyy-MM-dd') : null],
percentageShare: [member?.percentageShare ?? 100],
isDependent: [member?.isDependent || false],
guardianName: [member?.guardianName || ''],
guardianRelationship: [member?.guardianRelationship || ''],
remarks: [member?.remarks || '']
});
}
onSubmit() {
if (this.nomineeForm.valid) {
const formValue = { ...this.nomineeForm.value };
const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat;
if (formValue.dateOfBirth) {
if (formValue.dateOfBirth instanceof Date) {
formValue.dateOfBirth = this.dateUtils.formatDate(formValue.dateOfBirth, dateFormat);
}
} else {
delete formValue.dateOfBirth;
}
this.dialogRef.close({
...formValue,
dateFormat,
locale
});
}
}
onCancel() {
this.dialogRef.close(null);
}
}