import { Component, OnInit } from '@angular/core'; import { DarsApiService } from '../services/api.service'; import { DegreePrograms, DegreeProgram } from '../models/degree-program'; import { Observable, combineLatest } from 'rxjs'; import { FormBuilder, FormGroup, Validators, FormArray, FormControl, } from '@angular/forms'; import { HonorsOption } from '../models/honors-option'; import { map, share, flatMap, shareReplay } from 'rxjs/operators'; import { CourseBase } from '@app/core/models/course'; import { Store } from '@ngrx/store'; import { GlobalState } from '@app/core/state'; import { degreePlans } from '../store/selectors'; import { DegreePlan } from '@app/core/models/degree-plan'; import { MatDialogRef } from '@angular/material'; const inclusiveRange = (from: number, to: number) => { const range: number[] = []; for (let i = from; i <= to; i++) { range.push(i); } return range; }; export interface NewWhatIfAuditFields { darsInstitutionCode: string; darsDegreeProgramCode: string; roadmapId: number; darsHonorsOptionCode: string; includeCoursesFrom: string; fixedCredits: { termCode: string; subjectCode: string; courseId: string; credits: number; }[]; } @Component({ selector: 'cse-new-what-if-audit-dialog', templateUrl: './new-what-if-audit-dialog.component.html', styleUrls: ['./new-what-if-audit-dialog.component.scss'], }) export class NewWhatIfAuditDialogComponent implements OnInit { // Form-builder objects public chosenProgram: FormGroup; public chosenAuditSettings: FormGroup; public chosenCreditSettings: FormArray; // API observables public institutions$: Observable<DegreePrograms>; public programOrPlanOptions$: Observable<DegreeProgram[]>; public degreePlans$: Observable<DegreePlan[]>; public honorsOptions$: Observable<HonorsOption[]>; public variableCreditCourses$: Observable< (CourseBase & { range: number[] })[] >; constructor( private fb: FormBuilder, private api: DarsApiService, private store: Store<GlobalState>, private dialogRef: MatDialogRef< NewWhatIfAuditDialogComponent, NewWhatIfAuditFields >, ) { this.chosenProgram = fb.group({ institution: fb.control('', Validators.required), planOrProgram: fb.control( { value: '', disabled: true }, Validators.required, ), }); this.chosenAuditSettings = fb.group({ honorsOptions: fb.control('', Validators.required), includeCoursesFrom: fb.control('future', Validators.required), degreePlan: fb.control('', Validators.required), }); this.chosenCreditSettings = fb.array([]); } public ngOnInit() { this.institutions$ = this.api.getStaticData().pipe(share()); this.programOrPlanOptions$ = combineLatest([ this.institutions$, (this.chosenProgram.get('institution') as FormControl).valueChanges, ]).pipe( map(([institutions, darsInstitutionCode]) => { this.chosenProgram.controls.planOrProgram.enable(); if (institutions.hasOwnProperty(darsInstitutionCode)) { return institutions[darsInstitutionCode].programs; } else { return []; } }), ); this.degreePlans$ = this.store.select(degreePlans).pipe(shareReplay()); this.degreePlans$.subscribe(plans => { const primaryPlan = plans.find(p => p.primary); this.chosenAuditSettings.controls['degreePlan'].setValue(primaryPlan); }); this.honorsOptions$ = combineLatest([ this.institutions$, (this.chosenProgram.get('institution') as FormControl).valueChanges, ]).pipe( map(([institutions, darsInstitutionCode]) => { return institutions.hasOwnProperty(darsInstitutionCode) ? institutions[darsInstitutionCode].honorsOptions : []; }), ); // prettier-ignore this.variableCreditCourses$ = (this.chosenAuditSettings.get('degreePlan') as FormControl).valueChanges.pipe( flatMap(plan => this.api.getAllCourses(plan.roadmapId)), map(courses => { return courses.filter(course => { return ( !!course.creditMin && !!course.creditMax && course.creditMax > course.creditMin ); }); }), map(courses => courses.map(course => ({ ...course, range: inclusiveRange( course.creditMin as number, course.creditMax as number, ), })), ), share(), ); this.variableCreditCourses$.subscribe(courses => { while (this.chosenCreditSettings.length !== 0) { this.chosenCreditSettings.removeAt(0); } courses.forEach(course => { this.chosenCreditSettings.push( this.fb.group({ course, credits: this.fb.control(course.credits || '', Validators.required), }), ); }); }); } public comparePlans(a: DegreePlan, b: DegreePlan): boolean { return a.roadmapId === b.roadmapId; } public darsInstitutionCode<T>(fallback: T): string | T { const control = this.chosenProgram.get('institution'); if (control !== null) { return control.value.toString(); } else { return fallback; } } public darsDegreeProgramCode<T>(fallback: T): string | T { const control = this.chosenProgram.get('planOrProgram'); if (control !== null) { return control.value.toString(); } else { return fallback; } } public roadmapId<T>(fallback: T): number | T { const control = this.chosenAuditSettings.get('degreePlan'); if (control !== null) { return control.value.roadmapId; } else { return fallback; } } public darsHonorsOptionCode<T>(fallback: T): string | T { const control = this.chosenAuditSettings.get('honorsOptions'); if (control !== null) { return control.value.toString(); } else { return fallback; } } public includeCoursesFrom<T>(fallback: T): string | T { const control = this.chosenAuditSettings.get('includeCoursesFrom'); if (control !== null) { return control.value.toString(); } else { return fallback; } } public fixedCredits(): NewWhatIfAuditFields['fixedCredits'] { return (this.chosenCreditSettings.value as any[]).map( (group: { course: CourseBase; credits: number }) => { return { termCode: group.course.termCode || '', subjectCode: group.course.subjectCode || '', courseId: group.course.courseId || '', credits: group.credits, }; }, ); } public submitAudit() { this.dialogRef.close({ darsInstitutionCode: this.darsInstitutionCode(''), darsDegreeProgramCode: this.darsDegreeProgramCode(''), roadmapId: this.roadmapId(-1), darsHonorsOptionCode: this.darsHonorsOptionCode(''), includeCoursesFrom: this.includeCoursesFrom(''), fixedCredits: this.fixedCredits(), }); } }