ilios/frontend

View on GitHub
packages/ilios-common/addon/components/course/visualize-instructors-graph.js

Summary

Maintainability
A
0 mins
Test Coverage
import Component from '@glimmer/component';
import { isEmpty } from '@ember/utils';
import { htmlSafe } from '@ember/template';
import { restartableTask, timeout } from 'ember-concurrency';
import { map } from 'rsvp';
import { service } from '@ember/service';
import { cached, tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { cleanQuery } from 'ilios-common/utils/query-utils';
import { TrackedAsyncData } from 'ember-async-data';
import { findById, mapBy, uniqueValues } from 'ilios-common/utils/array-helpers';

export default class CourseVisualizeInstructorsGraph extends Component {
  @service router;
  @service intl;
  @tracked tooltipContent = null;
  @tracked tooltipTitle = null;
  @tracked sortBy = 'minutes';

  @cached
  get outputData() {
    return new TrackedAsyncData(this.getData(this.args.course));
  }

  get isLoaded() {
    return this.outputData.isResolved;
  }

  get data() {
    return this.outputData.isResolved ? this.outputData.value : [];
  }

  get hasData() {
    return this.data.length;
  }

  get chartData() {
    return this.data.filter((obj) => obj.data);
  }

  get filteredChartData() {
    return this.filterData(this.chartData);
  }

  get hasChartData() {
    return this.filteredChartData.length;
  }

  get filteredData() {
    return this.filterData(this.data);
  }

  get tableData() {
    return this.filteredData.map((obj) => {
      const rhett = {};
      rhett.minutes = obj.data;
      rhett.sessions = obj.meta.sessions;
      rhett.instructor = obj.meta.user;
      rhett.instructorName = obj.meta.user.fullName;
      rhett.sessionTitles = mapBy(rhett.sessions, 'title').join(', ');
      return rhett;
    });
  }

  get sortedAscending() {
    return this.sortBy.search(/desc/) === -1;
  }

  @action
  setSortBy(prop) {
    if (this.sortBy === prop) {
      prop += ':desc';
    }
    this.sortBy = prop;
  }

  filterData(data) {
    const q = cleanQuery(this.args.filter);
    if (q) {
      const exp = new RegExp(q, 'gi');
      return data.filter(({ label }) => label.match(exp));
    }
    return data;
  }

  async getData(course) {
    const sessions = await course.sessions;
    const sessionsWithInstructors = await map(sessions, async (session) => {
      const instructors = await session.getAllInstructors();
      const instructorsWithInstructionalTime = await map(instructors, async (instructor) => {
        const minutes = await session.getTotalSumOfferingsDurationByInstructor(instructor);
        return {
          instructor,
          minutes,
        };
      });
      return {
        session,
        instructorsWithInstructionalTime,
      };
    });

    return sessionsWithInstructors
      .reduce((set, { session, instructorsWithInstructionalTime }) => {
        instructorsWithInstructionalTime.forEach(({ instructor, minutes }) => {
          const id = instructor.id;
          let existing = findById(set, id);
          if (!existing) {
            existing = {
              id,
              data: 0,
              label: instructor.fullName,
              meta: {
                user: instructor,
                sessions: [],
              },
            };
            set.push(existing);
          }
          existing.data += minutes;
          existing.meta.sessions.push(session);
        });
        return set;
      }, [])
      .map((obj) => {
        obj.description = `${obj.meta.user.fullName} - ${obj.data} ${this.intl.t('general.minutes')}`;
        delete obj.id;
        return obj;
      })
      .sort((first, second) => {
        return first.data - second.data;
      });
  }

  barHover = restartableTask(async (obj) => {
    await timeout(100);
    if (this.args.isIcon || isEmpty(obj) || obj.empty) {
      this.tooltipTitle = null;
      this.tooltipContent = null;
      return;
    }
    this.tooltipTitle = htmlSafe(
      `${obj.meta.user.fullName} • ${obj.data} ${this.intl.t('general.minutes')}`,
    );
    this.tooltipContent = htmlSafe(
      uniqueValues(mapBy(obj.meta.sessions, 'title')).sort().join(', '),
    );
  });

  @action
  barClick(obj) {
    if (this.args.isIcon || isEmpty(obj) || obj.empty || isEmpty(obj.meta)) {
      return;
    }

    this.router.transitionTo('course-visualize-instructor', this.args.course.id, obj.meta.user.id);
  }
}