487 lines
16 KiB
JavaScript
487 lines
16 KiB
JavaScript
class ApplicantDashboardView extends EICDomContent {
|
|
|
|
constructor() { super(); }
|
|
|
|
version = 'r2';
|
|
|
|
DOMContentLoaded(options) {
|
|
|
|
this.url = options.url;
|
|
this.pic = options.pic;
|
|
this.profile = options.profile || { admin: true };
|
|
|
|
for(let model in options.models) this[model] = options.models[model];
|
|
|
|
let components = ui.eicfy(this.el);
|
|
|
|
this.orgSelector = components.find(item => item.el.hasAttribute('eicdropdown'))
|
|
|
|
this.memberAdd = components.find(item => item.el.classList.contains('member-add'))
|
|
this.memberAdd.addEventListener('click', this.onMemberAdd.bind(this))
|
|
|
|
this.proposalCreate = components.find(item => item.el.classList.contains('proposal-create'))
|
|
this.proposalCreate.addEventListener('click', this.onProposalCreate.bind(this));
|
|
this.proposalSearch = components.find(item => item.el.classList.contains('proposal-search'))
|
|
this.proposalSearch.addEventListener('click', this.onProposalSearch.bind(this));
|
|
|
|
this.fillDashboard(this.pic);
|
|
|
|
// registering SP Form event triggering
|
|
app.events.channel.addEventListener('proposal_updated', this.onProposalUpdate.bind(this))
|
|
}
|
|
|
|
fillDashboard(pic) {
|
|
|
|
ui.lock();
|
|
|
|
this.pic = pic;
|
|
this.todos = [];
|
|
this.memberAdd.disabled = true;
|
|
this.proposalCreate.disabled = true;
|
|
|
|
this.applicant.read(pic).then(this.fill.bind(this));
|
|
this.myOrganisations.list().then(this.fillOrganisations.bind(this));
|
|
this.members.list(pic).then(this.fillMembers.bind(this));
|
|
this.proposals.list(pic).then(this.fillProposals.bind(this));
|
|
this.coachings.list(pic).then(this.fillCoachings.bind(this));
|
|
|
|
ui.unlock();
|
|
}
|
|
|
|
/**
|
|
* Updates the registered organisations menu
|
|
* @param {*} results
|
|
*/
|
|
fillOrganisations(results) {
|
|
let items = [];
|
|
this.orgSelector.menu.clear();
|
|
|
|
results.sort((a,b) => a.legalName.toLowerCase() > b.legalName.toLowerCase() ? 1: -1)
|
|
|
|
for(let result of results) {
|
|
items.push({
|
|
id: result.pic,
|
|
label: `${result.legalName} <span class="pic">(${result.pic})</span>`,
|
|
icon: "icon-company",
|
|
onclick: this.onCompanySwitch.bind(this,result.pic)
|
|
});
|
|
}
|
|
|
|
items.push({
|
|
label: "Register another organisation",
|
|
icon: "icon-plus",
|
|
severity: "danger",
|
|
onclick: this.onCompanyRegister.bind(this)
|
|
});
|
|
|
|
this.orgSelector.menu.parse(items);
|
|
}
|
|
|
|
/**
|
|
* Updates the organisation information
|
|
* @param {*} data
|
|
*/
|
|
fill(data) {
|
|
this.find('.company-name b').innerHTML = data.legalName;
|
|
this.find('.company-pic').innerHTML = data.pic;
|
|
ui.unlock();
|
|
}
|
|
|
|
/**
|
|
* Updates the organisation members list
|
|
* @param {*} data
|
|
*/
|
|
fillMembers(data) {
|
|
let members = this.find('.members ul');
|
|
members.innerHTML = '';
|
|
this.find('.members header h2').innerHTML= "";
|
|
this.memberAdd.disabled = !this.members.hasPrivilege('add');
|
|
|
|
let memberMetrics = { active: 0, pending: 0 }
|
|
this.todos = this.todos.filter(item => item.scope != 'members');
|
|
|
|
for(let item of data) {
|
|
let actions = [];
|
|
switch(item.status) {
|
|
case 'active':
|
|
break;
|
|
case 'pending':
|
|
actions.push(new Chip(null, {label:'pending', severity: 'warning', size: 'xsmall'}))
|
|
break;
|
|
}
|
|
|
|
memberMetrics[item.status]++;
|
|
|
|
let li = ui.create(`<li class="cols-2 right middle">
|
|
<div>
|
|
<div><b>${item.firstname} ${item.lastname}</b></div>
|
|
<div xsmall class="props">
|
|
${item.position}
|
|
${item.email ? `<a href="mailto:${item.email}">${item.email}</a>`:''}
|
|
${item.phone ? `<a href="tel:${item.phone}">${item.phone}</a>`:''}
|
|
</div>
|
|
</div>
|
|
<span class="actions"></span>
|
|
</li>`);
|
|
|
|
for(let action of actions)
|
|
li.querySelector('.actions').append(action.el);
|
|
|
|
li.addEventListener('click', this.onMemberDetail.bind(this, item.uid))
|
|
members.append(li);
|
|
}
|
|
|
|
this.find('.members header h2').innerHTML = '';
|
|
|
|
if(memberMetrics.active > 0) {
|
|
this.find('.members header h2').append((new Chip(null,{ label: `${memberMetrics.active} active`, size: 'xsmall', severity: 'success'})).el)
|
|
}
|
|
|
|
if(memberMetrics.pending > 0) {
|
|
this.find('.members header h2').append((new Chip(null,{ label: `${memberMetrics.pending} pending`, size: 'xsmall', severity: 'warning'})).el)
|
|
this.todos.push(
|
|
{ scope: 'members', message: `${memberMetrics.pending} member${memberMetrics.pending > 1 ? 's':''} requesting access` }
|
|
)
|
|
}
|
|
|
|
this.fillTodos();
|
|
}
|
|
|
|
/**
|
|
* Updates the proposals list
|
|
* @param {*} data
|
|
*/
|
|
fillProposals(data) {
|
|
/** Proposals list */
|
|
|
|
this.find('.proposals > header h2').innerHTML = "";
|
|
let proposals = this.find('.proposals .list');
|
|
proposals.innerHTML = '';
|
|
let proposalMetrics = {submitted: 0, draft: 0, accessRequests: 0 };
|
|
|
|
this.todos = this.todos.filter(item => item.scope != 'proposals');
|
|
|
|
for(let item of data) {
|
|
let card = ui.create(`<article eiccard>
|
|
<header>
|
|
<h1>${item.acronym || '(unnamed)'}</h1>
|
|
<h2>
|
|
${item.proposalNumber}
|
|
${item.accessRequests > 0 ? `<span eicchip xsmall warning>${item.accessRequests} access request${item.accessRequests > 1 ? 's':''}</span>`:''}
|
|
</h2>
|
|
</header>
|
|
<section>
|
|
<span eicchip small primary>short ${item.version}</span>
|
|
<span eicchip small info>${item.status}</span>
|
|
</section>
|
|
<footer>
|
|
<div class="cols-2 center noflex"></div>
|
|
</footer>
|
|
</article>`);
|
|
|
|
let button = new Button(null, {label:'view', severity: 'primary', size: 'xsmall'});
|
|
button.el.addEventListener('click', this.onProposalView.bind(this, item.proposalNumber, item.version))
|
|
card.querySelector('footer div').append(button.el)
|
|
|
|
if(item.version != 'legacy' && item.status == 'draft') {
|
|
button = new Button(null, {label:'edit', severity: 'primary', size: 'xsmall'});
|
|
button.el.addEventListener('click', this.onProposalEdit.bind(this, item.proposalNumber, item.version))
|
|
card.querySelector('footer div').append(button.el)
|
|
}
|
|
|
|
proposalMetrics[item.status]++;
|
|
proposalMetrics.accessRequests += item.accessRequests;
|
|
proposals.append(card);
|
|
}
|
|
|
|
if(proposalMetrics.draft > 0) {
|
|
this.find('.proposals > header h2').append((new Chip(null,{ label: `${proposalMetrics.draft} draft`, size: 'xsmall', severity: 'warning'})).el)
|
|
this.todos.push(
|
|
{ scope: 'proposals', message: `${proposalMetrics.draft} draft proposal${proposalMetrics.draft > 1 ? 's':''} to be submitted` }
|
|
)
|
|
}
|
|
|
|
if(proposalMetrics.accessRequests > 0) {
|
|
this.todos.push(
|
|
{ scope: 'proposals', message: `${proposalMetrics.accessRequests} proposal access request${proposalMetrics.accessRequests > 1 ? 's':''} pending` }
|
|
)
|
|
}
|
|
|
|
this.proposalCreate.disabled = !this.proposals.hasPrivilege('create');
|
|
|
|
this.fillTodos();
|
|
}
|
|
|
|
/**
|
|
* Updates the proposals list
|
|
* @param {*} data
|
|
*/
|
|
fillCoachings(data) {
|
|
/** Proposals list */
|
|
|
|
// this.find('.coachings > header h2').innerHTML = "";
|
|
let coachings = this.find('.coachings .list');
|
|
coachings.innerHTML = '';
|
|
let coachingMetrics = { };
|
|
|
|
this.todos = this.todos.filter(item => item.scope != 'coachings');
|
|
|
|
for(let item of data) {
|
|
let card = ui.create(`<article eiccard>
|
|
<header>
|
|
<h1>${item.acronym || '(unnamed)'}</h1>
|
|
<h2>
|
|
${item.proposalNumber}
|
|
</h2>
|
|
</header>
|
|
<section>
|
|
<span eicchip small primary>short ${item.version}</span>
|
|
<span eicchip small info>${item.status}</span>
|
|
</section>
|
|
<footer>
|
|
<div class="cols-2 center noflex"></div>
|
|
</footer>
|
|
</article>`);
|
|
|
|
let button = new Button(null, {label:'view', severity: 'primary', size: 'xsmall'});
|
|
button.el.addEventListener('click', this.onCoachingView.bind(this, item.proposalNumber, item.version))
|
|
card.querySelector('footer div').append(button.el)
|
|
|
|
coachings.append(card);
|
|
}
|
|
|
|
// unused for now
|
|
//this.fillTodos();
|
|
}
|
|
|
|
/**
|
|
* Updates the TODO list
|
|
*/
|
|
fillTodos() {
|
|
let activities = this.find('.activities ul')
|
|
activities.innerHTML = '';
|
|
if(this.todos.length > 0) {
|
|
ui.show(activities);
|
|
for(let activity of this.todos) {
|
|
activities.append(ui.create(`<li>${activity.message}</li>`))
|
|
}
|
|
} else {
|
|
ui.hide(activities);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler for adding an organisation memnber
|
|
* @param {*} event
|
|
*/
|
|
async onMemberAdd(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
let member = await this.openDialog(
|
|
await this.loadContent(
|
|
'applicants/dialogs/ApplicantMemberDialog',
|
|
{ title: 'Add a member' },
|
|
{
|
|
model: this.members,
|
|
mode: 'create',
|
|
pic: this.pic
|
|
}
|
|
)
|
|
);
|
|
|
|
if(member) {
|
|
ui.growl.append('member added', 'success');
|
|
this.members.list(this.pic).then(this.fillMembers.bind(this));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler for getting organisation member details
|
|
* @param {*} event
|
|
*/
|
|
async onMemberDetail(uid, event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
let member = await this.openDialog(
|
|
await this.loadContent(
|
|
'applicants/dialogs/ApplicantMemberDialog',
|
|
{ title: 'edit a member' },
|
|
{
|
|
model: this.members,
|
|
mode: this.members.hasPrivilege('update') ? 'edit': 'read',
|
|
pic: this.pic,
|
|
uid: uid
|
|
}
|
|
)
|
|
);
|
|
|
|
if(member) {
|
|
if(member.status == 'removed')
|
|
ui.growl.append('member removed', 'danger');
|
|
else
|
|
ui.growl.append('member updated', 'success');
|
|
|
|
this.members.list(this.pic).then(this.fillMembers.bind(this));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Event handler for calling short proposal form on edit
|
|
* @param {*} number
|
|
* @param {*} version
|
|
* @param {*} event
|
|
*/
|
|
onProposalEdit(number, version, event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
app.Router.route(`/organisations/${this.pic}/proposals/${number}`, { mode: 'edit', version: version});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {*} number
|
|
* @param {*} version
|
|
* @param {*} event
|
|
*/
|
|
onProposalView(number, version, event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
app.Router.route(`/organisations/${this.pic}/proposals/${number}`, { mode: 'read', version: version});
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {*} number
|
|
* @param {*} version
|
|
* @param {*} event
|
|
*/
|
|
onProposalCreate(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.proposalCreate.loading = true;
|
|
|
|
this.proposals.create(this.pic)
|
|
.then( async payload => {
|
|
let number = payload.proposalNumber;
|
|
this.proposalCreate.loading = false;
|
|
this.proposals.list(this.pic).then(this.fillProposals.bind(this));
|
|
app.Router.route(`/organisations/${this.pic}/proposals/${number}`, { mode: 'edit'});
|
|
},
|
|
(err)=>{ this.proposalCreate.loading = false })
|
|
}
|
|
|
|
/**
|
|
* Event handler for getting organisation member details
|
|
* @param {*} event
|
|
*/
|
|
async onProposalSearch(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
await this.openDialog(
|
|
await this.loadContent(
|
|
'applicants/dialogs/ApplicantProposalSearchDialog',
|
|
{ title: 'search for existing proposals' },
|
|
{
|
|
model: this.proposals,
|
|
pic: this.pic
|
|
}
|
|
)
|
|
);
|
|
}
|
|
onProposalUpdate(event) {
|
|
this.proposals.list(this.pic).then(this.fillProposals.bind(this));
|
|
}
|
|
|
|
/**
|
|
*
|
|
* @param {*} number
|
|
* @param {*} version
|
|
* @param {*} event
|
|
*/
|
|
onCoachingView(number, version, event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.coachings.read();
|
|
}
|
|
|
|
/**
|
|
* Event handler for changing organisation profile
|
|
* @param {*} pic
|
|
* @param {*} event
|
|
*/
|
|
onCompanySwitch(pic, event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.orgSelector.collapse();
|
|
ui.lock();
|
|
|
|
app.User.getBusinessPermissions([
|
|
'/organisations',
|
|
'/organisations/' + pic,
|
|
'/organisations/' + pic + '/members',
|
|
'/organisations/' + pic + '/proposals'
|
|
], 'Org_Member')
|
|
.then(async payload => {
|
|
ui.unlock();
|
|
|
|
if(payload['/organisations/' + pic].permissions.includes('read')) {
|
|
|
|
this.applicant.setPrivileges('/organisations/{pic}', payload['/organisations/' + pic].permissions);
|
|
this.myOrganisations.setPrivileges('/organisations', payload['/organisations'].permissions);
|
|
this.members.setPrivileges('/organisations/{pic}/members', payload['/organisations/' + pic + '/members'].permissions);
|
|
this.proposals.setPrivileges('/organisations/{pic}/proposals', payload['/organisations/' + pic + '/proposals'].permissions);
|
|
|
|
this.fillDashboard(pic);
|
|
this._controller.changeUrl(this, this.url.replace(':pic', pic))
|
|
|
|
} else {
|
|
|
|
ui.unlock();
|
|
ui.growl.append('You don\'t have access to this organisation', 'danger' );
|
|
|
|
}
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Event handler for registering new organisation
|
|
* Using the onboarding as applicant scenario
|
|
* @param {*} event
|
|
*/
|
|
async onCompanyRegister(event) {
|
|
event.stopPropagation();
|
|
event.preventDefault();
|
|
|
|
this.orgSelector.collapse();
|
|
|
|
app.User.getBusinessPermissions(['/organisations'])
|
|
.then(async payload => {
|
|
let membership = await this.openDialog(
|
|
await this.loadContent(
|
|
'common/onboarding/dialogs/onboardingApplicantDialog',
|
|
{ title: 'Register to an organisation' },
|
|
{
|
|
models: { user: new onboardingUserModel(payload['/organisations'].permissions) },
|
|
cancelLabel: 'cancel'
|
|
}
|
|
)
|
|
);
|
|
|
|
if(membership) {
|
|
ui.growl.append('membership request sent', 'success');
|
|
}
|
|
},
|
|
() => {
|
|
ui.growl.append('Sorry, you don\'t have access to this feature', 'danger');
|
|
})
|
|
}
|
|
}
|
|
|
|
app.registerClass('ApplicantDashboardView', ApplicantDashboardView); |