/** * */ class ProjectFundingTechDDView extends EICDomContent { _autoSave = null; // save every 10s _autoSaveDelay = 10000; // validate every 2s _autoValidateDelay = 2000; // state flag avoiding saving form currently beeing loaded _busy = false; get autoSave() { return this._autoSave != null; } set autoSave(value) { if(value) { if(!this._autoSave) { this._autoSave = setInterval(this.onAutoSave.bind(this), this._autoSaveDelay) } } else { clearInterval(this._autoSave); this._autoSave = null; } } DOMContentLoaded(options) { this.components = ui.eicfy(this.el); this.projectNumber = options.projectNumber this.nodeId = options.nodeId; this.nodeType = 'techdd'; this.mode = options.mode; for(let model in options.models) this[model] = options.models[model]; this.container = this.components.find(o => o.el.classList.contains('funding-node')) this.container.loading = true; this._busy = true; this.techdd.get(this.projectNumber,this.nodeType, this.nodeId) .then(this.fill.bind(this)) } DOMContentBlured() { if(this.autoSave) { this.autoSave = false; } if(this.validationCheck) { clearInterval(this.validationCheck); this.validationCheck = null; } } DOMContentRemoved() { if(this.autoSave) { this.autoSave = false; if((this.mode == 'edit')) this.save(); }; if(this.validationCheck) { clearInterval(this.validationCheck); this.validationCheck = null; } } DOMContentFocused() { if(this.mode == 'edit') { if(this.saveToggler && this.saveToggler.value == 'true') { this.autoSave = true } if(!this.validationCheck) { this.validationCheck = setInterval(this.validateForm.bind(this), this._autoValidateDelay); } } } setupFormActions() { let saveControl = this.find('.autosave-control'); this.availableActions = []; this.reviewActions = []; ui.hide(saveControl); if(this.techdd.hasPrivilege('read')) { this.mode = 'read' } if(this.techdd.hasPrivilege('update')) { this.mode = 'edit'; ui.show(saveControl); this.availableActions.push({ id: 'cancel', ctrl: new Button(null, { severity: 'secondary', onclick: this.cancel.bind(this), label: 'discard changes' }) }) this.availableActions.push({ id: 'save', ctrl: new Button(null, { severity: 'primary', onclick: this.save.bind(this), label: 'save' }) }) this.autosave = true this.saveToggler.onToggle("true", this.saveToggler); } if(this.techdd.hasPrivilege('submit')) { this.availableActions.push({ id: 'submit', ctrl: new Button(null, { severity: 'primary', onclick: this.submit.bind(this), label: 'submit' }) }) } if(this.techdd.hasPrivilege('reject')) { this.mode = 'review'; this.availableActions.push({ id: 'reject', ctrl: new Button(null, { severity: 'primary', onclick: this.reject.bind(this), label: 'Reopen' }) }) this.reviewActions.push({ id: 'cancelReview', ctrl: new Button(null, { severity: 'secondary', onclick: this.cancelReview.bind(this), label: 'cancel' }) }) this.reviewActions.push({ id: 'commitReview', ctrl: new Button(null, { severity: 'primary', onclick: this.commitReview.bind(this), label: 'send remarks' }) }) } if(this.techdd.hasPrivilege('sendforreview')) { this.mode = 'review'; this.availableActions.push({ id: 'review', ctrl: new Button(null, { severity: 'primary', onclick: this.review.bind(this), label: 'send for consultation' }) }) } if(this.techdd.hasPrivilege('validate')) { this.mode = 'review'; this.availableActions.push({ id: 'validate', ctrl: new Button(null, { severity: 'success', onclick: this.validate.bind(this), label: 'validate' }) }) } if(this.techdd.hasPrivilege('cancel')) { this.mode = 'review'; this.availableActions.push({ id: 'abort', ctrl: new Button(null, { severity: 'danger', onclick: this.abort.bind(this), label: 'abort' }) }) } if(this.techdd.hasPrivilege('review')) { this.mode = 'review'; this.availableActions.push({ id: 'reject', ctrl: new Button(null, { severity: 'primary', onclick: this.reject.bind(this), label: 'add remarks' }) }) this.availableActions.push({ id: 'approve', ctrl: new Button(null, { severity: 'success', onclick: this.approve.bind(this), label: 'approve' }) }) this.reviewActions.push({ id: 'cancelReview', ctrl: new Button(null, { severity: 'secondary', onclick: this.cancelReview.bind(this), label: 'cancel' }) }) this.reviewActions.push({ id: 'commitReview', ctrl: new Button(null, { severity: 'primary', onclick: this.commitConsult.bind(this), label: 'send remarks' }) }) } let container = this.find('footer .actions'); container.innerHTML = ''; for(let action of this.availableActions) { container.append(action.ctrl.el) } } fill(data) { let path = 'projects/icmp/project/forms/' let view = 'ICMPFormTechddV1View'; let versionLabel = 'regular report' switch(data.form.version) { case 'techdd-report-v1-full': view = 'ICMPFormTechddV1FullView'; versionLabel = 'full report' break; case 'techdd-report-v1-enhanced': view = 'ICMPFormTechddV1EnhancedView' versionLabel = 'enhanced report' break; default: break; } let tags = []; this.find('header .version').innerHTML = `${versionLabel}` if(data.experts.length > 0) { tags.push(`reporting assigned to ${data.experts[0].information.name.firstName} ${data.experts[0].information.name.lastName}`) } tags.push(`${app.meta.getItem('icmp-techdd-status', data.statusHistory[0].value).label}`) this.find('header h2').innerHTML = tags.join(''); this.originalForm = JSON.parse(JSON.stringify(data)) this.loadContent(path + view, {}) .then(view => { this.find('.form').append(view.el) view.fill(data); this.formView = view; this.form = view.form; this.formComponents = view.components; this.setupForm(data) } ) } setupForm(data) { this.saveToggler = this.components.find(o => o.el.name == 'autosave'); this.saveToggler.onToggle = this.onAutoSaveToggle.bind(this) this.setupFormActions(); switch(this.mode) { case 'read': this.enableEditing(false); this.enableReviewing(false); break; case 'edit': this.enableEditing(true); this.enableReviewing(false); if(!this.validationCheck) { this.validationCheck = setInterval(this.validateForm.bind(this), this._autoValidateDelay); } break; case 'review': this.enableEditing(false); this.enableReviewing(false); break; } let comments = this.findAll('[data-path^="comments."]') for(let comment of comments) { let chunks = comment.dataset.path.split('.') if(chunks.length > 2 && comment.value != '') { let trigger = this.find(`li[data-target=${chunks[2]}]`) if(trigger) trigger.setAttribute('warning', '') } } if(data.documents && data.documents.length > 0) { for(let document of data.documents[0].attachments) { let url = app.config.api['/files'].get.uri url = url.replace('{id}', document.id) let alert = ui.create(`
Download report as PDF
`) this.find('.files').append(alert); } } this.container.loading = false; this._busy = false; } enableEditing(value) { let fields = this.form.scan(true); for(let field of fields) { let component = this.form.element2component(this.formComponents, field.el.name, field.el.dataset.path) if(component && component.el && component.el.dataset.path.search('form') == 0) { if(value) { component.el.dataset.type = component.el.nodeName == 'SELECT' ? 'boolean': 'text' component.disabled = false; } else { component.el.dataset.type = 'ignore' component.disabled = true; } } } } enableReviewing(value) { let fields = this.form.scan(true); this.originalComments = []; for(let field of fields) { let component = this.form.element2component(this.formComponents, field.el.name, field.el.dataset.path) if(component && component.el.dataset.path.search('comment') == 0) { if(value) { this.originalComments.push({ component: component, value: component.value }) component.el.dataset.type = 'text' component.disabled = false component.show() } else { component.el.dataset.type = 'ignore' component.disabled = true component.hide() let alerts = component.el.parentElement.querySelectorAll('[eicalert]'); for(let alert of alerts) { alert.remove(); } if(component.value != '') { component.el.parentElement.append((new Alert(null, {message: component.value,severity: 'warning'})).el) } } } } } validateForm() { //let valid = true; let valid = this.form.validate(); // init tab badges //let triggers = [].concat( this.formView.tabTech.triggers, this.formView.tabMarket.triggers ) console.log(this.formView.getTriggers()) let triggers = this.formView.getTriggers(); for(let trigger of triggers) { let badge = new Badge(trigger.querySelector('[eicbadge]')) badge.value = '' } // mark tabs on error if(!valid) { let errors = this.findAll('.validation-failed textarea, .validation-failed select') for(let error of errors) { let path = error.dataset.path.split('.') let trigger = triggers.find(el => el.dataset.target == path[2]); // todo make it count if(trigger) { let badge = new Badge(trigger.querySelector('[eicbadge]')) badge.value = parseInt(badge.value || 0) + 1; } } } /**/ return valid; } autoValidate() { this.validateForm(); } reject() { let container = this.find('footer .actions'); container.innerHTML = ''; for(let action of this.reviewActions) container.append(action.ctrl.el) this.enableReviewing(true); } cancelReview() { let container = this.find('footer .actions'); container.innerHTML = ''; for(let comment of this.originalComments) { comment.component.value = comment.value; } for(let action of this.availableActions) container.append(action.ctrl.el) this.enableReviewing(false); } commitReview() { for(let action of this.reviewActions) action.ctrl.disabled = true; this.reviewActions.find(o => o.id == 'commitReview').ctrl.loading = true; let payload = this.form.value; this.techdd.comment('rejecting', this.projectNumber, this.nodeType, this.nodeId, payload) .then( () => { for(let action of this.reviewActions) action.ctrl.disabled = false; this.reviewActions.find(o => o.id == 'commitReview').ctrl.loading = false; this.reviewActions.find(o => o.id == 'commitReview').ctrl.disabled = this.autoSave; app.events.trigger('project_node_updated', {node: this.nodeType, nodeId: this.nodeId}); }, () => { for(let action of this.reviewActions) action.ctrl.disabled = false; this.reviewActions.find(o => o.id == 'commitReview').ctrl.loading = false; this.reviewActions.find(o => o.id == 'commitReview').ctrl.disabled = this.autoSave; }) } approve() { for(let action of this.availableActions) action.ctrl.disabled = true; this.availableActions.find(o => o.id == 'approve').ctrl.loading = true; let payload = this.form.value; payload.comments = {}; this.techdd.comment('reviewing', this.projectNumber, this.nodeType, this.nodeId, payload) .then( () => { for(let action of this.availableActions) action.ctrl.disabled = false; this.availableActions.find(o => o.id == 'approve').ctrl.loading = false; app.events.trigger('project_node_updated', {node: this.nodeType, nodeId: this.nodeId}); }, () => { for(let action of this.availableActions) action.ctrl.disabled = false; this.availableActions.find(o => o.id == 'approve').ctrl.loading = false; }) } commitConsult() { for(let action of this.reviewActions) action.ctrl.disabled = true; this.reviewActions.find(o => o.id == 'commitReview').ctrl.loading = true; let payload = this.form.value; this.techdd.comment('reviewing', this.projectNumber, this.nodeType, this.nodeId, payload) .then( () => { for(let action of this.reviewActions) action.ctrl.disabled = false; this.reviewActions.find(o => o.id == 'commitReview').ctrl.loading = false; app.events.trigger('project_node_updated', {node: this.nodeType, nodeId: this.nodeId}); }, () => { for(let action of this.reviewActions) action.ctrl.disabled = false; this.reviewActions.find(o => o.id == 'commitReview').ctrl.loading = false; }) } save() { if(!app.User.isAuthenticated || this._busy) return; this.validateForm() for(let action of this.availableActions) action.ctrl.disabled = true; this.availableActions.find(o => o.id == 'save').ctrl.loading = true; let payload = this.form.value; this.techdd.process('saving', this.projectNumber, this.nodeType, this.nodeId, payload) .then( payload => { for(let action of this.availableActions) action.ctrl.disabled = false; this.availableActions.find(o => o.id == 'save').ctrl.loading = false; this.availableActions.find(o => o.id == 'save').ctrl.disabled = this.autoSave; this.availableActions.find(o => o.id == 'cancel').ctrl.disabled = this.autoSave; this.originalForm.form = payload.form; }, () => { for(let action of this.availableActions) action.ctrl.disabled = false; this.availableActions.find(o => o.id == 'save').ctrl.loading = false; this.availableActions.find(o => o.id == 'save').ctrl.disabled = this.autoSave; this.availableActions.find(o => o.id == 'cancel').ctrl.disabled = this.autoSave; }) } submit() { for(let action of this.availableActions) action.ctrl.disabled = true; this.availableActions.find(o => o.id == 'submit').ctrl.loading = true; if(this.validateForm()) { let payload = this.form.value; this.autoSave = false; this.techdd.process('submitting', this.projectNumber, this.nodeType, this.nodeId, payload) .then( () => { for(let action of this.availableActions) action.ctrl.disabled = false; this.availableActions.find(o => o.id == 'submit').ctrl.loading = false; app.events.trigger('project_node_updated', {node: this.nodeType, nodeId: this.nodeId}); }, () => { for(let action of this.availableActions) action.ctrl.disabled = false; this.availableActions.find(o => o.id == 'submit').ctrl.loading = false; }) } else { for(let action of this.availableActions) action.ctrl.disabled = false; this.availableActions.find(o => o.id == 'submit').ctrl.loading = false; ui.growl.append('The form still contains issues', 'danger') } } cancel() { this.form.fill(this.components,this.originalForm) } async abort() { let success = await this.openDialog( await this.loadContent( 'projects/icmp/project/dialogs/ICMPNodeActionConfirmDialog', { title: 'Dismiss expert and report', subtitle: 'You are about to dismiss this report. Another expert will be assigned to this task.' }, { models: { node: this.techdd }, number: this.projectNumber, type: this.nodeType, nodeId: this.nodeId, report: this.form.value, action: 'aborting' } ) ); if(success) { app.events.trigger('project_node_updated', {node: this.nodeType, nodeId: this.nodeId}); } } async validate() { let success = await this.openDialog( await this.loadContent( 'projects/icmp/project/dialogs/ICMPNodeActionConfirmDialog', { title: 'Validate this report', subtitle: 'You are about to approve this report and complete Technical Due Diligence' }, { models: { node: this.techdd }, number: this.projectNumber, type: this.nodeType, nodeId: this.nodeId, report: this.form.value, action: 'validating' } ) ); if(success) { app.events.trigger('project_node_updated', {node: this.nodeType, nodeId: this.nodeId}); } } async review() { let success = await this.openDialog( await this.loadContent( 'projects/icmp/project/dialogs/ICMPNodeActionConfirmDialog', { title: 'Send this report for reviewing' }, { models: { node: this.techdd }, number: this.projectNumber, type: this.nodeType, nodeId: this.nodeId, report: this.form.value, action: 'reviewing' } ) ) if(success) { app.events.trigger('project_node_updated', {node: this.nodeType, nodeId: this.nodeId}); } } onAutoSaveToggle(value, toggler) { if(value == 'true') { toggler.severity = 'primary' this.autoSave = true; } else { toggler.severity = 'secondary' this.autoSave = false; } this.availableActions.find(o => o.id == 'save').ctrl.disabled = this.autoSave; this.availableActions.find(o => o.id == 'cancel').ctrl.disabled = this.autoSave; } onAutoSave() { this.save(); } } app.registerClass('ProjectFundingTechDDView',ProjectFundingTechDDView);