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} (${result.pic})`, 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(`
  • ${item.firstname} ${item.lastname}
    ${item.position} ${item.email ? `${item.email}`:''} ${item.phone ? `${item.phone}`:''}
  • `); 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(`

    ${item.acronym || '(unnamed)'}

    ${item.proposalNumber} ${item.accessRequests > 0 ? `${item.accessRequests} access request${item.accessRequests > 1 ? 's':''}`:''}

    short ${item.version} ${item.status}
    `); 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(`

    ${item.acronym || '(unnamed)'}

    ${item.proposalNumber}

    short ${item.version} ${item.status}
    `); 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(`
  • ${activity.message}
  • `)) } } 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);