Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Curriculum Report #8268

Draft
wants to merge 20 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions packages/frontend/app/components/reports/choose-course.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<div class="reports-choose-course">
<ul class="schools">
{{#each this.sortedSchools as |s|}}
<li class="school">
<button
type="button"
aria-expanded={{if (eq s.id this.expandedSchool.id) "true" "false"}}
{{on "click" (fn this.toggleSchool s.id)}}
>
{{s.title}}
<FaIcon @icon={{if (eq s.id this.expandedSchool.id) "caret-down" "caret-right"}} />
</button>
{{#if (eq s.id this.expandedSchool.id)}}
{{#each this.visibleExapndedSchoolYears as |y|}}
<ul class="year {{if (eq y.year this.expandedYear) 'expanded' 'collapsed'}}">
<li>
<button
type="button"
aria-expanded={{if (eq y.year this.expandedYear) "true" "false"}}
{{on "click" (fn this.toggleYear y.year)}}
>
{{y.year}}
<FaIcon @icon={{if (eq y.year this.expandedYear) "caret-down" "caret-right"}} />
</button>
</li>
{{#if (eq y.year this.expandedYear)}}
<ul class="courses">
{{#each (sort-by "title" y.courses) as |c|}}
<li>
<label>
<input
type="checkbox"
checked={{includes c.id @selectedCourseIds}}
{{on
"click"
(fn (if (includes c.id @selectedCourseIds) @remove @add) c.id)
}}
/>
{{c.title}}
{{#if c.externalId}}
({{c.externalId}})
{{/if}}
</label>
</li>
{{/each}}
</ul>
{{/if}}
</ul>
{{/each}}
{{/if}}
</li>
{{/each}}
</ul>
</div>
114 changes: 114 additions & 0 deletions packages/frontend/app/components/reports/choose-course.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import Component from '@glimmer/component';
import { service } from '@ember/service';
import { cached, tracked } from '@glimmer/tracking';
import { TrackedAsyncData } from 'ember-async-data';
import { DateTime } from 'luxon';

export default class ReportsChooseCourse extends Component {
@service iliosConfig;
@service currentUser;

//tri state, null expands primary school, false collapses all, an id expands a specific school
@tracked userExpandedSchoolId = null;
@tracked expandedYear;
@tracked showAllYears = false;

constructor() {
super(...arguments);
let { month, year } = DateTime.now();
if (month < 7) {
// before July 1st (start of a new academic year) show the year before
year--;
}
this.expandedYear = year;
}

userModel = new TrackedAsyncData(this.currentUser.getModel());

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

get primarySchool() {
return this.args.schools.find(({ id }) => id === this.user?.belongsTo('school').id());
}

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

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

get bestExpandedSchoolId() {
if (this.userExpandedSchoolId) {
return this.userExpandedSchoolId;
}
if (this.userExpandedSchoolId === null) {
return this.primarySchool?.id;
}

return null;
}

get expandedSchool() {
return this.args.schools.find(({ id }) => id === this.bestExpandedSchoolId);
}

get visibleYears() {
let lastTenAndNextTenYears = [];
for (let i = DateTime.now().year - 10; i <= DateTime.now().year + 10; i++) {
lastTenAndNextTenYears.push(i);
}
if (this.showAllYears) {
return lastTenAndNextTenYears;
}

return lastTenAndNextTenYears.slice(8, 11);
}

get visibleExapndedSchoolYears() {
return this.expandedSchool?.years.filter(({ year }) => this.visibleYears.includes(year));
}

get filteredSchools() {
return this.args.schools.filter(({ years }) => {
const schoolYears = years.map(({ year }) => year);
return schoolYears.some((year) => this.visibleYears.includes(year));
});
}

get sortedSchools() {
return this.filteredSchools.sort((a, b) => {
if (a.id === this.primarySchool?.id) {
return -1;
}
if (b.id === this.primarySchool?.id) {
return 1;
}
return a.title.localeCompare(b.name);
});
}

toggleSchool = (schoolId) => {
if (
(this.userExpandedSchoolId === null && this.primarySchool?.id === schoolId) ||
this.userExpandedSchoolId === schoolId
) {
this.userExpandedSchoolId = false;
} else {
this.userExpandedSchoolId = schoolId;
}
};

toggleYear = (year) => {
if (this.expandedYear === year) {
this.expandedYear = null;
} else {
this.expandedYear = year;
}
};
}
60 changes: 60 additions & 0 deletions packages/frontend/app/components/reports/curriculum.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<div class="reports-curriculum" data-test-reports-curriculum ...attributes>
<div class="courses" data-test-courses>
{{#if @selectedCourseIds}}
<label>
{{t "general.runReport"}}:
<select {{on "change" this.changeSelectedReport}}>
{{#each this.reportList as |report|}}
<option value={{report.value}} selected={{eq report.value this.selectedReport}}>
{{report.label}}
</option>
{{/each}}
</select>
</label>
<ul class="selected-courses">
{{#each (sort-by "title" this.selectedCourses) as |course|}}
<li>
<button
type="button"
{{on "click" (fn this.removeCourse course.id)}}
data-test-selected-course
>
{{#if this.showCourseYears}}
{{course.year}}&nbsp;
{{/if}}
{{#if course.externalId}}
[{{course.externalId}}]&nbsp;
{{/if}}
{{course.title}}
<FaIcon @icon="xmark" class="remove" />
</button>
</li>
{{/each}}
</ul>
{{/if}}
</div>
{{#if this.reportIsRunning}}
<this.reportResultsComponent
@courses={{this.selectedCourses}}
@close={{set this "reportIsRunning" false}}
/>
{{else}}
<div class="input-buttons">
<button
type="button"
class="done text"
{{on "click" (set this "reportIsRunning" true)}}
disabled={{this.reportIsRunning}}
data-test-run
>
<FaIcon @icon="play" />
</button>
</div>
<Reports::ChooseCourse
@selectedCourseIds={{@selectedCourseIds}}
@schools={{@schools}}
@add={{this.pickCourse}}
@remove={{this.removeCourse}}
/>
{{/if}}
</div>
74 changes: 74 additions & 0 deletions packages/frontend/app/components/reports/curriculum.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import Component from '@glimmer/component';
import { service } from '@ember/service';
import { cached, tracked } from '@glimmer/tracking';
import { ensureSafeComponent } from '@embroider/util';
import SessionObjectives from './curriculum/session-objectives';
import LearnerGroups from './curriculum/learner-groups';

export default class ReportsCurriculumComponent extends Component {
@service store;
@service graphql;
@service router;
@service intl;

@tracked searchResults = null;
@tracked reportResults = null;
@tracked reportIsRunning = false;

reportList = [
{ value: 'sessionObjectives', label: this.intl.t('general.sessionObjectives') },
{ value: 'learnerGroups', label: this.intl.t('general.learnerGroups') },
];

get passedCourseIds() {
return this.args.selectedCourseIds?.map(Number) ?? [];
}

get selectedReport() {
return this.args.report ?? this.reportList[0].value;
}

@cached
get allCourses() {
return this.args.schools.reduce((all, school) => {
const courses = school.years.reduce((arr, year) => {
return [...arr, ...year.courses];
}, []);
return [...all, ...courses];
}, []);
}

get selectedCourses() {
return this.allCourses.filter((course) => this.passedCourseIds.includes(Number(course.id)));
}

get showCourseYears() {
const years = this.selectedCourses.map(({ year }) => year);
return years.some((year) => year !== years[0]);
}

get reportResultsComponent() {
switch (this.selectedReport) {
case 'sessionObjectives':
return ensureSafeComponent(SessionObjectives, this);
case 'learnerGroups':
return ensureSafeComponent(LearnerGroups, this);
}

return false;
}

pickCourse = (id) => {
this.args.setSelectedCourseIds([...this.passedCourseIds, id].sort());
};

removeCourse = (id) => {
this.reportIsRunning = false;
this.args.setSelectedCourseIds(this.passedCourseIds.filter((i) => i !== Number(id)).sort());
};

changeSelectedReport = ({ target }) => {
this.reportIsRunning = false;
this.args.setReport(target.value);
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Reports::Curriculum::ResultButtons
@running={{this.reportRunning}}
@finished={{this.finishedBuildingReport}}
@downloadReport={{perform this.downloadReport}}
@close={{@close}}
/>
{{#unless this.reportRunning}}
<div class="report-results">
<table>
<caption>{{t "general.resultsSummary"}}</caption>
<thead>
<tr>
<th>{{t "general.course"}}</th>
<th>{{t "general.sessions"}}</th>
<th>{{t "general.instructors"}}</th>
<th>{{t "general.learnerGroups"}}</th>
</tr>
</thead>
<tbody>
{{#each (sort-by "courseTitle" this.summary) as |o|}}
<tr>
<td>
<LinkTo @route="course" @model={{o.courseId}}>
{{o.courseTitle}}
</LinkTo>
</td>
<td>{{o.sessionCount}}</td>
<td>{{o.instructorsCount}}</td>
<td>{{o.learnerGroupsCount}}</td>
</tr>
{{/each}}
</tbody>
</table>
</div>
{{/unless}}
Loading
Loading