From 2feb176d2eb40af1df95835158dcc3e823132744 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Fri, 16 May 2025 12:24:18 -0700 Subject: [PATCH 01/33] Refactor create run dialog --- .../create-run-dialog.component.html | 206 +++++++++--------- .../create-run-dialog.component.spec.ts | 4 +- .../create-run-dialog.component.ts | 84 ++++--- 3 files changed, 147 insertions(+), 147 deletions(-) diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.html b/src/app/teacher/create-run-dialog/create-run-dialog.component.html index 85f7783d72f..0d266ffcc92 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.html +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.html @@ -1,97 +1,103 @@

Use with Class

-
- -

- {{ project.metadata.title }} (Unit ID: {{ project.id }}) -

-

1. Choose Periods

-

- - - {{ control.controls.name.value }} - - -

- - Add your own periods - - -

- For "Period 9", just enter the number 9. Separate periods with commas (e.g. "Section 1, - Section 2"). Manually named periods should be no more than 16 characters long. -

- -

2. Choose Students Per Team

- - Only 1 student - 1-3 students - - -

3. Set Schedule

-
-
- - Start date - - - - Start date is required - -
-
- - End date - - - - -
-
- - Lock After End Date   - help +@if (!isCreated) { + + +

+ {{ project.metadata.title }} + (Unit ID: {{ project.id }}) +

+

1. Choose Periods

+

+ @for (control of selectedPeriodsControl.controls; track $index) { + + + {{ control.controls.name.value }} + + + } +

+ + Add your own periods + + +

+ For "Period 9", just enter the number 9. Separate periods with commas (e.g. "Section 1, + Section 2"). Manually named periods should be no more than 16 characters long. +

+ +

2. Choose Students Per Team

+ + Only 1 student + 1-3 students + + +

3. Set Schedule

+
+
+ + Start date + + + + Start date is required + +
+
+ + End date + + + + +
+
+ + Lock After End Date   + help +
-
-

- Note: These dates can be changed at any time from your Class Schedule. Just select "Edit - Settings" from the unit's dropdown menu. -

- - - - - - - +

+ Note: These dates can be changed at any time from your Class Schedule. Just select "Edit + Settings" from the unit's dropdown menu. +

+ + + + + + +} @else {

Success! This unit has been added to your Class Schedule.

@@ -109,15 +115,11 @@

3. Set Schedule

- + @if (isGoogleUser() && isGoogleClassroomEnabled()) { + + } -
+} diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts b/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts index 1a04d0b61d9..0abeaae30d6 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.spec.ts @@ -135,7 +135,7 @@ describe('CreateRunDialogComponent', () => { component.periodsGroup.controls[0].get('checkbox').setValue(true); component.periodsGroup.controls[2].get('checkbox').setValue(true); component.periodsGroup.controls[4].get('checkbox').setValue(true); - component.customPeriods.setValue('hello'); + component['customPeriods'].setValue('hello'); expect(component.getPeriodsString()).toEqual('1,3,5,hello'); }); @@ -150,7 +150,7 @@ describe('CreateRunDialogComponent', () => { fixture.detectChanges(); expect(component.form.valid).toBeTruthy(); component.periodsGroup.controls[0].get('checkbox').setValue(false); - component.customPeriods.setValue('Section A, Section B'); + component['customPeriods'].setValue('Section A, Section B'); fixture.detectChanges(); expect(component.form.valid).toBeTruthy(); }); diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts index beaa061d1e1..0db01bd9a18 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts @@ -1,5 +1,7 @@ -import { Component, Inject } from '@angular/core'; import { CommonModule } from '@angular/common'; +import { Component, Inject } from '@angular/core'; +import { ConfigService } from '../../services/config.service'; +import { finalize } from 'rxjs/operators'; import { FormsModule, ReactiveFormsModule, @@ -9,50 +11,48 @@ import { FormGroup, Validators } from '@angular/forms'; +import { ListClassroomCoursesDialogComponent } from '../list-classroom-courses-dialog/list-classroom-courses-dialog.component'; import { MatButtonModule } from '@angular/material/button'; +import { MatCardModule } from '@angular/material/card'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatDatepickerModule } from '@angular/material/datepicker'; import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; -import { MatInputModule } from '@angular/material/input'; -import { MatCheckboxModule } from '@angular/material/checkbox'; -import { MatDatepickerModule } from '@angular/material/datepicker'; -import { provideNativeDateAdapter } from '@angular/material/core'; +import { MatDividerModule } from '@angular/material/divider'; +import { MatFormFieldModule } from '@angular/material/form-field'; import { MatIconModule } from '@angular/material/icon'; +import { MatInputModule } from '@angular/material/input'; +import { MatProgressBarModule } from '@angular/material/progress-bar'; +import { MatRadioModule } from '@angular/material/radio'; import { MatTooltipModule } from '@angular/material/tooltip'; -import { MatFormFieldModule } from '@angular/material/form-field'; -import { MatCardModule } from '@angular/material/card'; -import { Router } from '@angular/router'; -import { finalize } from 'rxjs/operators'; import { Project } from '../../domain/project'; +import { provideNativeDateAdapter } from '@angular/material/core'; +import { Router } from '@angular/router'; +import { TeacherRun } from '../teacher-run'; import { TeacherService } from '../teacher.service'; import { UserService } from '../../services/user.service'; -import { ConfigService } from '../../services/config.service'; -import { ListClassroomCoursesDialogComponent } from '../list-classroom-courses-dialog/list-classroom-courses-dialog.component'; -import { TeacherRun } from '../teacher-run'; -import { MatDividerModule } from '@angular/material/divider'; -import { MatRadioModule } from '@angular/material/radio'; -import { MatProgressBarModule } from '@angular/material/progress-bar'; @Component({ imports: [ CommonModule, FormsModule, - ReactiveFormsModule, MatButtonModule, - MatDialogModule, - MatDividerModule, - MatInputModule, + MatCardModule, MatCheckboxModule, MatDatepickerModule, + MatDialogModule, + MatDividerModule, + MatFormFieldModule, MatIconModule, + MatInputModule, + MatProgressBarModule, MatRadioModule, MatTooltipModule, - MatFormFieldModule, - MatProgressBarModule, - MatCardModule + ReactiveFormsModule ], providers: [provideNativeDateAdapter()], selector: 'create-run-dialog', @@ -60,17 +60,16 @@ import { MatProgressBarModule } from '@angular/material/progress-bar'; templateUrl: './create-run-dialog.component.html' }) export class CreateRunDialogComponent { + protected customPeriods: FormControl; + private endDateControl: FormControl; form: FormGroup; - project: Project; + protected isCreated: boolean = false; + protected isCreating: boolean = false; + protected maxStartDate: Date; + protected minEndDate: Date; + private periodOptions: string[] = []; periodsGroup: FormArray; - customPeriods: FormControl; - maxStudentsPerTeam: number; - maxStartDate: Date; - minEndDate: Date; - endDateControl: FormControl; - periodOptions: string[] = []; - isCreating: boolean = false; - isCreated: boolean = false; + project: Project; run: TeacherRun = null; constructor( @@ -84,10 +83,9 @@ export class CreateRunDialogComponent { private userService: UserService ) { this.project = data.project; - this.maxStudentsPerTeam = 3; } - ngOnInit() { + ngOnInit(): void { this.setPeriodOptions(); let hiddenControl = new FormControl('', Validators.required); this.periodsGroup = new FormArray( @@ -122,15 +120,15 @@ export class CreateRunDialogComponent { this.setDateRange(); } - isGoogleUser() { + protected isGoogleUser(): boolean { return this.userService.isGoogleUser(); } - isGoogleClassroomEnabled() { + protected isGoogleClassroomEnabled(): boolean { return this.configService.isGoogleClassroomEnabled(); } - setPeriodOptions() { + private setPeriodOptions(): void { for (let i = 1; i < 9; i++) { this.periodOptions.push(i.toString()); } @@ -140,12 +138,12 @@ export class CreateRunDialogComponent { return this.form.get('selectedPeriods'); } - mapPeriods(items: any[]): string[] { + private mapPeriods(items: any[]): string[] { const selectedPeriods = items.filter((item) => item.checkbox).map((item) => item.name); return selectedPeriods.length ? selectedPeriods : []; } - create() { + create(): void { this.isCreating = true; const combinedPeriods = this.getPeriodsString(); const startDate = this.form.controls['startDate'].value.getTime(); @@ -195,16 +193,16 @@ export class CreateRunDialogComponent { } } - setDateRange() { + protected setDateRange(): void { this.minEndDate = this.form.controls['startDate'].value; this.maxStartDate = this.form.controls['endDate'].value; } - closeAll() { + protected closeAll(): void { this.dialog.closeAll(); } - checkClassroomAuthorization() { + protected checkClassroomAuthorization(): void { this.teacherService .getClassroomAuthorizationUrl(this.userService.getUser().getValue().username) .subscribe(({ authorizationUrl }) => { @@ -222,7 +220,7 @@ export class CreateRunDialogComponent { }); } - getClassroomCourses() { + private getClassroomCourses(): void { this.teacherService .getClassroomCourses(this.userService.getUser().getValue().username) .subscribe((courses) => { @@ -233,7 +231,7 @@ export class CreateRunDialogComponent { }); } - updateLockedAfterEndDateCheckbox() { + updateLockedAfterEndDateCheckbox(): void { if (this.endDateControl.value == null) { this.form.controls['isLockedAfterEndDate'].setValue(false); this.form.controls['isLockedAfterEndDate'].disable(); From 9c454c35af8e51033ae3670c671b0571d04879bd Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Tue, 20 May 2025 12:24:45 -0700 Subject: [PATCH 02/33] Add run type selection to create run dialog --- src/app/domain/run.ts | 1 + .../create-run-dialog.component.html | 28 +++++++++++++++---- .../create-run-dialog.component.ts | 27 +++++++++++++----- src/app/teacher/teacher.service.ts | 2 ++ 4 files changed, 46 insertions(+), 12 deletions(-) diff --git a/src/app/domain/run.ts b/src/app/domain/run.ts index 51960909f40..ad1daf1fb97 100644 --- a/src/app/domain/run.ts +++ b/src/app/domain/run.ts @@ -15,6 +15,7 @@ export class Run { owner: User; sharedOwners: User[] = []; project: Project; + isSurvey: boolean; static readonly VIEW_STUDENT_WORK_PERMISSION: number = 1; static readonly GRADE_AND_MANAGE_PERMISSION: number = 2; diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.html b/src/app/teacher/create-run-dialog/create-run-dialog.component.html index 0d266ffcc92..3c08c9d64b7 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.html +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.html @@ -25,13 +25,31 @@

1. Choose Periods

Section 2"). Manually named periods should be no more than 16 characters long.

-

2. Choose Students Per Team

- - Only 1 student - 1-3 students +

2. Choose Run Type

+ + Default + Survey + help + -

3. Set Schedule

+ @if (isDefaultRun()) { +

3. Choose Students Per Team

+ + Only 1 student + 1-3 students + + + } +

{{ isDefaultRun() ? 4 : 3 }}. Set Schedule

diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts index 0db01bd9a18..c5b8e05e3aa 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts @@ -112,6 +112,7 @@ export class CreateRunDialogComponent { selectedPeriods: this.periodsGroup, customPeriods: this.customPeriods, periods: hiddenControl, + runType: new FormControl('default', Validators.required), maxStudentsPerTeam: new FormControl('3', Validators.required), startDate: new FormControl(new Date(), Validators.required), endDate: this.endDateControl, @@ -146,19 +147,23 @@ export class CreateRunDialogComponent { create(): void { this.isCreating = true; const combinedPeriods = this.getPeriodsString(); - const startDate = this.form.controls['startDate'].value.getTime(); - let endDateValue = this.form.controls['endDate'].value; - let endDate = null; + const startDate: number = this.getFormControlValue('startDate').getTime(); + let endDateValue: Date = this.getFormControlValue('endDate'); + let endDate: number = null; if (endDateValue) { endDateValue.setHours(23, 59, 59); endDate = endDateValue.getTime(); } - const isLockedAfterEndDate = this.form.controls['isLockedAfterEndDate'].value; - const maxStudentsPerTeam = this.form.controls['maxStudentsPerTeam'].value; + const isLockedAfterEndDate: boolean = this.getFormControlValue('isLockedAfterEndDate'); + const isSurvey: boolean = this.getFormControlValue('runType') === 'survey'; + const maxStudentsPerTeam: number = isSurvey + ? 1 + : this.getFormControlValue('maxStudentsPerTeam'); this.teacherService .createRun( this.project.id, combinedPeriods, + isSurvey, maxStudentsPerTeam, startDate, endDate, @@ -194,8 +199,8 @@ export class CreateRunDialogComponent { } protected setDateRange(): void { - this.minEndDate = this.form.controls['startDate'].value; - this.maxStartDate = this.form.controls['endDate'].value; + this.minEndDate = this.getFormControlValue('startDate'); + this.maxStartDate = this.getFormControlValue('endDate'); } protected closeAll(): void { @@ -239,4 +244,12 @@ export class CreateRunDialogComponent { this.form.controls['isLockedAfterEndDate'].enable(); } } + + protected isDefaultRun(): boolean { + return this.getFormControlValue('runType') === 'default'; + } + + private getFormControlValue(control: string): any { + return this.form.controls[control].value; + } } diff --git a/src/app/teacher/teacher.service.ts b/src/app/teacher/teacher.service.ts index a691bad2e3d..25786aae686 100644 --- a/src/app/teacher/teacher.service.ts +++ b/src/app/teacher/teacher.service.ts @@ -82,6 +82,7 @@ export class TeacherService { createRun( projectId: number, periods: string, + isSurvey: boolean, maxStudentsPerTeam: number, startDate: number, endDate: number, @@ -91,6 +92,7 @@ export class TeacherService { let body = new HttpParams(); body = body.set('projectId', projectId + ''); body = body.set('periods', periods); + body = body.set('isSurvey', isSurvey); body = body.set('maxStudentsPerTeam', maxStudentsPerTeam + ''); body = body.set('startDate', startDate + ''); if (endDate) { From 730d2541e4a41b6be4e4835505695aeae35ef5fa Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Wed, 21 May 2025 13:29:00 -0700 Subject: [PATCH 03/33] Restrict survey student access to student home --- src/app/app-routing.module.ts | 2 + src/app/app.module.ts | 8 +- src/app/auth.guard.spec.ts | 30 ++++ src/app/auth.guard.ts | 24 +++ .../header-account-menu.component.html | 148 +++++++++--------- src/app/services/user.service.ts | 39 ++--- 6 files changed, 157 insertions(+), 94 deletions(-) create mode 100644 src/app/auth.guard.spec.ts create mode 100644 src/app/auth.guard.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 7646f44884c..34b62e8a264 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,6 +4,7 @@ import { RouterModule, Routes } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { PublicLibraryComponent } from './modules/library/public-library/public-library.component'; import { PersonalLibraryComponent } from './modules/library/personal-library/personal-library.component'; +import { AuthGuard } from './auth.guard'; const routes: Routes = [ { path: '', loadChildren: () => import('./home/home.module').then((m) => m.HomeModule) }, @@ -51,6 +52,7 @@ const routes: Routes = [ }, { path: 'student', + canActivate: [AuthGuard], loadChildren: () => import('./student/student.module').then((m) => m.StudentModule) }, { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 8deefe3cc8e..9a70a8619c4 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -24,6 +24,7 @@ import { RecaptchaV3Module, RECAPTCHA_V3_SITE_KEY, RECAPTCHA_BASE_URL } from 'ng import { ArchiveProjectService } from './services/archive-project.service'; import { FooterComponent } from './modules/footer/footer.component'; import { HeaderComponent } from './modules/header/header.component'; +import { AuthGuard } from './auth.guard'; export function initialize( configService: ConfigService, @@ -68,14 +69,15 @@ export function initialize( ], providers: [ ArchiveProjectService, + AuthGuard, ConfigService, StudentService, TeacherService, UserService, provideAppInitializer(() => { - const initializerFn = (initialize)(inject(ConfigService), inject(UserService)); - return initializerFn(); - }), + const initializerFn = initialize(inject(ConfigService), inject(UserService)); + return initializerFn(); + }), { provide: MAT_SNACK_BAR_DEFAULT_OPTIONS, useValue: { diff --git a/src/app/auth.guard.spec.ts b/src/app/auth.guard.spec.ts new file mode 100644 index 00000000000..f995931e117 --- /dev/null +++ b/src/app/auth.guard.spec.ts @@ -0,0 +1,30 @@ +import { TestBed, inject } from '@angular/core/testing'; +import { AuthGuard } from './auth.guard'; +import { UserService } from './services/user.service'; +import { RouterTestingModule } from '@angular/router/testing'; +import { provideHttpClientTesting } from '@angular/common/http/testing'; +import { ConfigService } from './services/config.service'; +import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; + +export class MockUserService {} + +export class MockConfigService {} + +describe('SurveyStudentAuthGuard', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [RouterTestingModule], + providers: [ + AuthGuard, + { provide: UserService, useClass: MockUserService }, + { provide: ConfigService, useClass: MockConfigService }, + provideHttpClient(withInterceptorsFromDi()), + provideHttpClientTesting() + ] + }); + }); + + it('should create', inject([AuthGuard, UserService, ConfigService], (guard: AuthGuard) => { + expect(guard).toBeTruthy(); + })); +}); diff --git a/src/app/auth.guard.ts b/src/app/auth.guard.ts new file mode 100644 index 00000000000..0638a43315b --- /dev/null +++ b/src/app/auth.guard.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@angular/core'; +import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; +import { UserService } from './services/user.service'; + +@Injectable() +export class AuthGuard { + constructor( + private userService: UserService, + private router: Router + ) {} + + canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { + return this.checkLogin(); + } + + private checkLogin(): boolean { + if (this.userService.isSurveyStudent()) { + this.router.navigate(['/']); + return false; + } else { + return true; + } + } +} diff --git a/src/app/modules/header/header-account-menu/header-account-menu.component.html b/src/app/modules/header/header-account-menu/header-account-menu.component.html index 541a6e0bc16..db4550b0f1e 100644 --- a/src/app/modules/header/header-account-menu/header-account-menu.component.html +++ b/src/app/modules/header/header-account-menu/header-account-menu.component.html @@ -6,80 +6,82 @@ - - - - home - Student Home - - - home - Teacher Home - - - edit - Edit Profile - - - edit - Edit Profile - - - settings - Researcher Tools - - - settings - Admin Tools - - - help - Help - - + home + Student Home + + + home + Teacher Home + + + edit + Edit Profile + + + edit + Edit Profile + + + settings + Researcher Tools + + + settings + Admin Tools + + + help + Help + + + } exit_to_app Sign Out diff --git a/src/app/services/user.service.ts b/src/app/services/user.service.ts index 993ff48218d..c018f77e7fd 100644 --- a/src/app/services/user.service.ts +++ b/src/app/services/user.service.ts @@ -23,7 +23,10 @@ export class UserService { isAuthenticated = false; redirectUrl: string; // redirect here after logging in - constructor(private http: HttpClient, private configService: ConfigService) {} + constructor( + private http: HttpClient, + private configService: ConfigService + ) {} getUser(): BehaviorSubject { return this.user$; @@ -57,6 +60,10 @@ export class UserService { return this.isRole('admin'); } + isSurveyStudent(): boolean { + return this.isRole('surveyStudent'); + } + private isRole(role: string): boolean { return this.isAuthenticated && this.getRoles().includes(role); } @@ -75,16 +82,14 @@ export class UserService { retrieveUser(username?: string): Observable { const params = new HttpParams().set('username', username); - return this.http - .get(this.userUrl, { params: params }) - .pipe( - tap((user) => { - if (user != null && user.id != null) { - this.isAuthenticated = true; - } - this.user$.next(user); - }) - ); + return this.http.get(this.userUrl, { params: params }).pipe( + tap((user) => { + if (user != null && user.id != null) { + this.isAuthenticated = true; + } + this.user$.next(user); + }) + ); } checkAuthentication(username, password) { @@ -146,13 +151,11 @@ export class UserService { const headers = new HttpHeaders().set('Content-Type', 'application/x-www-form-urlencoded'); let body = new HttpParams(); body = body.set('newPassword', newPassword); - return this.http - .post(this.unlinkGoogleAccountUrl, body, { headers: headers }) - .pipe( - tap((user) => { - this.user$.next(user); - }) - ); + return this.http.post(this.unlinkGoogleAccountUrl, body, { headers: headers }).pipe( + tap((user) => { + this.user$.next(user); + }) + ); } getUserByGoogleId(googleUserId: string) { From 2c79ec687b485cbdfbe1f1a479f0e61bb884cbb2 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Wed, 21 May 2025 16:07:01 -0700 Subject: [PATCH 04/33] Survey runs have access links instead of access codes --- src/app/domain/run.ts | 6 ++- .../create-run-dialog.component.html | 44 ++++++++++++++----- .../create-run-dialog.component.scss | 4 ++ .../create-run-dialog.component.ts | 12 +++++ .../teacher-run-list-item.component.html | 33 +++++++++++--- .../teacher-run-list-item.component.scss | 4 ++ .../teacher-run-list-item.component.ts | 22 +++++++--- 7 files changed, 101 insertions(+), 24 deletions(-) diff --git a/src/app/domain/run.ts b/src/app/domain/run.ts index ad1daf1fb97..209fb9bb23c 100644 --- a/src/app/domain/run.ts +++ b/src/app/domain/run.ts @@ -15,7 +15,7 @@ export class Run { owner: User; sharedOwners: User[] = []; project: Project; - isSurvey: boolean; + private isSurvey: boolean; static readonly VIEW_STUDENT_WORK_PERMISSION: number = 1; static readonly GRADE_AND_MANAGE_PERMISSION: number = 2; @@ -93,6 +93,10 @@ export class Run { private hasEndTime(): boolean { return this.endTime != null; } + + isSurveyRun(): boolean { + return this.isSurvey; + } } export function sortByRunStartTimeDesc(a: Run, b: Run): number { diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.html b/src/app/teacher/create-run-dialog/create-run-dialog.component.html index 3c08c9d64b7..e9fa658f7f0 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.html +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.html @@ -120,16 +120,40 @@

{{ isDefaultRun() ? 4 : 3 }}. Set Schedule

Success! This unit has been added to your Class Schedule.

{{ run.name }}

-

Access Code: {{ run.runCode }}

-

- Important: Every classroom unit has a unique Access Code. Students use this code to register - for a unit. Give the code to your students when they first sign up for a WISE account. If - they already have WISE accounts, have them log in and then select "Add Unit" from the - student home page. -

-

- You can always find the Access Code for each classroom unit in your Class Schedule. -

+ @if (isDefaultRun()) { +

Access Code: {{ run.runCode }}

+

+ Important: Every classroom unit has a unique Access Code. Students use this code to + register for a unit. Give the code to your students when they first sign up for a WISE + account. If they already have WISE accounts, have them log in and then select "Add Unit" + from the student home page. +

+

+ You can always find the Access Code for each classroom unit in your Class Schedule. +

+ } @else { +

+ Access Link: + +

+

+ Important: Every survey unit has a unique Access Link. Students can use this link to + access and complete the survey without needing to create a WISE account. +

+

You can always find the Access Link for each survey unit in your Class Schedule.

+ }
diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.scss b/src/app/teacher/create-run-dialog/create-run-dialog.component.scss index 3140da7417d..6d21ba01128 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.scss +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.scss @@ -5,3 +5,7 @@ .period-input { width: 100%; } + +.access-link { + text-transform: lowercase; +} \ No newline at end of file diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts index c5b8e05e3aa..54b6f68eec9 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts @@ -35,9 +35,12 @@ import { Router } from '@angular/router'; import { TeacherRun } from '../teacher-run'; import { TeacherService } from '../teacher.service'; import { UserService } from '../../services/user.service'; +import { ClipboardModule } from '@angular/cdk/clipboard'; +import { MatSnackBar } from '@angular/material/snack-bar'; @Component({ imports: [ + ClipboardModule, CommonModule, FormsModule, MatButtonModule, @@ -60,6 +63,7 @@ import { UserService } from '../../services/user.service'; templateUrl: './create-run-dialog.component.html' }) export class CreateRunDialogComponent { + protected accessLink: string = ''; protected customPeriods: FormControl; private endDateControl: FormControl; form: FormGroup; @@ -79,6 +83,7 @@ export class CreateRunDialogComponent { public dialogRef: MatDialogRef, private fb: FormBuilder, private router: Router, + private snackBar: MatSnackBar, private teacherService: TeacherService, private userService: UserService ) { @@ -176,6 +181,9 @@ export class CreateRunDialogComponent { ) .subscribe((newRun: TeacherRun) => { this.run = new TeacherRun(newRun); + if (this.run.isSurveyRun()) { + this.accessLink = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-${this.run.periods[0]}`; + } this.dialogRef.afterClosed().subscribe(() => { this.router.navigate(['/teacher/home/schedule'], { queryParams: { newRunId: newRun.id } @@ -252,4 +260,8 @@ export class CreateRunDialogComponent { private getFormControlValue(control: string): any { return this.form.controls[control].value; } + + copyMsg() { + this.snackBar.open($localize`Copied to clipboard.`); + } } diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html index 55ef1c3c828..844648fe8d2 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html @@ -36,13 +36,32 @@
- Access Code: {{ run.runCode }} (Share with students) + @if (!run.isSurveyRun()) { + Access Code: {{ run.runCode }} (Share with students) + } @else { + Access Link: + + + }
Shared by {{ run.owner.firstName }} {{ run.owner.lastName }} diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss index bc0dc87e74f..5ae75033d30 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss @@ -28,3 +28,7 @@ margin: -8px; display: block; } + +.access-link { + text-transform: lowercase; +} \ No newline at end of file diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts index 48bda33ff6e..7b8f244667d 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts @@ -10,15 +10,17 @@ import { ShareRunCodeDialogComponent } from '../share-run-code-dialog/share-run- import { Subscription } from 'rxjs'; import { ProjectTagService } from '../../../assets/wise5/services/projectTagService'; import { Tag } from '../../domain/tag'; +import { MatSnackBar } from '@angular/material/snack-bar'; @Component({ - animations: [flash], - selector: 'app-teacher-run-list-item', - styleUrl: './teacher-run-list-item.component.scss', - templateUrl: './teacher-run-list-item.component.html', - standalone: false + animations: [flash], + selector: 'app-teacher-run-list-item', + styleUrl: './teacher-run-list-item.component.scss', + templateUrl: './teacher-run-list-item.component.html', + standalone: false }) export class TeacherRunListItemComponent implements OnInit { + protected accessLink: string = ''; protected animateDelay: string = '0s'; protected animateDuration: string = '0s'; protected manageStudentsLink: string = ''; @@ -35,7 +37,8 @@ export class TeacherRunListItemComponent implements OnInit { private router: Router, private elRef: ElementRef, private dialog: MatDialog, - private projectTagService: ProjectTagService + private projectTagService: ProjectTagService, + private snackBar: MatSnackBar ) {} ngOnInit(): void { @@ -43,6 +46,9 @@ export class TeacherRunListItemComponent implements OnInit { this.manageStudentsLink = `${this.configService.getContextPath()}/teacher/manage/unit/${ this.run.id }/manage-students`; + if (this.run.isSurveyRun()) { + this.accessLink = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-${this.run.periods}`; + } if (this.run.highlighted) { this.animateDuration = '2s'; this.animateDelay = '1s'; @@ -145,4 +151,8 @@ export class TeacherRunListItemComponent implements OnInit { this.runSelectedStatusChangedEvent.emit(); this.runArchiveStatusChangedEvent.emit(); } + + copyMsg() { + this.snackBar.open($localize`Copied to clipboard.`); + } } From 1f5d62dff94ca12766f72f8b25e14f7ad646c5be Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Thu, 22 May 2025 18:44:19 -0700 Subject: [PATCH 05/33] Fixed survey students restricted from survey bug --- src/app/app-routing.module.ts | 2 -- src/app/app.module.ts | 2 -- src/app/auth.guard.spec.ts | 30 ------------------- src/app/auth.guard.ts | 24 --------------- src/app/student/auth.guard.ts | 10 +++++-- .../teacher-run-list-item.component.ts | 2 +- 6 files changed, 9 insertions(+), 61 deletions(-) delete mode 100644 src/app/auth.guard.spec.ts delete mode 100644 src/app/auth.guard.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 34b62e8a264..7646f44884c 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,7 +4,6 @@ import { RouterModule, Routes } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { PublicLibraryComponent } from './modules/library/public-library/public-library.component'; import { PersonalLibraryComponent } from './modules/library/personal-library/personal-library.component'; -import { AuthGuard } from './auth.guard'; const routes: Routes = [ { path: '', loadChildren: () => import('./home/home.module').then((m) => m.HomeModule) }, @@ -52,7 +51,6 @@ const routes: Routes = [ }, { path: 'student', - canActivate: [AuthGuard], loadChildren: () => import('./student/student.module').then((m) => m.StudentModule) }, { diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 9a70a8619c4..be2e85b71a8 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -24,7 +24,6 @@ import { RecaptchaV3Module, RECAPTCHA_V3_SITE_KEY, RECAPTCHA_BASE_URL } from 'ng import { ArchiveProjectService } from './services/archive-project.service'; import { FooterComponent } from './modules/footer/footer.component'; import { HeaderComponent } from './modules/header/header.component'; -import { AuthGuard } from './auth.guard'; export function initialize( configService: ConfigService, @@ -69,7 +68,6 @@ export function initialize( ], providers: [ ArchiveProjectService, - AuthGuard, ConfigService, StudentService, TeacherService, diff --git a/src/app/auth.guard.spec.ts b/src/app/auth.guard.spec.ts deleted file mode 100644 index f995931e117..00000000000 --- a/src/app/auth.guard.spec.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { TestBed, inject } from '@angular/core/testing'; -import { AuthGuard } from './auth.guard'; -import { UserService } from './services/user.service'; -import { RouterTestingModule } from '@angular/router/testing'; -import { provideHttpClientTesting } from '@angular/common/http/testing'; -import { ConfigService } from './services/config.service'; -import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http'; - -export class MockUserService {} - -export class MockConfigService {} - -describe('SurveyStudentAuthGuard', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - imports: [RouterTestingModule], - providers: [ - AuthGuard, - { provide: UserService, useClass: MockUserService }, - { provide: ConfigService, useClass: MockConfigService }, - provideHttpClient(withInterceptorsFromDi()), - provideHttpClientTesting() - ] - }); - }); - - it('should create', inject([AuthGuard, UserService, ConfigService], (guard: AuthGuard) => { - expect(guard).toBeTruthy(); - })); -}); diff --git a/src/app/auth.guard.ts b/src/app/auth.guard.ts deleted file mode 100644 index 0638a43315b..00000000000 --- a/src/app/auth.guard.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; -import { UserService } from './services/user.service'; - -@Injectable() -export class AuthGuard { - constructor( - private userService: UserService, - private router: Router - ) {} - - canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { - return this.checkLogin(); - } - - private checkLogin(): boolean { - if (this.userService.isSurveyStudent()) { - this.router.navigate(['/']); - return false; - } else { - return true; - } - } -} diff --git a/src/app/student/auth.guard.ts b/src/app/student/auth.guard.ts index af6190eebe0..a981b1c6624 100644 --- a/src/app/student/auth.guard.ts +++ b/src/app/student/auth.guard.ts @@ -4,14 +4,20 @@ import { UserService } from '../services/user.service'; @Injectable() export class AuthGuard { - constructor(private userService: UserService, private router: Router) {} + constructor( + private userService: UserService, + private router: Router + ) {} canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { return this.checkLogin(state.url); } checkLogin(url: string): boolean { - if (this.userService.isStudent() || url.includes('/preview/unit')) { + if ( + (this.userService.isStudent() || url.includes('/preview/unit')) && + !(this.userService.isSurveyStudent() && url.includes('/home')) + ) { return true; } else if (this.userService.isAuthenticated) { this.router.navigate(['/']); diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts index 7b8f244667d..fe5fdc23a39 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts @@ -47,7 +47,7 @@ export class TeacherRunListItemComponent implements OnInit { this.run.id }/manage-students`; if (this.run.isSurveyRun()) { - this.accessLink = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-${this.run.periods}`; + this.accessLink = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-${this.run.periods[0]}`; } if (this.run.highlighted) { this.animateDuration = '2s'; From 15b48b0955f6032364f29189154a1801229f7371 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Thu, 22 May 2025 20:19:15 -0700 Subject: [PATCH 06/33] Workgroup limit reached page --- src/app/app-routing.module.ts | 6 +++++ src/app/student/auth.guard.ts | 14 ++++++++---- .../workgroup-limit-reached.component.html | 8 +++++++ .../workgroup-limit-reached.component.scss | 7 ++++++ .../workgroup-limit-reached.component.spec.ts | 22 +++++++++++++++++++ .../workgroup-limit-reached.component.ts | 10 +++++++++ 6 files changed, 63 insertions(+), 4 deletions(-) create mode 100644 src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.html create mode 100644 src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.scss create mode 100644 src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts create mode 100644 src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.ts diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index 7646f44884c..b66e015d015 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,6 +4,7 @@ import { RouterModule, Routes } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { PublicLibraryComponent } from './modules/library/public-library/public-library.component'; import { PersonalLibraryComponent } from './modules/library/personal-library/personal-library.component'; +import { WorkgroupLimitReachedComponent } from './student/workgroup-limit-reached/workgroup-limit-reached.component'; const routes: Routes = [ { path: '', loadChildren: () => import('./home/home.module').then((m) => m.HomeModule) }, @@ -56,6 +57,11 @@ const routes: Routes = [ { path: 'teacher', loadChildren: () => import('./teacher/teacher.module').then((m) => m.TeacherModule) + }, + { + path: 'workgroupLimitReached', + component: WorkgroupLimitReachedComponent, + pathMatch: 'full' } ]; diff --git a/src/app/student/auth.guard.ts b/src/app/student/auth.guard.ts index a981b1c6624..fcb6c885cf5 100644 --- a/src/app/student/auth.guard.ts +++ b/src/app/student/auth.guard.ts @@ -14,10 +14,7 @@ export class AuthGuard { } checkLogin(url: string): boolean { - if ( - (this.userService.isStudent() || url.includes('/preview/unit')) && - !(this.userService.isSurveyStudent() && url.includes('/home')) - ) { + if (this.canAccess(url)) { return true; } else if (this.userService.isAuthenticated) { this.router.navigate(['/']); @@ -28,4 +25,13 @@ export class AuthGuard { return false; } } + + private canAccess(url: string): boolean { + return ( + (this.userService.isStudent() || + url.includes('/preview/unit') || + url.includes('/workgroupLimitReached')) && + !(this.userService.isSurveyStudent() && url.includes('/home')) + ); + } } diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.html b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.html new file mode 100644 index 00000000000..e48924c85fa --- /dev/null +++ b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.html @@ -0,0 +1,8 @@ +
+ + +

This run has reached the maximum number of workgroups allowed.

+

Please talk to your teacher.

+
+
+
diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.scss b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.scss new file mode 100644 index 00000000000..8cac33fb341 --- /dev/null +++ b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.scss @@ -0,0 +1,7 @@ +mat-card { + background-color: #a8a8a2; + width: 50%; + text-align: center; + margin-left: 25%; + margin-top: 5%; +} \ No newline at end of file diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts new file mode 100644 index 00000000000..86198eb9c0c --- /dev/null +++ b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts @@ -0,0 +1,22 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { WorkgroupLimitReachedComponent } from './workgroup-limit-reached.component'; + +fdescribe('WorkgroupLimitReachedComponent', () => { + let component: WorkgroupLimitReachedComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [WorkgroupLimitReachedComponent] + }).compileComponents(); + + fixture = TestBed.createComponent(WorkgroupLimitReachedComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.ts b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.ts new file mode 100644 index 00000000000..0483a343ad1 --- /dev/null +++ b/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; + +@Component({ + selector: 'workgroup-limit-reached', + imports: [MatCardModule], + templateUrl: './workgroup-limit-reached.component.html', + styleUrl: './workgroup-limit-reached.component.scss' +}) +export class WorkgroupLimitReachedComponent {} From 25a33d485c37d5e4b7150241fc46500e5da8e0b6 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Fri, 23 May 2025 11:04:05 -0700 Subject: [PATCH 07/33] Log out page shown if logged in when accessing survey --- src/app/app-routing.module.ts | 6 +-- src/app/app.module.ts | 2 + .../header-account-menu.component.ts | 43 ++++++++----------- src/app/services/logOutService.spec.ts | 30 +++++++++++++ src/app/services/logOutService.ts | 28 ++++++++++++ .../log-out-page/log-out-page.component.html | 16 +++++++ .../log-out-page/log-out-page.component.scss | 39 +++++++++++++++++ .../log-out-page.component.spec.ts | 23 ++++++++++ .../log-out-page/log-out-page.component.ts | 19 ++++++++ .../student/survey/survey-routing.module.ts | 27 ++++++++++++ src/app/student/survey/survey.module.ts | 7 +++ .../workgroup-limit-reached.component.html | 0 .../workgroup-limit-reached.component.scss | 0 .../workgroup-limit-reached.component.spec.ts | 2 +- .../workgroup-limit-reached.component.ts | 0 15 files changed, 213 insertions(+), 29 deletions(-) create mode 100644 src/app/services/logOutService.spec.ts create mode 100644 src/app/services/logOutService.ts create mode 100644 src/app/student/survey/log-out-page/log-out-page.component.html create mode 100644 src/app/student/survey/log-out-page/log-out-page.component.scss create mode 100644 src/app/student/survey/log-out-page/log-out-page.component.spec.ts create mode 100644 src/app/student/survey/log-out-page/log-out-page.component.ts create mode 100644 src/app/student/survey/survey-routing.module.ts create mode 100644 src/app/student/survey/survey.module.ts rename src/app/student/{ => survey}/workgroup-limit-reached/workgroup-limit-reached.component.html (100%) rename src/app/student/{ => survey}/workgroup-limit-reached/workgroup-limit-reached.component.scss (100%) rename src/app/student/{ => survey}/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts (92%) rename src/app/student/{ => survey}/workgroup-limit-reached/workgroup-limit-reached.component.ts (100%) diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts index b66e015d015..87512025e54 100644 --- a/src/app/app-routing.module.ts +++ b/src/app/app-routing.module.ts @@ -4,7 +4,6 @@ import { RouterModule, Routes } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { PublicLibraryComponent } from './modules/library/public-library/public-library.component'; import { PersonalLibraryComponent } from './modules/library/personal-library/personal-library.component'; -import { WorkgroupLimitReachedComponent } from './student/workgroup-limit-reached/workgroup-limit-reached.component'; const routes: Routes = [ { path: '', loadChildren: () => import('./home/home.module').then((m) => m.HomeModule) }, @@ -59,9 +58,8 @@ const routes: Routes = [ loadChildren: () => import('./teacher/teacher.module').then((m) => m.TeacherModule) }, { - path: 'workgroupLimitReached', - component: WorkgroupLimitReachedComponent, - pathMatch: 'full' + path: 'survey', + loadChildren: () => import('./student/survey/survey.module').then((m) => m.SurveyModule) } ]; diff --git a/src/app/app.module.ts b/src/app/app.module.ts index be2e85b71a8..18f8695153e 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -24,6 +24,7 @@ import { RecaptchaV3Module, RECAPTCHA_V3_SITE_KEY, RECAPTCHA_BASE_URL } from 'ng import { ArchiveProjectService } from './services/archive-project.service'; import { FooterComponent } from './modules/footer/footer.component'; import { HeaderComponent } from './modules/header/header.component'; +import { LogOutService } from './services/logOutService'; export function initialize( configService: ConfigService, @@ -69,6 +70,7 @@ export function initialize( providers: [ ArchiveProjectService, ConfigService, + LogOutService, StudentService, TeacherService, UserService, diff --git a/src/app/modules/header/header-account-menu/header-account-menu.component.ts b/src/app/modules/header/header-account-menu/header-account-menu.component.ts index 652c3bde686..c1f4665be0a 100644 --- a/src/app/modules/header/header-account-menu/header-account-menu.component.ts +++ b/src/app/modules/header/header-account-menu/header-account-menu.component.ts @@ -9,37 +9,34 @@ import { MatDividerModule } from '@angular/material/divider'; import { RouterModule } from '@angular/router'; import { CommonModule } from '@angular/common'; import { FlexLayoutModule } from '@angular/flex-layout'; +import { LogOutService } from '../../../services/logOutService'; @Component({ - selector: 'app-header-account-menu', - templateUrl: './header-account-menu.component.html', - styleUrl: './header-account-menu.component.scss', - imports: [ - CommonModule, - FlexLayoutModule, - MatButtonModule, - MatIconModule, - MatMenuModule, - MatDividerModule, - RouterModule - ] + selector: 'app-header-account-menu', + templateUrl: './header-account-menu.component.html', + styleUrl: './header-account-menu.component.scss', + imports: [ + CommonModule, + FlexLayoutModule, + MatButtonModule, + MatIconModule, + MatMenuModule, + MatDividerModule, + RouterModule + ] }) -export class HeaderAccountMenuComponent implements OnInit { +export class HeaderAccountMenuComponent { protected firstName: string = ''; protected isPreviousAdmin: boolean; protected lastName: string = ''; - protected logOutURL: string; protected roles: string[] = []; private switchToOriginalUserURL = '/api/logout/impersonate'; @Input() user: User; - constructor(private configService: ConfigService, private http: HttpClient) {} - - ngOnInit(): void { - this.configService.getConfig().subscribe((config) => { - this.logOutURL = config.logOutURL; - }); - } + constructor( + private http: HttpClient, + private logOutService: LogOutService + ) {} ngOnChanges(changes: SimpleChanges): void { if (changes.user) { @@ -64,8 +61,6 @@ export class HeaderAccountMenuComponent implements OnInit { } protected logOut(): void { - this.http.get(this.logOutURL).subscribe(() => { - window.location.href = '/'; - }); + this.logOutService.logOut(); } } diff --git a/src/app/services/logOutService.spec.ts b/src/app/services/logOutService.spec.ts new file mode 100644 index 00000000000..502bc27a8bb --- /dev/null +++ b/src/app/services/logOutService.spec.ts @@ -0,0 +1,30 @@ +import { ConfigService } from './config.service'; +import { HttpClient } from '@angular/common/http'; +import { LogOutService } from './logOutService'; +import { MockProviders } from 'ng-mocks'; +import { of } from 'rxjs'; +import { fakeAsync, TestBed, tick } from '@angular/core/testing'; + +let service: LogOutService; +let httpSpy: jasmine.Spy; +export class MockConfigService {} + +fdescribe('LogOutService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [], + providers: [LogOutService, MockProviders(ConfigService, HttpClient)] + }); + service = TestBed.inject(LogOutService); + httpSpy = spyOn(TestBed.inject(HttpClient), 'get').and.returnValue(of({})); + spyOn(TestBed.inject(ConfigService), 'getConfig').and.returnValue( + of({ contextPath: '', logOutURL: 'api/logOutUrl', currentTime: 0 }) + ); + }); + + it('should make a GET request to the log out URL when logOut() is called', fakeAsync(() => { + service.logOut(); + tick(); + expect(httpSpy).toHaveBeenCalledWith('api/logOutUrl'); + })); +}); diff --git a/src/app/services/logOutService.ts b/src/app/services/logOutService.ts new file mode 100644 index 00000000000..c6c01b3f407 --- /dev/null +++ b/src/app/services/logOutService.ts @@ -0,0 +1,28 @@ +import { ConfigService } from './config.service'; +import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; + +@Injectable() +export class LogOutService { + private logOutUrl: string; + + constructor( + private configService: ConfigService, + private http: HttpClient + ) {} + + async logOut(): Promise { + if (!this.logOutUrl) { + await this.retrieveLogOutUrl(); + } + this.http.get(this.logOutUrl).subscribe(() => { + window.location.href = '/'; + }); + } + + private async retrieveLogOutUrl(): Promise { + this.configService.getConfig().subscribe((config) => { + this.logOutUrl = config.logOutURL; + }); + } +} diff --git a/src/app/student/survey/log-out-page/log-out-page.component.html b/src/app/student/survey/log-out-page/log-out-page.component.html new file mode 100644 index 00000000000..bd89834f8e0 --- /dev/null +++ b/src/app/student/survey/log-out-page/log-out-page.component.html @@ -0,0 +1,16 @@ +
+ + +

You must be logged out to access this survey.

+

Log out, and try the link again.

+ + + + exit_to_app + Log Out + + + +
+
+
diff --git a/src/app/student/survey/log-out-page/log-out-page.component.scss b/src/app/student/survey/log-out-page/log-out-page.component.scss new file mode 100644 index 00000000000..1e0a232cdd8 --- /dev/null +++ b/src/app/student/survey/log-out-page/log-out-page.component.scss @@ -0,0 +1,39 @@ +a { + display: flex; + align-items: center; +} + +span { + margin-left: 7px; +} + +.card { + background-color: #b8b8b2; + width: 50%; + text-align: center; + margin-left: 25%; + margin-top: 5%; +} + +.log-out { + cursor: pointer; + background-color: #afafa9; + width: 125px; + height:40px; + padding: 5px; + display: flex; + justify-self: center; + align-items: center; +} + +.log-out:hover { + background-color: #a9a9a3; +} + +.log-out-content { + padding: 0px; + margin: 0px; + height: 0px; + display: flex; + align-items: center; +} diff --git a/src/app/student/survey/log-out-page/log-out-page.component.spec.ts b/src/app/student/survey/log-out-page/log-out-page.component.spec.ts new file mode 100644 index 00000000000..59eaccee52f --- /dev/null +++ b/src/app/student/survey/log-out-page/log-out-page.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { LogOutPageComponent } from './log-out-page.component'; + +describe('LogOutPageComponent', () => { + let component: LogOutPageComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [LogOutPageComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(LogOutPageComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/student/survey/log-out-page/log-out-page.component.ts b/src/app/student/survey/log-out-page/log-out-page.component.ts new file mode 100644 index 00000000000..8401e862659 --- /dev/null +++ b/src/app/student/survey/log-out-page/log-out-page.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; +import { LogOutService } from '../../../services/logOutService'; +import { MatIconModule } from '@angular/material/icon'; +import { MatMenuModule } from '@angular/material/menu'; + +@Component({ + selector: 'logout', + imports: [MatCardModule, MatIconModule, MatMenuModule], + templateUrl: './log-out-page.component.html', + styleUrl: './log-out-page.component.scss' +}) +export class LogOutPageComponent { + constructor(private logOutService: LogOutService) {} + + protected logOut(): void { + this.logOutService.logOut(); + } +} diff --git a/src/app/student/survey/survey-routing.module.ts b/src/app/student/survey/survey-routing.module.ts new file mode 100644 index 00000000000..ea37048da1b --- /dev/null +++ b/src/app/student/survey/survey-routing.module.ts @@ -0,0 +1,27 @@ +import { NgModule } from '@angular/core'; +import { RouterModule, Routes } from '@angular/router'; +import { StudentComponent } from '../student.component'; +import { WorkgroupLimitReachedComponent } from './workgroup-limit-reached/workgroup-limit-reached.component'; +import { LogOutPageComponent } from './log-out-page/log-out-page.component'; + +const studentRoutes: Routes = [ + { + path: '', + component: StudentComponent, + children: [ + { path: '', redirectTo: '/', pathMatch: 'full' }, + { + path: 'workgroupLimitReached', + component: WorkgroupLimitReachedComponent, + pathMatch: 'full' + }, + { path: 'logout', component: LogOutPageComponent, pathMatch: 'full' } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(studentRoutes)], + exports: [RouterModule] +}) +export class SurveyRoutingModule {} diff --git a/src/app/student/survey/survey.module.ts b/src/app/student/survey/survey.module.ts new file mode 100644 index 00000000000..2c41a9c3914 --- /dev/null +++ b/src/app/student/survey/survey.module.ts @@ -0,0 +1,7 @@ +import { NgModule } from '@angular/core'; +import { SurveyRoutingModule } from './survey-routing.module'; + +@NgModule({ + imports: [SurveyRoutingModule] +}) +export class SurveyModule {} diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.html b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html similarity index 100% rename from src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.html rename to src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.scss b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.scss similarity index 100% rename from src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.scss rename to src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.scss diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts similarity index 92% rename from src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts rename to src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts index 86198eb9c0c..cf43b62fe4c 100644 --- a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts +++ b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.spec.ts @@ -2,7 +2,7 @@ import { ComponentFixture, TestBed } from '@angular/core/testing'; import { WorkgroupLimitReachedComponent } from './workgroup-limit-reached.component'; -fdescribe('WorkgroupLimitReachedComponent', () => { +describe('WorkgroupLimitReachedComponent', () => { let component: WorkgroupLimitReachedComponent; let fixture: ComponentFixture; diff --git a/src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.ts b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.ts similarity index 100% rename from src/app/student/workgroup-limit-reached/workgroup-limit-reached.component.ts rename to src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.ts From 32126f08e00de6b73286d1420b8b89d9915101b4 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Tue, 27 May 2025 14:11:09 -0700 Subject: [PATCH 08/33] Unique access links for each period --- .../create-run-dialog.component.html | 35 ++++++++------- .../create-run-dialog.component.ts | 11 ++++- .../teacher-run-list-item.component.html | 44 ++++++++++++------- .../teacher-run-list-item.component.scss | 5 +++ .../teacher-run-list-item.component.ts | 16 ++++++- 5 files changed, 75 insertions(+), 36 deletions(-) diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.html b/src/app/teacher/create-run-dialog/create-run-dialog.component.html index e9fa658f7f0..b000020cbf6 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.html +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.html @@ -132,22 +132,25 @@

{{ isDefaultRun() ? 4 : 3 }}. Set Schedule

You can always find the Access Code for each classroom unit in your Class Schedule.

} @else { -

- Access Link: - -

+ @for (accessLink of accessLinks; track accessLink) { +

+ Period {{ getPeriodFromAccessLink(accessLink) }} Access Link: + +

+
+ }

Important: Every survey unit has a unique Access Link. Students can use this link to access and complete the survey without needing to create a WISE account. diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts index 54b6f68eec9..c9058cb9dd1 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts @@ -63,7 +63,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; templateUrl: './create-run-dialog.component.html' }) export class CreateRunDialogComponent { - protected accessLink: string = ''; + protected accessLinks: string[] = []; protected customPeriods: FormControl; private endDateControl: FormControl; form: FormGroup; @@ -182,7 +182,10 @@ export class CreateRunDialogComponent { .subscribe((newRun: TeacherRun) => { this.run = new TeacherRun(newRun); if (this.run.isSurveyRun()) { - this.accessLink = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-${this.run.periods[0]}`; + const linkBase = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-`; + this.run.periods.forEach((period) => + this.accessLinks.push(linkBase + period.replaceAll(' ', '++')) + ); } this.dialogRef.afterClosed().subscribe(() => { this.router.navigate(['/teacher/home/schedule'], { @@ -264,4 +267,8 @@ export class CreateRunDialogComponent { copyMsg() { this.snackBar.open($localize`Copied to clipboard.`); } + + protected getPeriodFromAccessLink(link: string): string { + return link.slice(link.indexOf('-') + 1).replaceAll('++', ' '); + } } diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html index 844648fe8d2..6393282e5bc 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html @@ -45,22 +45,34 @@ >Share with students) } @else { - Access Link: - - + @for (accessLink of accessLinks; track accessLink) { + @if ($index < 3 || showAllLinks) { + Period {{ getPeriodFromAccessLink(accessLink) }} Access Link: + + +
+ } + } + @if (accessLinks.length > 3) { + @if (showAllLinks) { + Show less + } @else { + Show more + } + } }

diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss index 5ae75033d30..9b1d5b138a2 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.scss @@ -31,4 +31,9 @@ .access-link { text-transform: lowercase; +} + +.toggle-show:hover { + cursor: pointer; + text-decoration: underline; } \ No newline at end of file diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts index fe5fdc23a39..ed306649c58 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts @@ -20,7 +20,7 @@ import { MatSnackBar } from '@angular/material/snack-bar'; standalone: false }) export class TeacherRunListItemComponent implements OnInit { - protected accessLink: string = ''; + protected accessLinks: string[] = []; protected animateDelay: string = '0s'; protected animateDuration: string = '0s'; protected manageStudentsLink: string = ''; @@ -28,6 +28,7 @@ export class TeacherRunListItemComponent implements OnInit { @Input() run: TeacherRun = new TeacherRun(); @Output() runArchiveStatusChangedEvent: EventEmitter = new EventEmitter(); @Output() runSelectedStatusChangedEvent: EventEmitter = new EventEmitter(); + protected showAllLinks: boolean = false; private subscriptions: Subscription = new Subscription(); protected thumbStyle: SafeStyle; @@ -47,7 +48,10 @@ export class TeacherRunListItemComponent implements OnInit { this.run.id }/manage-students`; if (this.run.isSurveyRun()) { - this.accessLink = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-${this.run.periods[0]}`; + const linkBase = `${this.configService.getContextPath()}/api/survey/launch/${this.run.runCode}-`; + this.run.periods.forEach((period) => + this.accessLinks.push(linkBase + period.replaceAll(' ', '++')) + ); } if (this.run.highlighted) { this.animateDuration = '2s'; @@ -155,4 +159,12 @@ export class TeacherRunListItemComponent implements OnInit { copyMsg() { this.snackBar.open($localize`Copied to clipboard.`); } + + protected getPeriodFromAccessLink(link: string): string { + return link.slice(link.indexOf('-') + 1).replaceAll('++', ' '); + } + + protected toggleShowAllLinks(): void { + this.showAllLinks = !this.showAllLinks; + } } From 3850b8c1bb21ed514496829eae47168f2a7d3759 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Tue, 27 May 2025 14:29:40 -0700 Subject: [PATCH 09/33] Submit button --- src/app/student/run-info.ts | 1 + .../survey-completed.component.html | 6 + .../survey-completed.component.scss | 3 + .../survey-completed.component.spec.ts | 23 ++++ .../survey-completed.component.ts | 10 ++ .../student/survey/survey-routing.module.ts | 6 +- .../stepTools/step-tools.component.html | 14 ++- .../stepTools/step-tools.component.scss | 9 ++ .../stepTools/step-tools.component.ts | 116 +++++++++++++++--- 9 files changed, 167 insertions(+), 21 deletions(-) create mode 100644 src/app/student/survey/survey-completed/survey-completed.component.html create mode 100644 src/app/student/survey/survey-completed/survey-completed.component.scss create mode 100644 src/app/student/survey/survey-completed/survey-completed.component.spec.ts create mode 100644 src/app/student/survey/survey-completed/survey-completed.component.ts diff --git a/src/app/student/run-info.ts b/src/app/student/run-info.ts index 5dd5e449ad6..1ff607b851b 100644 --- a/src/app/student/run-info.ts +++ b/src/app/student/run-info.ts @@ -8,4 +8,5 @@ export class RunInfo { error: string; name: string; wiseVersion: number; + isSurvey: boolean; } diff --git a/src/app/student/survey/survey-completed/survey-completed.component.html b/src/app/student/survey/survey-completed/survey-completed.component.html new file mode 100644 index 00000000000..72d0a9abad5 --- /dev/null +++ b/src/app/student/survey/survey-completed/survey-completed.component.html @@ -0,0 +1,6 @@ + + +

Your responses have been submitted.

+

Thank you!

+
+
diff --git a/src/app/student/survey/survey-completed/survey-completed.component.scss b/src/app/student/survey/survey-completed/survey-completed.component.scss new file mode 100644 index 00000000000..d9477abdc8b --- /dev/null +++ b/src/app/student/survey/survey-completed/survey-completed.component.scss @@ -0,0 +1,3 @@ +mat-card { + margin: 0% 30%; +} \ No newline at end of file diff --git a/src/app/student/survey/survey-completed/survey-completed.component.spec.ts b/src/app/student/survey/survey-completed/survey-completed.component.spec.ts new file mode 100644 index 00000000000..4acabbf9981 --- /dev/null +++ b/src/app/student/survey/survey-completed/survey-completed.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { SurveyCompletedComponent } from './survey-completed.component'; + +describe('SurveyCompletedComponent', () => { + let component: SurveyCompletedComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [SurveyCompletedComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(SurveyCompletedComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/student/survey/survey-completed/survey-completed.component.ts b/src/app/student/survey/survey-completed/survey-completed.component.ts new file mode 100644 index 00000000000..73950b130fc --- /dev/null +++ b/src/app/student/survey/survey-completed/survey-completed.component.ts @@ -0,0 +1,10 @@ +import { Component } from '@angular/core'; +import { MatCardModule } from '@angular/material/card'; + +@Component({ + selector: 'survey-completed', + imports: [MatCardModule], + templateUrl: './survey-completed.component.html', + styleUrl: './survey-completed.component.scss' +}) +export class SurveyCompletedComponent {} diff --git a/src/app/student/survey/survey-routing.module.ts b/src/app/student/survey/survey-routing.module.ts index ea37048da1b..b3a3c5173ba 100644 --- a/src/app/student/survey/survey-routing.module.ts +++ b/src/app/student/survey/survey-routing.module.ts @@ -3,6 +3,7 @@ import { RouterModule, Routes } from '@angular/router'; import { StudentComponent } from '../student.component'; import { WorkgroupLimitReachedComponent } from './workgroup-limit-reached/workgroup-limit-reached.component'; import { LogOutPageComponent } from './log-out-page/log-out-page.component'; +import { SurveyCompletedComponent } from './survey-completed/survey-completed.component'; const studentRoutes: Routes = [ { @@ -10,12 +11,13 @@ const studentRoutes: Routes = [ component: StudentComponent, children: [ { path: '', redirectTo: '/', pathMatch: 'full' }, + { path: 'completed', component: SurveyCompletedComponent, pathMatch: 'full' }, + { path: 'logout', component: LogOutPageComponent, pathMatch: 'full' }, { path: 'workgroupLimitReached', component: WorkgroupLimitReachedComponent, pathMatch: 'full' - }, - { path: 'logout', component: LogOutPageComponent, pathMatch: 'full' } + } ] } ]; diff --git a/src/assets/wise5/themes/default/themeComponents/stepTools/step-tools.component.html b/src/assets/wise5/themes/default/themeComponents/stepTools/step-tools.component.html index aff929afab4..39c8473452e 100644 --- a/src/assets/wise5/themes/default/themeComponents/stepTools/step-tools.component.html +++ b/src/assets/wise5/themes/default/themeComponents/stepTools/step-tools.component.html @@ -12,7 +12,7 @@ {{ icons.prev }}
- +
  @@ -49,7 +49,7 @@
- +
+ @if (isSurvey) { +
+ +
+ }
-

Students Per Team

- - - Only 1 student - - - 1-3 students - - - + @if (isDefaultRun) { +

Students Per Team

+ + + Only 1 student + + + 1-3 students + + + + } +

Schedule  diff --git a/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts b/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts index 9bc4973b17e..abfdcee4150 100644 --- a/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts +++ b/src/app/teacher/run-settings-dialog/run-settings-dialog.component.spec.ts @@ -53,10 +53,9 @@ export class MockTeacherService { } } +let component: RunSettingsDialogComponent; +let fixture: ComponentFixture; describe('RunSettingsDialogComponent', () => { - let component: RunSettingsDialogComponent; - let fixture: ComponentFixture; - const getStartDateInput = () => { return fixture.debugElement.nativeElement.querySelectorAll('input')[1]; }; @@ -199,4 +198,20 @@ describe('RunSettingsDialogComponent', () => { const message = component.translateMessageCode('periodNameAlreadyExists'); expect(message).toEqual('There is already a period with that name.'); }); + + surveyRun(); }); + +function surveyRun() { + describe('Survey Run', () => { + beforeEach(() => { + component['isDefaultRun'] = false; + fixture.detectChanges(); + }); + + it('should hide Student Per Team section', () => { + const radioGroup = fixture.debugElement.nativeElement.querySelector('mat-radio-group'); + expect(radioGroup).toBeNull(); + }); + }); +} diff --git a/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts b/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts index 719e371c141..306f6d4926e 100644 --- a/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts +++ b/src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts @@ -7,10 +7,10 @@ import { formatDate } from '@angular/common'; import { TeacherRun } from '../teacher-run'; @Component({ - selector: 'app-run-settings-dialog', - templateUrl: './run-settings-dialog.component.html', - styleUrls: ['./run-settings-dialog.component.scss'], - standalone: false + selector: 'app-run-settings-dialog', + templateUrl: './run-settings-dialog.component.html', + styleUrls: ['./run-settings-dialog.component.scss'], + standalone: false }) export class RunSettingsDialogComponent implements OnInit { run: TeacherRun; @@ -28,6 +28,7 @@ export class RunSettingsDialogComponent implements OnInit { startDateMessage: string = ''; endDateMessage: string = ''; isLockedAfterEndDateMessage: string = ''; + protected isDefaultRun: boolean = true; maxStartDate: Date; minEndDate: Date; targetEndDate: Date; @@ -53,6 +54,7 @@ export class RunSettingsDialogComponent implements OnInit { this.isLockedAfterEndDateCheckboxEnabled = true; } this.initializeMessageCodeToMessage(); + this.isDefaultRun = !this.run.isSurveyRun(); } initializeMessageCodeToMessage() { diff --git a/src/messages.xlf b/src/messages.xlf index 474d49cdf1e..bc9dbaab450 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -433,7 +433,7 @@ src/app/teacher/create-run-dialog/create-run-dialog.component.html - 103,107 + 105,109 src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.html @@ -6543,7 +6543,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 168,171 + 170,173 src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.html @@ -6551,7 +6551,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 118,120 + 121,123 src/app/teacher/share-run-dialog/share-run-dialog.component.html @@ -8291,13 +8291,6 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.33,38 - - Log Out - - src/app/student/survey/log-out-page/log-out-page.component.html - 10,14 - - Team Sign In @@ -8548,39 +8541,18 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.87 - - 1. Choose Periods - - src/app/teacher/create-run-dialog/create-run-dialog.component.html - 9,11 - - - - Add your own periods - - src/app/teacher/create-run-dialog/create-run-dialog.component.html - 20,21 - - - - For "Period 9", just enter the number 9. Separate periods with commas (e.g. "Section 1, Section 2"). Manually named periods should be no more than 16 characters long. + + 1. Choose Run Type src/app/teacher/create-run-dialog/create-run-dialog.component.html - 24,28 - - - - 2. Choose Run Type - - src/app/teacher/create-run-dialog/create-run-dialog.component.html - 28,29 + 9,10 Default src/app/teacher/create-run-dialog/create-run-dialog.component.html - 30,31 + 11,12 src/assets/wise5/classroomMonitor/dataExport/export-raw-data/export-raw-data.component.html @@ -8598,121 +8570,142 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 33,41 + 14,22 Students can access survey units without a WISE account. The URL to access the unit will be generated when the run is created. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 36,39 + 17,20 + + + + 2. Choose Periods + + src/app/teacher/create-run-dialog/create-run-dialog.component.html + 25,27 + + + + Add your own periods + + src/app/teacher/create-run-dialog/create-run-dialog.component.html + 37,38 + + + + For "Period 9", just enter the number 9. Separate periods with commas (e.g. "Section 1, Section 2"). Manually named periods should be no more than 16 characters long. + + src/app/teacher/create-run-dialog/create-run-dialog.component.html + 41,45 3. Choose Students Per Team src/app/teacher/create-run-dialog/create-run-dialog.component.html - 45,46 + 47,48 Only 1 student src/app/teacher/create-run-dialog/create-run-dialog.component.html - 47,48 + 49,50 1-3 students src/app/teacher/create-run-dialog/create-run-dialog.component.html - 48,50 + 50,52 . Set Schedule src/app/teacher/create-run-dialog/create-run-dialog.component.html - 52,53 + 54,55 Start date src/app/teacher/create-run-dialog/create-run-dialog.component.html - 56,59 + 58,61 src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 69,72 + 72,75 Start date is required src/app/teacher/create-run-dialog/create-run-dialog.component.html - 66,70 + 68,72 End date src/app/teacher/create-run-dialog/create-run-dialog.component.html - 71,74 + 73,76 src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 85,88 + 88,91 Lock After End Date src/app/teacher/create-run-dialog/create-run-dialog.component.html - 85,88 + 87,90 src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 105,109 + 108,112 If the End Date has passed and the unit is locked, students will no longer be able to save new work src/app/teacher/create-run-dialog/create-run-dialog.component.html - 90,93 + 92,95 src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 109,112 + 112,115 Note: These dates can be changed at any time from your Class Schedule. Just select "Edit Settings" from the unit's dropdown menu. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 98,102 + 100,104 Create Run src/app/teacher/create-run-dialog/create-run-dialog.component.html - 114,118 + 116,120 Success! This unit has been added to your Class Schedule. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 121,123 + 123,125 Access Code: src/app/teacher/create-run-dialog/create-run-dialog.component.html - 124,125 + 126,127 src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html @@ -8723,14 +8716,14 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. Important: Every classroom unit has a unique Access Code. Students use this code to register for a unit. Give the code to your students when they first sign up for a WISE account. If they already have WISE accounts, have them log in and then select "Add Unit" from the student home page. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 126,132 + 128,134 You can always find the Access Code for each classroom unit in your Class Schedule. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 132,135 + 134,137 @@ -8746,14 +8739,14 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.content_copy src/app/teacher/create-run-dialog/create-run-dialog.component.html - 137,152 + 139,154 Copy link src/app/teacher/create-run-dialog/create-run-dialog.component.html - 143,145 + 145,147 src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html @@ -8768,28 +8761,28 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. Important: Every survey unit has a unique Access Link. Students can use this link to access and complete the survey without needing to create a WISE account. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 155,158 + 157,160 You can always find the Access Link for each survey unit in your Class Schedule. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 158,162 + 160,164 Share to Google Classroom src/app/teacher/create-run-dialog/create-run-dialog.component.html - 165,168 + 167,170 Copied to clipboard. src/app/teacher/create-run-dialog/create-run-dialog.component.ts - 270 + 272 src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts @@ -9265,161 +9258,161 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.Students Per Team src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 50,51 + 51,52 Only 1 student src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 53,55 + 54,56 1-3 students src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 56,59 + 57,60 Schedule src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 61,62 + 64,65 (Last student login: ) src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 63,65 + 66,68 Start date is required. src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 79,80 + 82,83 There is already a period with that name. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 60 + 62 You do not have permission to add periods to this unit. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 61 + 63 You are not allowed to delete a period that contains students. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 62 + 64 You do not have permission to delete periods from this unit. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 63 + 65 You do not have permission to change the number of students per team for this unit. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 64 + 66 You are not allowed to decrease the number of students per team because this unit already has teams with more than 1 student. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 65 + 67 You do not have permission to change the dates for this unit. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 66 + 68 End date can't be before start date. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 67 + 69 Start date can't be after end date. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 68 + 70 You do not have permission to change whether unit is locked after end date. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 69 + 71 Please enter a new period name. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 89 + 91 1-3 src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 130 + 132 Are you sure you want to change the students per team to ? src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 134 + 136 Are you sure you want to change the start date to ? src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 165 + 167 Are you sure you want to change the end date to ? src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 221 + 223 Are you sure you want to remove the end date? src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 223 + 225 Unit settings updated. src/app/teacher/run-settings-dialog/run-settings-dialog.component.ts - 306 + 308 From 1a669d58334b968776b2068e0819a515d2ad4d3c Mon Sep 17 00:00:00 2001 From: Jonathan Lim-Breitbart Date: Wed, 4 Jun 2025 09:46:02 -0700 Subject: [PATCH 25/33] Move survey unit links to share run code dialog --- .../create-run-dialog.component.html | 2 +- .../share-run-code-dialog.component.html | 141 ++++++++++++------ .../share-run-code-dialog.component.ts | 22 ++- .../teacher-run-list-item.component.html | 35 +---- .../teacher-run-list-item.component.ts | 23 +-- 5 files changed, 119 insertions(+), 104 deletions(-) diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.html b/src/app/teacher/create-run-dialog/create-run-dialog.component.html index 1953a94e04a..96500158f60 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.html +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.html @@ -14,7 +14,7 @@

1. Choose Run Type

helpShare with Students

+@if (run.isSurveyRun()) { +

Share with Participants

+} @else { +

Share with Students

+}

{{ run.project.name }} (Run ID: {{ run.id }})

-

Copy this link to share with your students:

- -

- Students with WISE accounts can also select Add Unit+ and type the Access - Code: -

- - -

Add as an assignment in Google Classroom:

+ @if (run.isSurveyRun()) { +

+ This is a survey unit. Participants complete survey units anonymously and do not need a WISE + account. +

+ @if (accessLinks.length === 1) { +

Copy this link to share with participants:

+ + } @else { +

Copy these links to share with participants:

+
    + @for (accessLink of accessLinks; track accessLink) { +
  • + Period {{ getPeriodFromAccessLink(accessLink) }}: + +
  • + } +
+ } + } @else { +

Copy this link to share with your students:

-
+

+ Students with WISE accounts can also select Add Unit+ and type the Access + Code: +

+ + +

Add as an assignment in Google Classroom:

+ +
+ }
diff --git a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts index 927ef5ac4b8..146c9f10497 100644 --- a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts +++ b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts @@ -6,19 +6,22 @@ import { UserService } from '../../services/user.service'; import { TeacherRun } from '../teacher-run'; import { TeacherService } from '../teacher.service'; import { ListClassroomCoursesDialogComponent } from '../list-classroom-courses-dialog/list-classroom-courses-dialog.component'; +import { AccessLinkService } from '../../services/accessLinkService'; @Component({ - selector: 'app-share-run-code-dialog', - templateUrl: './share-run-code-dialog.component.html', - styleUrls: ['./share-run-code-dialog.component.scss'], - standalone: false + selector: 'app-share-run-code-dialog', + templateUrl: './share-run-code-dialog.component.html', + styleUrls: ['./share-run-code-dialog.component.scss'], + standalone: false }) export class ShareRunCodeDialogComponent { - code: string; - link: string; + protected accessLinks: string[] = []; + protected code: string; + protected link: string; constructor( @Inject(MAT_DIALOG_DATA) public run: TeacherRun, + private accessLinkService: AccessLinkService, private dialog: MatDialog, private snackBar: MatSnackBar, private teacherService: TeacherService, @@ -30,6 +33,9 @@ export class ShareRunCodeDialogComponent { this.code = this.run.runCode; const host = this.configService.getWISEHostname() + this.configService.getContextPath(); this.link = `${host}/login?accessCode=${this.code}`; + if (this.run.isSurveyRun()) { + this.accessLinks = this.accessLinkService.getAccessLinks(this.run.runCode, this.run.periods); + } } copyMsg() { @@ -73,4 +79,8 @@ export class ShareRunCodeDialogComponent { }); }); } + + protected getPeriodFromAccessLink(link: string): string { + return this.accessLinkService.getPeriodFromAccessLink(link); + } } diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html index 6393282e5bc..567eccebc72 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html @@ -36,7 +36,11 @@
- @if (!run.isSurveyRun()) { + @if (run.isSurveyRun()) { + Survey Unit (Share with participants) + } @else { Access Code: {{ run.runCode }} (Share with students) - } @else { - @for (accessLink of accessLinks; track accessLink) { - @if ($index < 3 || showAllLinks) { - Period {{ getPeriodFromAccessLink(accessLink) }} Access Link: - - -
- } - } - @if (accessLinks.length > 3) { - @if (showAllLinks) { - Show less - } @else { - Show more - } - } }
diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts index ad54fd7ddd9..9efa3ae6ae7 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts @@ -3,7 +3,6 @@ import { ConfigService } from '../../services/config.service'; import { DomSanitizer } from '@angular/platform-browser'; import { flash } from '../../animations'; import { MatDialog } from '@angular/material/dialog'; -import { MatSnackBar } from '@angular/material/snack-bar'; import { ProjectTagService } from '../../../assets/wise5/services/projectTagService'; import { Router } from '@angular/router'; import { SafeStyle } from '@angular/platform-browser'; @@ -11,7 +10,6 @@ import { ShareRunCodeDialogComponent } from '../share-run-code-dialog/share-run- import { Subscription } from 'rxjs'; import { Tag } from '../../domain/tag'; import { TeacherRun } from '../teacher-run'; -import { AccessLinkService } from '../../services/accessLinkService'; @Component({ animations: [flash], @@ -21,7 +19,6 @@ import { AccessLinkService } from '../../services/accessLinkService'; templateUrl: './teacher-run-list-item.component.html' }) export class TeacherRunListItemComponent implements OnInit { - protected accessLinks: string[] = []; protected animateDelay: string = '0s'; protected animateDuration: string = '0s'; protected manageStudentsLink: string = ''; @@ -29,19 +26,16 @@ export class TeacherRunListItemComponent implements OnInit { @Input() run: TeacherRun = new TeacherRun(); @Output() runArchiveStatusChangedEvent: EventEmitter = new EventEmitter(); @Output() runSelectedStatusChangedEvent: EventEmitter = new EventEmitter(); - protected showAllLinks: boolean = false; private subscriptions: Subscription = new Subscription(); protected thumbStyle: SafeStyle; constructor( - private accessLinkService: AccessLinkService, private configService: ConfigService, private dialog: MatDialog, private elRef: ElementRef, private projectTagService: ProjectTagService, private router: Router, - private sanitizer: DomSanitizer, - private snackBar: MatSnackBar + private sanitizer: DomSanitizer ) {} ngOnInit(): void { @@ -49,9 +43,6 @@ export class TeacherRunListItemComponent implements OnInit { this.manageStudentsLink = `${this.configService.getContextPath()}/teacher/manage/unit/${ this.run.id }/manage-students`; - if (this.run.isSurveyRun()) { - this.accessLinks = this.accessLinkService.getAccessLinks(this.run.runCode, this.run.periods); - } if (this.run.highlighted) { this.animateDuration = '2s'; this.animateDelay = '1s'; @@ -154,16 +145,4 @@ export class TeacherRunListItemComponent implements OnInit { this.runSelectedStatusChangedEvent.emit(); this.runArchiveStatusChangedEvent.emit(); } - - protected copyMsg(): void { - this.snackBar.open($localize`Copied to clipboard.`); - } - - protected getPeriodFromAccessLink(link: string): string { - return this.accessLinkService.getPeriodFromAccessLink(link); - } - - protected toggleShowAllLinks(): void { - this.showAllLinks = !this.showAllLinks; - } } From a776d4d68780742929e18c11e3453d9c1e387277 Mon Sep 17 00:00:00 2001 From: Jonathan Lim-Breitbart Date: Wed, 4 Jun 2025 12:28:33 -0700 Subject: [PATCH 26/33] Hide class periods and lock after end date controls for survey units --- .../create-run-dialog.component.html | 128 ++++++++--------- .../create-run-dialog.component.ts | 7 +- .../run-settings-dialog.component.html | 133 +++++++++--------- .../share-run-code-dialog.component.html | 56 +++----- 4 files changed, 155 insertions(+), 169 deletions(-) diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.html b/src/app/teacher/create-run-dialog/create-run-dialog.component.html index 96500158f60..0df290fc81e 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.html +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.html @@ -7,32 +7,34 @@

Use with Class

(Unit ID: {{ project.id }})

1. Choose Run Type

- - Default - Survey - help - - +
+ + Default + Survey + + + help +
-

2. Choose Periods

-

- @for (control of selectedPeriodsControl.controls; track $index) { - - - {{ control.controls.name.value }} - - - } -

@if (isDefaultRun()) { +

2. Choose Periods

+

+ @for (control of selectedPeriodsControl.controls; track $index) { + + + {{ control.controls.name.value }} + + + } +

Add your own periods @@ -41,17 +43,18 @@

2. Choose Periods

For "Period 9", just enter the number 9. Separate periods with commas (e.g. "Section 1, Section 2"). Manually named periods should be no more than 16 characters long.

- } - - @if (isDefaultRun()) { +

3. Choose Students Per Team

Only 1 student 1-3 students +

4. Set Schedule

+ } + @if (!isDefaultRun()) { +

2. Set Schedule

} -

{{ isDefaultRun() ? 4 : 3 }}. Set Schedule

@@ -82,21 +85,23 @@

{{ isDefaultRun() ? 4 : 3 }}. Set Schedule

-
- - Lock After End Date   - help -
+ @if (isDefaultRun()) { +
+ + Lock After End Date   + help +
+ }
-

+

Note: These dates can be changed at any time from your Class Schedule. Just select "Edit Settings" from the unit's dropdown menu.

@@ -134,28 +139,25 @@

{{ isDefaultRun() ? 4 : 3 }}. Set Schedule

You can always find the Access Code for each classroom unit in your Class Schedule.

} @else { - @for (accessLink of accessLinks; track accessLink) { -

- Period {{ getPeriodFromAccessLink(accessLink) }} Access Link: - -

-
- } +

+ Access Link: + +

- Important: Every survey unit has a unique Access Link. Students can use this link to - access and complete the survey without needing to create a WISE account. + Important: Every survey unit has a unique Access Link. Participants can use this link to + complete the unit without a WISE account.

You can always find the Access Link for each survey unit in your Class Schedule.

} diff --git a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts index 6924ccf28a4..ae689a58345 100644 --- a/src/app/teacher/create-run-dialog/create-run-dialog.component.ts +++ b/src/app/teacher/create-run-dialog/create-run-dialog.component.ts @@ -276,7 +276,12 @@ export class CreateRunDialogComponent { return this.accessLinkService.getPeriodFromAccessLink(link); } - protected clearCustomPeriods(): void { + protected setAsSurveyUnit(): void { this.customPeriods.setValue(''); + this.periodsGroup.controls.forEach((control, index) => { + index === 0 + ? control.get('checkbox').setValue(true) + : control.get('checkbox').setValue(false); + }); } } diff --git a/src/app/teacher/run-settings-dialog/run-settings-dialog.component.html b/src/app/teacher/run-settings-dialog/run-settings-dialog.component.html index b671057e8d2..272eba9b018 100644 --- a/src/app/teacher/run-settings-dialog/run-settings-dialog.component.html +++ b/src/app/teacher/run-settings-dialog/run-settings-dialog.component.html @@ -3,51 +3,51 @@

Edit Settings

{{ run.name }} (Run ID: {{ run.id }})

-

Class Periods

-
-
- {{ period }} - -
-
-
- - Add new period - - - {{ addPeriodMessage }} - -

- For "Period 9", just enter the number 9. -

-
- @if (isDefaultRun) { +

Class Periods

+
+
+ {{ period }} + +
+
+
+ + Add new period + + + {{ addPeriodMessage }} + +

+ For "Period 9", just enter the number 9. +

+
+

Students Per Team

@@ -59,12 +59,13 @@

Students Per Team

} -

Schedule  - - (Last student login: {{ run.lastRun | date: 'short' }}) - + @if (run.lastRun) { + + (Last student login: {{ run.lastRun | date: 'short' }}) + + }

@@ -98,23 +99,25 @@

{{ endDateMessage }}

-
- - Lock After End Date   - help -
+ @if (isDefaultRun) { +
+ + Lock After End Date   + help +
+ }
diff --git a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html index 6450f8ca806..57087867790 100644 --- a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html +++ b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html @@ -13,46 +13,22 @@

Share with Students

This is a survey unit. Participants complete survey units anonymously and do not need a WISE account.

- @if (accessLinks.length === 1) { -

Copy this link to share with participants:

- - } @else { -

Copy these links to share with participants:

-
    - @for (accessLink of accessLinks; track accessLink) { -
  • - Period {{ getPeriodFromAccessLink(accessLink) }}: - -
  • - } -
- } +

Copy this link to share with participants:

+ } @else {

Copy this link to share with your students:

3. Choose Students Per Team

4. Set Schedule

- } - @if (!isDefaultRun()) { + } @else {

2. Set Schedule

}
@@ -86,9 +85,9 @@

2. Set Schedule

@if (isDefaultRun()) { -
+
- Lock After End Date   { expect(component.getPeriodsString()).toEqual('1,3,5,hello'); }); - it('should not show max workgroup size option if survey is checked', () => { + it('should not show period or max workgroup size options if survey is checked', () => { let h3ElementsText = Array.from(fixture.nativeElement.querySelectorAll('h3')).map( (element: HTMLElement) => element.innerText ); expect(h3ElementsText.length).toEqual(4); + expect(h3ElementsText.includes('2. Choose Periods')).toBeTrue(); expect(h3ElementsText.includes('3. Choose Students Per Team')).toBeTrue(); component.form.controls['runType'].setValue('survey'); @@ -152,7 +153,8 @@ describe('CreateRunDialogComponent', () => { h3ElementsText = Array.from(fixture.nativeElement.querySelectorAll('h3')).map( (element: HTMLElement) => element.innerText ); - expect(h3ElementsText.length).toEqual(3); + expect(h3ElementsText.length).toEqual(2); + expect(h3ElementsText.includes('2. Choose Periods')).toBeFalse(); expect(h3ElementsText.includes('3. Choose Students Per Team')).toBeFalse(); }); diff --git a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts index fcae1fd4a53..6d6cf12efb0 100644 --- a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts +++ b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts @@ -9,6 +9,7 @@ import { TeacherService } from '../teacher.service'; import { UserService } from '../../services/user.service'; import { TeacherRun } from '../teacher-run'; import { Project } from '../../domain/project'; +import { AccessLinkService } from '../../services/accessLinkService'; const runObj = new TeacherRun(); runObj.id = 1; @@ -49,6 +50,7 @@ describe('ShareRunCodeDialogComponent', () => { declarations: [ShareRunCodeDialogComponent], imports: [BrowserAnimationsModule, MatDialogModule, MatSnackBarModule], providers: [ + AccessLinkService, { provide: ConfigService, useClass: MockConfigService }, { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService }, diff --git a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts index 4b37ea12296..4b65661c4e8 100644 --- a/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts +++ b/src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.spec.ts @@ -22,7 +22,6 @@ import { TeacherService } from '../teacher.service'; import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed'; import { User } from '../../domain/user'; import { UserService } from '../../services/user.service'; -import { AccessLinkService } from '../../services/accessLinkService'; export class MockTeacherService {} @@ -65,7 +64,6 @@ describe('TeacherRunListItemComponent', () => { RouterTestingModule ], providers: [ - AccessLinkService, ArchiveProjectService, { provide: ConfigService, useClass: MockConfigService }, ProjectTagService, From 69f0b9786a2b8a96ebcf47b1705bcdcf64b58241 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 4 Jun 2025 20:29:00 +0000 Subject: [PATCH 28/33] Updated messages --- src/messages.xlf | 263 ++++++++++++++++++++++++----------------------- 1 file changed, 132 insertions(+), 131 deletions(-) diff --git a/src/messages.xlf b/src/messages.xlf index bc9dbaab450..f124be0d2f0 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -299,7 +299,7 @@ src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 61,63 + 88,90 src/assets/wise5/authoringTool/components/component-info-dialog/component-info-dialog.component.html @@ -433,7 +433,7 @@ src/app/teacher/create-run-dialog/create-run-dialog.component.html - 105,109 + 109,113 src/app/teacher/edit-run-warning-dialog/edit-run-warning-dialog.component.html @@ -5962,7 +5962,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 114,117 + 89,92 @@ -6543,7 +6543,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 170,173 + 171,174 src/app/teacher/list-classroom-courses-dialog/list-classroom-courses-dialog.component.html @@ -6551,7 +6551,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 121,123 + 124,126 src/app/teacher/share-run-dialog/share-run-dialog.component.html @@ -8217,7 +8217,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 146,147 + 121,122 @@ -8545,60 +8545,53 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.1. Choose Run Type src/app/teacher/create-run-dialog/create-run-dialog.component.html - 9,10 + 9,11 Default src/app/teacher/create-run-dialog/create-run-dialog.component.html - 11,12 + 12,13 src/assets/wise5/classroomMonitor/dataExport/export-raw-data/export-raw-data.component.html 15,16 - - Survey help + + Survey src/app/teacher/create-run-dialog/create-run-dialog.component.html - 14,22 + 15,17 - - Students can access survey units without a WISE account. The URL to access the unit will be generated when the run is created. + + Participants complete survey units anonymously without a WISE account. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 17,20 + 20,23 2. Choose Periods src/app/teacher/create-run-dialog/create-run-dialog.component.html - 25,27 + 28,30 Add your own periods src/app/teacher/create-run-dialog/create-run-dialog.component.html - 37,38 + 39,40 For "Period 9", just enter the number 9. Separate periods with commas (e.g. "Section 1, Section 2"). Manually named periods should be no more than 16 characters long. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 41,45 + 43,47 @@ -8622,160 +8615,154 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.50,52 - - . Set Schedule + + 4. Set Schedule src/app/teacher/create-run-dialog/create-run-dialog.component.html - 54,55 + 53,57 + + + + 2. Set Schedule + + src/app/teacher/create-run-dialog/create-run-dialog.component.html + 55,57 Start date src/app/teacher/create-run-dialog/create-run-dialog.component.html - 58,61 + 60,63 src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 72,75 + 73,76 Start date is required src/app/teacher/create-run-dialog/create-run-dialog.component.html - 68,72 + 70,74 End date src/app/teacher/create-run-dialog/create-run-dialog.component.html - 73,76 + 75,78 src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 88,91 + 89,92 - - Lock After End Date + + Lock After End Date src/app/teacher/create-run-dialog/create-run-dialog.component.html - 87,90 - - - src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 108,112 + 90,93 If the End Date has passed and the unit is locked, students will no longer be able to save new work src/app/teacher/create-run-dialog/create-run-dialog.component.html - 92,95 + 95,98 src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 112,115 + 114,117 Note: These dates can be changed at any time from your Class Schedule. Just select "Edit Settings" from the unit's dropdown menu. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 100,104 + 104,108 Create Run src/app/teacher/create-run-dialog/create-run-dialog.component.html - 116,120 + 120,124 Success! This unit has been added to your Class Schedule. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 123,125 + 127,129 Access Code: src/app/teacher/create-run-dialog/create-run-dialog.component.html - 126,127 + 130,131 src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 40,41 + 44,45 Important: Every classroom unit has a unique Access Code. Students use this code to register for a unit. Give the code to your students when they first sign up for a WISE account. If they already have WISE accounts, have them log in and then select "Add Unit" from the student home page. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 128,134 + 132,138 You can always find the Access Code for each classroom unit in your Class Schedule. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 134,137 + 138,141 - - Period Access Link: content_copy + + Access Link: src/app/teacher/create-run-dialog/create-run-dialog.component.html - 139,154 + 142,145 Copy link src/app/teacher/create-run-dialog/create-run-dialog.component.html - 145,147 + 148,150 src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 13,16 + 23,25 - src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 57,59 + src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html + 39,41 - - Important: Every survey unit has a unique Access Link. Students can use this link to access and complete the survey without needing to create a WISE account. + + Important: Every survey unit has a unique Access Link. Participants can use this link to complete the unit without a WISE account. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 157,160 + 158,161 You can always find the Access Link for each survey unit in your Class Schedule. src/app/teacher/create-run-dialog/create-run-dialog.component.html - 160,164 + 161,165 Share to Google Classroom src/app/teacher/create-run-dialog/create-run-dialog.component.html - 167,170 + 168,171 @@ -8786,11 +8773,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts - 36 - - - src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts - 159 + 42 @@ -8834,7 +8817,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 5,7 + 9,11 src/app/teacher/share-run-dialog/share-run-dialog.component.html @@ -9014,7 +8997,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 53,57 + 79,83 @@ -9223,35 +9206,35 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.Class Periods src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 6,8 + 7,9 Delete period src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 15,20 + 16,20 Add new period src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 23,26 + 24,27 Add period src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 39,43 + 40,44 For "Period 9", just enter the number 9. src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 46,50 + 47,51 @@ -9279,7 +9262,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.Schedule src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 64,65 + 63,65 @@ -9293,7 +9276,14 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.Start date is required. src/app/teacher/run-settings-dialog/run-settings-dialog.component.html - 82,83 + 83,84 + + + + Lock After End Date + + src/app/teacher/run-settings-dialog/run-settings-dialog.component.html + 110,113 @@ -9423,7 +9413,7 @@ Click "Cancel" to keep the invalid JSON open so you can fix it. src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 94,96 + 69,71 @@ -9526,39 +9516,60 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.6,9 + + Share with Participants + + src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html + 2,6 + + Share with Students src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 1,4 + 4,8 + + + + This is a survey unit. Participants complete survey units anonymously and do not need a WISE account. + + src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html + 13,16 + + + + Copy this link to share with participants: + + src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html + 16,19 Copy this link to share with your students: src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 7,11 + 33,37 Students with WISE accounts can also select Add Unit+ and type the Access Code: src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 23,26 + 49,52 Copy Access Code src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 31,33 + 57,59 Add as an assignment in Google Classroom: src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.html - 43,47 + 69,73 @@ -9739,88 +9750,60 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.33,34 - - Share with students + + Survey Unit src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 45,48 - - - - Period Access Link: content_copy - - src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 51,66 + 40,41 - - Show less + + Share with participants src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 71,73 - - - src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 27,30 + 41,44 - - Show more + + Share with students src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 73,78 - - - src/assets/wise5/classroomMonitor/classroomMonitorComponents/view-component-revisions/view-component-revisions.component.html - 53,58 - - - src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html - 29,35 + 49,54 Shared by src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 79,81 + 54,56 (Legacy Unit) src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 117,120 + 92,95 Last student login: src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 143,145 + 118,120 Teacher Tools src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.html - 155,159 + 130,134 Class Periods: src/app/teacher/teacher-run-list-item/teacher-run-list-item.component.ts - 126 + 117 @@ -14847,6 +14830,17 @@ Are you sure you want to proceed? 1,2 + + Show more + + src/assets/wise5/classroomMonitor/classroomMonitorComponents/view-component-revisions/view-component-revisions.component.html + 53,58 + + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 29,35 + + Team has not saved any work @@ -21756,6 +21750,13 @@ If this problem continues, let your teacher know and move on to the next activit 6,10 + + Show less + + src/assets/wise5/directives/teacher-summary-display/match-summary-display/match-summary-display.component.html + 27,30 + + Choice Frequency From 59a2af560fd8b9db239cc5d0168810777413221b Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Wed, 4 Jun 2025 14:23:49 -0700 Subject: [PATCH 29/33] Cleaned up ShareRunCodeDialog and added tests --- .../share-run-code-dialog.component.spec.ts | 69 ++++++++++++++----- .../share-run-code-dialog.component.ts | 12 ++-- 2 files changed, 59 insertions(+), 22 deletions(-) diff --git a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts index 6d6cf12efb0..835db62da05 100644 --- a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts +++ b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts @@ -3,13 +3,15 @@ import { ShareRunCodeDialogComponent } from './share-run-code-dialog.component'; import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { MatSnackBarModule } from '@angular/material/snack-bar'; -import { NO_ERRORS_SCHEMA } from '@angular/core'; import { ConfigService } from '../../services/config.service'; import { TeacherService } from '../teacher.service'; import { UserService } from '../../services/user.service'; import { TeacherRun } from '../teacher-run'; import { Project } from '../../domain/project'; import { AccessLinkService } from '../../services/accessLinkService'; +import { MatIconModule } from '@angular/material/icon'; +import { ClipboardModule } from '@angular/cdk/clipboard'; +import { MockProvider } from 'ng-mocks'; const runObj = new TeacherRun(); runObj.id = 1; @@ -18,6 +20,7 @@ const project = new Project(); project.id = 1; project.name = 'Photosynthesis'; runObj.project = project; +runObj.isSurveyRun = () => false; export class MockConfigService { getWISEHostname(): string { @@ -41,23 +44,27 @@ export class MockUserService { } } +let component: ShareRunCodeDialogComponent; +let fixture: ComponentFixture; describe('ShareRunCodeDialogComponent', () => { - let component: ShareRunCodeDialogComponent; - let fixture: ComponentFixture; - beforeEach(waitForAsync(() => { TestBed.configureTestingModule({ declarations: [ShareRunCodeDialogComponent], - imports: [BrowserAnimationsModule, MatDialogModule, MatSnackBarModule], + imports: [ + BrowserAnimationsModule, + ClipboardModule, + MatDialogModule, + MatIconModule, + MatSnackBarModule + ], providers: [ - AccessLinkService, + MockProvider(AccessLinkService), { provide: ConfigService, useClass: MockConfigService }, { provide: TeacherService, useClass: MockTeacherService }, { provide: UserService, useClass: MockUserService }, { provide: MatDialogRef, useValue: {} }, { provide: MAT_DIALOG_DATA, useValue: runObj } - ], - schemas: [NO_ERRORS_SCHEMA] + ] }).compileComponents(); })); @@ -67,14 +74,44 @@ describe('ShareRunCodeDialogComponent', () => { fixture.detectChanges(); }); - it('should create', () => { - expect(component).toBeTruthy(); + it('should show run info', () => { + expect(fixture.debugElement.nativeElement.textContent).toContain('Photosynthesis (Run ID: 1)'); }); - it('should show run info and sharing url', () => { - const compiled = fixture.debugElement.nativeElement; - expect(compiled.textContent).toContain('Photosynthesis (Run ID: 1)'); - const url = `http://localhost:8080/login?accessCode=${component.run.runCode}`; - expect(compiled.textContent).toContain(url); - }); + defaultRun(); + surveyRun(); }); + +function defaultRun() { + describe('when run is a default run', () => { + beforeEach(() => { + component.run.isSurveyRun = () => false; + fixture.detectChanges(); + }); + it('should show access links to share with students', () => { + const textContent = fixture.debugElement.nativeElement.textContent; + expect(textContent).toContain('Copy this link to share with your students:'); + expect(textContent).toContain( + `http://localhost:8080/login?accessCode=${component.run.runCode}` + ); + }); + }); +} + +function surveyRun() { + describe('when run is a survey run', () => { + beforeEach(() => { + component.run.isSurveyRun = () => true; + spyOn(TestBed.inject(AccessLinkService), 'getAccessLinks').and.returnValue([ + 'http://localhost:8080/run-survey/Dog123-1' + ]); + component.ngOnInit(); + fixture.detectChanges(); + }); + it('should show access links to share with participants', () => { + const textContent = fixture.debugElement.nativeElement.textContent; + expect(textContent).toContain('Copy this link to share with participants:'); + expect(textContent).toContain(`http://localhost:8080/run-survey/Dog123-1`); + }); + }); +} diff --git a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts index 146c9f10497..775dbc21b3f 100644 --- a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts +++ b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.ts @@ -29,7 +29,7 @@ export class ShareRunCodeDialogComponent { private configService: ConfigService ) {} - ngOnInit() { + ngOnInit(): void { this.code = this.run.runCode; const host = this.configService.getWISEHostname() + this.configService.getContextPath(); this.link = `${host}/login?accessCode=${this.code}`; @@ -38,19 +38,19 @@ export class ShareRunCodeDialogComponent { } } - copyMsg() { + protected copyMsg(): void { this.snackBar.open($localize`Copied to clipboard.`); } - isGoogleUser() { + protected isGoogleUser(): boolean { return this.userService.isGoogleUser(); } - isGoogleClassroomEnabled() { + protected isGoogleClassroomEnabled(): boolean { return this.configService.isGoogleClassroomEnabled(); } - checkClassroomAuthorization() { + protected checkClassroomAuthorization(): void { this.teacherService .getClassroomAuthorizationUrl(this.userService.getUser().getValue().username) .subscribe(({ authorizationUrl }) => { @@ -68,7 +68,7 @@ export class ShareRunCodeDialogComponent { }); } - getClassroomCourses() { + private getClassroomCourses(): void { this.teacherService .getClassroomCourses(this.userService.getUser().getValue().username) .subscribe((courses) => { From 332c37e2e84a71e15469b54a88c9647bc7866010 Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Wed, 4 Jun 2025 14:45:37 -0700 Subject: [PATCH 30/33] Clean up test and make sure isSurveyRun is set before each test. This was causing threading issues. --- .../share-run-code-dialog.component.spec.ts | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts index 835db62da05..2b0a592941b 100644 --- a/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts +++ b/src/app/teacher/share-run-code-dialog/share-run-code-dialog.component.spec.ts @@ -5,22 +5,23 @@ import { MatDialogModule, MatDialogRef, MAT_DIALOG_DATA } from '@angular/materia import { MatSnackBarModule } from '@angular/material/snack-bar'; import { ConfigService } from '../../services/config.service'; import { TeacherService } from '../teacher.service'; -import { UserService } from '../../services/user.service'; import { TeacherRun } from '../teacher-run'; import { Project } from '../../domain/project'; import { AccessLinkService } from '../../services/accessLinkService'; import { MatIconModule } from '@angular/material/icon'; import { ClipboardModule } from '@angular/cdk/clipboard'; -import { MockProvider } from 'ng-mocks'; +import { MockProviders } from 'ng-mocks'; +import { CommonModule } from '@angular/common'; +import { UserService } from '../../services/user.service'; const runObj = new TeacherRun(); runObj.id = 1; runObj.runCode = 'Dog123'; +runObj.isSurveyRun = () => false; const project = new Project(); project.id = 1; project.name = 'Photosynthesis'; runObj.project = project; -runObj.isSurveyRun = () => false; export class MockConfigService { getWISEHostname(): string { @@ -36,14 +37,6 @@ export class MockConfigService { } } -export class MockTeacherService {} - -export class MockUserService { - isGoogleUser() { - return true; - } -} - let component: ShareRunCodeDialogComponent; let fixture: ComponentFixture; describe('ShareRunCodeDialogComponent', () => { @@ -52,17 +45,15 @@ describe('ShareRunCodeDialogComponent', () => { declarations: [ShareRunCodeDialogComponent], imports: [ BrowserAnimationsModule, + CommonModule, ClipboardModule, MatDialogModule, MatIconModule, MatSnackBarModule ], providers: [ - MockProvider(AccessLinkService), + MockProviders(AccessLinkService, MatDialogRef, TeacherService, UserService), { provide: ConfigService, useClass: MockConfigService }, - { provide: TeacherService, useClass: MockTeacherService }, - { provide: UserService, useClass: MockUserService }, - { provide: MatDialogRef, useValue: {} }, { provide: MAT_DIALOG_DATA, useValue: runObj } ] }).compileComponents(); @@ -71,6 +62,7 @@ describe('ShareRunCodeDialogComponent', () => { beforeEach(() => { fixture = TestBed.createComponent(ShareRunCodeDialogComponent); component = fixture.componentInstance; + component.run.isSurveyRun = () => false; fixture.detectChanges(); }); From 62fdf03a12f57d0ad9c8f55bdfa7ff48be9d621b Mon Sep 17 00:00:00 2001 From: Jonathan Lim-Breitbart Date: Wed, 4 Jun 2025 15:13:59 -0700 Subject: [PATCH 31/33] Update survey workgroup limit reaced and completed pages --- src/app/app.component.ts | 3 +- src/app/student/student.component.ts | 14 ++++++---- .../survey-completed.component.html | 28 +++++++++++++++---- .../workgroup-limit-reached.component.html | 21 +++++++++++--- .../workgroup-limit-reached.component.scss | 7 ----- .../workgroup-limit-reached.component.ts | 3 +- 6 files changed, 51 insertions(+), 25 deletions(-) delete mode 100644 src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.scss diff --git a/src/app/app.component.ts b/src/app/app.component.ts index 808b00ed04f..5f5c39966c0 100644 --- a/src/app/app.component.ts +++ b/src/app/app.component.ts @@ -190,7 +190,8 @@ export class AppComponent { !this.router.url.includes('/login') && !this.router.url.includes('/join') && !this.router.url.includes('/contact') && - !this.router.url.includes('/forgot') + !this.router.url.includes('/forgot') && + !this.router.url.includes('/survey') ); } diff --git a/src/app/student/student.component.ts b/src/app/student/student.component.ts index e80097f191b..06ea93d924f 100644 --- a/src/app/student/student.component.ts +++ b/src/app/student/student.component.ts @@ -2,10 +2,10 @@ import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; @Component({ - selector: 'app-student', - templateUrl: './student.component.html', - styleUrls: ['./student.component.scss'], - standalone: false + selector: 'app-student', + templateUrl: './student.component.html', + styleUrls: ['./student.component.scss'], + standalone: false }) export class StudentComponent implements OnInit { constructor(private router: Router) {} @@ -13,6 +13,10 @@ export class StudentComponent implements OnInit { ngOnInit() {} isShowingAngularJSApp() { - return this.router.url.includes('/student/unit') || this.router.url.includes('/preview/unit'); + return ( + this.router.url.includes('/student/unit') || + this.router.url.includes('/preview/unit') || + this.router.url.includes('/survey') + ); } } diff --git a/src/app/student/survey/survey-completed/survey-completed.component.html b/src/app/student/survey/survey-completed/survey-completed.component.html index 72d0a9abad5..e1ec8d73ac7 100644 --- a/src/app/student/survey/survey-completed/survey-completed.component.html +++ b/src/app/student/survey/survey-completed/survey-completed.component.html @@ -1,6 +1,22 @@ - - -

Your responses have been submitted.

-

Thank you!

-
-
+
+ + + +

Your responses have been submitted.

+

Thank you!

+
+
+
diff --git a/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html index e48924c85fa..43f66ae2fe8 100644 --- a/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html +++ b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html @@ -1,8 +1,21 @@ -
- +
+ + -

This run has reached the maximum number of workgroups allowed.

-

Please talk to your teacher.

+

Sorry, this unit has reached the maximum number of submissions.

diff --git a/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.scss b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.scss deleted file mode 100644 index 8cac33fb341..00000000000 --- a/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.scss +++ /dev/null @@ -1,7 +0,0 @@ -mat-card { - background-color: #a8a8a2; - width: 50%; - text-align: center; - margin-left: 25%; - margin-top: 5%; -} \ No newline at end of file diff --git a/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.ts b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.ts index 0483a343ad1..3ad55ad5d7d 100644 --- a/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.ts +++ b/src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.ts @@ -4,7 +4,6 @@ import { MatCardModule } from '@angular/material/card'; @Component({ selector: 'workgroup-limit-reached', imports: [MatCardModule], - templateUrl: './workgroup-limit-reached.component.html', - styleUrl: './workgroup-limit-reached.component.scss' + templateUrl: './workgroup-limit-reached.component.html' }) export class WorkgroupLimitReachedComponent {} From 2d209efb1782e3db5339cb29f8dfd87559b79054 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 4 Jun 2025 22:18:40 +0000 Subject: [PATCH 32/33] Updated messages --- src/messages.xlf | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/messages.xlf b/src/messages.xlf index f124be0d2f0..be7cf90724e 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -2248,6 +2248,10 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.src/app/contact/contact-form/contact-form.component.html 141,145 + + src/app/student/survey/survey-completed/survey-completed.component.html + 19,23 + Submit for Review @@ -5480,6 +5484,14 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.src/app/register/register.component.html 12,17 + + src/app/student/survey/survey-completed/survey-completed.component.html + 12,16 + + + src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html + 12,16 + src/assets/wise5/authoringTool/components/top-bar/top-bar.component.html 10,14 @@ -8291,6 +8303,20 @@ Click "Cancel" to keep the invalid JSON open so you can fix it.33,38 + + Your responses have been submitted. + + src/app/student/survey/survey-completed/survey-completed.component.html + 18,22 + + + + Sorry, this unit has reached the maximum number of submissions. + + src/app/student/survey/workgroup-limit-reached/workgroup-limit-reached.component.html + 18,22 + + Team Sign In From d2ab2568620d9d0e4859c92d63da31a26c46ffdd Mon Sep 17 00:00:00 2001 From: Hiroki Terashima Date: Wed, 4 Jun 2025 15:46:07 -0700 Subject: [PATCH 33/33] Hide browser alert when submitting survey --- src/app/services/logOutService.ts | 4 +--- src/assets/wise5/vle/vle.component.ts | 6 +++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/app/services/logOutService.ts b/src/app/services/logOutService.ts index c6c01b3f407..f16bf204766 100644 --- a/src/app/services/logOutService.ts +++ b/src/app/services/logOutService.ts @@ -15,9 +15,7 @@ export class LogOutService { if (!this.logOutUrl) { await this.retrieveLogOutUrl(); } - this.http.get(this.logOutUrl).subscribe(() => { - window.location.href = '/'; - }); + this.http.get(this.logOutUrl).subscribe(); } private async retrieveLogOutUrl(): Promise { diff --git a/src/assets/wise5/vle/vle.component.ts b/src/assets/wise5/vle/vle.component.ts index 8df9a7ce148..203f1ee4a6a 100644 --- a/src/assets/wise5/vle/vle.component.ts +++ b/src/assets/wise5/vle/vle.component.ts @@ -87,10 +87,10 @@ export class VLEComponent implements AfterViewInit { private wiseLinkService: WiseLinkService ) {} - @HostListener('window:beforeunload') - beforeUnload($event): void { + @HostListener('window:beforeunload', ['$event']) + beforeUnload($event: BeforeUnloadEvent): void { if (this.isSurvey) { - $event.preventDefault(); + $event.stopImmediatePropagation(); } if (this.sessionService.isSessionActive()) { this.saveNodeExitedEvent();