From bd5d016f7953f0852522e98f8c6e0db7b34aa2d7 Mon Sep 17 00:00:00 2001 From: Saniya <37302318+Saby-Bishops@users.noreply.github.com> Date: Wed, 3 Dec 2025 14:27:01 -0600 Subject: [PATCH 1/2] Fix achievements update form handling --- ...sers-achievements-update.component.spec.ts | 64 +++++++++++ .../users-achievements-update.component.ts | 107 +++++++++++++----- 2 files changed, 140 insertions(+), 31 deletions(-) create mode 100644 src/app/users/users-achievements/users-achievements-update.component.spec.ts diff --git a/src/app/users/users-achievements/users-achievements-update.component.spec.ts b/src/app/users/users-achievements/users-achievements-update.component.spec.ts new file mode 100644 index 0000000000..e6cfd33ed7 --- /dev/null +++ b/src/app/users/users-achievements/users-achievements-update.component.spec.ts @@ -0,0 +1,64 @@ +import { UntypedFormBuilder } from '@angular/forms'; +import { of } from 'rxjs'; + +import { UsersAchievementsUpdateComponent } from './users-achievements-update.component'; + +describe('UsersAchievementsUpdateComponent', () => { + const fb = new UntypedFormBuilder(); + const user = { + _id: 'user-id', + name: 'User Name', + firstName: 'User', + middleName: '', + lastName: 'Name' + } as any; + const configuration = { code: 'conf', parentCode: 'parent' } as any; + + const couchService = { post: jasmine.createSpy('post') } as any; + const route = {} as any; + const router = { navigate: jasmine.createSpy('navigate') } as any; + const userService = { get: () => user, minBirthDate: new Date(), updateUser: jasmine.createSpy('updateUser') } as any; + const planetMessageService = { showMessage: jasmine.createSpy('showMessage'), showAlert: jasmine.createSpy('showAlert') } as any; + const dialogsFormService = { openDialogsForm: jasmine.createSpy('openDialogsForm') } as any; + const validatorService = { notDateInFuture$: () => of(null) } as any; + const stateService = { configuration } as any; + const planetStepListService = { stepMoveClick$: of(null) } as any; + + it('initializes achievement-related form arrays with loaded data', () => { + const achievementsResponse = { + _id: 'user-id@conf', + _rev: '1-abc', + purpose: 'Purpose', + goals: 'Goals', + achievementsHeader: 'Header', + achievements: [ { title: 'First', description: 'desc', date: '2023-01-01' } ], + references: [ { name: 'Ref 1', email: 'ref@example.com' } ], + links: [ { title: 'Link 1', url: 'https://example.com' } ], + otherInfo: [ 'note' ], + dateSortOrder: 'asc' + } as any; + const usersAchievementsService = { getAchievements: jasmine.createSpy('getAchievements').and.returnValue(of(achievementsResponse)) } as any; + + const component = new UsersAchievementsUpdateComponent( + fb, + couchService, + route, + router, + userService, + planetMessageService, + usersAchievementsService, + dialogsFormService, + stateService, + validatorService, + planetStepListService + ); + + component.ngOnInit(); + + expect(component.achievements.length).toBe(1); + expect(component.references.length).toBe(1); + expect(component.links.length).toBe(1); + expect(component.editForm.controls.dateSortOrder.value).toBe('asc'); + expect(component.hasUnsavedChanges).toBeFalse(); + }); +}); diff --git a/src/app/users/users-achievements/users-achievements-update.component.ts b/src/app/users/users-achievements/users-achievements-update.component.ts index c3d162ead4..74112aa119 100644 --- a/src/app/users/users-achievements/users-achievements-update.component.ts +++ b/src/app/users/users-achievements/users-achievements-update.component.ts @@ -67,12 +67,18 @@ export class UsersAchievementsUpdateComponent implements OnInit, OnDestroy, CanC catchError(() => this.usersAchievementsService.getAchievements(this.user._id)) ) .subscribe((achievements) => { - this.editForm.patchValue(achievements); - this.editForm.controls.achievements = this.fb.array(achievements.achievements || []); - this.editForm.controls.references = this.fb.array(achievements.references || []); - this.editForm.controls.links = this.fb.array(achievements.links || []); + this.editForm.patchValue({ + purpose: achievements.purpose, + goals: achievements.goals, + achievementsHeader: achievements.achievementsHeader, + sendToNation: achievements.sendToNation, + dateSortOrder: achievements.dateSortOrder || 'none' + }); + this.editForm.setControl('achievements', this.buildAchievementsFormArray(achievements.achievements)); + this.editForm.setControl('references', this.buildReferencesFormArray(achievements.references)); + this.editForm.setControl('links', this.buildLinksFormArray(achievements.links)); // Keeping older otherInfo property so we don't lose this info on database - this.editForm.controls.otherInfo = this.fb.array(achievements.otherInfo || []); + this.editForm.setControl('otherInfo', this.buildOtherInfoFormArray(achievements.otherInfo)); if (this.docInfo._id === achievements._id) { this.docInfo._rev = achievements._rev; @@ -82,6 +88,8 @@ export class UsersAchievementsUpdateComponent implements OnInit, OnDestroy, CanC }, (error) => { console.log(error); this.achievementNotFound = true; + this.captureInitialState(); + this.onFormChanges(); }); this.planetStepListService.stepMoveClick$.pipe(takeUntil(this.onDestroy$)).subscribe( @@ -104,37 +112,17 @@ export class UsersAchievementsUpdateComponent implements OnInit, OnDestroy, CanC onFormChanges() { this.editForm.valueChanges .pipe( - debounce(() => race(interval(200), of(true))) + debounce(() => race(interval(200), of(true))), + takeUntil(this.onDestroy$) ) - .subscribe(() => { - const currentState = JSON.stringify({ - editForm: { - ...this.editForm.value, - achievements: this.achievements.value, - references: this.references.value, - links: this.links.value - }, - profileForm: this.profileForm.value - }); - this.hasUnsavedChanges = currentState !== this.initialFormValues; - }); + .subscribe(() => this.updateUnsavedChangesFlag()); this.profileForm.valueChanges .pipe( - debounce(() => race(interval(200), of(true))) + debounce(() => race(interval(200), of(true))), + takeUntil(this.onDestroy$) ) - .subscribe(() => { - const currentState = JSON.stringify({ - editForm: { - ...this.editForm.value, - achievements: this.achievements.value, - references: this.references.value, - links: this.links.value - }, - profileForm: this.profileForm.value - }); - this.hasUnsavedChanges = currentState !== this.initialFormValues; - }); + .subscribe(() => this.updateUnsavedChangesFlag()); } ngOnDestroy() { @@ -171,6 +159,63 @@ export class UsersAchievementsUpdateComponent implements OnInit, OnDestroy, CanC }); } + private buildAchievementsFormArray(achievements: any[] = []) { + return this.fb.array(achievements.map((achievement) => this.createAchievementGroup(achievement))); + } + + private buildReferencesFormArray(references: any[] = []) { + return this.fb.array(references.map((reference) => this.createReferenceGroup(reference))); + } + + private buildLinksFormArray(links: any[] = []) { + return this.fb.array(links.map((link) => this.createLinkGroup(link))); + } + + private buildOtherInfoFormArray(otherInfo: any[] = []) { + return this.fb.array(otherInfo); + } + + private createAchievementGroup(achievement: any = { title: '', description: '', link: '', date: '' }) { + if (typeof achievement === 'string') { + achievement = { title: '', description: achievement, link: '', date: '' }; + } + return this.fb.group({ + title: [ achievement.title || '', CustomValidators.required ], + description: [ achievement.description || '' ], + link: [ achievement.link || '', [], CustomValidators.validLink ], + date: [ achievement.date || '', null, ac => this.validatorService.notDateInFuture$(ac) ] + }); + } + + private createReferenceGroup(reference: any = { name: '' }) { + return this.fb.group({ + name: [ reference.name || '', CustomValidators.required ], + relationship: reference.relationship || '', + phone: reference.phone || '', + email: [ reference.email || '', Validators.email ] + }); + } + + private createLinkGroup(link: any = { title: '', url: '' }) { + return this.fb.group({ + title: [ link.title || '', CustomValidators.required ], + url: [ link.url || '', CustomValidators.required, CustomValidators.validLink ] + }); + } + + private updateUnsavedChangesFlag() { + const currentState = JSON.stringify({ + editForm: { + ...this.editForm.value, + achievements: this.achievements.value, + references: this.references.value, + links: this.links.value + }, + profileForm: this.profileForm.value + }); + this.hasUnsavedChanges = currentState !== this.initialFormValues; + } + addAchievement(index = -1, achievement = { title: '', description: '', link: '', date: '' }) { if (typeof achievement === 'string') { achievement = { title: '', description: achievement, link: '', date: '' }; From 753749d188084abadf446b15b94e2161130bfde8 Mon Sep 17 00:00:00 2001 From: Saniya <37302318+Saby-Bishops@users.noreply.github.com> Date: Wed, 3 Dec 2025 15:08:41 -0600 Subject: [PATCH 2/2] Delete src/app/users/users-achievements/users-achievements-update.component.spec.ts --- ...sers-achievements-update.component.spec.ts | 64 ------------------- 1 file changed, 64 deletions(-) delete mode 100644 src/app/users/users-achievements/users-achievements-update.component.spec.ts diff --git a/src/app/users/users-achievements/users-achievements-update.component.spec.ts b/src/app/users/users-achievements/users-achievements-update.component.spec.ts deleted file mode 100644 index e6cfd33ed7..0000000000 --- a/src/app/users/users-achievements/users-achievements-update.component.spec.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { UntypedFormBuilder } from '@angular/forms'; -import { of } from 'rxjs'; - -import { UsersAchievementsUpdateComponent } from './users-achievements-update.component'; - -describe('UsersAchievementsUpdateComponent', () => { - const fb = new UntypedFormBuilder(); - const user = { - _id: 'user-id', - name: 'User Name', - firstName: 'User', - middleName: '', - lastName: 'Name' - } as any; - const configuration = { code: 'conf', parentCode: 'parent' } as any; - - const couchService = { post: jasmine.createSpy('post') } as any; - const route = {} as any; - const router = { navigate: jasmine.createSpy('navigate') } as any; - const userService = { get: () => user, minBirthDate: new Date(), updateUser: jasmine.createSpy('updateUser') } as any; - const planetMessageService = { showMessage: jasmine.createSpy('showMessage'), showAlert: jasmine.createSpy('showAlert') } as any; - const dialogsFormService = { openDialogsForm: jasmine.createSpy('openDialogsForm') } as any; - const validatorService = { notDateInFuture$: () => of(null) } as any; - const stateService = { configuration } as any; - const planetStepListService = { stepMoveClick$: of(null) } as any; - - it('initializes achievement-related form arrays with loaded data', () => { - const achievementsResponse = { - _id: 'user-id@conf', - _rev: '1-abc', - purpose: 'Purpose', - goals: 'Goals', - achievementsHeader: 'Header', - achievements: [ { title: 'First', description: 'desc', date: '2023-01-01' } ], - references: [ { name: 'Ref 1', email: 'ref@example.com' } ], - links: [ { title: 'Link 1', url: 'https://example.com' } ], - otherInfo: [ 'note' ], - dateSortOrder: 'asc' - } as any; - const usersAchievementsService = { getAchievements: jasmine.createSpy('getAchievements').and.returnValue(of(achievementsResponse)) } as any; - - const component = new UsersAchievementsUpdateComponent( - fb, - couchService, - route, - router, - userService, - planetMessageService, - usersAchievementsService, - dialogsFormService, - stateService, - validatorService, - planetStepListService - ); - - component.ngOnInit(); - - expect(component.achievements.length).toBe(1); - expect(component.references.length).toBe(1); - expect(component.links.length).toBe(1); - expect(component.editForm.controls.dateSortOrder.value).toBe('asc'); - expect(component.hasUnsavedChanges).toBeFalse(); - }); -});