ilios/frontend

View on GitHub
packages/frontend/app/components/user-profile-permissions.js

Summary

Maintainability
A
3 hrs
Test Coverage
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { DateTime } from 'luxon';
import { filter } from 'rsvp';
import { use } from 'ember-could-get-used-to-this';
import AsyncProcess from 'ilios-common/classes/async-process';
import { TrackedAsyncData } from 'ember-async-data';
import { cached } from '@glimmer/tracking';
import { findById, sortBy, uniqueValues } from 'ilios-common/utils/array-helpers';

export default class UserProfilePermissionsComponent extends Component {
  @service store;
  @service iliosConfig;

  @tracked programCollapsed = false;
  @tracked programYearCollapsed = false;
  @tracked courseCollapsed = false;
  @tracked sessionCollapsed = false;

  get programExpanded() {
    return !this.programCollapsed;
  }

  get programYearExpanded() {
    return !this.programYearCollapsed;
  }

  get courseExpanded() {
    return !this.courseCollapsed;
  }

  get sessionExpanded() {
    return !this.sessionCollapsed;
  }

  @cached
  get schoolData() {
    return new TrackedAsyncData(this.store.findAll('school'));
  }

  @cached
  get academicYearData() {
    return new TrackedAsyncData(this.store.findAll('academic-year'));
  }

  @cached
  get defaultSchoolData() {
    return new TrackedAsyncData(this.args.user.school);
  }

  @cached
  get directedSchoolsData() {
    return new TrackedAsyncData(this.args.user.directedSchools);
  }

  @cached
  get administeredSchoolsData() {
    return new TrackedAsyncData(this.args.user.administeredSchools);
  }

  crossesBoundaryConfig = new TrackedAsyncData(
    this.iliosConfig.itemFromConfig('academicYearCrossesCalendarYearBoundaries'),
  );

  @cached
  get academicYearCrossesCalendarYearBoundaries() {
    return this.crossesBoundaryConfig.isResolved ? this.crossesBoundaryConfig.value : false;
  }

  get schools() {
    return this.schoolData.isResolved ? this.schoolData.value.slice() : [];
  }

  get academicYears() {
    return this.academicYearData.isResolved ? this.academicYearData.value.slice() : [];
  }

  @cached
  get defaultSchool() {
    return this.defaultSchoolData.isResolved ? this.defaultSchoolData.value : null;
  }

  @cached
  get isDirectingSchool() {
    if (!this.directedSchoolsData.isResolved) {
      return false;
    }
    return this.directedSchoolsData.value.includes(this.bestSelectedSchool);
  }

  @cached
  get isAdministeringSchool() {
    if (!this.administeredSchoolsData.isResolved) {
      return false;
    }
    return this.administeredSchoolsData.value.includes(this.bestSelectedSchool);
  }

  get isLoaded() {
    return !!this.bestSelectedSchool && !!this.selectedYearId;
  }

  get courseCount() {
    if (
      !this.directedCourses ||
      !this.administeredCourses ||
      !this.instructedCourses ||
      !this.studentAdvisedCourses
    ) {
      return 0;
    }
    return (
      this.directedCourses.length +
      this.administeredCourses.length +
      this.instructedCourses.length +
      this.studentAdvisedCourses.length
    );
  }

  get sessionCount() {
    if (!this.administeredSessions || !this.instructedSessions || !this.studentAdvisedSessions) {
      return 0;
    }
    return (
      this.administeredSessions.length +
      this.instructedSessions.length +
      this.studentAdvisedSessions.length
    );
  }

  get selectedYearId() {
    return this.bestSelectedYear?.id;
  }

  @cached
  get _userDirectedProgramsData() {
    return new TrackedAsyncData(this.args.user.directedPrograms);
  }

  get _userDirectedPrograms() {
    return this._userDirectedProgramsData.isResolved ? this._userDirectedProgramsData.value : null;
  }

  @use directedPrograms = new AsyncProcess(() => [
    this.getDirectedPrograms.bind(this),
    this.bestSelectedSchool,
    this._userDirectedPrograms?.slice() ?? [],
  ]);

  @cached
  get _userProgramYearsData() {
    return new TrackedAsyncData(this.args.user.programYears);
  }

  get _userProgramYears() {
    return this._userProgramYearsData.isResolved ? this._userProgramYearsData.value : null;
  }

  @use directedProgramYears = new AsyncProcess(() => [
    this.getDirectedProgramYears.bind(this),
    this.bestSelectedSchool,
    this._userProgramYears?.slice() ?? [],
  ]);

  @cached
  get _userDirectedCoursesData() {
    return new TrackedAsyncData(this.args.user.directedCourses);
  }

  get _userDirectedCourses() {
    return this._userDirectedCoursesData.isResolved ? this._userDirectedCoursesData.value : null;
  }

  @use directedCourses = new AsyncProcess(() => [
    this.getDirectedCourses.bind(this),
    this.bestSelectedSchool,
    this.selectedYearId,
    this._userDirectedCourses?.slice() ?? [],
  ]);

  @cached
  get _userAdministeredCoursesData() {
    return new TrackedAsyncData(this.args.user.administeredCourses);
  }

  get _userAdministeredCourses() {
    return this._userAdministeredCoursesData.isResolved
      ? this._userAdministeredCoursesData.value
      : null;
  }

  @use administeredCourses = new AsyncProcess(() => [
    this.getAdministeredCourses.bind(this),
    this.bestSelectedSchool,
    this.selectedYearId,
    this._userAdministeredCourses?.slice() ?? [],
  ]);
  @use instructedCourses = new AsyncProcess(() => [
    this.getInstructedCourses.bind(this),
    this.bestSelectedSchool,
    this.selectedYearId,
    this.args.user.allInstructedCourses,
  ]);

