From 6ad9b0d16dab47729478c466b15aea09a027a4b7 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Tue, 21 May 2024 15:45:15 +1200 Subject: [PATCH 01/10] Change existingEmail to googleId --- ...dmin-reject-with-reason-modal.component.ts | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts index 051d145d4d6..90ad09df5b2 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts @@ -2,7 +2,9 @@ import { Component, Input, OnInit } from '@angular/core'; import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap'; import { RejectWithReasonModalComponentResult } from './admin-reject-with-reason-modal-model'; import { environment } from '../../../../environments/environment'; +import { AdminSearchResult, InstructorAccountSearchResult, SearchService } from '../../../../services/search.service'; import { StatusMessageService } from '../../../../services/status-message.service'; +import { ErrorMessageOutput } from '../../../error-message-output'; /** * Modal to select reject account requests with reason. @@ -21,6 +23,24 @@ export class RejectWithReasonModalComponent implements OnInit { @Input() accountRequestEmail: string = ''; + existingAccount: InstructorAccountSearchResult = { + name: '', + email: '', + googleId: '', + courseId: '', + courseName: '', + isCourseDeleted: false, + institute: '', + courseJoinLink: '', + homePageLink: '', + manageAccountLink: '', + showLinks: false, + awaitingSessions: {}, + openSessions: {}, + notOpenSessions: {}, + publishedSessions: {}, + }; + rejectionReasonBody: string = '

Hi, {accountRequestName}

\n\n' + '

Thanks for your interest in using TEAMMATES. ' + 'We are unable to create a TEAMMATES instructor account for you.

' @@ -33,17 +53,40 @@ export class RejectWithReasonModalComponent implements OnInit { + 'Remedy: If you are a student but you still need an instructor account, ' + 'please send your justification to {supportEmail}

\n\n' + '

Reason: You already have an account for this email address and this institution.
' - + 'Remedy: You can login to TEAMMATES using your Google account {existingEmail}

\n\n' + + 'Remedy: You can login to TEAMMATES using your Google account: {googleId}

\n\n' + '

If you need further clarification or would like to appeal this decision, please ' + 'feel free to contact us at {supportEmail}

' + '

Regards,
TEAMMATES Team.

'; rejectionReasonTitle: string = 'We are Unable to Create an Account for you'; - constructor(public activeModal: NgbActiveModal, public statusMessageService: StatusMessageService) {} + constructor( + public activeModal: NgbActiveModal, + public statusMessageService: StatusMessageService, + + private searchService: SearchService, + ) {} ngOnInit(): void { this.rejectionReasonBody = this.rejectionReasonBody.replace('{accountRequestName}', this.accountRequestName); - this.rejectionReasonBody = this.rejectionReasonBody.replace('{existingEmail}', this.accountRequestEmail); + + this.searchService.searchAdmin(this.accountRequestEmail) + .subscribe({ + next: (resp: AdminSearchResult) => { + const hasInstructors: boolean = !!(resp.instructors && resp.instructors.length); + + if (!hasInstructors) { + this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', 'GOOGLEID NOT FOUND'); + return; + } + + this.existingAccount = resp.instructors[0]; + this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', this.existingAccount.googleId); + }, + error: (resp: ErrorMessageOutput) => { + this.statusMessageService.showErrorToast(resp.error.message); + }, + }); + this.rejectionReasonBody = this.rejectionReasonBody.replaceAll('{supportEmail}', environment.supportEmail); } From 76285736dbdf74207cbef325f9f117194b276f5f Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Tue, 21 May 2024 16:00:51 +1200 Subject: [PATCH 02/10] Add extra info to instructions --- .../admin-reject-with-reason-modal.component.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts index 90ad09df5b2..f8dcd278a9a 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts @@ -54,6 +54,8 @@ export class RejectWithReasonModalComponent implements OnInit { + 'please send your justification to {supportEmail}

\n\n' + '

Reason: You already have an account for this email address and this institution.
' + 'Remedy: You can login to TEAMMATES using your Google account: {googleId}

\n\n' + + '

If you are logged into multiple Google accounts, remember to logout from other Google accounts first, ' + + 'or use an incognito Browser window. Let us know (with a screenshot) if that doesn\'t work.

' + '

If you need further clarification or would like to appeal this decision, please ' + 'feel free to contact us at {supportEmail}

' + '

Regards,
TEAMMATES Team.

