From 0b293505b6bc2e4f3037a4b7762bbef2ac636e7b Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 30 May 2024 11:56:35 +1200 Subject: [PATCH 01/25] Change admin add account flow to use instructor request flow --- .../admin-home-page.component.ts | 47 +++++++++++++------ 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts index 0fd8d9f6eb1..da55436e699 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts @@ -7,6 +7,7 @@ import { LinkService } from '../../../services/link.service'; import { StatusMessageService } from '../../../services/status-message.service'; import { TimezoneService } from '../../../services/timezone.service'; import { AccountRequest, AccountRequests } from '../../../types/api-output'; +import { AccountCreateRequest } from '../../../types/api-request'; import { AccountRequestTableRowModel } from '../../components/account-requests-table/account-request-table-model'; import { FormatDateDetailPipe } from '../../components/teammates-common/format-date-detail.pipe'; import { ErrorMessageOutput } from '../../error-message-output'; @@ -64,12 +65,18 @@ export class AdminHomePageComponent implements OnInit { invalidLines.push(instructorDetail); continue; } - this.instructorsConsolidated.push({ - name: instructorDetailSplit[0], - email: instructorDetailSplit[1], - institution: instructorDetailSplit[2], - status: 'PENDING', - isCurrentlyBeingEdited: false, + + const requestData: AccountCreateRequest = { + instructorEmail: instructorDetailSplit[1], + instructorName: instructorDetailSplit[0], + instructorInstitution: instructorDetailSplit[2], + }; + + this.accountService.createAccountRequest(requestData) + .subscribe({ + next: () => { + this.fetchAccountRequests(); + }, }); } this.instructorDetails = invalidLines.join('\r\n'); @@ -83,16 +90,26 @@ export class AdminHomePageComponent implements OnInit { // TODO handle error return; } - this.instructorsConsolidated.push({ - name: this.instructorName, - email: this.instructorEmail, - institution: this.instructorInstitution, - status: 'PENDING', - isCurrentlyBeingEdited: false, + + const requestData: AccountCreateRequest = { + instructorEmail: this.instructorEmail, + instructorName: this.instructorName, + instructorInstitution: this.instructorInstitution, + }; + + this.accountService.createAccountRequest(requestData) + .subscribe({ + next: () => { + this.instructorName = ''; + this.instructorEmail = ''; + this.instructorInstitution = ''; + + this.fetchAccountRequests(); + }, + error: (resp: ErrorMessageOutput) => { + this.statusMessageService.showErrorToast(resp.error.message); + }, }); - this.instructorName = ''; - this.instructorEmail = ''; - this.instructorInstitution = ''; } /** From 13f7add2b7a93799ee9510196c147c628ea656f5 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 30 May 2024 12:06:33 +1200 Subject: [PATCH 02/25] Remove old flow results table --- .../admin-home-page.component.html | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.html b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.html index bd4e12fa2db..81024bc9040 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.html +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.html @@ -47,41 +47,4 @@ -
-
- Result -
-
-
- - - - - - - - - - - - - - -
NameEmailInstitutionActionStatusMessage
- -
-
-
- From 4f24c5e9d9d33d542a7d3454814e7bf1a7541bcc Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 30 May 2024 12:09:03 +1200 Subject: [PATCH 03/25] Remove old flow functions --- .../admin-home-page.component.ts | 68 +------------------ 1 file changed, 1 insertion(+), 67 deletions(-) diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts index da55436e699..70bfede9de0 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts @@ -1,12 +1,10 @@ import { Component, OnInit } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { finalize } from 'rxjs/operators'; import { InstructorData } from './instructor-data'; import { AccountService } from '../../../services/account.service'; -import { LinkService } from '../../../services/link.service'; import { StatusMessageService } from '../../../services/status-message.service'; import { TimezoneService } from '../../../services/timezone.service'; -import { AccountRequest, AccountRequests } from '../../../types/api-output'; +import { AccountRequests } from '../../../types/api-output'; import { AccountCreateRequest } from '../../../types/api-request'; import { AccountRequestTableRowModel } from '../../components/account-requests-table/account-request-table-model'; import { FormatDateDetailPipe } from '../../components/teammates-common/format-date-detail.pipe'; @@ -40,7 +38,6 @@ export class AdminHomePageComponent implements OnInit { private accountService: AccountService, private statusMessageService: StatusMessageService, private timezoneService: TimezoneService, - private linkService: LinkService, private formatDateDetailPipe: FormatDateDetailPipe, ) {} @@ -112,69 +109,6 @@ export class AdminHomePageComponent implements OnInit { }); } - /** - * Adds the instructor at the i-th index. - */ - addInstructor(i: number): void { - const instructor: InstructorData = this.instructorsConsolidated[i]; - if (this.instructorsConsolidated[i].isCurrentlyBeingEdited - || (instructor.status !== 'PENDING' && instructor.status !== 'FAIL')) { - return; - } - this.activeRequests += 1; - instructor.status = 'ADDING'; - - this.isAddingInstructors = true; - this.accountService.createAccountRequest({ - instructorEmail: instructor.email, - instructorName: instructor.name, - instructorInstitution: instructor.institution, - }) - .pipe(finalize(() => { - this.isAddingInstructors = false; - })) - .subscribe({ - next: (resp: AccountRequest) => { - instructor.status = 'SUCCESS'; - instructor.statusCode = 200; - instructor.joinLink = this.linkService.generateAccountRegistrationLink(resp.registrationKey); - this.activeRequests -= 1; - }, - error: (resp: ErrorMessageOutput) => { - instructor.status = 'FAIL'; - instructor.statusCode = resp.status; - instructor.message = resp.error.message; - this.activeRequests -= 1; - }, - }); - } - - /** - * Removes the instructor at the i-th index. - */ - removeInstructor(i: number): void { - this.instructorsConsolidated.splice(i, 1); - } - - /** - * Sets the i-th instructor data row's edit mode status. - * - * @param i The index. - * @param isEnabled Whether the edit mode status is enabled. - */ - setInstructorRowEditModeEnabled(i: number, isEnabled: boolean): void { - this.instructorsConsolidated[i].isCurrentlyBeingEdited = isEnabled; - } - - /** - * Adds all the pending and failed-to-add instructors. - */ - addAllInstructors(): void { - for (let i: number = 0; i < this.instructorsConsolidated.length; i += 1) { - this.addInstructor(i); - } - } - private formatAccountRequests(requests: AccountRequests): AccountRequestTableRowModel[] { const timezone: string = this.timezoneService.guessTimezone() || 'UTC'; return requests.accountRequests.map((request) => { From 880278fa29754a60aa60fbb6e64a76bfff78639d Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 30 May 2024 12:43:19 +1200 Subject: [PATCH 04/25] Remove old flow variables --- .../__snapshots__/admin-home-page.component.spec.ts.snap | 6 ------ .../admin-home-page/admin-home-page.component.ts | 4 ---- 2 files changed, 10 deletions(-) diff --git a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap index b091996522f..2a8a92b70f0 100644 --- a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap +++ b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap @@ -11,8 +11,6 @@ exports[`AdminHomePageComponent should snap with default view 1`] = ` instructorEmail="" instructorInstitution="" instructorName="" - instructorsConsolidated={[Function Array]} - isAddingInstructors="false" items$={[Function Observable]} linkService={[Function LinkService]} pageSize={[Function Number]} @@ -139,8 +137,6 @@ exports[`AdminHomePageComponent should snap with disabled adding instructor butt instructorEmail="" instructorInstitution="" instructorName="" - instructorsConsolidated={[Function Array]} - isAddingInstructors={[Function Boolean]} items$={[Function Observable]} linkService={[Function LinkService]} pageSize={[Function Number]} @@ -431,8 +427,6 @@ exports[`AdminHomePageComponent should snap with some instructors details 1`] = instructorEmail="" instructorInstitution="" instructorName="" - instructorsConsolidated={[Function Array]} - isAddingInstructors="false" items$={[Function Observable]} linkService={[Function LinkService]} pageSize={[Function Number]} diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts index 70bfede9de0..ddc88d081d5 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.ts @@ -1,6 +1,5 @@ import { Component, OnInit } from '@angular/core'; import { Observable, of } from 'rxjs'; -import { InstructorData } from './instructor-data'; import { AccountService } from '../../../services/account.service'; import { StatusMessageService } from '../../../services/status-message.service'; import { TimezoneService } from '../../../services/timezone.service'; @@ -25,15 +24,12 @@ export class AdminHomePageComponent implements OnInit { instructorEmail: string = ''; instructorInstitution: string = ''; - instructorsConsolidated: InstructorData[] = []; accountReqs: AccountRequestTableRowModel[] = []; activeRequests: number = 0; currentPage: number = 1; pageSize: number = 20; items$: Observable = of([]); - isAddingInstructors: boolean = false; - constructor( private accountService: AccountService, private statusMessageService: StatusMessageService, From 86ecbaf7a8c96228f7ff4da2b83b4162fd8bb22a Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Thu, 30 May 2024 13:42:41 +1200 Subject: [PATCH 05/25] Remove old flow tests --- .../admin-home-page.component.spec.ts | 281 ------------------ 1 file changed, 281 deletions(-) diff --git a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts index 357a21ba951..7b147c14305 100644 --- a/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts +++ b/src/web/app/pages-admin/admin-home-page/admin-home-page.component.spec.ts @@ -149,259 +149,6 @@ describe('AdminHomePageComponent', () => { }); }); - it('should remove instructor out of queue if REMOVE is requested', () => { - const instructorData: InstructorData = { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }; - component.instructorsConsolidated = [instructorData]; - fixture.detectChanges(); - - const index: number = 0; - component.removeInstructor(index); - - expect(component.instructorsConsolidated.includes(instructorData)).toBeFalsy(); - expect(component.instructorsConsolidated.length).toEqual(0); - }); - - it('should add instructor and update field when successful', () => { - component.instructorsConsolidated = [ - { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - ]; - jest.spyOn(accountService, 'createAccountRequest').mockReturnValue(of({ - id: 'some.person@example.com%NUS', - email: 'some.person@example.com', - name: 'Some Person', - institute: 'NUS', - status: AccountRequestStatus.APPROVED, - registrationKey: 'registrationKey', - createdAt: 528, - })); - jest.spyOn(linkService, 'generateAccountRegistrationLink') - .mockReturnValue('http://localhost:4200/web/join?iscreatingaccount=true&key=registrationKey'); - fixture.detectChanges(); - - const index: number = 0; - component.addInstructor(index); - - expect(component.instructorsConsolidated[index].status).toEqual('SUCCESS'); - expect(component.instructorsConsolidated[index].joinLink) - .toEqual('http://localhost:4200/web/join?iscreatingaccount=true&key=registrationKey'); - expect(component.activeRequests).toEqual(0); - }); - - it('should not add instructor and update field during failure', () => { - component.instructorsConsolidated = [ - { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - ]; - jest.spyOn(accountService, 'createAccountRequest').mockReturnValue(throwError(() => ({ - error: { - message: 'This is the error message', - }, - }))); - fixture.detectChanges(); - - const index: number = 0; - component.addInstructor(index); - - expect(component.instructorsConsolidated[index].status).toEqual('FAIL'); - expect(component.instructorsConsolidated[index].message).toEqual('This is the error message'); - expect(component.activeRequests).toEqual(0); - }); - - it('should enter edit mode for only the specified instructor', () => { - component.instructorsConsolidated = [ - { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - { - name: 'Instructor B', - email: 'instructorb@example.com', - institution: 'Sample Institution B', - status: 'SUCCESS', - statusCode: 200, - isCurrentlyBeingEdited: false, - joinLink: 'http://localhost:4200/web/join', - message: 'This should not be displayed', - }, - { - name: 'Instructor C', - email: 'instructorc@example.com', - institution: 'Sample Institution C', - status: 'FAIL', - statusCode: 400, - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'The instructor cannot be added for some reason', - }, - ]; - - const index: number = 2; - component.setInstructorRowEditModeEnabled(index, true); - - for (let i: number = 0; i < component.instructorsConsolidated.length; i += 1) { - expect(component.instructorsConsolidated[i].isCurrentlyBeingEdited).toEqual(i === index); - } - }); - - it('should exit edit mode for only the specified instructor', () => { - component.instructorsConsolidated = [ - { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - { - name: 'Instructor B', - email: 'instructorb@example.com', - institution: 'Sample Institution B', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - { - name: 'Instructor C', - email: 'instructorc@example.com', - institution: 'Sample Institution C', - status: 'FAIL', - statusCode: 400, - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'The instructor cannot be added for some reason', - }, - ]; - for (let i: number = 0; i < component.instructorsConsolidated.length; i += 1) { - component.setInstructorRowEditModeEnabled(i, true); - } - fixture.detectChanges(); - - const index: number = 1; - component.setInstructorRowEditModeEnabled(index, false); - - for (let i: number = 0; i < component.instructorsConsolidated.length; i += 1) { - expect(component.instructorsConsolidated[i].isCurrentlyBeingEdited).toEqual(i !== index); - } - }); - - it('should add all instructors when prompted', () => { - component.instructorsConsolidated = [ - { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - { - name: 'Instructor B', - email: 'instructorb@example.com', - institution: 'Sample Institution B', - status: 'SUCCESS', - statusCode: 200, - isCurrentlyBeingEdited: false, - joinLink: 'http://localhost:4200/web/join', - message: 'This should not be displayed', - }, - { - name: 'Instructor C', - email: 'instructorc@example.com', - institution: 'Sample Institution C', - status: 'FAIL', - statusCode: 400, - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'The instructor cannot be added for some reason', - }, - ]; - // No need to spy here as this test only tests the number of active requests added - // Testing of adding individual instructors have been done before - fixture.detectChanges(); - - const button: any = fixture.debugElement.nativeElement.querySelector('#add-all-instructors'); - button.click(); - - expect(component.instructorsConsolidated[0].status).toEqual('ADDING'); - expect(component.instructorsConsolidated[1].status).toEqual('SUCCESS'); - expect(component.instructorsConsolidated[2].status).toEqual('ADDING'); - expect(component.activeRequests).toEqual(2); - }); - - it('should add only instructors that are not currently in edit mode when trying to add all', () => { - component.instructorsConsolidated = [ - { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - { - name: 'Instructor B', - email: 'instructorb@example.com', - institution: 'Sample Institution B', - status: 'PENDING', - isCurrentlyBeingEdited: true, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - { - name: 'Instructor C', - email: 'instructorc@example.com', - institution: 'Sample Institution C', - status: 'FAIL', - statusCode: 400, - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'The instructor cannot be added for some reason', - }, - ]; - fixture.detectChanges(); - - const addAllButton: any = fixture.debugElement.nativeElement.querySelector('#add-all-instructors'); - addAllButton.click(); - - expect(component.instructorsConsolidated[0].status).toEqual('ADDING'); - expect(component.instructorsConsolidated[1].status).toEqual('PENDING'); - expect(component.instructorsConsolidated[2].status).toEqual('ADDING'); - expect(component.activeRequests).toEqual(2); - }); - it('should snap with default view', () => { expect(fixture).toMatchSnapshot(); }); @@ -442,34 +189,6 @@ describe('AdminHomePageComponent', () => { expect(fixture).toMatchSnapshot(); }); - it('should snap with disabled adding instructor button if there are active requests', () => { - component.instructorsConsolidated = [ - { - name: 'Instructor A', - email: 'instructora@example.com', - institution: 'Sample Institution A', - status: 'ADDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - { - name: 'Instructor B', - email: 'instructorb@example.com', - institution: 'Sample Institution B', - status: 'PENDING', - isCurrentlyBeingEdited: false, - joinLink: 'This should not be displayed', - message: 'This should not be displayed', - }, - ]; - component.activeRequests = 1; - component.isAddingInstructors = true; - - fixture.detectChanges(); - expect(fixture).toMatchSnapshot(); - }); - it('should add multiple instructors split by tabs', () => { component.instructorDetails = `Instructor A \t instructora@example.com \t Sample Institution A\n Instructor B \t instructorb@example.com \t Sample Institution B`; From b97b6372c09b86d39f7b3181257059aee37f20b9 Mon Sep 17 00:00:00 2001 From: Andy Wang <128531452+Andy-W-Developer@users.noreply.github.com> Date: Fri, 31 May 2024 01:45:45 +1200 Subject: [PATCH 06/25] Change tests to new instructor request flow --- .../admin-home-page.component.spec.ts.snap | 686 +++++++----------- .../admin-home-page.component.spec.ts | 320 ++++---- 2 files changed, 452 insertions(+), 554 deletions(-) diff --git a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap index 2a8a92b70f0..15acae8f022 100644 --- a/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap +++ b/src/web/app/pages-admin/admin-home-page/__snapshots__/admin-home-page.component.spec.ts.snap @@ -12,7 +12,6 @@ exports[`AdminHomePageComponent should snap with default view 1`] = ` instructorInstitution="" instructorName="" items$={[Function Observable]} - linkService={[Function LinkService]} pageSize={[Function Number]} statusMessageService={[Function StatusMessageService]} timezoneService={[Function TimezoneService]} @@ -126,11 +125,11 @@ exports[`AdminHomePageComponent should snap with default view 1`] = ` `; -exports[`AdminHomePageComponent should snap with disabled adding instructor button if there are active requests 1`] = ` +exports[`AdminHomePageComponent should snap with some instructors details 1`] = ` -
+
- - Result - -
-
+
+ + Pending Account Requests + +
@@ -276,499 +273,336 @@ exports[`AdminHomePageComponent should snap with disabled adding instructor butt Email + - + - - - - - - - - - -
- Institution + Status - Action + Institute, Country - Status + Created At + + Comments - Message + Options
- - Instructor A - + Instructor A - - instructora@example.com - + instructora@example.com - - Sample Institution A - - + PENDING - ADDING + + Sample Institution and Country A -
- - Instructor B - + Sample Created Time A - - instructorb@example.com - - - - Sample Institution B - + Sample Comment A +
-
+
- Add - + + + +
+
+ + + +
- + + +
- PENDING - -
- -
-
- -
-`; - -exports[`AdminHomePageComponent should snap with some instructors details 1`] = ` - -
-
-
- - Adding Multiple Instructors - -

- - Add Instructor Details in the format: Name | Email | Institution -

-