  @cached
  get _userStudentAdvisedCoursesData() {
    return new TrackedAsyncData(this.args.user.studentAdvisedCourses);
  }

  get _userStudentAdvisedCourses() {
    return this._userStudentAdvisedCoursesData.isResolved
      ? this._userStudentAdvisedCoursesData.value
      : null;
  }

  @use studentAdvisedCourses = new AsyncProcess(() => [
    this.getStudentAdvisedCourses.bind(this),
    this.bestSelectedSchool,
    this.selectedYearId,
    this._userStudentAdvisedCourses?.slice() ?? [],
  ]);

  @cached
  get _userAdministeredSessionsData() {
    return new TrackedAsyncData(this.args.user.administeredSessions);
  }

  get _userAdministeredSessions() {
    return this._userAdministeredSessionsData.isResolved
      ? this._userAdministeredSessionsData.value
      : null;
  }

  @use administeredSessions = new AsyncProcess(() => [
    this.getAdministeredSessions.bind(this),
    this.bestSelectedSchool,
    this.selectedYearId,
    this._userAdministeredSessions?.slice() ?? [],
  ]);
  @use instructedSessions = new AsyncProcess(() => [
    this.getInstructedSessions.bind(this),
    this.bestSelectedSchool,
    this.selectedYearId,
    this.args.user.allInstructedSessions,
  ]);

  @cached
  get _userStudentAdvisedSessionsData() {
    return new TrackedAsyncData(this.args.user.studentAdvisedSessions);
  }

  get _userStudentAdvisedSessions() {
    return this._userStudentAdvisedSessionsData.isResolved
      ? this._userStudentAdvisedSessionsData.value
      : null;
  }

  @use studentAdvisedSessions = new AsyncProcess(() => [
    this.getStudentAdvisedSessions.bind(this),
    this.bestSelectedSchool,
    this.selectedYearId,
    this._userStudentAdvisedSessions?.slice() ?? [],
  ]);

  async getDirectedPrograms(selectedSchool, directedPrograms) {
    return filter(directedPrograms, async (program) => {
      const school = await program.school;
      return school === selectedSchool;
    });
  }

  async getDirectedProgramYears(selectedSchool, directedProgramYears) {
    return filter(directedProgramYears, async (programYear) => {
      const program = await programYear.program;
      const school = await program.school;
      return school === selectedSchool;
    });
  }

  async getDirectedCourses(selectedSchool, selectedYearId, directedCourses) {
    return filter(directedCourses, async (course) => {
      const school = await course.school;
      return school === selectedSchool && selectedYearId === course.year.toString();
    });
  }

  async getAdministeredCourses(selectedSchool, selectedYearId, administeredCourses) {
    return filter(administeredCourses, async (course) => {
      const school = await course.school;
      return school === selectedSchool && selectedYearId === course.year.toString();
    });
  }

  async getInstructedCourses(selectedSchool, selectedYearId, allInstructedCourses) {
    const rhett = await filter(allInstructedCourses ?? [], async (course) => {
      const school = await course.school;
      return school === selectedSchool && selectedYearId === course.year.toString();
    });
    return uniqueValues(rhett);
  }

  async getStudentAdvisedCourses(selectedSchool, selectedYearId, studentAdvisedCourses) {
    return filter(studentAdvisedCourses, async (course) => {
      const school = await course.school;
      return school === selectedSchool && selectedYearId === course.year.toString();
    });
  }

  async getAdministeredSessions(selectedSchool, selectedYearId, administeredSessions) {
    return filter(administeredSessions, async (session) => {
      const course = await session.course;
      const school = await course.school;
      return school === selectedSchool && selectedYearId === course.year.toString();
    });
  }

  async getInstructedSessions(selectedSchool, selectedYearId, allInstructedSessions) {
    return filter(allInstructedSessions, async (session) => {
      const course = await session.course;
      const school = await course.school;
      return school === selectedSchool && selectedYearId === course.year.toString();
    });
  }

  async getStudentAdvisedSessions(selectedSchool, selectedYearId, studentAdvisedSessions) {
    return filter(studentAdvisedSessions, async (session) => {
      const course = await session.course;
      const school = await course.school;
      return school === selectedSchool && selectedYearId === course.year.toString();
    });
  }

  get bestSelectedYear() {
    let currentYear;
    if (this.args.selectedYearId) {
      currentYear = Number(this.args.selectedYearId);
    } else {
      currentYear = DateTime.now().year;
    }
    const currentMonth = DateTime.now().month;
    if (this.academicYearCrossesCalendarYearBoundaries && currentMonth < 6) {
      currentYear--;
    }
    let selectedYear = this.academicYears.find((year) => Number(year.id) === currentYear);
    if (!selectedYear) {
      selectedYear = this.academicYears.reverse()[0];
    }
    return selectedYear;
  }

  get bestSelectedSchool() {
    if (this.args.selectedSchoolId) {
      return findById(this.schools, this.args.selectedSchoolId);
    } else if (this.defaultSchool) {
      return this.defaultSchool;
    } else if (this.schools.length) {
      return sortBy(this.schools, 'title')[0];
    }
    return null;
  }

  @action
  changeSchool(schoolId) {
    this.resetCollapsers();
    this.args.setSchool(schoolId);
  }

  @action
  changeYear(year) {
    this.resetCollapsers();
    this.args.setYear(year);
  }

  resetCollapsers() {
    this.programCollapsed = false;
    this.programYearCollapsed = false;
    this.courseCollapsed = false;
    this.sessionCollapsed = false;
  }
}