File: D:/HostingSpaces/SBogers47/leden.ehbocranendonck.nl/resources/assets/js/site/presenceManager.js
/**
* Can do api calls to the backend to mark a user as present
*/
class PresenceManager {
/**
* The constructor.
*
* @param inputSelector (string) must reference an text input element
* @param courseId (int) the id of the course a user could check-in to
* @param apiUrl The base url to the api to manage user presence without trailing slash.
* Must support the following additional path suffixes: check-in
*/
constructor(inputSelector, apiUrl = 'presence')
{
//Create and initialize properties
this.input = null; //The text input element
this.apiUrl = apiUrl;
this.eventMap = {};
this.userTrackingLists = {};
//Delegate control to specialist parts of the class
this.validateInputSelectorAndSetInputAndCourseId(inputSelector);
this.enableListeners();
this.focusOnInput();
}
/**
* Focusses the the cursor to the input field and puts the cursor in the ind
*/
focusOnInput()
{
this.input.focus();
if(this.input.value !== '') {
let value = this.input.value;
this.input.value = '';
this.input.value = value; //set that value back. Cursor is at the end of the input
}
}
/**
* Validates the inout selector to make sure it references an text input
*/
validateInputSelectorAndSetInputAndCourseId(selector)
{
let element = document.querySelector(selector);
console.log(element);
if(!element) {
console.error('PresenceManager: The input selector parameter was invalid. It does not match a text input element');
return
}
if(!element.dataset.courseId) {
console.error('PresenceManager: The input selector parameter was invalid. It does not have a courseId data attribute');
return
}
this.courseId = element.dataset.courseId;
this.input = element
}
/**
* @param enable
*/
enableListeners(enable = true) {
if (enable) {
this.input.addEventListener('keypress', this.reactOnlyToKeyPressFromKeyCode(this.markUserAsPresentUsingCode.bind(this), 13)); //13 = Enter key
this.input.addEventListener('focusout', this.focusOnInput.bind(this));
}
}
/**
* Returns a function for an event listener to call and lets that function call the callback when the enter key is pressed
*
* @param callback
* @param reactToKeyCode The keyCode to react to (see: https://gist.github.com/tylerbuchea/8011573 for example)
* @returns {Function}
*/
reactOnlyToKeyPressFromKeyCode(callback, reactToKeyCode)
{
return function(event) {
let keyCode = event.keyCode;
if(keyCode === reactToKeyCode) callback();
}
}
/**
* Marks a user as present for the current course using a code from a badge
*/
markUserAsPresentUsingCode()
{
let self = this;
let userCode = self.input.value;
if(userCode != "") {
axios.post(this.apiUrl + '/check-in', {
userCode: userCode,
courseId: self.courseId
})
.then(function (response) {
self.input.value = '';
if (response.data.hasOwnProperty('user'))
self.userCheckedInSuccessFully(response.data.user);
else
self.apiErrorOccurred('User was checked in successfully. But user information was not returned properly.')
})
.catch(function (error) {
self.input.value = '';
switch (error.response.status) {
case 400:
self.userFailedToCheckIn(userCode, error.response.data);
break;
default:
self.apiErrorOccurred(error.response.statusText)
}
});
}
}
/**
* Triggered in response to markUserAsPresentUsingCode
*/
userCheckedInSuccessFully(user)
{
let audio = new Audio('/audio/success.mp3');
audio.play();
this.updateTrackingLists();
this.dispatchEvent('check-in', [user]);
}
/**
* Triggered in response to markUserAsPresentUsingCode
*/
userFailedToCheckIn(userCode, error)
{
let audio = new Audio('/audio/error.mp3');
audio.play();
this.updateTrackingLists();
this.dispatchEvent('check-in-fail', [error]);
}
/**
* Triggered in response to markUserAsPresentUsingCode
*/
apiErrorOccurred(error)
{
let audio = new Audio('/audio/error.mp3');
audio.play();
this.dispatchEvent('api-error', [error]);
}
/**
* Registers a event to a callback and returns the a reference to the event handler for that specific event
*
* @param event (string)
* @param callback (callable)
*/
on(event, callback)
{
if(!this.eventMap.hasOwnProperty(event)) this.eventMap[event] = [];
return this.eventMap[event].push(callback);
}
/**
* Call event callbacks
*
* @param event (string)
* @param eventArgs (array) an array of arguments to pass to the callback
*/
dispatchEvent(event, eventArgs) {
if(!this.eventMap.hasOwnProperty(event)) return;
for (cb of this.eventMap[event])
{
if(eventArgs && eventArgs.length > 0) {
cb.apply(this, eventArgs)
} else {
cb.call(this);
}
}
}
/**
* Updates ul or ol elements registered with the trackCheckedInUsersInList method
* with present and subscribed users
*/
updateTrackingLists()
{
let self = this;
if(self.userTrackingLists.hasOwnProperty('present') && self.userTrackingLists.present.length > 0) {
this.getPresentUsers(function (users) {
for (let listElement of self.userTrackingLists['present']) {
self.updateListElementWithUsers(listElement, users);
}
});
}
if(self.userTrackingLists.hasOwnProperty('subscribed') && self.userTrackingLists.subscribed.length > 0) {
this.getSubscribedUsers(function (users) {
for (let listElement of self.userTrackingLists['subscribed']) {
self.updateListElementWithUsers(listElement, users);
}
});
}
}
/**
* Updates a ul or ol element with li's that match the items in the user array which each have a first_name and last_name property
*
* @param listElement
* @param users
*/
updateListElementWithUsers(listElement, users)
{
console.log(listElement, users);
while( listElement.firstChild ){
listElement.removeChild( listElement.firstChild );
}
for(let user of users) {
let userListItem = document.createElement('li');
if(!user.hasOwnProperty('first_name') || !user.hasOwnProperty('last_name')) {
console.error('Could not create an li element with user data.')
continue;
}
userListItem.innerText = user.first_name+' '+user.last_name;
listElement.append(userListItem);
}
}
/**
* @param callback a callback function that will retrieve an array of present users
*/
getPresentUsers(callback)
{
this.getUsersForACertainState('present', callback);
}
/**
* @param callback a callback function that will retrieve an array of subscribed users
*/
getSubscribedUsers(callback)
{
this.getUsersForACertainState('subscribed', callback);
}
/**
* @param state (string) 'present' or 'subscribed'
* @param callback a callback function that will retrieve an array of users
*/
getUsersForACertainState(state, callback)
{
let self = this;
axios.get(this.apiUrl+'/tracking/'+state+'/'+self.courseId)
.then(function (response) {
if(response.data.hasOwnProperty('users'))
callback.call(this, response.data.users);
else
self.apiErrorOccurred('Could not get the "'+state+'" users')
})
.catch(function (error) {
console.log(error);
switch (error.response.status)
{
default:
self.apiErrorOccurred(error.response.statusText)
}
});
}
/**
* Marks an ul or li that can be selected with the given selector so that it
* automatically will be updated with the present users
*
* @param selector (string)
*/
trackCheckedInUsersInList(selector)
{
let presentUsersList = document.querySelector(selector);
if(presentUsersList) {
if(!this.userTrackingLists.hasOwnProperty('present')) this.userTrackingLists['present'] = [];
this.userTrackingLists['present'].push(presentUsersList);
} else {
console.error('The selector "'+selector+'" does not reference a ul or li and therefore it cannot be used to track the users that are checked-in for the current training');
}
this.updateTrackingLists();
}
/**
* Marks an ul or li that can be selected with the given selector so that it
* automatically will be updated with the subscribed users
*
* @param selector
*/
trackSubscribedUsersInList(selector)
{
let subscribedUsersList = document.querySelector(selector);
if(subscribedUsersList) {
if(!this.userTrackingLists.hasOwnProperty('subscribed')) this.userTrackingLists['subscribed'] = [];
this.userTrackingLists['subscribed'].push(subscribedUsersList);
} else {
console.error('The selector "'+selector+'" does not reference a ul or li and therefore it cannot be used to track the users that are signed up for the current training');
}
this.updateTrackingLists();
}
}