ilios/frontend

View on GitHub
packages/frontend/app/components/program-year/competencies.js

Summary

Maintainability
A
0 mins
Test Coverage
import Component from '@glimmer/component';
import { filter, map } from 'rsvp';
import { task, timeout } from 'ember-concurrency';
import { service } from '@ember/service';
import AsyncProcess from 'ilios-common/classes/async-process';
import { TrackedAsyncData } from 'ember-async-data';
import { cached } from '@glimmer/tracking';
import { uniqueValues } from 'ilios-common/utils/array-helpers';
import { use } from 'ember-could-get-used-to-this';
import { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

export default class ProgramYearCompetenciesComponent extends Component {
  @service flashMessages;
  @tracked competenciesToAdd = [];
  @tracked competenciesToRemove = [];

  @cached
  get upstreamRelationshipsData() {
    return new TrackedAsyncData(this.resolveUpstreamRelationships(this.args.programYear));
  }

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

  get program() {
    return this.upstreamRelationships?.program;
  }

  get school() {
    return this.upstreamRelationships?.school;
  }

  get competencies() {
    return this.upstreamRelationships?.competencies || [];
  }

  get programYearCompetencies() {
    return this.upstreamRelationships?.programYearCompetencies || [];
  }

  @use competenciesWithSelectedChildren = new AsyncProcess(() => [
    this.getCompetenciesWithSelectedChildren,
    this.selectedCompetencies,
    this.competencies,
  ]);

  get domains() {
    return uniqueValues(this.upstreamRelationships?.domains || []);
  }

  get selectedCompetencies() {
    const filteredCurrent = this.programYearCompetencies.filter((c) => {
      return !this.competenciesToRemove.includes(c);
    });
    return uniqueValues([...this.competenciesToAdd, ...filteredCurrent]);
  }

  async resolveUpstreamRelationships(programYear) {
    const program = await programYear.program;
    const school = await program.school;
    const competencies = await school.competencies;
    const domains = await map(competencies.slice(), async (c) => c.getDomain());
    const programYearCompetencies = await programYear.competencies;

    return { program, school, competencies, domains, programYearCompetencies };
  }

  async getCompetenciesWithSelectedChildren(selectedCompetencies, competencies) {
    return await filter(competencies.slice(), async (competency) => {
      const children = await competency.children;
      const selectedChildren = children.filter((c) => selectedCompetencies.includes(c));
      return selectedChildren.length > 0;
    });
  }

  @action
  cancel() {
    this.competenciesToAdd = [];
    this.competenciesToRemove = [];
    this.args.setIsManaging(false);
  }

  @action
  addCompetencyToBuffer(competency, children) {
    this.competenciesToAdd = [...this.competenciesToAdd, competency];
    this.competenciesToAdd = [...this.competenciesToAdd, ...children];
    this.competenciesToRemove = this.competenciesToRemove.filter((c) => {
      return c !== competency && !children.includes(c);
    });
  }

  @action
  removeCompetencyFromBuffer(competency, children) {
    this.competenciesToRemove = [...this.competenciesToRemove, competency];
    this.competenciesToRemove = [...this.competenciesToRemove, ...children];
    this.competenciesToAdd = this.competenciesToAdd.filter((c) => {
      return c !== competency && !children.includes(c);
    });
  }

  @action
  collapse() {
    if (this.selectedCompetencies.length) {
      this.args.collapse();
    }
  }

  save = task(async () => {
    await timeout(10);
    this.args.programYear.set('competencies', this.selectedCompetencies);
    try {
      await this.args.programYear.save();
    } finally {
      this.flashMessages.success('general.savedSuccessfully');
      this.args.setIsManaging(false);
      this.args.expand();
    }
  });
}