Merge pull request #2652 from JaySoni1/WEB-223-make-date-of-birth-handling-for-client-creation-consistent

WEB-223 Make Date of Birth handling for Client Creation consistent
This commit is contained in:
Gopi Kishan
2025-09-16 11:43:31 +05:30
committed by GitHub
10 changed files with 253 additions and 69 deletions
@@ -32,11 +32,7 @@
<mat-form-field class="flex-48"> <mat-form-field class="flex-48">
<mat-label>{{ 'labels.inputs.Age' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Age' | translate }}</mat-label>
<input type="number" formControlName="age" matInput [min]="0" /> <input type="number" formControlName="age" matInput [min]="0" disabled />
<mat-error *ngIf="familyMemberForm.controls.age.hasError('required')">
{{ 'labels.inputs.Age' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required and cannot be negative' | translate }}</strong>
</mat-error>
</mat-form-field> </mat-form-field>
<mat-checkbox formControlName="isDependent" labelPosition="before" class="flex-48" <mat-checkbox formControlName="isDependent" labelPosition="before" class="flex-48"
@@ -89,19 +85,22 @@
<mat-form-field class="flex-98" (click)="dueDatePicker.open()"> <mat-form-field class="flex-98" (click)="dueDatePicker.open()">
<mat-label>{{ 'labels.inputs.Date Of Birth' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Date Of Birth' | translate }}</mat-label>
<input formControlName="dateOfBirth" matInput [max]="maxDate" [matDatepicker]="dueDatePicker" required /> <input
formControlName="dateOfBirth"
matInput
[max]="maxDate"
[matDatepicker]="dueDatePicker"
placeholder="Optional"
/>
<mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle>
<mat-datepicker #dueDatePicker></mat-datepicker> <mat-datepicker #dueDatePicker></mat-datepicker>
<mat-error *ngIf="familyMemberForm.controls.dateOfBirth.hasError('required')">
{{ 'labels.inputs.Date Of Birth' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<mat-dialog-actions align="center"> <mat-dialog-actions align="center">
<button mat-raised-button mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button> <button type="button" mat-raised-button mat-dialog-close>{{ 'labels.buttons.Cancel' | translate }}</button>
<button <button
type="button"
mat-raised-button mat-raised-button
color="primary" color="primary"
[disabled]="!familyMemberForm.valid" [disabled]="!familyMemberForm.valid"
@@ -0,0 +1,6 @@
.mat-form-field-hint {
font-size: 75%;
color: rgb(0 0 0 / 60%);
margin-top: 4px;
margin-bottom: 8px;
}
@@ -70,6 +70,41 @@ export class ClientFamilyMemberDialogComponent implements OnInit {
dateOfBirth: this.data.member.dateOfBirth && new Date(this.data.member.dateOfBirth) dateOfBirth: this.data.member.dateOfBirth && new Date(this.data.member.dateOfBirth)
}); });
} }
// Add subscription to date of birth changes to update age
this.familyMemberForm.get('dateOfBirth').valueChanges.subscribe((dateOfBirth: any) => {
if (dateOfBirth) {
const age = this.calculateAge(dateOfBirth);
this.familyMemberForm.get('age').setValue(age);
} else {
this.familyMemberForm.get('age').setValue('');
}
});
// If a date of birth is already set, calculate the age
const currentDob = this.familyMemberForm.get('dateOfBirth').value;
if (currentDob) {
const age = this.calculateAge(currentDob);
this.familyMemberForm.get('age').setValue(age);
}
}
/**
* Calculates age from date of birth
* @param {Date} dateOfBirth Date of Birth
* @returns {number} Age
*/
calculateAge(dateOfBirth: Date): number {
const today = new Date(this.settingsService.businessDate);
const birthDate = new Date(dateOfBirth);
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
} }
/** /**
@@ -88,9 +123,7 @@ export class ClientFamilyMemberDialogComponent implements OnInit {
], ],
qualification: [''], qualification: [''],
age: [ age: [
'', { value: '', disabled: true }],
Validators.required
],
isDependent: [''], isDependent: [''],
relationshipId: [ relationshipId: [
'', '',
@@ -102,10 +135,7 @@ export class ClientFamilyMemberDialogComponent implements OnInit {
], ],
professionId: [''], professionId: [''],
maritalStatusId: [''], maritalStatusId: [''],
dateOfBirth: [ dateOfBirth: ['']
'',
Validators.required
]
}); });
} }
@@ -113,22 +143,43 @@ export class ClientFamilyMemberDialogComponent implements OnInit {
* Returns Formatted Family Member * Returns Formatted Family Member
*/ */
get familyMember() { get familyMember() {
const familyMemberFormData = this.familyMemberForm.value; // Get form values including disabled controls like age
const formValue = {
...this.familyMemberForm.getRawValue()
};
const locale = this.settingsService.language.code; const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat; const dateFormat = this.settingsService.dateFormat;
if (familyMemberFormData.dateOfBirth instanceof Date) { const prevDateOfBirth: Date = formValue.dateOfBirth;
familyMemberFormData.dateOfBirth = this.dateUtils.formatDate(familyMemberFormData.dateOfBirth, dateFormat);
// Calculate age from dateOfBirth if present
if (prevDateOfBirth) {
if (formValue.dateOfBirth instanceof Date) {
formValue.dateOfBirth = this.dateUtils.formatDate(prevDateOfBirth, dateFormat);
}
// Ensure age is calculated even if it wasn't already
if (!formValue.age && prevDateOfBirth) {
formValue.age = this.calculateAge(prevDateOfBirth);
}
} else {
// If no date of birth, remove age and dateOfBirth from submission
delete formValue.age;
delete formValue.dateOfBirth;
} }
const familyMember = { const familyMember = {
...familyMemberFormData, ...formValue,
dateFormat, dateFormat,
locale locale
}; };
// Remove empty fields
for (const key in familyMember) { for (const key in familyMember) {
if (familyMember[key] === '' || familyMember[key] === undefined) { if (familyMember[key] === '' || familyMember[key] === undefined) {
delete familyMember[key]; delete familyMember[key];
} }
} }
return familyMember; return familyMember;
} }
} }
@@ -3,7 +3,14 @@
<div class="layout-row-wrap responsive-column align-start-center"> <div class="layout-row-wrap responsive-column align-start-center">
<mat-form-field class="flex-50-minus-25px"> <mat-form-field class="flex-50-minus-25px">
<mat-label>{{ 'labels.inputs.First Name' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.First Name' | translate }}</mat-label>
<input formControlName="firstName" required matInput /> <input
formControlName="firstName"
required
matInput
aria-label="First Name"
placeholder="First Name"
title="First Name"
/>
<mat-error *ngIf="addFamilyMemberForm.controls.firstName.hasError('required')"> <mat-error *ngIf="addFamilyMemberForm.controls.firstName.hasError('required')">
{{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.inputs.First Name' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong> <strong>{{ 'labels.commons.required' | translate }}</strong>
@@ -12,12 +19,25 @@
<mat-form-field class="flex-50-minus-25px"> <mat-form-field class="flex-50-minus-25px">
<mat-label>{{ 'labels.inputs.Middle Name' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Middle Name' | translate }}</mat-label>
<input formControlName="middleName" matInput /> <input
formControlName="middleName"
matInput
aria-label="Middle Name"
placeholder="Middle Name"
title="Middle Name"
/>
</mat-form-field> </mat-form-field>
<mat-form-field class="flex-50-minus-25px"> <mat-form-field class="flex-50-minus-25px">
<mat-label>{{ 'labels.inputs.Last Name' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Last Name' | translate }}</mat-label>
<input formControlName="lastName" required matInput /> <input
formControlName="lastName"
required
matInput
aria-label="Last Name"
placeholder="Last Name"
title="Last Name"
/>
<mat-error *ngIf="addFamilyMemberForm.controls.lastName.hasError('required')"> <mat-error *ngIf="addFamilyMemberForm.controls.lastName.hasError('required')">
{{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.is' | translate }} {{ 'labels.inputs.Last Name' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong> <strong>{{ 'labels.commons.required' | translate }}</strong>
@@ -26,16 +46,27 @@
<mat-form-field class="flex-50-minus-25px"> <mat-form-field class="flex-50-minus-25px">
<mat-label>{{ 'labels.inputs.Qualification' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Qualification' | translate }}</mat-label>
<input formControlName="qualification" matInput /> <input
formControlName="qualification"
matInput
aria-label="Qualification"
placeholder="Qualification"
title="Qualification"
/>
</mat-form-field> </mat-form-field>
<mat-form-field class="flex-50-minus-25px"> <mat-form-field class="flex-50-minus-25px">
<mat-label>{{ 'labels.inputs.Age' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Age' | translate }}</mat-label>
<input type="number" formControlName="age" required matInput [min]="minAge" /> <input
<mat-error *ngIf="addFamilyMemberForm.controls.age.hasError('required')"> type="number"
{{ 'labels.inputs.Age' | translate }} {{ 'labels.commons.is' | translate }} formControlName="age"
<strong>{{ 'labels.commons.required and cannot be negative' | translate }}</strong> matInput
</mat-error> [min]="minAge"
disabled
aria-label="Age"
placeholder="Age"
title="Age"
/>
</mat-form-field> </mat-form-field>
<mat-checkbox formControlName="isDependent" labelPosition="before" class="flex-50-minus-25px"> <mat-checkbox formControlName="isDependent" labelPosition="before" class="flex-50-minus-25px">
@@ -91,13 +122,17 @@
<mat-form-field class="flex-fill" (click)="dueDatePicker.open()"> <mat-form-field class="flex-fill" (click)="dueDatePicker.open()">
<mat-label>{{ 'labels.inputs.Date Of Birth' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Date Of Birth' | translate }}</mat-label>
<input formControlName="dateOfBirth" matInput [max]="maxDate" [matDatepicker]="dueDatePicker" required /> <input
formControlName="dateOfBirth"
matInput
[max]="maxDate"
[matDatepicker]="dueDatePicker"
placeholder="Optional"
aria-label="Date Of Birth"
title="Date Of Birth"
/>
<mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle>
<mat-datepicker #dueDatePicker></mat-datepicker> <mat-datepicker #dueDatePicker></mat-datepicker>
<mat-error *ngIf="addFamilyMemberForm.controls.dateOfBirth.hasError('required')">
{{ 'labels.inputs.Date Of Birth' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -15,3 +15,10 @@
} }
} }
} }
.mat-form-field-hint {
font-size: 75%;
color: rgb(0 0 0 / 60%);
margin-top: 4px;
margin-bottom: 8px;
}
@@ -59,6 +59,32 @@ export class AddFamilyMemberComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.maxDate = this.settingsService.businessDate; this.maxDate = this.settingsService.businessDate;
this.createAddFamilyMemberForm(); this.createAddFamilyMemberForm();
this.addFamilyMemberForm.get('dateOfBirth').valueChanges.subscribe((dateOfBirth: any) => {
if (dateOfBirth) {
const age = this.calculateAge(dateOfBirth);
this.addFamilyMemberForm.get('age').setValue(age);
} else {
this.addFamilyMemberForm.get('age').setValue('');
}
});
}
/**
* Calculates age from date of birth
* @param {Date} dateOfBirth Date of Birth
* @returns {number} Age
*/
calculateAge(dateOfBirth: Date): number {
const today = new Date(this.settingsService.businessDate);
const birthDate = new Date(dateOfBirth);
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
} }
/** /**
@@ -77,9 +103,7 @@ export class AddFamilyMemberComponent implements OnInit {
], ],
qualification: [''], qualification: [''],
age: [ age: [
'', { value: '', disabled: true }],
Validators.required
],
isDependent: [''], isDependent: [''],
relationshipId: [ relationshipId: [
'', '',
@@ -91,10 +115,7 @@ export class AddFamilyMemberComponent implements OnInit {
], ],
professionId: [''], professionId: [''],
maritalStatusId: [''], maritalStatusId: [''],
dateOfBirth: [ dateOfBirth: ['']
'',
Validators.required
]
}); });
} }
@@ -102,18 +123,36 @@ export class AddFamilyMemberComponent implements OnInit {
* Submits the form and adds the family member * Submits the form and adds the family member
*/ */
submit() { submit() {
const addFamilyMemberFormData = this.addFamilyMemberForm.value; // Get form values including disabled controls like age
const formValue = {
...this.addFamilyMemberForm.getRawValue()
};
const locale = this.settingsService.language.code; const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat; const dateFormat = this.settingsService.dateFormat;
const prevDateOfBirth: Date = this.addFamilyMemberForm.value.dateOfBirth; const prevDateOfBirth: Date = formValue.dateOfBirth;
if (addFamilyMemberFormData.dateOfBirth instanceof Date) {
addFamilyMemberFormData.dateOfBirth = this.dateUtils.formatDate(prevDateOfBirth, dateFormat); // Calculate age from dateOfBirth if present
if (prevDateOfBirth) {
if (formValue.dateOfBirth instanceof Date) {
formValue.dateOfBirth = this.dateUtils.formatDate(prevDateOfBirth, dateFormat);
}
// Ensure age is calculated even if it wasn't already
if (!formValue.age && prevDateOfBirth) {
formValue.age = this.calculateAge(prevDateOfBirth);
}
} else {
// If no date of birth, remove age and dateOfBirth from submission
delete formValue.age;
delete formValue.dateOfBirth;
} }
const data = { const data = {
...addFamilyMemberFormData, ...formValue,
dateFormat, dateFormat,
locale locale
}; };
this.clientsService.addFamilyMember(this.clientId, data).subscribe((res) => { this.clientsService.addFamilyMember(this.clientId, data).subscribe((res) => {
this.router.navigate(['../'], { relativeTo: this.route }); this.router.navigate(['../'], { relativeTo: this.route });
}); });
@@ -31,11 +31,7 @@
<mat-form-field class="flex-50-minus-25px"> <mat-form-field class="flex-50-minus-25px">
<mat-label>{{ 'labels.inputs.Age' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Age' | translate }}</mat-label>
<input type="number" formControlName="age" required matInput /> <input type="number" formControlName="age" matInput disabled />
<mat-error *ngIf="editFamilyMemberForm.controls.age.hasError('required')">
{{ 'labels.inputs.Age' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required and cannot be negative' | translate }}</strong>
</mat-error>
</mat-form-field> </mat-form-field>
<mat-checkbox formControlName="isDependent" labelPosition="before" class="flex-50-minus-25px"> <mat-checkbox formControlName="isDependent" labelPosition="before" class="flex-50-minus-25px">
@@ -91,13 +87,15 @@
<mat-form-field class="flex-fill" (click)="dueDatePicker.open()"> <mat-form-field class="flex-fill" (click)="dueDatePicker.open()">
<mat-label>{{ 'labels.inputs.Date Of Birth' | translate }}</mat-label> <mat-label>{{ 'labels.inputs.Date Of Birth' | translate }}</mat-label>
<input formControlName="dateOfBirth" matInput [max]="maxDate" [matDatepicker]="dueDatePicker" required /> <input
formControlName="dateOfBirth"
matInput
[max]="maxDate"
[matDatepicker]="dueDatePicker"
placeholder="Optional"
/>
<mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle> <mat-datepicker-toggle matSuffix [for]="dueDatePicker"></mat-datepicker-toggle>
<mat-datepicker #dueDatePicker></mat-datepicker> <mat-datepicker #dueDatePicker></mat-datepicker>
<mat-error *ngIf="editFamilyMemberForm.controls.dateOfBirth.hasError('required')">
{{ 'labels.inputs.Date Of Birth' | translate }} {{ 'labels.commons.is' | translate }}
<strong>{{ 'labels.commons.required' | translate }}</strong>
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
@@ -13,3 +13,10 @@
} }
} }
} }
.mat-form-field-hint {
font-size: 75%;
color: rgb(0 0 0 / 60%);
margin-top: 4px;
margin-bottom: 8px;
}
@@ -57,6 +57,32 @@ export class EditFamilyMemberComponent implements OnInit {
ngOnInit() { ngOnInit() {
this.maxDate = this.settingsService.businessDate; this.maxDate = this.settingsService.businessDate;
this.createEditFamilyMemberForm(this.familyMemberDetails); this.createEditFamilyMemberForm(this.familyMemberDetails);
this.editFamilyMemberForm.get('dateOfBirth').valueChanges.subscribe((dateOfBirth: any) => {
if (dateOfBirth) {
const age = this.calculateAge(dateOfBirth);
this.editFamilyMemberForm.get('age').setValue(age);
} else {
this.editFamilyMemberForm.get('age').setValue('');
}
});
}
/**
* Calculates age from date of birth
* @param {Date} dateOfBirth Date of Birth
* @returns {number} Age
*/
calculateAge(dateOfBirth: Date): number {
const today = new Date(this.settingsService.businessDate);
const birthDate = new Date(dateOfBirth);
let age = today.getFullYear() - birthDate.getFullYear();
const monthDiff = today.getMonth() - birthDate.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
age--;
}
return age;
} }
/** /**
@@ -76,9 +102,7 @@ export class EditFamilyMemberComponent implements OnInit {
], ],
qualification: [familyMember.qualification], qualification: [familyMember.qualification],
age: [ age: [
familyMember.age, { value: familyMember.age, disabled: true }],
Validators.required
],
isDependent: [familyMember.isDependent], isDependent: [familyMember.isDependent],
relationshipId: [ relationshipId: [
familyMember.relationshipId, familyMember.relationshipId,
@@ -91,8 +115,7 @@ export class EditFamilyMemberComponent implements OnInit {
professionId: [familyMember.professionId], professionId: [familyMember.professionId],
maritalStatusId: [familyMember.maritalStatusId], maritalStatusId: [familyMember.maritalStatusId],
dateOfBirth: [ dateOfBirth: [
this.dateUtils.formatDate(familyMember.dateOfBirth, 'yyyy-MM-dd'), familyMember.dateOfBirth ? this.dateUtils.formatDate(familyMember.dateOfBirth, 'yyyy-MM-dd') : null
Validators.required
] ]
}); });
} }
@@ -101,18 +124,36 @@ export class EditFamilyMemberComponent implements OnInit {
* Submits the form and updates the client family member. * Submits the form and updates the client family member.
*/ */
submit() { submit() {
const editFamilyMemberFormData = this.editFamilyMemberForm.value; // Get form values including disabled controls like age
const formValue = {
...this.editFamilyMemberForm.getRawValue()
};
const locale = this.settingsService.language.code; const locale = this.settingsService.language.code;
const dateFormat = this.settingsService.dateFormat; const dateFormat = this.settingsService.dateFormat;
const prevDateOfBirth: Date = this.editFamilyMemberForm.value.dateOfBirth; const prevDateOfBirth: Date = formValue.dateOfBirth;
if (editFamilyMemberFormData.dateOfBirth instanceof Date) {
editFamilyMemberFormData.dateOfBirth = this.dateUtils.formatDate(prevDateOfBirth, dateFormat); // Calculate age from dateOfBirth if present
if (prevDateOfBirth) {
if (formValue.dateOfBirth instanceof Date) {
formValue.dateOfBirth = this.dateUtils.formatDate(prevDateOfBirth, dateFormat);
}
// Ensure age is calculated even if it wasn't already
if (!formValue.age && prevDateOfBirth) {
formValue.age = this.calculateAge(prevDateOfBirth);
}
} else {
// If no date of birth, remove age and dateOfBirth from submission
delete formValue.age;
delete formValue.dateOfBirth;
} }
const data = { const data = {
...editFamilyMemberFormData, ...formValue,
dateFormat, dateFormat,
locale locale
}; };
this.clientsService this.clientsService
.editFamilyMember(this.familyMemberDetails.clientId, this.familyMemberDetails.id, data) .editFamilyMember(this.familyMemberDetails.clientId, this.familyMemberDetails.id, data)
.subscribe((res) => { .subscribe((res) => {
+2 -1
View File
@@ -6,7 +6,7 @@ import { RouterLink } from '@angular/router';
import { NgIf, NgFor } from '@angular/common'; import { NgIf, NgFor } from '@angular/common';
import { MatCard, MatCardContent, MatCardActions } from '@angular/material/card'; import { MatCard, MatCardContent, MatCardActions } from '@angular/material/card';
import { MatFormField, MatLabel, MatError, MatSuffix } from '@angular/material/form-field'; import { MatFormField, MatLabel, MatError, MatSuffix, MatHint } from '@angular/material/form-field';
import { MatInput } from '@angular/material/input'; import { MatInput } from '@angular/material/input';
import { MatSelect } from '@angular/material/select'; import { MatSelect } from '@angular/material/select';
import { MatOption } from '@angular/material/core'; import { MatOption } from '@angular/material/core';
@@ -33,6 +33,7 @@ export const STANDALONE_SHARED_IMPORTS = [
MatLabel, MatLabel,
MatError, MatError,
MatSuffix, MatSuffix,
MatHint,
MatInput, MatInput,
MatSelect, MatSelect,
MatOption, MatOption,