Skip to content
Snippets Groups Projects
new-what-if-audit-dialog.component.ts 8.07 KiB
Newer Older
import { Component, OnInit } from '@angular/core';
import { DarsApiService } from '../services/api.service';
import { DegreePrograms, DegreeProgram } from '../models/degree-program';
import { Observable, combineLatest, Subject } from 'rxjs';
import {
  FormBuilder,
  FormGroup,
  Validators,
  FormArray,
  FormControl,
} from '@angular/forms';
import { HonorsOption } from '../models/honors-option';
import {
  map,
  share,
  flatMap,
  shareReplay,
  distinctUntilChanged,
} 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;
  degreePlannerPlanName: string;
  whichEnrolledCoursesIncluded: 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 chosenRoadmapId$ = new Subject<number>();
  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('planned', 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.honorsOptions$ = combineLatest([
      this.institutions$,
      (this.chosenProgram.get('institution') as FormControl).valueChanges,
    ]).pipe(
      map(([institutions, darsInstitutionCode]) => {
        return institutions.hasOwnProperty(darsInstitutionCode)
          ? institutions[darsInstitutionCode].honorsOptions
          : [];
      }),
    );

    const degreePlanFormControl = this.chosenAuditSettings.get('degreePlan');

    const includeCoursesFrom = this.chosenAuditSettings.get(
      'includeCoursesFrom',
    );
    if (includeCoursesFrom && degreePlanFormControl) {
      includeCoursesFrom.valueChanges.subscribe(selected => {
        if (selected !== 'planned') {
          degreePlanFormControl.reset();
          degreePlanFormControl.disable();
        } else {
          degreePlanFormControl.enable();
          degreePlanFormControl.valueChanges.subscribe(plan => {
            this.chosenRoadmapId$.next(plan.roadmapId);
          });
        }
    if (degreePlanFormControl) {
      this.variableCreditCourses$ = this.chosenRoadmapId$.pipe(
        distinctUntilChanged(),
        flatMap(roadmapId => this.api.getAllCourses(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,
            ),
          })),
        ),
        shareReplay(),
      );
    }

    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),

    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);

      if (primaryPlan) {
        this.chosenRoadmapId$.next(primaryPlan.roadmapId);
      }
    });
  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 degreePlannerPlanName<T>(fallback: T): string | T {
    const control = this.chosenAuditSettings.get('degreePlan');
    if (control && control.value !== null) {
      return control.value.name;
    } 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;
    }
  }

  // If a degree plan name is included in the request we do not need this property
  public includeCoursesFrom<T>(fallback: T): string | T {
    const controlDegreePlan = this.chosenAuditSettings.get('degreePlan');
    if (controlDegreePlan && controlDegreePlan.status === 'DISABLED') {
      const control = this.chosenAuditSettings.get('includeCoursesFrom');
      if (control !== null) {
        return control.value.toString();
      } else {
        return fallback;
      }
    } 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(''),
      degreePlannerPlanName: this.degreePlannerPlanName(''),
      darsHonorsOptionCode: this.darsHonorsOptionCode(''),
      whichEnrolledCoursesIncluded: this.includeCoursesFrom(''),