'; From 47516aea2b6b93cfdfa492a28c2be0c35d417079 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Tue, 21 May 2024 16:10:07 +1200 Subject: [PATCH 03/10] Fix missing googleId if multiple accounts exist --- .../admin-reject-with-reason-modal.component.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts index f8dcd278a9a..45717d36444 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts @@ -81,7 +81,12 @@ export class RejectWithReasonModalComponent implements OnInit { return; } - this.existingAccount = resp.instructors[0]; + for (const instructor of resp.instructors) { + if (instructor.googleId !== '') { + this.existingAccount = instructor; + } + } + this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', this.existingAccount.googleId); }, error: (resp: ErrorMessageOutput) => { From cd32bb3c9325884fa9df7c7bd38603051db33ac3 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 23 May 2024 05:12:10 +1200 Subject: [PATCH 04/10] Add function name to test name --- .../admin-reject-with-reason-modal.component.spec.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts index 66c831077b0..59391826287 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts @@ -36,7 +36,7 @@ describe('RejectWithReasonModal', () => { expect(fixture).toMatchSnapshot(); }); - it('should show error message when title is empty upon submitting', () => { + it('reject: should show error message when title is empty upon submitting', () => { component.rejectionReasonTitle = ''; fixture.detectChanges(); @@ -51,7 +51,7 @@ describe('RejectWithReasonModal', () => { expect(spyStatusMessageService).toHaveBeenCalled(); }); - it('should show error message when body is empty upon submitting', () => { + it('reject: should show error message when body is empty upon submitting', () => { component.rejectionReasonBody = ''; fixture.detectChanges(); @@ -65,7 +65,7 @@ describe('RejectWithReasonModal', () => { expect(spyStatusMessageService).toHaveBeenCalled(); }); - it('should close modal with data', () => { + it('reject: should close modal with data', () => { const spyActiveModal = jest.spyOn(component.activeModal, 'close'); component.rejectionReasonTitle = 'Rejection Title'; component.rejectionReasonBody = 'Rejection Body'; From 0f48d2208d8047daea9e8d94b998d6279a0c82cc Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 23 May 2024 16:40:18 +1200 Subject: [PATCH 05/10] Move account search to function --- .../admin-reject-with-reason-modal.component.ts | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts index 45717d36444..2849a8c2aa8 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts @@ -70,7 +70,16 @@ export class RejectWithReasonModalComponent implements OnInit { ngOnInit(): void { this.rejectionReasonBody = this.rejectionReasonBody.replace('{accountRequestName}', this.accountRequestName); + this.rejectionReasonBody = this.rejectionReasonBody.replaceAll('{supportEmail}', environment.supportEmail); + + this.replaceGoogleId(); + } + + onRejectionReasonBodyChange(updatedText: string): void { + this.rejectionReasonBody = updatedText; + } + replaceGoogleId(): void { this.searchService.searchAdmin(this.accountRequestEmail) .subscribe({ next: (resp: AdminSearchResult) => { @@ -93,12 +102,6 @@ export class RejectWithReasonModalComponent implements OnInit { this.statusMessageService.showErrorToast(resp.error.message); }, }); - - this.rejectionReasonBody = this.rejectionReasonBody.replaceAll('{supportEmail}', environment.supportEmail); - } - - onRejectionReasonBodyChange(updatedText: string): void { - this.rejectionReasonBody = updatedText; } /** From 1f33eff60d5022a8cf383994cbe1f0c5ad2fd229 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 23 May 2024 22:51:45 +1200 Subject: [PATCH 06/10] Change missing googleId string to be shorter --- .../admin-reject-with-reason-modal.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts index 2849a8c2aa8..154773486ec 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts @@ -86,7 +86,7 @@ export class RejectWithReasonModalComponent implements OnInit { const hasInstructors: boolean = !!(resp.instructors && resp.instructors.length); if (!hasInstructors) { - this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', 'GOOGLEID NOT FOUND'); + this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', 'NO_GOOGLEID'); return; } From 7c247b664f9eab5f214f8c02195eb35757e15db0 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Fri, 24 May 2024 04:24:18 +1200 Subject: [PATCH 07/10] Add check for accounts with no googleId --- .../admin-reject-with-reason-modal.component.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts index 154773486ec..080b813161d 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.ts @@ -96,7 +96,12 @@ export class RejectWithReasonModalComponent implements OnInit { } } - this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', this.existingAccount.googleId); + if (this.existingAccount.googleId === '') { + // When an instructor account exists, but for some reason does not have a googleId + this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', 'NO_GOOGLEID'); + } else { + this.rejectionReasonBody = this.rejectionReasonBody.replace('{googleId}', this.existingAccount.googleId); + } }, error: (resp: ErrorMessageOutput) => { this.statusMessageService.showErrorToast(resp.error.message); From 563cc9fd1e23fdf3407d89a9938995c53e239690 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Fri, 24 May 2024 04:36:50 +1200 Subject: [PATCH 08/10] Add tests for replaceGoogleId --- ...t-with-reason-modal.component.spec.ts.snap | 2 + ...reject-with-reason-modal.component.spec.ts | 66 ++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/__snapshots__/admin-reject-with-reason-modal.component.spec.ts.snap b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/__snapshots__/admin-reject-with-reason-modal.component.spec.ts.snap index 6d39aa0d076..f657de589c6 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/__snapshots__/admin-reject-with-reason-modal.component.spec.ts.snap +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/__snapshots__/admin-reject-with-reason-modal.component.spec.ts.snap @@ -5,8 +5,10 @@ exports[`RejectWithReasonModal should show empty title and body 1`] = ` accountRequestEmail="" accountRequestName="" activeModal={[Function NgbActiveModal]} + existingAccount={[Function Object]} rejectionReasonBody={[Function String]} rejectionReasonTitle={[Function String]} + searchService={[Function SearchService]} statusMessageService={[Function StatusMessageService]} >
{ + let searchService: SearchService; let statusMessageService: StatusMessageService; let fixture: ComponentFixture; let component: RejectWithReasonModalComponent; @@ -15,14 +48,16 @@ describe('RejectWithReasonModal', () => { declarations: [], imports: [ HttpClientTestingModule, + RouterTestingModule, ], - providers: [NgbActiveModal, StatusMessageService], + providers: [NgbActiveModal, SearchService, StatusMessageService], }) .compileComponents(); })); beforeEach(() => { fixture = TestBed.createComponent(RejectWithReasonModalComponent); + searchService = TestBed.inject(SearchService); statusMessageService = TestBed.inject(StatusMessageService); fixture.detectChanges(); component = fixture.componentInstance; @@ -36,6 +71,35 @@ describe('RejectWithReasonModal', () => { expect(fixture).toMatchSnapshot(); }); + it('replaceGoogleId: should set the googleId to an empty string if no instructor accounts are found', () => { + jest.spyOn(searchService, 'searchAdmin').mockReturnValue(of({ + students: [], + instructors: [], + accountRequests: [], + })); + + component.replaceGoogleId(); + + expect(component.existingAccount.googleId).toEqual(''); + }); + + it('replaceGoogleId: should set the googleId to the instructor accounts googleId', () => { + const testInstructor = { + ...DEFAULT_INSTRUCTOR_SEARCH_RESULT, + googleId: 'instructorGoogleId', + }; + + jest.spyOn(searchService, 'searchAdmin').mockReturnValue(of({ + students: [], + instructors: [testInstructor], + accountRequests: [], + })); + + component.replaceGoogleId(); + + expect(component.existingAccount.googleId).toEqual('instructorGoogleId'); + }); + it('reject: should show error message when title is empty upon submitting', () => { component.rejectionReasonTitle = ''; fixture.detectChanges(); From 7046a10cb910eeaaf18566949548335dc49191da Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Mon, 27 May 2024 17:14:15 +1200 Subject: [PATCH 09/10] Change instructor search result to use createBuilder --- .../admin-reject-with-reason-modal.component.spec.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts index 1dff8bd1463..43ba22100f5 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts @@ -10,6 +10,7 @@ import { SearchService, } from '../../../../services/search.service'; import { StatusMessageService } from '../../../../services/status-message.service'; +import { createBuilder } from '../../../../test-helpers/generic-builder'; const DEFAULT_FEEDBACK_SESSION_GROUP: FeedbackSessionsGroup = { sessionName: { @@ -19,7 +20,7 @@ const DEFAULT_FEEDBACK_SESSION_GROUP: FeedbackSessionsGroup = { }, }; -const DEFAULT_INSTRUCTOR_SEARCH_RESULT: InstructorAccountSearchResult = { +const instructorAccountSearchResultBuilder = createBuilder({ name: 'name', email: 'email', googleId: 'googleId', @@ -35,7 +36,7 @@ const DEFAULT_INSTRUCTOR_SEARCH_RESULT: InstructorAccountSearchResult = { openSessions: DEFAULT_FEEDBACK_SESSION_GROUP, notOpenSessions: DEFAULT_FEEDBACK_SESSION_GROUP, publishedSessions: DEFAULT_FEEDBACK_SESSION_GROUP, -}; +}); describe('RejectWithReasonModal', () => { let searchService: SearchService; @@ -84,10 +85,7 @@ describe('RejectWithReasonModal', () => { }); it('replaceGoogleId: should set the googleId to the instructor accounts googleId', () => { - const testInstructor = { - ...DEFAULT_INSTRUCTOR_SEARCH_RESULT, - googleId: 'instructorGoogleId', - }; + const testInstructor = instructorAccountSearchResultBuilder.googleId('instructorGoogleId').build(); jest.spyOn(searchService, 'searchAdmin').mockReturnValue(of({ students: [], From 88ae2394a02a354f0cb45278013f90b5abb3d53e Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Mon, 27 May 2024 17:16:34 +1200 Subject: [PATCH 10/10] Change a test name to fit convention --- .../admin-reject-with-reason-modal.component.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts index 43ba22100f5..e59fb7814cf 100644 --- a/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts +++ b/src/web/app/components/account-requests-table/admin-reject-with-reason-modal/admin-reject-with-reason-modal.component.spec.ts @@ -84,7 +84,8 @@ describe('RejectWithReasonModal', () => { expect(component.existingAccount.googleId).toEqual(''); }); - it('replaceGoogleId: should set the googleId to the instructor accounts googleId', () => { + it('replaceGoogleId: should set the googleId to the instructor accounts googleId ' + + 'if an instructor account is found', () => { const testInstructor = instructorAccountSearchResultBuilder.googleId('instructorGoogleId').build(); jest.spyOn(searchService, 'searchAdmin').mockReturnValue(of({