packages/frontend/app/components/new-directory-user.js
import Component from '@glimmer/component';
import { tracked } from '@glimmer/tracking';
import { action } from '@ember/object';
import { service } from '@ember/service';
import { isEmpty, isPresent } from '@ember/utils';
import { filter } from 'rsvp';
import { dropTask, restartableTask } from 'ember-concurrency';
import { validatable, Length, NotBlank } from 'ilios-common/decorators/validation';
import { findBy, findById } from 'ilios-common/utils/array-helpers';
import { ValidateIf } from 'class-validator';
import { TrackedAsyncData } from 'ember-async-data';
import { cached } from '@glimmer/tracking';
import { DateTime } from 'luxon';
@validatable
export default class NewDirectoryUserComponent extends Component {
@service fetch;
@service iliosConfig;
@service intl;
@service store;
@service currentUser;
@service flashMessages;
@service permissionChecker;
@tracked isSearching = false;
@tracked searchResults = [];
@tracked searchResultsReturned = false;
@tracked selectedUser = false;
@tracked tooManyResults = false;
@tracked firstName;
@tracked middleName;
@tracked lastName;
@tracked campusId;
@tracked @Length(0, 16) otherId;
@tracked email;
@tracked @ValidateIf((o) => o.allowCustomUserName) @NotBlank() @Length(1, 100) username;
@tracked @ValidateIf((o) => o.allowCustomUserName) @NotBlank() password;
@tracked phone;
@tracked schoolId = null;
@tracked primaryCohortId = null;
@tracked isSaving = false;
@tracked nonStudentMode = true;
userModel = new TrackedAsyncData(this.currentUser.getModel());
authTypeConfig = new TrackedAsyncData(this.iliosConfig.getAuthenticationType());
@cached
get allowCustomUserName() {
if (!this.authTypeConfig.isResolved) {
return false;
}
return this.authTypeConfig.value === 'form';
}
get allSchools() {
return this.store.peekAll('school');
}
@cached
get user() {
return this.userModel.isResolved ? this.userModel.value : null;
}
@cached
get schoolsWithCreatePermissions() {
return new TrackedAsyncData(
filter(this.allSchools, async (school) => {
return this.permissionChecker.canCreateUser(school);
}),
);
}
@cached
get schools() {
return this.schoolsWithCreatePermissions.isResolved
? this.schoolsWithCreatePermissions.value
: [];
}
get primarySchool() {
return findById(this.allSchools, this.user.belongsTo('school').id());
}
@cached
get currentSchoolCohorts() {
const programIds = this.store
.peekAll('program')
.filter((program) => program.belongsTo('school').id() === this.bestSelectedSchool?.id)
.map((program) => program.id);
const programYearIds = this.store
.peekAll('program-year')
.filter((programYear) => programIds.includes(programYear.belongsTo('program').id()))
.map((programYear) => programYear.id);
return this.store
.peekAll('cohort')
.filter((cohort) => programYearIds.includes(cohort.belongsTo('programYear').id()));
}
@cached
get cohorts() {
const programYears = this.store.peekAll('program-year');
const programs = this.store.peekAll('program');
const objects = this.currentSchoolCohorts.map((cohort) => {
const programYear = programYears.find((p) => p.id === cohort.belongsTo('programYear').id());
const program = programs.find((p) => p.id === programYear.belongsTo('program').id());
return {
id: cohort.id,
title: `${program.title} ${cohort.title}`,
startYear: programYear.startYear,
duration: program.duration,
};
});
const lastYear = DateTime.now().year - 1;
return objects.filter((obj) => {
const finalYear = Number(obj.startYear) + Number(obj.duration);
return finalYear > lastYear;
});
}
get bestSelectedSchool() {
if (this.schoolId) {
const currentSchool = findById(this.schools, this.schoolId);
if (currentSchool) {
return currentSchool;
}
}
if (this.schools.includes(this.primarySchool)) {
return this.primarySchool;
}
return this.schools[0];
}
get bestSelectedCohort() {
if (this.primaryCohortId) {
const currentCohort = findById(this.currentSchoolCohorts.slice(), this.primaryCohortId);
if (currentCohort) {
return currentCohort;
}
}
return this.currentSchoolCohorts.slice().reverse()[0];
}
load = restartableTask(async () => {
if (this.args.searchTerms) {
await this.findUsersInDirectory.perform(this.args.searchTerms);
}
});
@action
pickUser(user) {
this.selectedUser = true;
this.firstName = user.firstName;
this.lastName = user.lastName;
this.email = user.email;
this.campusId = user.campusId;
this.phone = user.telephoneNumber;
this.username = user.username;
}
@action
unPickUser() {
this.selectedUser = false;
this.firstName = null;
this.lastName = null;
this.email = null;
this.campusId = null;
this.phone = null;
this.username = null;
}
@action
setSchool(id) {
this.schoolId = id;
}
@action
setPrimaryCohort(id) {
this.primaryCohortId = id;
}
@action
keyboard(event) {
const keyCode = event.keyCode;
const target = event.target;
if ('text' === target.type) {
if (13 === keyCode) {
this.save.perform();
return;
}
if (27 === keyCode) {
this.args.close();
}
return;
}
if ('search' === target.type) {
if (13 === keyCode) {
this.findUsersInDirectory.perform(this.args.searchTerms);
return;
}
if (27 === keyCode) {
this.searchTerms = '';
}
}
}
findUsersInDirectory = restartableTask(async (searchTerms) => {
this.searchResultsReturned = false;
this.tooManyResults = false;
if (!isEmpty(searchTerms)) {
const url = '/application/directory/search?limit=51&searchTerms=' + searchTerms;
const data = await this.fetch.getJsonFromApiHost(url);
const mappedResults = data.results.map((result) => {
result.addable =
isPresent(result.firstName) &&
isPresent(result.lastName) &&
isPresent(result.email) &&
isPresent(result.campusId);
return result;
});
this.tooManyResults = mappedResults.length > 50;
this.searchResults = mappedResults;
this.searchResultsReturned = true;
}
});
save = dropTask(async () => {
this.addErrorDisplaysFor([
'firstName',
'middleName',
'lastName',
'campusId',
'otherId',
'email',
'phone',
'username',
'password',
]);
const isValid = await this.isValid();
if (!isValid) {
return false;
}
const roles = await this.store.findAll('user-role');
const primaryCohort = this.bestSelectedCohort;
let user = this.store.createRecord('user', {
firstName: this.firstName,
middleName: this.middleName,
lastName: this.lastName,
campusId: this.campusId,
otherId: this.otherId,
email: this.email,
phone: this.phone,
school: this.bestSelectedSchool,
enabled: true,
root: false,
});
if (!this.nonStudentMode) {
user.set('primaryCohort', primaryCohort);
const studentRole = findBy(roles.slice(), 'title', 'Student');
user.set('roles', [studentRole]);
}
user = await user.save();
const authentication = this.store.createRecord('authentication', {
user,
username: this.username,
password: this.password,
});
await authentication.save();
this.clearErrorDisplay();
this.flashMessages.success('general.saved');
this.args.transitionToUser(user.id);
});
}