packages/ilios-common/addon/components/course/visualize-term-graph.js
import Component from '@glimmer/component';
import { map } from 'rsvp';
import { htmlSafe } from '@ember/template';
import { restartableTask, timeout } from 'ember-concurrency';
import { service } from '@ember/service';
import { cached, tracked } from '@glimmer/tracking';
import { TrackedAsyncData } from 'ember-async-data';
import { findById, mapBy } from 'ilios-common/utils/array-helpers';
import { action } from '@ember/object';
export default class CourseVisualizeTermGraph extends Component {
@service router;
@service intl;
@tracked tooltipContent = null;
@tracked tooltipTitle = null;
@tracked sortBy = 'minutes';
@cached
get outputData() {
return new TrackedAsyncData(this.getDataObjects(this.args.course, this.args.term));
}
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 hasChartData() {
return this.chartData.length;
}
get isLoaded() {
return this.outputData.isResolved;
}
get tableData() {
return this.data.map((obj) => {
const rhett = {};
rhett.minutes = obj.data;
rhett.sessions = obj.meta.sessions;
rhett.sessionType = obj.meta.sessionType.title;
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;
}
async getDataObjects(course, term) {
const sessions = await course.sessions;
const sessionIds = term.hasMany('sessions').ids();
const filteredSessions = sessions.filter((session) => sessionIds.includes(session.id));
const sessionTypes = await Promise.all(filteredSessions.map((s) => s.sessionType));
const sessionTypeData = await map(filteredSessions, async (session) => {
const hours = await session.getTotalSumDuration();
const sessionType = findById(sessionTypes, session.belongsTo('sessionType').id());
return {
session,
sessionType,
minutes: Math.round(hours * 60),
};
});
return sessionTypeData
.reduce((set, { sessionType, session, minutes }) => {
const id = sessionType.id;
let existing = findById(set, id);
if (!existing) {
existing = {
id,
data: 0,
label: sessionType.title,
meta: {
sessionType: sessionType,
sessions: [],
},
};
set.push(existing);
}
existing.data += minutes;
existing.meta.sessions.push(session);
return set;
}, [])
.map((obj) => {
obj.description = `${obj.meta.sessionType.title} - ${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 || !obj || obj.empty) {
this.tooltipTitle = null;
this.tooltipContent = null;
return;
}
const { data, meta } = obj;
this.tooltipTitle = htmlSafe(
`${meta.sessionType.title} • ${data} ${this.intl.t('general.minutes')}`,
);
this.tooltipContent = htmlSafe(mapBy(meta.sessions, 'title').sort().join(', '));
});
}