unclean SPARC
This commit is contained in:
+42
@@ -0,0 +1,42 @@
|
||||
<article eiccard class="fasttracks dashboard">
|
||||
<header>
|
||||
<h1>Bypass</h1>
|
||||
<h2>Global monitoring</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="tabs-extended">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li>Tokens</li>
|
||||
<li>History</li>
|
||||
<li class="settings">Settings</li>
|
||||
</menu>
|
||||
</section>
|
||||
<!--
|
||||
<section>
|
||||
<button eicbutton rounded basic primary small icon="icon-cog" title="Manage settings"></button>
|
||||
</section>-->
|
||||
</div>
|
||||
<article eiccard class="tokens content">
|
||||
<section>
|
||||
<div class="cols-2 right middle">
|
||||
<div class="metrics">
|
||||
<div class="assigned-past"><span>0</span><label>assigned <span class="year"></span></label></div>
|
||||
<div class="assigned-current"><span>0</span><label>assigned <span class="year"></span></label></div>
|
||||
<div class="consumed-past"><span success>0</span><label success>consumed <span class="year"></span></label></div>
|
||||
<div class="consumed-current"><span success>0</span><label success>consumed <span class="year"></span></label></div>
|
||||
</div>
|
||||
<button eicbutton primary class="grant-token">Grant a token</button>
|
||||
</div>
|
||||
<div eicdatagrid class="global-granted-tokens" footer="hidden"></div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="history content">
|
||||
<section>
|
||||
<div eicdatagrid class="history-tokens"></div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="config content"></article>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
+365
@@ -0,0 +1,365 @@
|
||||
class BypassAdminDashboard extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
ui.eicfy(this.el);
|
||||
|
||||
this.profile = options.profile;
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.tabs = new Tab();
|
||||
this.tabs.addTabs(this.findAll('.tabs-extended menu li'), this.findAll('.content'))
|
||||
|
||||
this.gridTokens = new DataGrid(this.find('.global-granted-tokens'), {
|
||||
headers: [
|
||||
{ label: 'PIC', type: 'markup', filter: 'text', sortable: true},
|
||||
{ label: 'Company name', filter: 'text', sortable: true},
|
||||
{ label: 'Token granted',type: 'date', filter: 'text', sortable: true},
|
||||
{ label: 'Scheme', filter: 'list', sortable: true},
|
||||
{ label: 'Entity', filter: 'text', sortable: true},
|
||||
{ label: 'Status', filter: 'list', sortable: true},
|
||||
{ label: 'Project', filter: 'text', sortable: true},
|
||||
{ label: 'Short proposal submission', type: 'date', filter: 'text', sortable: true}
|
||||
],
|
||||
height: 'calc(100vh - 472px)',
|
||||
actions: []
|
||||
});
|
||||
|
||||
this.btTokenDl = new Button(null, {icon: 'icon-download', severity: 'secondary', rounded: true, size: 'small', hint: 'Download complete list as CSV'})
|
||||
this.btTokenDl.addEventListener('click', this.downloadTokensList.bind(this));
|
||||
this.gridTokens.el.querySelector('header .cell.actions').append(this.btTokenDl.el);
|
||||
|
||||
this.btTokenRefresh = new Button(null, {icon: 'icon-refresh', severity: 'primary', rounded: true, size: 'small', hint: 'Refresh the list'})
|
||||
this.btTokenRefresh.addEventListener('click', this.refreshTokensList.bind(this));
|
||||
this.gridTokens.el.querySelector('header .cell.actions').append(this.btTokenRefresh.el);
|
||||
|
||||
this.gridTokens.onRowFiltered = this.refreshMetrics.bind(this);
|
||||
|
||||
this.gridHistory = new DataGrid(this.find('.history-tokens'), {
|
||||
headers: [
|
||||
{ label: 'Date', type: 'dateTime', filter: 'text', sortable: true},
|
||||
{ label: 'PIC', filter: 'text', sortable: true},
|
||||
{ label: 'Company name', filter: 'text', sortable: true},
|
||||
{ label: 'Action', filter: 'list', sortable: true},
|
||||
{ label: 'Managed by', filter: 'text', sortable: true},
|
||||
],
|
||||
height: 'calc(100vh - 420px)',
|
||||
actions: []
|
||||
});
|
||||
|
||||
let btGrant = this.find('.grant-token');
|
||||
if(this.company.hasPrivilege('grant')) {
|
||||
btGrant.addEventListener('click', this.onTokenGrant.bind(this));
|
||||
} else {
|
||||
ui.hide(btGrant);
|
||||
}
|
||||
|
||||
if(this.tokens.hasPrivilege('list')) { this.refreshTokensList(); }
|
||||
|
||||
if(this.users) {
|
||||
this.loadContent(
|
||||
'projects/bypass/BypassAdminManagementContent',
|
||||
{ title: 'Accelerator configuration', subtitle: 'Users access & token management' },
|
||||
{ profile: this.profile, models: { "users": this.users, "tokens": this.tokens } }
|
||||
).then(view => this.setupUserManagement(view));
|
||||
} else {
|
||||
ui.hide(this.find('li.settings'));
|
||||
}
|
||||
}
|
||||
|
||||
setupUserManagement(view) {
|
||||
this.userManagement = view;
|
||||
this.find('.content.config').append(view.el);
|
||||
}
|
||||
|
||||
fill() {
|
||||
let year = new Date().getFullYear();
|
||||
let awardedPast = this.tokens.awardedTokens(year - 1);
|
||||
awardedPast.forEach(item => item.ref = 'assigned-past');
|
||||
let awardedCurrent = this.tokens.awardedTokens(year);
|
||||
awardedCurrent.forEach(item => item.ref = 'assigned-current');
|
||||
let consumedPast = this.tokens.consumedTokens(year - 1);
|
||||
consumedPast.forEach(item => item.ref = 'consumed-past');
|
||||
let consumedCurrent = this.tokens.consumedTokens(year);
|
||||
consumedCurrent.forEach(item => item.ref = 'consumed-current');
|
||||
|
||||
let history = this.tokens.history();
|
||||
|
||||
this.find('.metrics .assigned-current .year').innerText = year;
|
||||
this.find('.metrics .consumed-current .year').innerText = year;
|
||||
this.find('.metrics .assigned-past .year').innerText = year - 1;
|
||||
this.find('.metrics .consumed-past .year').innerText = year - 1;
|
||||
|
||||
this.gridHistory.loading = false;
|
||||
this.gridTokens.loading = false;
|
||||
this.btTokenRefresh.loading = false;
|
||||
|
||||
this.gridTokens.clear();
|
||||
|
||||
let awarded = awardedCurrent.concat(awardedPast);
|
||||
awarded.sort((a,b) => a.itemData.granted < b.itemData.granted ? 1: -1);
|
||||
|
||||
for (const item of awarded) {
|
||||
let priority = '';
|
||||
switch(item.itemData.status) {
|
||||
case 'consumed':
|
||||
priority = 'success';
|
||||
break;
|
||||
}
|
||||
|
||||
let row = this.gridTokens.addRow(item.itemData.id, [
|
||||
`<a href="#" data-id="${item.itemData.id}" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" title="View token details">${item.itemData.enterprise.pic}</a>`,
|
||||
item.itemData.enterprise.legalname,
|
||||
item.itemData.granted,
|
||||
app.meta.toString('accelerator-tracks', item.itemData.track),
|
||||
app.meta.toString('accelerator-tracks', item.itemData.domain),
|
||||
`<span ${priority}>${item.itemData.status}</span>`,
|
||||
item.itemData.project ? item.itemData.project.acronym: '',
|
||||
item.itemData.project ? item.itemData.project.submissionDate: ''
|
||||
], true);
|
||||
|
||||
let tokenLink = row.querySelector('.cell:nth-child(2) a')
|
||||
tokenLink.addEventListener('click', this.onTokenView.bind(this));
|
||||
|
||||
row.classList.add(item.ref);
|
||||
|
||||
// checking if token can be revoked (meaning status is allocated and user has privilege for that action)
|
||||
if((item.itemData.status == "allocated") && this.company.hasPrivilege('revoke') && (!item.itemData.isAssignedToShortProposal)) {
|
||||
let buttonBar = ui.create(`<div class="cols-2"></div>`)
|
||||
let revokeButton = ui.create(`<button eicbutton xsmall danger title="revoke this token" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" data-track="${item.itemData.track}" data-domain="${item.itemData.domain}" >Revoke</button>`);
|
||||
revokeButton.addEventListener('click', this.onTokenRevoke.bind(this));
|
||||
buttonBar.appendChild(revokeButton);
|
||||
|
||||
let useButton = ui.create(`<button eicbutton xsmall info title="Activate the coaching" data-id="${item.itemData.id}" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" data-track="${item.itemData.track}" data-domain="${item.itemData.domain}">Consume</button>`);
|
||||
useButton.addEventListener('click', this.onTokenConsume.bind(this));
|
||||
buttonBar.appendChild(useButton);
|
||||
|
||||
row.querySelector('.cell.actions').appendChild(buttonBar);
|
||||
} else if((item.itemData.status == "allocated") && this.company.hasPrivilege('revoke') && (item.itemData.isAssignedToShortProposal)){
|
||||
row.querySelector('.cell.actions').appendChild(ui.create(`<span>Consuming...</span>`));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
this.gridTokens.updateFilters();
|
||||
|
||||
// filling the history list
|
||||
this.gridHistory.clear();
|
||||
for (const item of history) {
|
||||
let priority = ''
|
||||
switch(item.action) {
|
||||
case 'revoked': priority = 'danger'; break;
|
||||
case 'consumed': priority = 'success'; break;
|
||||
}
|
||||
let row = this.gridHistory.addRow(item.date, [
|
||||
item.date,
|
||||
item.pic,
|
||||
item.legalname,
|
||||
`<span ${priority}>${item.action}</span>`,
|
||||
`${item.actor.firstname ? item.actor.firstname[0] + '. ': ''}${item.actor.lastname}`
|
||||
], true);
|
||||
}
|
||||
this.gridHistory.updateFilters();
|
||||
|
||||
this.refreshMetrics();
|
||||
}
|
||||
|
||||
refreshMetrics() {
|
||||
let rows = Array.from(this.gridTokens.filteredRows);
|
||||
|
||||
let assignedPast = rows.filter(item => item.classList.contains('assigned-past'));
|
||||
let assignedCurrent = rows.filter(item => item.classList.contains('assigned-current'));
|
||||
let consumedPast = rows.filter(item => item.classList.contains('consumed-past'));
|
||||
let consumedCurrent = rows.filter(item => item.classList.contains('consumed-current'));
|
||||
|
||||
this.find('.metrics .assigned-past span').innerText = assignedPast.length;
|
||||
this.find('.metrics .assigned-current span').innerText = assignedCurrent.length;
|
||||
this.find('.metrics .consumed-past span').innerText = consumedPast.length;
|
||||
this.find('.metrics .consumed-current span').innerText = consumedCurrent.length;
|
||||
}
|
||||
|
||||
refreshTokensList(event) {
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
this.btTokenRefresh.loading = true;
|
||||
this.gridTokens.loading = true;
|
||||
this.gridHistory.loading = true;
|
||||
this.tokens.list().then(this.fill.bind(this));
|
||||
}
|
||||
|
||||
downloadTokensList(event) {
|
||||
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
this.btTokenDl.loading = true;
|
||||
this.tokens.export({filter: { includePersons: true }}).then(this.buildCSV.bind(this));
|
||||
}
|
||||
|
||||
buildCSV(payload) {
|
||||
|
||||
let options = {
|
||||
headers: [
|
||||
'PIC',
|
||||
'Organisation Name',
|
||||
'Granted on',
|
||||
'Scheme',
|
||||
'Entity',
|
||||
'Program',
|
||||
'Status',
|
||||
'Project',
|
||||
'Submission date',
|
||||
'Contact name',
|
||||
'Contact gender',
|
||||
'Contact position',
|
||||
'Contact email',
|
||||
'Contact phone',
|
||||
'SPOC name',
|
||||
'SPOC email',
|
||||
],
|
||||
filename: 'EIC-Bypass',
|
||||
delimiter: ';'
|
||||
}
|
||||
|
||||
let data = [];
|
||||
|
||||
for(let token of payload.tokens) {
|
||||
let row = [
|
||||
token.enterprise.pic,
|
||||
token.enterprise.legalname,
|
||||
token.granted,
|
||||
app.meta.toString('accelerator-tracks', token.track),
|
||||
app.meta.toString('accelerator-tracks', token.domain),
|
||||
token.program || "",
|
||||
token.history[0].status,
|
||||
token.project ? token.project.acronym: '',
|
||||
token.project ? token.project.submissiondate: '',
|
||||
token.shortProposal ? token.shortProposal.contact.lastname + ' ' + token.shortProposal.contact.firstname: '',
|
||||
token.shortProposal ? token.shortProposal.contact.gender: '',
|
||||
token.shortProposal ? token.shortProposal.contact.position: '',
|
||||
token.shortProposal ? token.shortProposal.contact.email: '',
|
||||
token.shortProposal ? token.shortProposal.contact.phone: '',
|
||||
token.spoc ? token.spoc.lastname + ' ' + token.spoc.firstname: '',
|
||||
token.spoc ? token.spoc.email: '',
|
||||
]
|
||||
|
||||
data.push(row);
|
||||
}
|
||||
|
||||
app.helpers.translator.toCSV( data, options );
|
||||
this.btTokenDl.loading = false;
|
||||
}
|
||||
|
||||
async onTokenGrant(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let grantResult = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantDialog',
|
||||
{ title: 'Grant a token' },
|
||||
{ models: { 'company': this.company, 'tokens': this.tokens }, profile: this.profile }
|
||||
)
|
||||
);
|
||||
|
||||
if(grantResult) {
|
||||
ui.growl.append('Token granted', 'success');
|
||||
let ConsumeResult = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantProposalDialog',
|
||||
{ title: 'Activate the coaching for...', subtitle: `${this.company.itemData.legalname} (${this.company.itemData.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
profile: this.profile,
|
||||
tokenId: this.company.itemData.token.id,
|
||||
pic: this.company.itemData.pic,
|
||||
legalname: this.company.itemData.legalname,
|
||||
wizard: true,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
this.refreshTokensList();
|
||||
}
|
||||
|
||||
async onTokenRevoke(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenRevokeDialog',
|
||||
{ title: 'Revoke a token for...', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
model: this.company,
|
||||
profile: this.profile,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
track: event.currentTarget.dataset.track,
|
||||
domain: event.currentTarget.dataset.domain,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token revoked', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
|
||||
async onTokenView(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantProposalDialog',
|
||||
{ title: 'Token granted to ', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
mode: 'read',
|
||||
profile: this.profile,
|
||||
tokenId: event.currentTarget.dataset.id,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
legalname: event.currentTarget.dataset.legalname,
|
||||
wizard: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token consumed', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
|
||||
async onTokenConsume(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantProposalDialog',
|
||||
{ title: 'Activate the coaching for...', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
profile: this.profile,
|
||||
tokenId: event.currentTarget.dataset.id,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
legalname: event.currentTarget.dataset.legalname,
|
||||
wizard: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token consumed', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('BypassAdminDashboard', BypassAdminDashboard);
|
||||
@@ -0,0 +1,42 @@
|
||||
<div>
|
||||
<section>
|
||||
<article eiccard collapsable>
|
||||
<header>
|
||||
<h1>Users Access</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="users-list"></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right"><button eicbutton primary small class="user-add">add user...</button></div>
|
||||
</footer>
|
||||
</article>
|
||||
</section>
|
||||
<section class="cols-2">
|
||||
<article eiccard collapsable>
|
||||
<header>
|
||||
<h1>EIC Fast Track Settings</h1>
|
||||
<h2>Yearly token assignment per domain</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="kic-domains-list" footer="hidden"></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right"><button class="kic-edit" eicbutton primary small>update...</button></div>
|
||||
</footer>
|
||||
</article>
|
||||
<article eiccard collapsable>
|
||||
<header>
|
||||
<h1>EIC Plug In Settings</h1>
|
||||
<h2>Yearly token assignment per domain</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="plugin-domains-list" footer="hidden"></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right"><button class="plugin-edit" eicbutton primary small>update...</button></div>
|
||||
</footer>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,104 @@
|
||||
class BypassAdminManagementContent extends BypassBaseManagementContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
super.DOMContentLoaded(options);
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
|
||||
this.gridKics = new DataGrid(this.find('.kic-domains-list'), {
|
||||
headers: [ { label: 'Thematic'}, { label: 'Tokens' }, { label: 'Remaining' } ],
|
||||
height: '320px'
|
||||
});
|
||||
|
||||
this.gridPlugins = new DataGrid(this.find('.plugin-domains-list'), {
|
||||
headers: [ { label: 'Country'}, { label: 'Tokens'}, { label: 'Remaining' } ],
|
||||
height: '320px'
|
||||
});
|
||||
|
||||
this.pluginEditButton = components.find(component => component.el.classList.contains('plugin-edit'));
|
||||
if(this.tokens.hasPrivilege('setSettings')) {
|
||||
this.pluginEditButton.addEventListener('click', this.onPluginEdit.bind(this));
|
||||
} else {
|
||||
ui.hide(this.pluginEditButton);
|
||||
}
|
||||
|
||||
this.kicEditButton = components.find(component => component.el.classList.contains('kic-edit'));
|
||||
if(this.tokens.hasPrivilege('setSettings')) {
|
||||
this.kicEditButton.addEventListener('click', this.onKicEdit.bind(this));
|
||||
} else {
|
||||
ui.hide(this.kicEditButton);
|
||||
}
|
||||
|
||||
this.gridKics.loading = true;
|
||||
this.tokens.getSettings('d7xAg5kIhQYeDMB1H6eXnBg')
|
||||
.then(this.fillKics.bind(this));
|
||||
|
||||
this.gridPlugins.loading = true;
|
||||
this.tokens.getSettings('daTai94ymStyRbQWybH3eDw')
|
||||
.then(this.fillPlugins.bind(this));
|
||||
}
|
||||
|
||||
fillKics(payload) {
|
||||
this.gridKics.clear();
|
||||
this.gridKics.loading = false;
|
||||
|
||||
for(let item of payload.domains) {
|
||||
this.gridKics.addRow(item.domain, [app.meta.toString('accelerator-tracks', item.domain), item.tokens, item.remaining])
|
||||
}
|
||||
this.gridKics.sort(1, 'asc');
|
||||
}
|
||||
|
||||
fillPlugins(payload) {
|
||||
this.gridPlugins.clear();
|
||||
this.gridPlugins.loading = false;
|
||||
for(let item of payload.domains) {
|
||||
this.gridPlugins.addRow(item.domain, [app.meta.toString('accelerator-tracks', item.domain), item.tokens, item.remaining])
|
||||
}
|
||||
this.gridPlugins.sort(1, 'asc');
|
||||
}
|
||||
|
||||
async onKicEdit(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTrackSetupDialog',
|
||||
{ title: 'Fast-Track Yearly token attribution', subtitle: 'Set up the yearly token attribution per domain' },
|
||||
{ model: this.tokens, track: 'd7xAg5kIhQYeDMB1H6eXnBg' }
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Settings updated', 'success');
|
||||
|
||||
this.gridKics.loading = true;
|
||||
this.tokens.getSettings('d7xAg5kIhQYeDMB1H6eXnBg')
|
||||
.then(this.fillKics.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
async onPluginEdit(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTrackSetupDialog',
|
||||
{ title: 'Plugins Yearly token attribution', subtitle: 'Set up the yearly token attribution per country' },
|
||||
{ model: this.tokens, track: 'daTai94ymStyRbQWybH3eDw' }
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Settings updated', 'success');
|
||||
|
||||
this.gridPlugins.loading = true;
|
||||
this.tokens.getSettings('daTai94ymStyRbQWybH3eDw')
|
||||
.then(this.fillPlugins.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassAdminManagementContent', BypassAdminManagementContent);
|
||||
@@ -0,0 +1,117 @@
|
||||
class BypassBaseManagementContent extends EICDomContent {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
Object.assign(this, app.helpers.basicDialogs)
|
||||
}
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
this.profile = options.profile;
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.users.getRoles(); // We'll need them later, but quite slow
|
||||
|
||||
this.gridUsers = new DataGrid(this.find('.users-list'), {
|
||||
headers: [
|
||||
{ label: 'Login', filter: 'text', sortable: true},
|
||||
{ label: 'Email', filter: 'text', sortable: true},
|
||||
{ label: 'Fullname', filter: 'text', sortable: true},
|
||||
{ label: 'Role', filter: 'list', sortable: true},
|
||||
{ label: 'Scheme', filter: 'list', sortable: true},
|
||||
{ label: 'Domain', filter: 'list', sortable: true},
|
||||
],
|
||||
height: 'calc(100vh - 620px)',
|
||||
actions: []
|
||||
});
|
||||
this.btUsersRefresh = new Button(null, {icon: 'icon-refresh', severity: 'primary', rounded: true, size: 'small', hint: 'Refresh the list'})
|
||||
this.btUsersRefresh.addEventListener('click', this.refreshUsersList.bind(this));
|
||||
this.gridUsers.el.querySelector('header .cell.actions').append(this.btUsersRefresh.el);
|
||||
|
||||
this.userAddButton = this.find('.user-add');
|
||||
if(this.users.hasPrivilege('create')) {
|
||||
this.userAddButton.addEventListener('click', this.onUserCreate.bind(this));
|
||||
} else {
|
||||
ui.hide(this.userAddButton);
|
||||
}
|
||||
|
||||
this.gridUsers.loading = true;
|
||||
this.users.list().then(this.fillUsers.bind(this));
|
||||
}
|
||||
|
||||
fillUsers() {
|
||||
this.gridUsers.clear();
|
||||
this.gridUsers.loading = false;
|
||||
this.btUsersRefresh.loading = false;
|
||||
|
||||
for (const item of this.users.collection) {
|
||||
let row = this.gridUsers.addRow(item.uid, [
|
||||
item.uid,
|
||||
item.email,
|
||||
`${item.firstname} ${item.lastname}`,
|
||||
item.BProle,
|
||||
item.track.label,
|
||||
item.domain.label,
|
||||
], true);
|
||||
|
||||
if(this.users.hasPrivilege('revoke') && (item.uid != app.User.identity.uuid )) {
|
||||
let button = ui.create(`<button eicbutton xsmall danger title="revoke this user" data-uid="${item.uid}">revoke</button>`);
|
||||
button.addEventListener('click', this.onUserRevoke.bind(this));
|
||||
row.querySelector('.cell.actions').appendChild(button);
|
||||
}
|
||||
}
|
||||
this.gridUsers.updateFilters();
|
||||
}
|
||||
|
||||
refreshUsersList(event) {
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
this.btUsersRefresh.loading = true;
|
||||
this.gridUsers.loading = true;
|
||||
this.users.list().then(this.fillUsers.bind(this));
|
||||
}
|
||||
|
||||
async onUserCreate(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(await this.loadContent('projects/bypass/dialogs/BypassUserProfileDialog',{
|
||||
title: 'Add a user',
|
||||
subtitle: 'Please first enter the user UUID then define role and scopes of activity'
|
||||
}, {
|
||||
model: this.users,
|
||||
mode: 'create',
|
||||
profile: this.profile
|
||||
}));
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('User added', 'success');
|
||||
this.refreshUsersList();
|
||||
}
|
||||
}
|
||||
|
||||
async onUserRevoke(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
let uid = event.currentTarget.dataset.uid;
|
||||
let user = this.users.getUid(uid);
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Revoke a user',
|
||||
message: `Please be aware that you are about to revoke the user "<strong>${user.firstname} ${user.lastname}</strong>"<br>
|
||||
This is permanent !<br>
|
||||
Are you positive ?`,
|
||||
okLabel: 'Revoke user',
|
||||
okPromise:() => { return(this.users.revoke(uid)) },
|
||||
})
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('User revoked', 'success');
|
||||
this.refreshUsersList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassBaseManagementContent', BypassBaseManagementContent);
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
<article eiccard class="fasttracks dashboard">
|
||||
<header>
|
||||
<h1>EIC Fast Track</h1>
|
||||
<h2>Monitoring for entity/programme <b info>${app.meta.toString('accelerator-tracks', profile.domain)}</b></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="tabs-extended">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li>Tokens</li>
|
||||
<li>History</li>
|
||||
<li class="settings">Settings</li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<article eiccard class="tokens content">
|
||||
<section>
|
||||
<div class="cols-2 right middle">
|
||||
<div class="metrics">
|
||||
<div class="available"><span>0</span><label>available</label></div>
|
||||
<div class="assigned-past"><span>0</span><label>assigned <span class="year"></span></label></div>
|
||||
<div class="assigned-current"><span>0</span><label>assigned <span class="year"></span></label></div>
|
||||
<div class="consumed-past"><span success>0</span><label success>consumed <span class="year"></span></label></div>
|
||||
<div class="consumed-current"><span success>0</span><label success>consumed <span class="year"></span></label></div>
|
||||
</div>
|
||||
<button eicbutton primary class="grant-token">Grant a token</button>
|
||||
</div>
|
||||
<div eicdatagrid class="granted-tokens" footer="hidden"></div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="history content">
|
||||
<section>
|
||||
<div eicdatagrid class="history-tokens"></div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="config content">
|
||||
<section>
|
||||
<div eicdatagrid class="users" footer="hidden"></div>
|
||||
</section>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
</article>
|
||||
+359
@@ -0,0 +1,359 @@
|
||||
class BypassKICDashboard extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
ui.eicfy(this.el);
|
||||
|
||||
this.profile = options.profile;
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.tabs = new Tab();
|
||||
this.tabs.addTabs(this.findAll('menu li'), this.findAll('.content'))
|
||||
|
||||
this.gridTokens = new DataGrid(this.find('.granted-tokens'), {
|
||||
headers: [
|
||||
{ label: 'PIC', type:'markup', filter: 'text', sortable: true},
|
||||
{ label: 'Company name', filter: 'text', sortable: true},
|
||||
{ label: 'Entity/programme', filter: 'list', sortable: true},
|
||||
{ label: 'Token granted', type: 'date', filter: 'text', sortable: true},
|
||||
{ label: 'Status', filter: 'list', sortable: true},
|
||||
{ label: 'Project', filter: 'text', sortable: true},
|
||||
{ label: 'Short proposal submission', type: 'date', filter: 'text', sortable: true}
|
||||
],
|
||||
height: 'calc(100vh - 472px)',
|
||||
actions: []
|
||||
});
|
||||
|
||||
this.btTokenDl = new Button(null, {icon: 'icon-download', severity: 'secondary', rounded: true, size: 'small', hint: 'Download complete list as CSV'})
|
||||
this.btTokenDl.addEventListener('click', this.downloadTokensList.bind(this));
|
||||
this.gridTokens.el.querySelector('header .cell.actions').append(this.btTokenDl.el);
|
||||
|
||||
this.btTokenRefresh = new Button(null, {icon: 'icon-refresh', severity: 'primary', rounded: true, size: 'small', hint: 'Refresh the list'})
|
||||
this.btTokenRefresh.addEventListener('click', this.refreshTokensList.bind(this));
|
||||
this.gridTokens.el.querySelector('header .cell.actions').append(this.btTokenRefresh.el);
|
||||
|
||||
this.gridTokens.onRowFiltered = this.refreshMetrics.bind(this);
|
||||
|
||||
this.gridHistory = new DataGrid(this.find('.history-tokens'), {
|
||||
headers: [
|
||||
{ label: 'Date', type: 'dateTime', filter: 'text', sortable: true},
|
||||
{ label: 'PIC', filter: 'text', sortable: true},
|
||||
{ label: 'Company name', filter: 'text', sortable: true},
|
||||
{ label: 'Action', filter: 'list', sortable: true},
|
||||
{ label: 'Managed by', filter: 'text', sortable: true},
|
||||
],
|
||||
height: 'calc(100vh - 420px)',
|
||||
actions: []
|
||||
});
|
||||
|
||||
let btGrant = this.find('.grant-token');
|
||||
if(this.company.hasPrivilege('grant')) {
|
||||
btGrant.addEventListener('click', this.onTokenGrant.bind(this));
|
||||
} else {
|
||||
ui.hide(btGrant);
|
||||
}
|
||||
|
||||
if(this.tokens.hasPrivilege('list')) { this.refreshTokensList(); }
|
||||
|
||||
if(this.users) {
|
||||
this.loadContent(
|
||||
'projects/bypass/BypassKICManagementContent',
|
||||
{ title: 'Accelerator configuration', subtitle: 'Users access & token management' },
|
||||
{ profile: this.profile, models: { "users": this.users } }
|
||||
).then(view => this.setupUserManagement(view))
|
||||
} else {
|
||||
ui.hide(this.find('li.settings'));
|
||||
}
|
||||
}
|
||||
|
||||
setupUserManagement(view) {
|
||||
this.userManagement = view;
|
||||
this.find('.content.config').append(view.el);
|
||||
}
|
||||
|
||||
fill() {
|
||||
let year = new Date().getFullYear();
|
||||
let awardedPast = this.tokens.awardedTokens(year - 1);
|
||||
awardedPast.forEach(item => item.ref = 'assigned-past');
|
||||
let awardedCurrent = this.tokens.awardedTokens(year);
|
||||
awardedCurrent.forEach(item => item.ref = 'assigned-current');
|
||||
let consumedPast = this.tokens.consumedTokens(year - 1);
|
||||
consumedPast.forEach(item => item.ref = 'consumed-past');
|
||||
let consumedCurrent = this.tokens.consumedTokens(year);
|
||||
consumedCurrent.forEach(item => item.ref = 'consumed-current');
|
||||
|
||||
let history = this.tokens.history();
|
||||
|
||||
this.find('.metrics .available span').innerText = this.tokens.freeTokens - awardedCurrent.length;
|
||||
this.find('.metrics .assigned-current .year').innerText = year;
|
||||
this.find('.metrics .consumed-current .year').innerText = year;
|
||||
this.find('.metrics .assigned-past .year').innerText = year - 1;
|
||||
this.find('.metrics .consumed-past .year').innerText = year - 1;
|
||||
|
||||
this.gridHistory.loading = false;
|
||||
this.gridTokens.loading = false;
|
||||
this.btTokenRefresh.loading = false;
|
||||
|
||||
this.gridTokens.clear();
|
||||
|
||||
let awarded = awardedCurrent.concat(awardedPast);
|
||||
awarded.sort((a,b) => a.itemData.granted < b.itemData.granted ? 1: -1);
|
||||
|
||||
for (const item of awarded) {
|
||||
let priority = ''
|
||||
switch(item.itemData.status) {
|
||||
case 'consumed':
|
||||
priority = 'success';
|
||||
break;
|
||||
}
|
||||
|
||||
let row = this.gridTokens.addRow(item.itemData.id, [
|
||||
`<a href="#" data-id="${item.itemData.id}" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" title="View token details">${item.itemData.enterprise.pic}</a>`,
|
||||
item.itemData.enterprise.legalname,
|
||||
app.meta.toString('accelerator-tracks', item.itemData.domain),
|
||||
item.itemData.granted,
|
||||
`<span ${priority}>${item.itemData.status}</span>`,
|
||||
item.itemData.project ? item.itemData.project.acronym: '',
|
||||
item.itemData.project ? item.itemData.project.submissionDate: ''
|
||||
], true);
|
||||
|
||||
let tokenLink = row.querySelector('.cell:nth-child(2) a')
|
||||
tokenLink.addEventListener('click', this.onTokenView.bind(this));
|
||||
|
||||
row.classList.add(item.ref);
|
||||
|
||||
// checking if token can be revoked (meaning status is allocated and user has privilege for that action)
|
||||
if((item.itemData.status == "allocated") && this.company.hasPrivilege('revoke') && (!item.itemData.isAssignedToShortProposal)) {
|
||||
let buttonBar = ui.create(`<div class="cols-2"></div>`)
|
||||
let revokeButton = ui.create(`<button eicbutton xsmall danger title="revoke this token" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" data-track="${item.itemData.track}" data-domain="${item.itemData.domain}" >Revoke</button>`);
|
||||
revokeButton.addEventListener('click', this.onTokenRevoke.bind(this));
|
||||
buttonBar.appendChild(revokeButton);
|
||||
|
||||
let useButton = ui.create(`<button eicbutton xsmall info title="Activate the coaching" data-id="${item.itemData.id}" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" data-track="${item.itemData.track}" data-domain="${item.itemData.domain}" >Consume</button>`);
|
||||
useButton.addEventListener('click', this.onTokenConsume.bind(this));
|
||||
buttonBar.appendChild(useButton);
|
||||
|
||||
row.querySelector('.cell.actions').appendChild(buttonBar);
|
||||
} else if((item.itemData.status == "allocated") && this.company.hasPrivilege('revoke') && (item.itemData.isAssignedToShortProposal)){
|
||||
row.querySelector('.cell.actions').appendChild(ui.create(`<span>Consuming...</span>`));
|
||||
}
|
||||
}
|
||||
this.gridTokens.updateFilters();
|
||||
|
||||
// filling the history list
|
||||
this.gridHistory.clear();
|
||||
for (const item of history) {
|
||||
let priority = ''
|
||||
switch(item.action) {
|
||||
case 'revoked': priority = 'danger'; break;
|
||||
case 'consumed': priority = 'success'; break;
|
||||
}
|
||||
let row = this.gridHistory.addRow(item.date, [
|
||||
item.date,
|
||||
item.pic,
|
||||
item.legalname,
|
||||
`<span ${priority}>${item.action}</span>`,
|
||||
`${item.actor.firstname ? item.actor.firstname[0] + '. ': ''}${item.actor.lastname}`
|
||||
], true);
|
||||
}
|
||||
this.gridHistory.updateFilters();
|
||||
|
||||
this.refreshMetrics();
|
||||
}
|
||||
|
||||
refreshMetrics() {
|
||||
let rows = Array.from(this.gridTokens.filteredRows);
|
||||
|
||||
let assignedPast = rows.filter(item => item.classList.contains('assigned-past'));
|
||||
let assignedCurrent = rows.filter(item => item.classList.contains('assigned-current'));
|
||||
let consumedPast = rows.filter(item => item.classList.contains('consumed-past'));
|
||||
let consumedCurrent = rows.filter(item => item.classList.contains('consumed-current'));
|
||||
|
||||
this.find('.metrics .assigned-past span').innerText = assignedPast.length;
|
||||
this.find('.metrics .assigned-current span').innerText = assignedCurrent.length;
|
||||
this.find('.metrics .consumed-past span').innerText = consumedPast.length;
|
||||
this.find('.metrics .consumed-current span').innerText = consumedCurrent.length;
|
||||
}
|
||||
|
||||
refreshTokensList(event) {
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
this.btTokenRefresh.loading = true;
|
||||
this.gridTokens.loading = true;
|
||||
this.gridHistory.loading = true;
|
||||
this.tokens.list().then(this.fill.bind(this));
|
||||
}
|
||||
|
||||
downloadTokensList(event) {
|
||||
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
this.btTokenDl.loading = true;
|
||||
this.tokens.export({filter: { includePersons: true }}).then(this.buildCSV.bind(this));
|
||||
}
|
||||
|
||||
buildCSV(payload) {
|
||||
|
||||
let options = {
|
||||
headers: [
|
||||
'PIC',
|
||||
'Organisation Name',
|
||||
'Granted on',
|
||||
'Scheme',
|
||||
'Entity',
|
||||
'Status',
|
||||
'Project',
|
||||
'Submission date',
|
||||
'Contact name',
|
||||
'Contact gender',
|
||||
'Contact position',
|
||||
'Contact email',
|
||||
'Contact phone',
|
||||
'SPOC name',
|
||||
'SPOC email',
|
||||
],
|
||||
filename: 'EIC-Bypass',
|
||||
delimiter: ';'
|
||||
}
|
||||
|
||||
let data = [];
|
||||
|
||||
for(let token of payload.tokens) {
|
||||
let row = [
|
||||
token.enterprise.pic,
|
||||
token.enterprise.legalname,
|
||||
token.granted,
|
||||
app.meta.toString('accelerator-tracks', token.track),
|
||||
app.meta.toString('accelerator-tracks', token.domain),
|
||||
token.history[0].status,
|
||||
token.project ? token.project.acronym: '',
|
||||
token.project ? token.project.submissiondate: '',
|
||||
token.shortProposal ? token.shortProposal.contact.lastname + ' ' + token.shortProposal.contact.firstname: '',
|
||||
token.shortProposal ? token.shortProposal.contact.gender: '',
|
||||
token.shortProposal ? token.shortProposal.contact.position: '',
|
||||
token.shortProposal ? token.shortProposal.contact.email: '',
|
||||
token.shortProposal ? token.shortProposal.contact.phone: '',
|
||||
token.spoc ? token.spoc.lastname + ' ' + token.spoc.firstname: '',
|
||||
token.spoc ? token.spoc.email: '',
|
||||
]
|
||||
|
||||
data.push(row);
|
||||
}
|
||||
|
||||
app.helpers.translator.toCSV( data, options );
|
||||
this.btTokenDl.loading = false;
|
||||
}
|
||||
async onTokenGrant(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let grantResult = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantDialog',
|
||||
{ title: 'Grant a token' },
|
||||
{ models: { 'company': this.company, 'tokens': this.tokens }, profile: this.profile }
|
||||
)
|
||||
);
|
||||
|
||||
if(grantResult) {
|
||||
ui.growl.append('Token granted', 'success');
|
||||
let ConsumeResult = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantPropaDialog',
|
||||
{ title: 'Activate the coaching for...', subtitle: `${this.company.itemData.legalname} (${this.company.itemData.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
profile: this.profile,
|
||||
tokenId: this.company.itemData.token.id,
|
||||
pic: this.company.pic,
|
||||
legalname: this.company.itemData.legalname,
|
||||
wizard: true,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
this.refreshTokensList();
|
||||
}
|
||||
|
||||
async onTokenRevoke(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenRevokeDialog',
|
||||
{ title: 'Revoke a token for...', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
model: this.company,
|
||||
profile: this.profile,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
track: event.currentTarget.dataset.track,
|
||||
domain: event.currentTarget.dataset.domain,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token revoked', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
|
||||
async onTokenView(event) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantProposalDialog',
|
||||
{ title: 'Token granted to ', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
mode: 'read',
|
||||
profile: this.profile,
|
||||
tokenId: event.currentTarget.dataset.id,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
legalname: event.currentTarget.dataset.legalname,
|
||||
wizard: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token consumed', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
|
||||
async onTokenConsume(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantProposalDialog',
|
||||
{ title: 'Activate the coaching for...', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
profile: this.profile,
|
||||
tokenId: event.currentTarget.dataset.id,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
legalname: event.currentTarget.dataset.legalname,
|
||||
wizard: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token consumed', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassKICDashboard', BypassKICDashboard);
|
||||
@@ -0,0 +1,16 @@
|
||||
<div>
|
||||
<section>
|
||||
<article eiccard collapsable>
|
||||
<header>
|
||||
<h1>Users Access</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="users-list"></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right"><button eicbutton primary small class="user-add">add user...</button></div>
|
||||
</footer>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,7 @@
|
||||
class BypassKICManagementContent extends BypassBaseManagementContent {
|
||||
|
||||
DOMContentLoaded(options) { super.DOMContentLoaded(options); }
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('BypassKICManagementContent', BypassKICManagementContent);
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
<article eiccard class="fasttracks dashboard">
|
||||
<header>
|
||||
<h1>EIC Plug In</h1>
|
||||
<h2>Monitoring for country <b info>${app.meta.toString('accelerator-tracks', profile.domain)}</b></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="tabs-extended">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li>Tokens</li>
|
||||
<li>History</li>
|
||||
<li class="settings">Settings</li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<article eiccard class="tokens content">
|
||||
<section>
|
||||
<div class="cols-2 right middle">
|
||||
<div class="metrics">
|
||||
<div class="available"><span>0</span><label>available</label></div>
|
||||
<div class="assigned-past"><span>0</span><label>assigned <span class="year"></span></label></div>
|
||||
<div class="assigned-current"><span>0</span><label>assigned <span class="year"></span></label></div>
|
||||
<div class="consumed-past"><span success>0</span><label success>consumed <span class="year"></span></label></div>
|
||||
<div class="consumed-current"><span success>0</span><label success>consumed <span class="year"></span></label></div>
|
||||
</div>
|
||||
<button eicbutton primary class="grant-token">Grant a token</button>
|
||||
</div>
|
||||
<div eicdatagrid class="granted-tokens" footer="hidden"></div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="history content">
|
||||
<section>
|
||||
<div eicdatagrid class="history-tokens"></div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="config content">
|
||||
<section>
|
||||
<div eicdatagrid class="users" footer="hidden"></div>
|
||||
</section>
|
||||
</article>
|
||||
</section>
|
||||
</article>
|
||||
+364
@@ -0,0 +1,364 @@
|
||||
class BypassPluginDashboard extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
ui.eicfy(this.el);
|
||||
|
||||
this.profile = options.profile;
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.tabs = new Tab();
|
||||
this.tabs.addTabs(this.findAll('menu li'), this.findAll('.content'))
|
||||
|
||||
this.gridTokens = new DataGrid(this.find('.granted-tokens'), {
|
||||
headers: [
|
||||
{ label: 'PIC', type: 'markup', filter: 'text', sortable: true},
|
||||
{ label: 'Company name', filter: 'text', sortable: true},
|
||||
{ label: 'Country', filter: 'list', sortable: true},
|
||||
{ label: 'Token granted', type: 'date', filter: 'text', sortable: true},
|
||||
{ label: 'Status', filter: 'list', sortable: true},
|
||||
{ label: 'Project', filter: 'text', sortable: true},
|
||||
{ label: 'Short proposal submission', type: 'date', filter: 'text', sortable: true},
|
||||
],
|
||||
height: 'calc(100vh - 472px)',
|
||||
actions: []
|
||||
});
|
||||
|
||||
this.btTokenDl = new Button(null, {icon: 'icon-download', severity: 'secondary', rounded: true, size: 'small', hint: 'Download complete list as CSV'})
|
||||
this.btTokenDl.addEventListener('click', this.downloadTokensList.bind(this));
|
||||
this.gridTokens.el.querySelector('header .cell.actions').append(this.btTokenDl.el);
|
||||
|
||||
this.btTokenRefresh = new Button(null, {icon: 'icon-refresh', severity: 'primary', rounded: true, size: 'small', hint: 'Refresh the list'})
|
||||
this.btTokenRefresh.addEventListener('click', this.refreshTokensList.bind(this));
|
||||
this.gridTokens.el.querySelector('header .cell.actions').append(this.btTokenRefresh.el);
|
||||
|
||||
this.gridTokens.onRowFiltered = this.refreshMetrics.bind(this);
|
||||
|
||||
this.gridHistory = new DataGrid(this.find('.history-tokens'), {
|
||||
headers: [
|
||||
{ label: 'Date', type: 'dateTime', filter: 'text', sortable: true},
|
||||
{ label: 'PIC', filter: 'text', sortable: true},
|
||||
{ label: 'Company name', filter: 'text', sortable: true},
|
||||
{ label: 'Action', filter: 'list', sortable: true},
|
||||
{ label: 'Managed by', filter: 'text', sortable: true},
|
||||
],
|
||||
height: 'calc(100vh - 420px)',
|
||||
actions: []
|
||||
});
|
||||
|
||||
let btGrant = this.find('.grant-token');
|
||||
if(this.company.hasPrivilege('grant')) {
|
||||
btGrant.addEventListener('click', this.onTokenGrant.bind(this));
|
||||
} else {
|
||||
ui.hide(btGrant);
|
||||
}
|
||||
|
||||
if(this.tokens.hasPrivilege('list')) { this.refreshTokensList(); }
|
||||
|
||||
if(this.users) {
|
||||
this.loadContent(
|
||||
'projects/bypass/BypassPluginManagementContent',
|
||||
{ title: 'Accelerator configuration', subtitle: 'Users access & token management' },
|
||||
{ profile: this.profile, models: { users: this.users, tokens: this.tokens } }
|
||||
).then(view => this.setupUserManagement(view))
|
||||
} else {
|
||||
ui.hide(this.find('li.settings'));
|
||||
}
|
||||
}
|
||||
|
||||
setupUserManagement(view) {
|
||||
this.userManagement = view;
|
||||
this.find('.content.config').append(view.el);
|
||||
}
|
||||
|
||||
fill() {
|
||||
let year = new Date().getFullYear();
|
||||
let awardedPast = this.tokens.awardedTokens(year - 1);
|
||||
awardedPast.forEach(item => item.ref = 'assigned-past');
|
||||
let awardedCurrent = this.tokens.awardedTokens(year);
|
||||
awardedCurrent.forEach(item => item.ref = 'assigned-current');
|
||||
let consumedPast = this.tokens.consumedTokens(year - 1);
|
||||
consumedPast.forEach(item => item.ref = 'consumed-past');
|
||||
let consumedCurrent = this.tokens.consumedTokens(year);
|
||||
consumedCurrent.forEach(item => item.ref = 'consumed-current');
|
||||
|
||||
let history = this.tokens.history();
|
||||
|
||||
this.find('.metrics .available span').innerText = this.tokens.freeTokens - awardedCurrent.length;
|
||||
this.find('.metrics .assigned-current .year').innerText = year;
|
||||
this.find('.metrics .consumed-current .year').innerText = year;
|
||||
this.find('.metrics .assigned-past .year').innerText = year - 1;
|
||||
this.find('.metrics .consumed-past .year').innerText = year - 1;
|
||||
|
||||
this.gridHistory.loading = false;
|
||||
this.gridTokens.loading = false;
|
||||
this.btTokenRefresh.loading = false;
|
||||
|
||||
this.gridTokens.clear();
|
||||
|
||||
let awarded = awardedCurrent.concat(awardedPast);
|
||||
awarded.sort((a,b) => a.itemData.granted < b.itemData.granted ? 1: -1);
|
||||
|
||||
for (const item of awarded) {
|
||||
let priority = ''
|
||||
switch(item.itemData.status) {
|
||||
case 'consumed':
|
||||
priority = 'success';
|
||||
break;
|
||||
}
|
||||
|
||||
let row = this.gridTokens.addRow(item.itemData.id, [
|
||||
`<a href="#" data-id="${item.itemData.id}" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" title="View token details">${item.itemData.enterprise.pic}</a>`,
|
||||
item.itemData.enterprise.legalname,
|
||||
app.meta.toString('accelerator-tracks', item.itemData.domain),
|
||||
item.itemData.granted,
|
||||
`<span ${priority}>${item.itemData.status}</span>`,
|
||||
item.itemData.project ? item.itemData.project.acronym: '',
|
||||
item.itemData.project ? item.itemData.project.submissionDate: ''
|
||||
], true);
|
||||
|
||||
let tokenLink = row.querySelector('.cell:nth-child(2) a')
|
||||
tokenLink.addEventListener('click', this.onTokenView.bind(this));
|
||||
|
||||
row.classList.add(item.ref);
|
||||
|
||||
// checking if token can be revoked (meaning status is allocated and user has privilege for that action)
|
||||
if((item.itemData.status == "allocated") && this.company.hasPrivilege('revoke') && (!item.itemData.isAssignedToShortProposal)) {
|
||||
let buttonBar = ui.create(`<div class="cols-2"></div>`)
|
||||
let revokeButton = ui.create(`<button eicbutton xsmall danger title="revoke this token" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" data-track="${item.itemData.track}" data-domain="${item.itemData.domain}" >Revoke</button>`);
|
||||
revokeButton.addEventListener('click', this.onTokenRevoke.bind(this));
|
||||
buttonBar.appendChild(revokeButton);
|
||||
|
||||
let useButton = ui.create(`<button eicbutton xsmall info title="Activate the coaching" data-id="${item.itemData.id}" data-pic="${item.itemData.enterprise.pic}" data-legalname="${item.itemData.enterprise.legalname}" data-track="${item.itemData.track}" data-domain="${item.itemData.domain}" >Consume</button>`);
|
||||
useButton.addEventListener('click', this.onTokenConsume.bind(this));
|
||||
buttonBar.appendChild(useButton);
|
||||
|
||||
row.querySelector('.cell.actions').appendChild(buttonBar);
|
||||
} else if((item.itemData.status == "allocated") && this.company.hasPrivilege('revoke') && (item.itemData.isAssignedToShortProposal)){
|
||||
row.querySelector('.cell.actions').appendChild(ui.create(`<span>Consuming...</span>`));
|
||||
}
|
||||
}
|
||||
this.gridTokens.updateFilters();
|
||||
|
||||
// filling the history list
|
||||
this.gridHistory.clear();
|
||||
for (const item of history) {
|
||||
let priority = ''
|
||||
switch(item.action) {
|
||||
case 'revoked': priority = 'danger'; break;
|
||||
case 'consumed': priority = 'success'; break;
|
||||
}
|
||||
let row = this.gridHistory.addRow(item.date, [
|
||||
item.date,
|
||||
item.pic,
|
||||
item.legalname,
|
||||
`<span ${priority}>${item.action}</span>`,
|
||||
`${item.actor.firstname ? item.actor.firstname[0] + '. ': ''}${item.actor.lastname}`
|
||||
], true);
|
||||
}
|
||||
this.gridHistory.updateFilters();
|
||||
|
||||
this.refreshMetrics();
|
||||
}
|
||||
|
||||
refreshMetrics() {
|
||||
let rows = Array.from(this.gridTokens.filteredRows);
|
||||
|
||||
let assignedPast = rows.filter(item => item.classList.contains('assigned-past'));
|
||||
let assignedCurrent = rows.filter(item => item.classList.contains('assigned-current'));
|
||||
let consumedPast = rows.filter(item => item.classList.contains('consumed-past'));
|
||||
let consumedCurrent = rows.filter(item => item.classList.contains('consumed-current'));
|
||||
|
||||
this.find('.metrics .assigned-past span').innerText = assignedPast.length;
|
||||
this.find('.metrics .assigned-current span').innerText = assignedCurrent.length;
|
||||
this.find('.metrics .consumed-past span').innerText = consumedPast.length;
|
||||
this.find('.metrics .consumed-current span').innerText = consumedCurrent.length;
|
||||
}
|
||||
|
||||
refreshTokensList(event) {
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
this.btTokenRefresh.loading = true;
|
||||
this.gridTokens.loading = true;
|
||||
this.gridHistory.loading = true;
|
||||
this.tokens.list().then(this.fill.bind(this));
|
||||
}
|
||||
|
||||
downloadTokensList(event) {
|
||||
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
this.btTokenDl.loading = true;
|
||||
this.tokens.export({filter: { includePersons: true }}).then(this.buildCSV.bind(this));
|
||||
}
|
||||
|
||||
buildCSV(payload) {
|
||||
|
||||
let options = {
|
||||
headers: [
|
||||
'PIC',
|
||||
'Organisation Name',
|
||||
'Granted on',
|
||||
'Scheme',
|
||||
'Entity',
|
||||
'Program',
|
||||
'Status',
|
||||
'Project',
|
||||
'Submission date',
|
||||
'Contact name',
|
||||
'Contact gender',
|
||||
'Contact position',
|
||||
'Contact email',
|
||||
'Contact phone',
|
||||
'SPOC name',
|
||||
'SPOC email',
|
||||
],
|
||||
filename: 'EIC-Bypass',
|
||||
delimiter: ';'
|
||||
}
|
||||
|
||||
let data = [];
|
||||
|
||||
for(let token of payload.tokens) {
|
||||
let row = [
|
||||
token.enterprise.pic,
|
||||
token.enterprise.legalname,
|
||||
token.granted,
|
||||
app.meta.toString('accelerator-tracks', token.track),
|
||||
app.meta.toString('accelerator-tracks', token.domain),
|
||||
token.program || '',
|
||||
token.history[0].status,
|
||||
token.project ? token.project.acronym: '',
|
||||
token.project ? token.project.submissiondate: '',
|
||||
token.shortProposal ? token.shortProposal.contact.lastname + ' ' + token.shortProposal.contact.firstname: '',
|
||||
token.shortProposal ? token.shortProposal.contact.gender: '',
|
||||
token.shortProposal ? token.shortProposal.contact.position: '',
|
||||
token.shortProposal ? token.shortProposal.contact.email: '',
|
||||
token.shortProposal ? token.shortProposal.contact.phone: '',
|
||||
token.spoc ? token.spoc.lastname + ' ' + token.spoc.firstname: '',
|
||||
token.spoc ? token.spoc.email: '',
|
||||
]
|
||||
|
||||
data.push(row);
|
||||
}
|
||||
|
||||
app.helpers.translator.toCSV( data, options );
|
||||
this.btTokenDl.loading = false;
|
||||
}
|
||||
|
||||
async onTokenGrant(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let grantResult = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantDialog',
|
||||
{ title: 'Grant a token' },
|
||||
{ models: { 'company': this.company, 'tokens': this.tokens }, profile: this.profile }
|
||||
)
|
||||
);
|
||||
|
||||
if(grantResult) {
|
||||
ui.growl.append('Token granted', 'success');
|
||||
let ConsumeResult = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantPropaDialog',
|
||||
{ title: 'Activate the coaching for...', subtitle: `${this.company.itemData.legalname} (${this.company.itemData.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
mode: 'edit',
|
||||
profile: this.profile,
|
||||
tokenId: this.company.itemData.token.id,
|
||||
pic: this.company.pic,
|
||||
legalname: this.company.itemData.legalname,
|
||||
wizard: true,
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
this.refreshTokensList();
|
||||
}
|
||||
|
||||
async onTokenRevoke(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenRevokeDialog',
|
||||
{ title: 'Revoke a token for...', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
model: this.company,
|
||||
profile: this.profile,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
track: event.currentTarget.dataset.track,
|
||||
domain: event.currentTarget.dataset.domain,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token revoked', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
|
||||
async onTokenView(event) {
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantProposalDialog',
|
||||
{ title: 'Token granted to ', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
mode: 'read',
|
||||
profile: this.profile,
|
||||
tokenId: event.currentTarget.dataset.id,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
legalname: event.currentTarget.dataset.legalname,
|
||||
wizard: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token consumed', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
|
||||
async onTokenConsume(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassTokenGrantProposalDialog',
|
||||
{ title: 'Activate the coaching for...', subtitle: `${event.currentTarget.dataset.legalname} (${event.currentTarget.dataset.pic})` },
|
||||
{
|
||||
models: { users: this.users, token: this.tokens },
|
||||
mode: 'edit',
|
||||
profile: this.profile,
|
||||
tokenId: event.currentTarget.dataset.id,
|
||||
pic: event.currentTarget.dataset.pic,
|
||||
legalname: event.currentTarget.dataset.legalname,
|
||||
wizard: false,
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Token consumed', 'success');
|
||||
this.refreshTokensList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassPluginDashboard', BypassPluginDashboard);
|
||||
@@ -0,0 +1,30 @@
|
||||
<div>
|
||||
<section>
|
||||
<article eiccard collapsable>
|
||||
<header>
|
||||
<h1>Users Access</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="users-list"></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right"><button eicbutton primary small class="user-add">add user...</button></div>
|
||||
</footer>
|
||||
</article>
|
||||
</section>
|
||||
<section>
|
||||
<article eiccard collapsable class="programmes-article">
|
||||
<header>
|
||||
<h1>Programme Settings</h1>
|
||||
<h2>Programmes managed by your country</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid class="programmes-list" footer="hidden"></div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right"><button class="programme-create" eicbutton primary small>add programme...</button></div>
|
||||
</footer>
|
||||
</article>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,98 @@
|
||||
class BypassPluginManagementContent extends BypassBaseManagementContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
super.DOMContentLoaded(options);
|
||||
Object.assign(this, app.helpers.basicDialogs)
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
|
||||
this.programsArticle = components.find(component => component.el.classList.contains('programmes-article'));
|
||||
|
||||
this.programsGrid = new DataGrid(this.find('.programmes-list'), {
|
||||
headers: [ { label: 'Program'} ],
|
||||
height: '320px',
|
||||
actions: []
|
||||
});
|
||||
|
||||
let programmeBt = this.find('.programme-create');
|
||||
if(this.tokens.hasPrivilege('addProgram')) {
|
||||
programmeBt.addEventListener('click', this.onProgramAdd.bind(this));
|
||||
} else {
|
||||
ui.hide(programmeBt);
|
||||
}
|
||||
|
||||
this.programsGrid.loading = true;
|
||||
this.tokens.getPrograms(this.profile.track, this.profile.domain)
|
||||
.then(this.fillPrograms.bind(this));
|
||||
}
|
||||
|
||||
fillPrograms(payload) {
|
||||
this.programsGrid.clear();
|
||||
this.programsGrid.loading = false;
|
||||
|
||||
for(let item of payload) {
|
||||
let row = this.programsGrid.addRow(0, [ item ]);
|
||||
|
||||
// checking if programme can be deleted
|
||||
if(this.tokens.hasPrivilege('removeProgram')) {
|
||||
let button = ui.create(`<button eicbutton small danger title="delete this programme" data-label="${item}" data-track="${this.profile.track}" data-domain="${this.profile.domain}" >delete</button>`);
|
||||
button.addEventListener('click', this.onProgramDelete.bind(this));
|
||||
row.querySelector('.cell.actions').appendChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onProgramAdd(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/bypass/dialogs/BypassProgramAddDialog',
|
||||
{ title: 'Add a programme' },
|
||||
{ model: this.tokens, profile: this.profile }
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
ui.growl.append('Programme added', 'success');
|
||||
this.programsGrid.loading = true;
|
||||
this.tokens.getPrograms(this.profile.track, this.profile.domain)
|
||||
.then((payload) => {
|
||||
this.fillPrograms(payload);
|
||||
this.programsArticle.expand();
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async onProgramDelete(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
let result = await this.confirmDialog({
|
||||
title: 'Delete this programme',
|
||||
message: `Please be aware that you are about to delete the programme "<strong>${event.target.dataset.label}</strong>"<br>
|
||||
Are you positive ?`,
|
||||
okLabel: 'Delete',
|
||||
okPromise:() => {
|
||||
let payload = {
|
||||
track: event.target.dataset.track,
|
||||
domain: event.target.dataset.domain,
|
||||
program: event.target.dataset.label
|
||||
}
|
||||
// this promise should always return a result (awaited above)
|
||||
// either built here or from the model action.
|
||||
return(this.tokens.removeProgram(payload))
|
||||
}
|
||||
});
|
||||
|
||||
if(result) {
|
||||
this.programsGrid.loading = true;
|
||||
this.tokens.getPrograms(this.profile.track, this.profile.domain)
|
||||
.then(this.fillPrograms.bind(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassPluginManagementContent', BypassPluginManagementContent);
|
||||
@@ -0,0 +1,8 @@
|
||||
<div class="fasttracks ">
|
||||
<section class="program">
|
||||
<input type="hidden" name="track" value="" />
|
||||
<input type="hidden" name="domain" value="" />
|
||||
<label>Programme name</label>
|
||||
<input type="text" class="required" eicinput name="program" value="" />
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,54 @@
|
||||
class BypassProgramAddDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Create programme',
|
||||
onclick: this.add.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'add',
|
||||
disabled: false
|
||||
}
|
||||
]
|
||||
|
||||
constructor(options) { super(options); }
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.model = options.model;
|
||||
this.profile = options.profile;
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
|
||||
this.form = new Form(this.find('.program'));
|
||||
this.find('input[name="track"]').value = this.profile.track;
|
||||
this.find('input[name="domain"]').value = this.profile.domain;
|
||||
}
|
||||
|
||||
add() {
|
||||
if(this.form.validate()) {
|
||||
this.actions.find(o => o.role == 'add').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.model.addProgram(this.form.value)
|
||||
.then( this.onSuccess.bind(this), this.onFailed.bind(this) )
|
||||
}
|
||||
}
|
||||
|
||||
onSuccess() {
|
||||
this.actions.find(o => o.role == 'add').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.commit(true);
|
||||
}
|
||||
|
||||
onFailed() {
|
||||
this.actions.find(o => o.role == 'add').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.abort();
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassProgramAddDialog',BypassProgramAddDialog);
|
||||
@@ -0,0 +1,34 @@
|
||||
<div class="fasttracks ">
|
||||
<section class="cols-2 right top search">
|
||||
<input eicinput type="search" class="required validate-number" data-path="query" hint="The PIC number of a company is at least 8 digit long number" placeholder="Enter a PIC number..." />
|
||||
<button eicbutton primary icon="icon-search" class="search"></button>
|
||||
</section>
|
||||
<section class="result">
|
||||
|
||||
</section>
|
||||
<section class="explanation">
|
||||
<input type="hidden" data-path="pic" value="" />
|
||||
<alert eicalert info>In order to complete your request, please fill in the following</alert>
|
||||
<p><span class="company"></span> should get access to the Accelerator under this specific scheme:</p>
|
||||
<section class="cols-2">
|
||||
<div>
|
||||
<label>Scheme</label>
|
||||
<select eicselect data-path="track" class="required"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Entity</label>
|
||||
<select eicselect data-path="domain" class="required"></select>
|
||||
</div>
|
||||
</section>
|
||||
<section>
|
||||
<textarea name="justification" eictextarea class="required"
|
||||
placeholder="Please explain why you are proposing this company for the EIC Accelerator"></textarea>
|
||||
<div class="programme">
|
||||
<label>Related programme</label>
|
||||
<select data-path="programme" eicselect placeholder="Please select a programme..."></select>
|
||||
</div>
|
||||
<input eiccheckbox type="checkbox" data-path="justification.independentExpert" data-type="ignore" class="required" label="You confirm that you have relied on independent external experts to perform the project review" value="yes" />
|
||||
<input eiccheckbox type="checkbox" data-path="justification.rulesCompliant" data-type="ignore" class="required" label="You confirm that you have complied with all the conditions and requirements set out for the scheme" value="yes" />
|
||||
</section>
|
||||
</section>
|
||||
</div>
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
class BypassTokenGrantDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Grant token',
|
||||
onclick: this.grant.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'grant',
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
|
||||
status = '';
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
this.profile = options.profile;
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
|
||||
this.searchbar = this.find('.search');
|
||||
this.result = this.find('.result');
|
||||
this.query = components.find(component => component.el.dataset.path == 'query');
|
||||
this.query.onQuery = this.onSearch.bind(this);
|
||||
this.trackSelect = components.find(component => component.el.dataset.path == 'track');
|
||||
this.domainSelect = components.find(component => component.el.dataset.path == 'domain');
|
||||
this.programmeSelect = components.find(component => component.el.dataset.path == 'programme');
|
||||
this.searchButton = components.find(component => component.el.classList.contains('search'));
|
||||
|
||||
ui.hide(this.find('.programme'));
|
||||
this.programmeSelect.el.classList.remove('required');
|
||||
|
||||
let tracks = app.meta.toOptions(app.meta.getCollection('accelerator-tracks'), this.profile.track, true);
|
||||
tracks.forEach(item => this.trackSelect.el.append(item));
|
||||
|
||||
if(this.profile.track) {
|
||||
this.fillDomain(this.profile.track, this.profile.domain);
|
||||
//NIKE : Quickpatch because <option selected> is broken by the famous "couldn't care less update" in eicui
|
||||
this.trackSelect.value = this.profile.track
|
||||
this.trackSelect.disabled = true;
|
||||
|
||||
} else {
|
||||
this.trackSelect.change(this.onTrackChange.bind(this));
|
||||
}
|
||||
|
||||
if(this.profile.domain) {
|
||||
//NIKE : Quickpatch because <option selected> is broken by the famous "couldn't care less update" in eicui
|
||||
this.domainSelect.value = this.profile.domain
|
||||
this.domainSelect.disabled = true;
|
||||
// MFA: added this as it seems when using SPOCs, programmes were not handled
|
||||
this.fillProgramme(this.trackSelect.value, this.domainSelect.value);
|
||||
} else {
|
||||
this.domainSelect.change(this.onDomainChange.bind(this));
|
||||
}
|
||||
|
||||
this.form = new Form(this.find('.explanation'));
|
||||
|
||||
ui.hide(this.result)
|
||||
ui.hide(this.form.el)
|
||||
|
||||
this.searchButton.el.addEventListener('click', this.onSearch.bind(this));
|
||||
}
|
||||
|
||||
onTrackChange(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
if(this.trackSelect.value) {
|
||||
this.fillDomain(this.trackSelect.value);
|
||||
} else {
|
||||
this.domainSelect.empty();
|
||||
}
|
||||
|
||||
this.fillProgramme(this.trackSelect.value, this.domainSelect.value);
|
||||
}
|
||||
|
||||
onDomainChange(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.fillProgramme(this.trackSelect.value, this.domainSelect.value);
|
||||
}
|
||||
|
||||
fillDomain(track, domain) {
|
||||
let domains = track ? app.meta.toOptions(app.meta.getItem('accelerator-tracks', track).children, domain, true): [];
|
||||
this.domainSelect.empty();
|
||||
domains.forEach(item => this.domainSelect.el.append(item));
|
||||
}
|
||||
|
||||
fillProgramme(track, domain) {
|
||||
|
||||
ui.hide(this.find('.programme'));
|
||||
this.programmeSelect.empty();
|
||||
this.programmeSelect.el.classList.remove('required');
|
||||
this.programmeSelect.el.dataset.type = 'ignore';
|
||||
|
||||
if(track == 'daTai94ymStyRbQWybH3eDw') {
|
||||
if(domain) {
|
||||
|
||||
ui.lock();
|
||||
this.tokens.getPrograms(track, domain)
|
||||
.then(payload => {
|
||||
ui.unlock();
|
||||
if(payload.length > 0) {
|
||||
ui.show(this.find('.programme'));
|
||||
this.programmeSelect.el.classList.add('required');
|
||||
this.programmeSelect.el.dataset.type = 'text';
|
||||
this.programmeSelect.el.append(ui.create('<option value="">(select a program)</option>'))
|
||||
for(let program of payload) {
|
||||
this.programmeSelect.el.append(ui.create(`<option value="${program}">${program}</option>`))
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onSearch(event) {
|
||||
if(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
this.query.value = this.query.value.trim();
|
||||
if(this.query.value != '') {
|
||||
ui.hide(this.result);
|
||||
this.searchButton.loading = true;
|
||||
this.company.search(this.query.value)
|
||||
.then(this.onResult.bind(this), this.onEmptyResult.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
onResult(result) {
|
||||
ui.show(this.result);
|
||||
this.searchButton.loading = false;
|
||||
let hasToken = result.token && Object.keys(result.token).length > 0;
|
||||
let status = hasToken ? 'danger': 'success';
|
||||
let message = hasToken ?
|
||||
'This company is already benefitting of the EIC Accelerator':
|
||||
'This company is elligible for the EIC Accelerator';
|
||||
|
||||
this.result.innerHTML = `<div class="company" ${status} class="icon-success">
|
||||
<div><span class="legalname" primary>${result.legalname}</span></div>
|
||||
<div class="cols-3 top">
|
||||
<div><label>PIC</label><span class="pic">${result.pic}</span></div>
|
||||
<div><label>Country</label><span class="domain">${result.country || '(unknown)'}</span></div>
|
||||
<div><label>Location</label><span class="domain">${result.city || '(unknown)'}</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div eicalert ${status} success>${message}</div>`;
|
||||
|
||||
this.actions.find(o => o.role == 'grant').button.disabled = true;
|
||||
|
||||
// resolving user interaction
|
||||
if(!hasToken) {
|
||||
//this.find('[data-path="pic"]').value
|
||||
this.legalname = result.legalname;
|
||||
this.status = 'selecting'
|
||||
this.find('span.company').innerHTML = `${result.legalname} (${result.pic})`;
|
||||
this.actions.find(o => o.role == 'grant').button.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
onEmptyResult() {
|
||||
ui.show(this.result);
|
||||
this.searchButton.loading = false;
|
||||
this.result.innerHTML = `<div eicalert danger>Sorry, this PIC is not registered</div>`;
|
||||
this.status = 'selecting';
|
||||
this.actions.find(o => o.role == 'grant').button.disabled = true;
|
||||
}
|
||||
|
||||
onSelect() {
|
||||
this.find('[data-path="pic"]').value = this.query.value;
|
||||
ui.hide(this.searchbar);
|
||||
ui.hide(this.result);
|
||||
ui.show(this.form.el);
|
||||
|
||||
this.status = 'completing';
|
||||
}
|
||||
|
||||
grant() {
|
||||
switch(this.status) {
|
||||
case 'selecting':
|
||||
this.onSelect();
|
||||
break;
|
||||
case 'completing':
|
||||
if(this.form.validate()) {
|
||||
this.actions.find(o => o.role == 'grant').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.company.grant(this.company.itemData.pic, this.form.value)
|
||||
.then( this.onGrantSuccess.bind(this), this.onGrantFailed.bind(this))
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
onGrantSuccess() {
|
||||
this.actions.find(o => o.role == 'grant').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.commit(true);
|
||||
}
|
||||
|
||||
onGrantFailed() {
|
||||
this.actions.find(o => o.role == 'grant').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.abort();
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassTokenGrantDialog',BypassTokenGrantDialog);
|
||||
@@ -0,0 +1,155 @@
|
||||
<style>
|
||||
.fasttracks-propa {
|
||||
padding: var(--eicui-base-spacing-xl);
|
||||
transition: all 0.5s;
|
||||
}
|
||||
.fasttracks-propa.loading {
|
||||
padding: var(--eicui-base-spacing-xl);
|
||||
transition: all 0.5s;
|
||||
overflow: hidden;
|
||||
|
||||
}
|
||||
.fasttracks-propa .spinner {
|
||||
text-align: center;
|
||||
display: none;
|
||||
animation: spin 1s infinite linear;
|
||||
}
|
||||
.fasttracks-propa .content { display: block; }
|
||||
.fasttracks-propa.loading .spinner { display: block; }
|
||||
.fasttracks-propa.loading .content { display: none; }
|
||||
.fasttracks-propa .fields-group { min-height: 490px; }
|
||||
.fasttracks-propa .fields-group.read { min-height: 380px; }
|
||||
</style>
|
||||
<div class="fasttracks-propa loading">
|
||||
<div large class="spinner icon-spinner">
|
||||
</div>
|
||||
<div class="content">
|
||||
<input eicinput type="hidden" name="pic" data-path="organisation" value="" />
|
||||
<div eicalert success class="wizard-message">A token has been successfully granted to <span class="company"></span></div>
|
||||
<p class="instructions">In order to immediately activate the coaching, please fill-in the following informations :</p>
|
||||
<div class="cols-2 left">
|
||||
<div class="tabs-extended vertical">
|
||||
<menu eictab>
|
||||
<li data-target="token">Token</li>
|
||||
<li data-target="enterprise">Company <span xxsmall eicbadge danger data-target="enterprise"></span></li>
|
||||
<li data-target="proposal">Proposal <span xxsmall eicbadge danger data-target="proposal"></span></li>
|
||||
<li data-target="contact">Contact person <span xxsmall eicbadge danger data-target="contact"></span></li>
|
||||
</menu>
|
||||
</div>
|
||||
<div class="sections">
|
||||
<article eiccard class="fields-group" data-target="token">
|
||||
<section class="cols-2">
|
||||
<div>
|
||||
<label>granted</label>
|
||||
<input eicinput name="granted" data-path="token" data-type="ignore" disabled />
|
||||
</div>
|
||||
<div>
|
||||
<label>status</label>
|
||||
<input eicinput name="status" data-path="" data-type="ignore" disabled />
|
||||
</div>
|
||||
<div>
|
||||
<label>Scheme</label>
|
||||
<input eicinput name="track" data-path="token" data-type="ignore" disabled />
|
||||
</div>
|
||||
<div>
|
||||
<label>Domain</label>
|
||||
<input eicinput name="domain" data-path="token" data-type="ignore" disabled />
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="fields-group" data-target="enterprise">
|
||||
<section>
|
||||
<div class="cols-2">
|
||||
<div>
|
||||
<label>PIC</label>
|
||||
<input eicinput name="pic" data-path="token.enterprise" data-type="ignore" disabled />
|
||||
</div>
|
||||
<div>
|
||||
<label>Legal name</label>
|
||||
<input eicinput name="legalname" data-path="token.enterprise" data-type="ignore" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Type of entity</label>
|
||||
<select eicselect name="companyType" data-path="organisation" class="required">
|
||||
<option value=""></option>
|
||||
<option value="sme">SME</option>
|
||||
<option value="midcap">Small mid-cap</option>
|
||||
<option value="person">Natural person</option>
|
||||
</select>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="fields-group" data-target="proposal">
|
||||
<section>
|
||||
<div class="cols-2">
|
||||
<div>
|
||||
<label>Acronym</label>
|
||||
<input eicinput name="acronym" data-path="proposal" data-type="text" maxlen="50" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Submitted</label>
|
||||
<input eicinput name="submissiondate" data-path="token.project" data-type="ignore" disabled />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Full Title</label>
|
||||
<input eicinput name="title" data-path="proposal" data-type="text" maxlen="200" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="proposal" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="fields-group" data-target="contact">
|
||||
<section>
|
||||
<div class="lookup">
|
||||
<label>Please identify the contact person by the EU-Login</label>
|
||||
<div class="cols-2 right center">
|
||||
<input eicinput type="search" name="euLoginId" data-path="contact" class="required" value="" placeholder="Enter member's EU Login ID or email and press enter"/>
|
||||
<button eicbutton icon="icon-search" class="user-search" primary ></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-3">
|
||||
<div>
|
||||
<label>First name</label>
|
||||
<input eicinput disabled name="firstname" data-path="contact" data-type="text" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Last name</label>
|
||||
<input eicinput disabled name="lastname" data-path="contact" data-type="text" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Gender</label>
|
||||
<select eicselect name="gender" data-path="contact" data-type="text" class="required">
|
||||
<option></option>
|
||||
<option value="F">Female</option>
|
||||
<option value="M">Male</option>
|
||||
<option value="I">I prefer not to declare</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2">
|
||||
<div>
|
||||
<label>email</label>
|
||||
<input eicinput disabled type="email" name="email" data-path="contact" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Phone</label>
|
||||
<input eicinput type="tel" name="phone" data-path="contact" data-type="text" maxlen="100" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Position</label>
|
||||
<select eicselect lookup name="position" data-path="contact" data-type="text" class="required"></select>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,220 @@
|
||||
class BypassTokenGrantProposalDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
disabled: false,
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Assign token to this proposal',
|
||||
onclick: this.consume.bind(this),
|
||||
severity: 'primary',
|
||||
disabled: false,
|
||||
role: 'consume'
|
||||
}
|
||||
]
|
||||
|
||||
status = '';
|
||||
mode = 'edit';
|
||||
validationInterval = 1000;
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
console.log('==BypassTokenGrantProposalDialog==>', options)
|
||||
|
||||
this.models = options.models;
|
||||
this.mode = options.mode || this.mode;
|
||||
this.enableWizard = options.wizard;
|
||||
this.tokenId = options.tokenId;
|
||||
this.components = ui.eicfy(this.el);
|
||||
this.find('.fasttracks-propa').classList.add(this.mode)
|
||||
|
||||
this.tabs = new Tab();
|
||||
this.tabs.addTabs(this.findAll('.tabs-extended menu li'), this.findAll('.fields-group'))
|
||||
|
||||
let position = this.find('select[name="position"]');
|
||||
app.meta.toOptions('organisation-functions', null, true).forEach(item => position.append(item));
|
||||
|
||||
this.find('[name="pic"]').value = options.pic;
|
||||
|
||||
this.euid = this.components.find(component => component.el.name == 'euLoginId');
|
||||
this.firstname = this.components.find(component => component.el.name == 'firstname');
|
||||
this.lastname = this.components.find(component => component.el.name == 'lastname');
|
||||
this.email = this.components.find(component => component.el.name == 'email');
|
||||
this.userSearchButton = this.components.find(component => component.el.classList.contains('user-search'));
|
||||
this.cards = this.components.filter(component => component.el.classList.contains('fields-group'))
|
||||
|
||||
this.globalForm = new Form(this.el);
|
||||
this.miniForms = Array.from(this.cards.map(card=>card.el).map(el => new Form(el)))
|
||||
this.euid.onQuery = this.onUserSearch.bind(this)
|
||||
this.userSearchButton.el.addEventListener('click', this.onUserSearch.bind(this))
|
||||
|
||||
}
|
||||
|
||||
DOMContentFocused() {
|
||||
this.actions.forEach(o => o.button.disabled = true);
|
||||
this.models.token.viewToken(this.tokenId)
|
||||
.then(payload => { this.setMode(this.mode, payload) });
|
||||
}
|
||||
|
||||
setMode(mode, payload) {
|
||||
this.find('.fasttracks-propa').classList.remove('loading');
|
||||
// cleanup
|
||||
payload.token.granted = payload.token.granted ? ui.format.dateTime(payload.token.granted): ''
|
||||
payload.token.track = app.meta.toString('accelerator-tracks', payload.token.track)
|
||||
payload.token.domain = app.meta.toString('accelerator-tracks', payload.token.domain)
|
||||
payload.status = payload.token.history[0].status;
|
||||
if(!payload.token.project) payload.token.project = {}
|
||||
|
||||
payload.token.project.submissiondate = payload.token.project.submissiondate ? ui.format.dateTime(payload.token.project.submissiondate): ''
|
||||
|
||||
let fields = this.globalForm.scan();
|
||||
for(let field of fields) {
|
||||
let component = this.el2component(field.el.dataset.path, field.el.name);
|
||||
|
||||
if(component) component.disabled = this.mode == 'read' || field.el.hasAttribute('disabled');
|
||||
|
||||
let item = this.findField(payload, field.el.dataset.path, field.el.name)
|
||||
if(item) {
|
||||
if(component.el.type == 'checkbox'){
|
||||
component.el.checked = item == true;
|
||||
}
|
||||
else
|
||||
component.value = item;
|
||||
}
|
||||
}
|
||||
|
||||
switch(mode) {
|
||||
case 'read':
|
||||
ui.hide(this.find('.wizard-message'));
|
||||
ui.hide(this.find('.lookup'));
|
||||
ui.hide(this.find('.instructions'));
|
||||
ui.hide(this.actions.find(o => o.role == 'consume').button.el);
|
||||
this.actions.find(o => o.role == 'cancel').label = 'Close';
|
||||
|
||||
break;
|
||||
case 'edit':
|
||||
if(!this.enableWizard)
|
||||
ui.hide(this.find('.wizard-message'))
|
||||
else
|
||||
this.actions.find(o => o.role == 'cancel').label = 'Skip';
|
||||
|
||||
this.tabs.selectByIndex(1);
|
||||
|
||||
this.validationCheck = setInterval(this.validateTabs.bind(this), this.validationInterval);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
this.actions.forEach(o => o.button.disabled = false);
|
||||
|
||||
|
||||
}
|
||||
|
||||
el2component(path, name) {
|
||||
let item = this.components
|
||||
.find(o =>
|
||||
o.el.name == name &&
|
||||
( !path || (path && o.el.dataset.path == path))
|
||||
)
|
||||
return item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a data field value based on path an name
|
||||
* @param {*} path
|
||||
* @param {*} name
|
||||
* @returns
|
||||
*/
|
||||
findField(payload, path, name) {
|
||||
let levels = path && path != '' && path !== undefined ? path.split('.'): [];
|
||||
let tree = payload;
|
||||
|
||||
for(let level of levels)
|
||||
if(tree.hasOwnProperty(level)) tree = tree[level];
|
||||
if(tree.hasOwnProperty(name)) return tree[name];
|
||||
}
|
||||
|
||||
onUserSearch(event) {
|
||||
this.userSearchButton.loading = true;
|
||||
this.models.users.search(this.euid.value).then(
|
||||
(userList => {
|
||||
this.userSearchButton.loading = false;
|
||||
if(userList.length==0) return
|
||||
// TODO better manage if several (take most complete?)
|
||||
|
||||
let user = userList[0];
|
||||
this.euid.value = user.uid;
|
||||
this.firstname.value = user.given_name;
|
||||
this.lastname.value = user.family_name;
|
||||
this.email.value = user.email;
|
||||
}),
|
||||
(err) => {
|
||||
this.userSearchButton.loading = false;
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
validateTabs() {
|
||||
let valid = true;
|
||||
for(let form of this.miniForms) {
|
||||
|
||||
let report = form.validate(true)
|
||||
let badge = this.components.find(item => item.el.hasAttribute('eicbadge') && item.el.dataset.target == form.el.dataset.target)
|
||||
if(badge) {
|
||||
badge.value = report.issues;
|
||||
}
|
||||
valid = valid && report.valid;
|
||||
}
|
||||
|
||||
this.actions.find(o => o.role == 'consume').button.disabled = !valid;
|
||||
}
|
||||
|
||||
consume() {
|
||||
if(this.globalForm.validate()) {
|
||||
this.actions.find(o => o.role == 'consume').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.models.token.consumeToken(this.globalForm.value)
|
||||
.then( this.onConsumeSuccess.bind(this), this.onConsumeFailed.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
onConsumeSuccess() {
|
||||
this.actions.find(o => o.role == 'consume').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.commit(true);
|
||||
}
|
||||
|
||||
onConsumeFailed() {
|
||||
this.actions.find(o => o.role == 'consume').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.abort();
|
||||
}
|
||||
|
||||
DOMContentResized() {
|
||||
super.DOMContentResized();
|
||||
|
||||
let device = this.parentContainer.getAttribute('device');
|
||||
//let tabs = this.find('.tabs-extended');
|
||||
|
||||
switch(device) {
|
||||
case 'desktop':
|
||||
this.tabs.triggers.forEach(item => ui.show(item));
|
||||
let current = this.tabs.getSelected();
|
||||
let target = current.dataset.target;
|
||||
this.findAll('article.fields-group').forEach(content => { if(content.dataset.target != target) ui.hide(content) });
|
||||
break;
|
||||
default:
|
||||
this.tabs.triggers.forEach(item => ui.hide(item));
|
||||
|
||||
this.findAll('article.fields-group').forEach(content => { ui.show(content) })
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassTokenGrantProposalDialog',BypassTokenGrantProposalDialog);
|
||||
@@ -0,0 +1,14 @@
|
||||
<div class="fasttracks ">
|
||||
<section class="explanation">
|
||||
<alert eicalert danger>Please be aware that you are about to revoke an awarded token.</alert>
|
||||
</section>
|
||||
<section class="agreement">
|
||||
<input type="hidden" name="track" value="" />
|
||||
<input type="hidden" name="domain" value="" />
|
||||
<textarea data-path="justification" eictextarea maxlen="200" class="required"
|
||||
hint="No more than 200 characters please."
|
||||
placeholder="Please explain why you are removing this company for the EIC Accelerator"></textarea>
|
||||
|
||||
<input eiccheckbox type="checkbox" name="agreed" data-type="ignore" value="yes" label="I understand" />
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,58 @@
|
||||
class BypassTokenRevokeDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Revoke token',
|
||||
onclick: this.revoke.bind(this),
|
||||
severity: 'danger',
|
||||
role: 'revoke',
|
||||
disabled: true
|
||||
}
|
||||
]
|
||||
|
||||
constructor(options) { super(options); }
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.model = options.model;
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
|
||||
this.form = new Form(this.find('.agreement'));
|
||||
this.pic = options.pic;
|
||||
this.find('input[name="track"]').value = options.track;
|
||||
this.find('input[name="domain"]').value = options.domain;
|
||||
this.optin = this.find('input[name="agreed"]');
|
||||
this.optin.addEventListener('change',this.onAgreement.bind(this));
|
||||
}
|
||||
|
||||
onAgreement() { this.actions.find(o => o.role == 'revoke').button.disabled = !this.optin.checked; }
|
||||
|
||||
revoke() {
|
||||
if(this.form.validate()) {
|
||||
this.actions.find(o => o.role == 'revoke').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.model.revoke(this.pic, this.form.value)
|
||||
.then( this.commit.bind(this), this.abort.bind(this) )
|
||||
}
|
||||
}
|
||||
|
||||
onRevokeSuccess() {
|
||||
this.actions.find(o => o.role == 'revoke').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.commit(true);
|
||||
}
|
||||
|
||||
onRevokeFailed() {
|
||||
this.actions.find(o => o.role == 'revoke').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.abort();
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassTokenRevokeDialog',BypassTokenRevokeDialog);
|
||||
@@ -0,0 +1,9 @@
|
||||
<div class="fasttracks settings">
|
||||
<section class="explanation">
|
||||
<alert eicalert danger>Please be aware that your changes will take place immediately.</alert>
|
||||
</section>
|
||||
<section class="domains">
|
||||
<input type="hidden" data-path="track" value="${track}" />
|
||||
<div eicdatagrid footer="hidden"></div>
|
||||
</section>
|
||||
</div>
|
||||
@@ -0,0 +1,80 @@
|
||||
class BypassTrackSetupDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Save',
|
||||
onclick: this.save.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'save'
|
||||
}
|
||||
]
|
||||
|
||||
constructor(options) { super(options); }
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.model = options.model;
|
||||
this.track = options.track;
|
||||
this.domains = options.domains;
|
||||
|
||||
|
||||
|
||||
this.grid = new DataGrid(this.find('.domains [eicdatagrid]'), {
|
||||
headers: [ { label: 'Domain'}, { label: 'Tokens', type: 'markup' }, { label: 'Min.' } ],
|
||||
height: '320px'
|
||||
});
|
||||
|
||||
this.grid.clear();
|
||||
this.grid.loading = true;
|
||||
this.model.getSettings(this.track)
|
||||
.then(this.fill.bind(this));
|
||||
}
|
||||
|
||||
fill(payload) {
|
||||
this.grid.clear();
|
||||
this.grid.loading = false;
|
||||
let index = 0;
|
||||
for(let item of payload.domains) {
|
||||
this.grid.addRow(item.domain, [
|
||||
app.meta.toString('accelerator-tracks', item.domain),
|
||||
`<input type="hidden" data-path="domains[${index}].domain" value="${item.domain}" />
|
||||
<input eicinput type="number" min="${item.tokens - item.remaining}" data-path="domains[${index}].tokens" value="${item.tokens}" />`,
|
||||
item.tokens - item.remaining
|
||||
]);
|
||||
index++;
|
||||
}
|
||||
this.grid.sort(1, 'asc');
|
||||
this.form = new Form(this.find('.domains'));
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
}
|
||||
|
||||
save() {
|
||||
if(this.form.validate()) {
|
||||
this.actions.find(o => o.role == 'save').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.model.setSettings(this.form.value)
|
||||
.then(this.onSuccess.bind(this))
|
||||
.catch(this.onFailed.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
onSuccess() {
|
||||
this.actions.find(o => o.role == 'save').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.commit(true);
|
||||
}
|
||||
|
||||
onFailed() {
|
||||
this.actions.find(o => o.role == 'save').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
//this.abort();
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('BypassTrackSetupDialog',BypassTrackSetupDialog);
|
||||
@@ -0,0 +1,51 @@
|
||||
<div class="user-dialog">
|
||||
<div>
|
||||
<label>EU login or email</label>
|
||||
<div class="cols-2 right top">
|
||||
<input eicinput type="search" data-path="euid" class="required" value="" />
|
||||
<button eicbutton icon="icon-search" class="user-search" primary ></button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Email</label>
|
||||
<input eicinput disabled value="" class="required email" data-path="email"/>
|
||||
</div>
|
||||
<div class="cols-2">
|
||||
<div>
|
||||
<label>Firstname</label>
|
||||
<input eicinput disabled value="" class="required" data-path="firstname"/>
|
||||
</div>
|
||||
<div>
|
||||
<label>Lastname</label>
|
||||
<input eicinput disabled value="" class="required" data-path="lastname"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Gender</label>
|
||||
<select eicselect class="required" data-path="gender">
|
||||
<option></option>
|
||||
<option value="M">Male</option>
|
||||
<option value="F">Female</option>
|
||||
<option value="I">Rather not say</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div hidden class="roleSelector">
|
||||
<label>Role</label>
|
||||
<select eicselect class="required" class="required" data-path="role">
|
||||
<option></option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="cols-3">
|
||||
<div hidden class="trackSelector">
|
||||
<label>Track</label>
|
||||
<select eicselect data-path="track"></select>
|
||||
</div>
|
||||
<div hidden class="domainSelector">
|
||||
<label>Domain</label>
|
||||
<select eicselect data-path="domain"></select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@@ -0,0 +1,252 @@
|
||||
class BypassUserProfileDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Add user',
|
||||
onclick: this.create.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'add',
|
||||
disabled: true
|
||||
},
|
||||
]
|
||||
|
||||
async DOMContentLoaded(options) {
|
||||
this.mode = options.mode ? options.mode: 'create';
|
||||
this.model = options.model;
|
||||
this.profile = options.profile;
|
||||
|
||||
let components = ui.eicfy(this.el);
|
||||
|
||||
this.euid = components.find(component => component.el.dataset.path == 'euid');
|
||||
this.firstname = components.find(component => component.el.dataset.path == 'firstname');
|
||||
this.lastname = components.find(component => component.el.dataset.path == 'lastname');
|
||||
this.email = components.find(component => component.el.dataset.path == 'email');
|
||||
|
||||
this.genderSelect = components.find(component => component.el.dataset.path == 'gender');
|
||||
this.roleSelect = components.find(component => component.el.dataset.path == 'role');
|
||||
this.roleSelect.block = this.find('.roleSelector')
|
||||
this.trackSelect = components.find(component => component.el.dataset.path == 'track');
|
||||
this.trackSelect.block = this.find('.trackSelector')
|
||||
this.domainSelect = components.find(component => component.el.dataset.path == 'domain');
|
||||
this.domainSelect.block = this.find('.domainSelector');
|
||||
|
||||
this.userSearcButton = components.find(component => component.el.classList.contains('user-search'));
|
||||
|
||||
this.genderSelect.change(this.changeRoleAvailable.bind(this));
|
||||
|
||||
this.roleSelect.change(this.onRoleChange.bind(this));
|
||||
this.roleSelect.disabled = true;
|
||||
this.trackSelect.change(this.onTrackChange.bind(this));
|
||||
this.trackSelect.disabled = true;
|
||||
this.domainSelect.change(this.onDomainChange.bind(this));
|
||||
this.domainSelect.disabled = true;
|
||||
|
||||
this.form = new Form(this.find('.user-dialog'));
|
||||
|
||||
let tracks = app.meta.toOptions(app.meta.getCollection('accelerator-tracks'), null, true);
|
||||
tracks.forEach(item => this.trackSelect.el.append(item));
|
||||
|
||||
this.roleDelegations = {}
|
||||
this.roleDelegations = await app.Assets.loadJson({name:'global/bypass/roleDelegations.json'})
|
||||
|
||||
this.userSearcButton.el.addEventListener('click', this.onUserSearch.bind(this));
|
||||
}
|
||||
|
||||
changeRoleAvailable() {
|
||||
if( this.form.validateField(this.euid).valid &&
|
||||
this.form.validateField(this.firstname).valid &&
|
||||
this.form.validateField(this.lastname).valid &&
|
||||
this.form.validateField(this.email).valid &&
|
||||
this.form.validateField(this.genderSelect).valid
|
||||
) {
|
||||
this.roleSelect.disabled = false;
|
||||
this.changeAddAvailable();
|
||||
let roles4select = this.getDelegationRules().map(x=>x.role);
|
||||
let options = app.meta.toOptions(roles4select.map(x=>({id:x, label:x})), '', true)
|
||||
this.roleSelect.empty();
|
||||
options.forEach(item => this.roleSelect.el.append(item));
|
||||
ui.show(this.roleSelect.block);
|
||||
} else {
|
||||
this.hideSelect(this.roleSelect);
|
||||
}
|
||||
}
|
||||
|
||||
changeAddAvailable() {
|
||||
if(this.form.validate() && this.validateDelegation()) {
|
||||
this.actions.find(o => o.role == 'add').button.disabled = false;
|
||||
} else {
|
||||
this.actions.find(o => o.role == 'add').button.disabled = true;
|
||||
}
|
||||
}
|
||||
|
||||
getDelegationRules() {
|
||||
/* In delegations, which of my roles shall I use as master... (no role hierarchy)
|
||||
For now priority is top-down in the roleDelegation masters order.
|
||||
Bad, not 100% reliable, but in our case, there should be no conflicts (mutually exclusive in delegations)
|
||||
*/
|
||||
let delegationsRules = [];
|
||||
for(let roleDeleg of this.roleDelegations){
|
||||
if(app.User.hasRole(roleDeleg.master)) {
|
||||
delegationsRules = roleDeleg.surrogates;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return(delegationsRules)
|
||||
}
|
||||
|
||||
hideSelect(sel) {
|
||||
sel.disabled = true;
|
||||
sel.value = '';
|
||||
ui.hide(sel.block);
|
||||
}
|
||||
|
||||
ruleSelect(rule, sel, inheritValue) {
|
||||
if(rule=='inherit') { // Forced to my own value
|
||||
sel.disabled = true;
|
||||
sel.value = inheritValue;
|
||||
ui.show(sel.block);
|
||||
} else if(rule=='set') { // Can choose
|
||||
sel.disabled = false;
|
||||
ui.show(sel.block);
|
||||
} else if(rule=='ignore'){ // Not applicable
|
||||
this.hideSelect(sel);
|
||||
} else { // Forced to fixed value
|
||||
sel.disabled = true;
|
||||
sel.value = rule;
|
||||
ui.show(sel.block);
|
||||
}
|
||||
}
|
||||
|
||||
validateDelegation(){
|
||||
let rules = this.getDelegationRules().find(x => x.role==this.roleSelect.value);
|
||||
if(!rules) return(false)
|
||||
|
||||
const validateSel = (rule, sel, inheritValue) => {
|
||||
let ok = false;
|
||||
if(rule=='inherit') ok = (sel.value == inheritValue)
|
||||
else if(rule=='set') ok = Boolean(sel.value)
|
||||
else if(rule=='ignore') ok = (true)
|
||||
else ok = (sel.value == rule)
|
||||
if(ok) sel.el.nextSibling.classList.remove('validation-failed');
|
||||
else sel.el.nextSibling.classList.add('validation-failed');
|
||||
return(ok)
|
||||
}
|
||||
return( validateSel(rules.track, this.trackSelect, this.profile.track) &&
|
||||
validateSel(rules.domain, this.domainSelect, this.profile.domain)
|
||||
)
|
||||
}
|
||||
|
||||
onRoleChange(event) {
|
||||
if(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
|
||||
// pas de role: pas de track, pas de track : pas de domaine, pas de domaine: pas de palais, etc
|
||||
if(!this.roleSelect.value){
|
||||
this.hideSelect(this.trackSelect);
|
||||
this.hideSelect(this.domainSelect);
|
||||
return
|
||||
}
|
||||
|
||||
let rules = this.getDelegationRules().find(x => x.role==this.roleSelect.value);
|
||||
|
||||
if(!rules) {
|
||||
console.warn('Delegation not found for role '+this.roleSelect.value);
|
||||
this.hideSelect(this.trackSelect);
|
||||
this.hideSelect(this.domainSelect);
|
||||
return
|
||||
}
|
||||
|
||||
this.ruleSelect(rules.track, this.trackSelect, this.profile.track);
|
||||
this.onTrackChange();
|
||||
this.ruleSelect(rules.domain, this.domainSelect, this.profile.domain);
|
||||
|
||||
this.changeAddAvailable();
|
||||
}
|
||||
|
||||
|
||||
onUserSearch(event) {
|
||||
this.userSearcButton.loading = true;
|
||||
this.model.search(this.euid.value).then(
|
||||
(userList => {
|
||||
this.userSearcButton.loading = false;
|
||||
if(userList.length==0) return
|
||||
// TODO better manage if several (take most complete?)
|
||||
|
||||
let user = userList[0];
|
||||
this.euid.value = user.uid;
|
||||
this.firstname.value = user.given_name;
|
||||
this.lastname.value = user.family_name;
|
||||
this.email.value = user.email;
|
||||
this.changeRoleAvailable();
|
||||
}),
|
||||
(err) => {
|
||||
this.userSearcButton.loading = false;
|
||||
}
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
onTrackChange(event) {
|
||||
if(event){
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
}
|
||||
if(this.trackSelect.value) {
|
||||
this.fillDomain(this.trackSelect.value);
|
||||
} else {
|
||||
this.domainSelect.empty();
|
||||
this.hideSelect(this.domainSelect);
|
||||
}
|
||||
|
||||
this.changeAddAvailable();
|
||||
}
|
||||
|
||||
onDomainChange(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.changeAddAvailable();
|
||||
}
|
||||
|
||||
fillDomain(track, domain) {
|
||||
let domains = track ? app.meta.toOptions(app.meta.getItem('accelerator-tracks', track).children, domain, true): [];
|
||||
this.domainSelect.empty();
|
||||
domains.forEach(item => this.domainSelect.el.append(item));
|
||||
}
|
||||
|
||||
DOMContentFocused() {
|
||||
|
||||
}
|
||||
|
||||
create(event) {
|
||||
if(this.form.validate() && this.validateDelegation()) {
|
||||
this.actions.find(o => o.role == 'add').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.model.create(this.form.value)
|
||||
.then( this.onSuccess.bind(this), this.onFailed.bind(this))
|
||||
}
|
||||
}
|
||||
|
||||
onSuccess() {
|
||||
this.actions.find(o => o.role == 'add').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.commit(true);
|
||||
}
|
||||
|
||||
onFailed() {
|
||||
this.actions.find(o => o.role == 'add').button.loading = false;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = false;
|
||||
this.abort();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('BypassUserProfileDialog', BypassUserProfileDialog);
|
||||
@@ -0,0 +1,61 @@
|
||||
<style>
|
||||
.expert-dashboard > header { background-color: rgb(8, 64, 143); }
|
||||
.expert-dashboard .todo { min-width: 240px; }
|
||||
.expert-dashboard .projects-list {
|
||||
display: grid;
|
||||
grid-gap: var(--eicui-base-spacing-m);
|
||||
grid-template-columns: repeat(auto-fill, 240px);
|
||||
}
|
||||
.expert-dashboard .projects-list article { cursor: pointer; }
|
||||
.expert-dashboard .projects-list article:hover {
|
||||
background-color: var(--eicui-base-color-info-10);
|
||||
}
|
||||
.projects-list section { text-align: center; }
|
||||
</style>
|
||||
<article eiccard media class="expert-dashboard">
|
||||
<header>
|
||||
<h1>Expert dashboard</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>General information</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<ul nonbulleted>
|
||||
<li>
|
||||
<label>...</label>
|
||||
<span>...</span>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard danger class="todo">
|
||||
<header>
|
||||
<h1>TODOs</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<ul bulleted>
|
||||
<li>2 invitations</li>
|
||||
<li>1 report pending</li>
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>Assigned projects</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<ul class="projects-list" nonbulleted></ul>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,110 @@
|
||||
/**
|
||||
*/
|
||||
|
||||
class ProjectFundingExpertDashboardView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.list = this.find('.projects-list');
|
||||
/*
|
||||
this.list = new DataGrid(this.find('[eicdatagrid]'), {
|
||||
headers: [
|
||||
{label: 'number', filter: 'text', sortable: true},
|
||||
{label: 'acronym', filter: 'text', sortable: true},
|
||||
{label: 'organisation', filter: 'text', sortable: true},
|
||||
{label: 'phase', filter: 'list', sortable: true},
|
||||
{label: 'status', filter: 'list', sortable: true},
|
||||
{label: 'updated', type: 'dateTime', filter: 'list', sortable: true}
|
||||
]
|
||||
});
|
||||
this.list.onRowClick = this.onProjectSelect.bind(this);
|
||||
this.list.loading = true;
|
||||
*/
|
||||
app.events.channel.addEventListener('project_node_updated', this.onProjectUpdated.bind(this))
|
||||
|
||||
this.projects.get('techdd')
|
||||
.then(this.refreshProjects.bind(this));
|
||||
}
|
||||
|
||||
refreshProjects(data) {
|
||||
/*
|
||||
this.list.loading = false;
|
||||
this.list.clear();
|
||||
|
||||
for(let item of data) {
|
||||
for(let phase of item.phases) {
|
||||
let id = JSON.stringify({
|
||||
number: item.project.number,
|
||||
node: phase.type,
|
||||
nodeId: phase.id
|
||||
})
|
||||
this.list.addRow(id, [
|
||||
item.project.number,
|
||||
item.project.acronym,
|
||||
item.organisation.name,
|
||||
phase.type,
|
||||
phase.statusHistory[0].value,
|
||||
phase.statusHistory[0].dateTime
|
||||
])
|
||||
}
|
||||
*/
|
||||
this.list.innerHTML = '';
|
||||
|
||||
for(let item of data) {
|
||||
|
||||
for(let phase of item.phases) {
|
||||
|
||||
if(phase.type != 'techdd') continue;
|
||||
let id = JSON.stringify({
|
||||
number: item.project.number,
|
||||
node: phase.type,
|
||||
nodeId: phase.id
|
||||
})
|
||||
|
||||
let version = 'Regular'
|
||||
|
||||
switch(phase.version) {
|
||||
case 'techdd-report-v1-enhanced':
|
||||
version = 'Enhanced'
|
||||
break;
|
||||
case 'techdd-report-v1-full':
|
||||
version = 'Full'
|
||||
break;
|
||||
}
|
||||
|
||||
let card = ui.create(`<article eiccard>
|
||||
<header>
|
||||
<h1>${item.project.acronym}</h1>
|
||||
<h2>${item.project.number}</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<span eicchip info>${version}</span>
|
||||
<span eicchip primary>${app.meta.getItem('icmp-techdd-status', phase.statusHistory[0].value).label}</span>
|
||||
</div>
|
||||
<p small>${ui.format.dateTime(phase.statusHistory[0].dateTime)}</p>
|
||||
</section>
|
||||
</article>`);
|
||||
card.dataset.id = id;
|
||||
card.addEventListener('click', this.onProjectSelect)
|
||||
this.list.append(card)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onProjectSelect(event) {
|
||||
//let id = JSON.parse(row.dataset.id)
|
||||
let id = JSON.parse(event.currentTarget.dataset.id)
|
||||
app.Router.route(`/icmp/projects/${id.number}/${id.node}/${id.nodeId}`, { });
|
||||
}
|
||||
|
||||
onProjectUpdated() {
|
||||
this.projects.get('techdd')
|
||||
.then(this.refreshProjects.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('ProjectFundingExpertDashboardView',ProjectFundingExpertDashboardView);
|
||||
@@ -0,0 +1,48 @@
|
||||
<style>
|
||||
.fundings-dashboard {}
|
||||
.fundings-dashboard > header {
|
||||
background-color: rgb(8, 64, 143);
|
||||
}
|
||||
.fundings-dashboard .projects-nav {
|
||||
min-height: 120px;
|
||||
height: 210px;
|
||||
}
|
||||
.fundings-dashboard .activities {
|
||||
min-width: 240px;
|
||||
}
|
||||
.fundings-dashboard .projects-list .grid-row {
|
||||
grid-template-columns: 120px auto 1fr 120px 120px 1fr 120px;
|
||||
}
|
||||
.fundings-dashboard .projects-list .dataset .cell:nth-child(2) { text-align: right; }
|
||||
.fundings-dashboard .projects-list .dataset .cell:nth-child(5),
|
||||
.fundings-dashboard .projects-list .dataset .cell:nth-child(6),
|
||||
.fundings-dashboard .projects-list .dataset .cell:nth-child(7) { text-align: center; }
|
||||
|
||||
</style>
|
||||
<article eiccard media class="fundings-dashboard">
|
||||
<header>
|
||||
<h1>ICMP</h1>
|
||||
<h2 >Investment Coordination and Monitoring Platform</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicnodemap class="projects-nav"></div>
|
||||
<h2>Technical Due Diligences</h2>
|
||||
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<article eiccard danger class="activities">
|
||||
<header>
|
||||
<h1>Checks</h1>
|
||||
</header>
|
||||
<section>
|
||||
<ul bulleted></ul>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
<div>
|
||||
<div eicdatagrid class="projects-list"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
*/
|
||||
|
||||
class ProjectFundingPODashboardView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.list = new DataGrid(this.find('.projects-list'), {
|
||||
height: '39vh',
|
||||
headers: [
|
||||
{label: 'number', filter: 'text', sortable: true},
|
||||
{label: 'Acronym', filter: 'text', sortable: true},
|
||||
{label: 'Organisation', filter: 'text', sortable: true},
|
||||
{label: 'Type', filter: 'list', sortable: true},
|
||||
{label: 'Status', filter: 'list', sortable: true},
|
||||
{label: 'Referrer', type: 'markup', filter: 'text', sortable: false},
|
||||
{label: 'Last update', type: 'date', filter: 'text', sortable: true},
|
||||
]
|
||||
});
|
||||
this.list.onRowClick = this.onProjectSelect.bind(this);
|
||||
|
||||
this.nav = new NodeMap(this.find('[eicnodemap]'), {
|
||||
"orientation": "linear",
|
||||
"resizable": true,
|
||||
"allowDrag": true,
|
||||
"entity": {
|
||||
"width": 160,
|
||||
"height": 80,
|
||||
"gap": 60
|
||||
}
|
||||
});
|
||||
this.nav.click = this.onNavChange.bind(this);
|
||||
|
||||
app.events.channel.addEventListener('project_node_updated', this.onProjectUpdated.bind(this))
|
||||
app.Assets.loadJson({name:'workflows/wf-projects-funding.json'})
|
||||
.then(this.setupWorflow.bind(this));
|
||||
|
||||
this.list.loading = true;
|
||||
|
||||
this.projects.get('techdd')
|
||||
.then(this.refreshProjects.bind(this));
|
||||
}
|
||||
|
||||
setupWorflow(data) { this.nav.data = data; }
|
||||
|
||||
refreshProjects(data) {
|
||||
this.list.loading = false;
|
||||
this.list.clear();
|
||||
|
||||
this.todos = [];
|
||||
|
||||
let metrics = {
|
||||
draft: 0,
|
||||
approval: 0,
|
||||
rejected: 0
|
||||
}
|
||||
|
||||
let phases = {}
|
||||
|
||||
for(let item of data) {
|
||||
for(let phase of item.phases) {
|
||||
let id = JSON.stringify({
|
||||
number: item.project.number,
|
||||
node: phase.type,
|
||||
nodeId: phase.id
|
||||
})
|
||||
|
||||
let status = '';
|
||||
let label = app.meta.getItem('icmp-techdd-status', phase.statusHistory[0].value).label;
|
||||
let severity = '';
|
||||
let version = 'Regular'
|
||||
|
||||
switch(phase.version) {
|
||||
case 'techdd-report-v1-enhanced':
|
||||
version = 'Enhanced'
|
||||
break;
|
||||
case 'techdd-report-v1-full':
|
||||
version = 'Full'
|
||||
break;
|
||||
}
|
||||
|
||||
switch(phase.statusHistory[0].value) {
|
||||
case 'created':
|
||||
case 'updated':
|
||||
status = 'draft';
|
||||
severity = 'info'
|
||||
break;
|
||||
case 'submitted':
|
||||
case 'submitting':
|
||||
status = 'approval';
|
||||
severity = 'accent'
|
||||
break;
|
||||
default:
|
||||
status = phase.statusHistory[0].value;
|
||||
severity = 'danger'
|
||||
}
|
||||
|
||||
let contributors = [];
|
||||
for(let contributor of item.project.contributors) {
|
||||
let fullname = contributor.information.name.firstName + ' ' + contributor.information.name.lastName;
|
||||
let component = new UserIcon(null, {uuid: contributor.euLoginId, fullname: fullname, showStatus: true})
|
||||
contributors.push(`<span style="font-size:0">${fullname}</span>` + component.el.outerHTML)
|
||||
}
|
||||
|
||||
let row = this.list.addRow(id, [
|
||||
item.project.number,
|
||||
item.project.acronym,
|
||||
item.organisation.name,
|
||||
version,
|
||||
`<span eicchip small ${severity}>${label}</span>`,
|
||||
contributors.join(''),
|
||||
phase.statusHistory[0].dateTime
|
||||
])
|
||||
|
||||
if(item.project.contributor) {
|
||||
row.setAttribute('accent', '')
|
||||
}
|
||||
|
||||
phases[phase.type] = phases[phase.type] ? phases[phase.type] + 1: 1;
|
||||
|
||||
metrics[status]++;
|
||||
}
|
||||
}
|
||||
|
||||
if(metrics.approval > 0) {
|
||||
this.todos.push({
|
||||
scope: 'projects',
|
||||
message: `${metrics.approval} report${metrics.approval > 1 ? 's':''} requesting review`
|
||||
})
|
||||
}
|
||||
if(metrics.draft > 0) {
|
||||
this.todos.push({
|
||||
scope: 'projects',
|
||||
message: `${metrics.draft} report${metrics.draft > 1 ? 's':''} being drafted`
|
||||
})
|
||||
}
|
||||
if(metrics.rejected > 0) {
|
||||
this.todos.push({
|
||||
scope: 'projects',
|
||||
message: `${metrics.rejected} project${metrics.rejected > 1 ? 's':''} requesting reopening`
|
||||
})
|
||||
}
|
||||
|
||||
this.refreshMetrics(phases);
|
||||
this.refreshTodos();
|
||||
}
|
||||
|
||||
refreshMetrics(data) {
|
||||
for(let id in data) {
|
||||
let node = this.nav.entities.find(o => o.id == id)
|
||||
node.badge = data[id];
|
||||
}
|
||||
}
|
||||
|
||||
refreshTodos() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
onNavChange(node) {
|
||||
switch(node.id) {
|
||||
case 'techdd':
|
||||
break;
|
||||
default:
|
||||
ui.growl.append('You don\'t have access to this resource', 'danger', 900)
|
||||
}
|
||||
}
|
||||
|
||||
onProjectSelect(row) {
|
||||
let id = JSON.parse(row.dataset.id)
|
||||
app.Router.route(`/icmp/projects/${id.number}/${id.node}/${id.nodeId}`, { });
|
||||
}
|
||||
|
||||
onProjectUpdated() {
|
||||
this.projects.get('techdd')
|
||||
.then(this.refreshProjects.bind(this));
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('ProjectFundingPODashboardView',ProjectFundingPODashboardView);
|
||||
@@ -0,0 +1,102 @@
|
||||
<style>
|
||||
.project-funding-sheet > header {
|
||||
background-color: black;
|
||||
}
|
||||
.project-funding-sheet > header h2 {
|
||||
white-space: normal !important;
|
||||
line-height: 120% !important;
|
||||
}
|
||||
.project-funding-sheet .funding-nav {
|
||||
min-height: 120px;
|
||||
height: 120px;
|
||||
}
|
||||
.project-funding-sheet .side-info {
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<article eiccard media class="project-funding-sheet">
|
||||
<header>
|
||||
<h1 class="project-acronym">...</h1>
|
||||
<h2 class="project-title">...</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="cols-2 left">
|
||||
<div class="side-info">
|
||||
<article eiccard collapsable class="project-info">
|
||||
<header>
|
||||
<h1>Project information</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="project-number">
|
||||
<label xsmall>Number</label><span></span>
|
||||
</div>
|
||||
<div class="project-instrument">
|
||||
<label xsmall>Instrument</label><span></span>
|
||||
</div>
|
||||
<div class="project-cutoff">
|
||||
<label xsmall>Cutoff</label><span></span>
|
||||
</div>
|
||||
<div class="project-funding-type">
|
||||
<label xsmall>Funding type</label><span></span>
|
||||
</div>
|
||||
<div class="project-signature">
|
||||
<label xsmall>Signature date</label><span></span>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard collapsable collapsed class="org-info">
|
||||
<header>
|
||||
<h1>Organisation</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="org-name">
|
||||
<label xsmall>Name</label><b><span></span></b>
|
||||
</div>
|
||||
<div class="org-pic">
|
||||
<label xsmall>PIC</label><span></span>
|
||||
</div>
|
||||
<div class="org-location">
|
||||
<label xsmall>Address</label>
|
||||
<span class="org-street"></span></br>
|
||||
<span class="org-postcode"></span> <span class="org-city"></span></br>
|
||||
<span class="org-country"></span>
|
||||
</div>
|
||||
<div class="org-website">
|
||||
<label xsmall>Website</label><span></span>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
<div>
|
||||
<div class="cols-2 right">
|
||||
<div eicnodemap class="funding-nav"></div>
|
||||
<div eicdropdown>
|
||||
<button eicbutton small rounded icon="icon-more" ></button>
|
||||
<menu eicmenu>
|
||||
<li menuitem>
|
||||
<a href="#" title="Project documents" data-target="docs" class="button aggregation">
|
||||
<i class="icon-folder-open"></i><label small>Project documents</label>
|
||||
</a>
|
||||
</li>
|
||||
<li menuitem>
|
||||
<a href="#" title="Project contributors" data-target="team" class="button aggregation">
|
||||
<i class="icon-users"></i><label small>People involved</label>
|
||||
</a>
|
||||
</li>
|
||||
<li menuitem>
|
||||
<a href="#" title="Project history" data-target="history" class="button aggregation">
|
||||
<i class="icon-history"></i><label small>Project history</label>
|
||||
</a>
|
||||
</li>
|
||||
</menu>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,252 @@
|
||||
/**
|
||||
*/
|
||||
|
||||
class ProjectFundingProjectView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
this.number = options.projectNumber
|
||||
this.node = options.node;
|
||||
this.nodeId = options.nodeId;
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
// reference to embedded articles
|
||||
this.panels = {
|
||||
project: this.components.find(o => o.el.classList.contains('project-info')),
|
||||
org: this.components.find(o => o.el.classList.contains('org-info'))
|
||||
}
|
||||
|
||||
this.nav = new NodeMap(this.find('[eicnodemap]'), {
|
||||
"orientation": "linear",
|
||||
"resizable": true,
|
||||
"allowDrag": true,
|
||||
"entity": {
|
||||
"width": 150,
|
||||
"height": 50,
|
||||
"gap": 30
|
||||
}
|
||||
});
|
||||
this.nav.click = this.onProjectNodeSelect.bind(this);
|
||||
|
||||
this.addEventTo('.button.aggregation', 'click', this.onAggregationSelect.bind(this))
|
||||
app.events.channel.addEventListener('project_node_updated', this.onProjectNodeUpdate.bind(this));
|
||||
this.panels.project.loading = true;
|
||||
this.panels.org.loading = true;
|
||||
|
||||
this.project.get(this.number)
|
||||
.then(this.fillProject.bind(this));
|
||||
}
|
||||
|
||||
DOMContentBlured() {
|
||||
if(this.currentPart) {
|
||||
this.currentPart.DOMContentBlured()
|
||||
}
|
||||
}
|
||||
|
||||
DOMContentRemoved() {
|
||||
if(this.currentPart) {
|
||||
this.currentPart.DOMContentRemoved()
|
||||
}
|
||||
}
|
||||
|
||||
DOMContentFocused() {
|
||||
if(this.currentPart) {
|
||||
this.currentPart.DOMContentFocused()
|
||||
}
|
||||
}
|
||||
|
||||
fillProject(data) {
|
||||
|
||||
this.panels.project.loading = false;
|
||||
this.panels.org.loading = false;
|
||||
|
||||
this.find('.project-number span').innerHTML = data.project.number;
|
||||
this.find('.project-instrument span').innerHTML = app.meta.getItem('icmp-instruments', data.project.instrument).label;
|
||||
this.find('.project-cutoff span').innerHTML = ui.format.date(data.project.cutOffDate);
|
||||
this.find('.project-acronym').innerHTML = data.project.acronym;
|
||||
this.find('.project-title').innerHTML = data.project.title;
|
||||
this.find('.org-name span').innerHTML = data.organisation.name;
|
||||
this.find('.org-pic span').innerHTML = data.organisation.pic;
|
||||
this.find('.org-location .org-street').innerHTML = data.organisation.street || '';
|
||||
this.find('.org-location .org-postcode').innerHTML = data.organisation.postalCode || '';
|
||||
this.find('.org-location .org-city').innerHTML = data.organisation.city || '';
|
||||
this.find('.org-location .org-country').innerHTML = app.meta.getItem('icmp-countries', data.organisation.country).label;
|
||||
if(data.organisation.website) {
|
||||
this.find('.org-website span').innerHTML = `<a href="${ui.format.url(data.organisation.website)}" title="${data.organisation.name} website" target="_blank">${data.organisation.website}</a>`;
|
||||
} else {
|
||||
ui.hide(this.find('.org-website'))
|
||||
}
|
||||
this.find('.project-signature span').innerHTML = ui.format.date(data.project.signatureDate);
|
||||
this.find('.project-funding-type span').innerHTML = app.meta.getItem('icmp-fundings', data.project.fundingType).label;
|
||||
|
||||
let active = 'info';
|
||||
|
||||
let wf = {
|
||||
entities: [
|
||||
{
|
||||
title: 'summary',
|
||||
subtitle: '',
|
||||
severity: 'primary',
|
||||
data: {
|
||||
id: 'info',
|
||||
type: 'info'
|
||||
}
|
||||
}
|
||||
],
|
||||
relations: [ ]
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
for(let phase of data.phases) {
|
||||
// to be upgraded
|
||||
if(phase.type != 'kyc' && phase.type != 'equityAgreement') {
|
||||
|
||||
wf.entities.push({
|
||||
title: 'Tech DD',
|
||||
subtitle: app.meta.getItem('icmp-techdd-status', phase.statusHistory[0].value).label,
|
||||
severity: phase.active ? 'accent': 'primary',
|
||||
data: {
|
||||
id: phase.type + i,
|
||||
type: 'techdd'
|
||||
}
|
||||
})
|
||||
wf.relations.push({source: 'info', target: phase.type + i})
|
||||
if(phase.active) active = phase.type;
|
||||
i++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.setupWorkflow(wf);
|
||||
this.onProjectNodeSelect({
|
||||
options: {
|
||||
data: {
|
||||
type: active
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setupWorkflow(data) { this.nav.data = data; }
|
||||
|
||||
loadPart(part) {
|
||||
|
||||
let contentPath = 'projects/icmp/project/';
|
||||
|
||||
if(this.currentPart) {
|
||||
if(this.currentPart.ref == part) return;
|
||||
this.currentPart.DOMContentRemoved();
|
||||
this.currentPart = null;
|
||||
}
|
||||
|
||||
this.find('.content').innerHTML = '';
|
||||
|
||||
switch(part) {
|
||||
case 'info':
|
||||
this.loadContent( contentPath + 'ProjectFundingInfoView', {}, { models: { project: this.project} } )
|
||||
.then( view => this.appendContent(part, view))
|
||||
break;
|
||||
case 'techdd':
|
||||
|
||||
ui.lock();
|
||||
|
||||
app.User.getBusinessPermissions([ `/icmp/projects/${this.number}/${this.node}/${this.nodeId}` ])
|
||||
.then(async payload => {
|
||||
|
||||
ui.unlock();
|
||||
|
||||
if(payload[`/icmp/projects/${this.number}/${this.node}/${this.nodeId}`].permissions.length > 0) {
|
||||
this.loadContent(
|
||||
contentPath + 'ProjectFundingTechDDView',
|
||||
{},
|
||||
{
|
||||
projectNumber: this.number,
|
||||
nodeType: this.node,
|
||||
nodeId: this.nodeId,
|
||||
mode: 'edit',
|
||||
models: {
|
||||
project: this.project,
|
||||
techdd: new ICMPProjectNodeModel(payload[`/icmp/projects/${this.number}/${this.node}/${this.nodeId}`].permissions)
|
||||
}
|
||||
})
|
||||
.then( view => this.appendContent(part, view));
|
||||
} else {
|
||||
ui.growl.append('You don\'t have access to this resource', 'danger' );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
break;
|
||||
case 'progressmeeting':
|
||||
|
||||
ui.lock();
|
||||
|
||||
app.User.getBusinessPermissions([ `/icmp/projects/${this.number}/${this.node}/${this.nodeId}` ])
|
||||
.then(async payload => {
|
||||
|
||||
ui.unlock();
|
||||
|
||||
if(payload[`/icmp/projects/${this.number}/${this.node}/${this.nodeId}`].permissions.length > 0) {
|
||||
this.loadContent(
|
||||
contentPath + 'ProjectProgressMeetingView',
|
||||
{},
|
||||
{
|
||||
projectNumber: this.number,
|
||||
nodeType: this.node,
|
||||
nodeId: this.nodeId,
|
||||
mode: 'edit',
|
||||
models: {
|
||||
project: this.project,
|
||||
techdd: new ICMPProjectNodeModel(payload[`/icmp/projects/${this.number}/${this.node}/${this.nodeId}`].permissions)
|
||||
}
|
||||
})
|
||||
.then( view => this.appendContent(part, view));
|
||||
} else {
|
||||
ui.growl.append('You don\'t have access to this resource', 'danger' );
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
break;
|
||||
case 'docs':
|
||||
this.loadContent( contentPath + 'ProjectFundingDocumentsView', {}, { models: { project: this.project} } )
|
||||
.then( view => this.appendContent(part, view))
|
||||
break;
|
||||
case 'history':
|
||||
this.loadContent( contentPath + 'ProjectFundingHistoryView', {}, { models: { project: this.project} } )
|
||||
.then( view => this.appendContent(part, view))
|
||||
break;
|
||||
case 'team':
|
||||
this.loadContent( contentPath + 'ProjectFundingTeamView', {}, { models: { project: this.project} } )
|
||||
.then( view => this.appendContent(part, view))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
appendContent(name, view) {
|
||||
this.currentPart = view;
|
||||
this.currentPart.ref = name;
|
||||
this.find(`.content`).append(view.el);
|
||||
}
|
||||
|
||||
onAggregationSelect(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.loadPart(event.currentTarget.dataset.target);
|
||||
}
|
||||
|
||||
onProjectNodeSelect(node) {
|
||||
this.loadPart(node.options.data.type);
|
||||
}
|
||||
|
||||
onProjectNodeUpdate(event) {
|
||||
this.currentPart.ref = '';
|
||||
this.loadPart('techdd');
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('ProjectFundingProjectView',ProjectFundingProjectView);
|
||||
@@ -0,0 +1,9 @@
|
||||
<article eiccard class="journal">
|
||||
<header>
|
||||
<h1>Documents available for this project</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
*/
|
||||
|
||||
class ProjectFundingDocumentsView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
this.project.documents(this.project.current.project.number)
|
||||
.then(this.fill.bind(this))
|
||||
}
|
||||
|
||||
fill(data) {
|
||||
let content = this.find('section');
|
||||
for(let section of data) {
|
||||
content.append(ui.create(`<label large><b>${section.category}</b></label>`));
|
||||
let files = ui.create(`<ul bulleted small>`)
|
||||
content.append(files);
|
||||
for(let file of section.attachments) {
|
||||
let url = app.config.api['/files'].get.uri
|
||||
url = url.replace('{id}', file.id)
|
||||
files.append(ui.create(`<li><a href="${url}" noroute target="_blank">${file.label}</a></li>`));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('ProjectFundingDocumentsView',ProjectFundingDocumentsView);
|
||||
@@ -0,0 +1,9 @@
|
||||
<article eiccard class="history">
|
||||
<header>
|
||||
<h1>Project history</h1>
|
||||
<h2>Key events of the project life cycle</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicdatagrid></div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
*/
|
||||
|
||||
class ProjectFundingHistoryView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.list = new DataGrid(this.find('[eicdatagrid]'), {
|
||||
headers: [
|
||||
{label: 'date', type: 'dateTime'},
|
||||
{label: 'action'}
|
||||
],
|
||||
height: '45vh'
|
||||
})
|
||||
|
||||
this.fill(this.project.current.project.statusHistory);
|
||||
}
|
||||
|
||||
fill(data) {
|
||||
this.list.clear();
|
||||
|
||||
for(let item of data) {
|
||||
this.list.addRow(0, [ item.dateTime, item.value ]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('ProjectFundingHistoryView',ProjectFundingHistoryView);
|
||||
@@ -0,0 +1,53 @@
|
||||
<article eiccard class="history">
|
||||
<header>
|
||||
<h1>Summary</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<p class="project-abstract"></p>
|
||||
<div class="project-keywords">
|
||||
<label xsmall><b>Keywords</b></label>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="project-extra-keywords">
|
||||
<label xsmall><b>Extra keywords</b></label>
|
||||
<span></span>
|
||||
</div>
|
||||
<hr/>
|
||||
<label xlarge><b>Fundings information</b></label>
|
||||
<div class="project-grant">
|
||||
<label large><b>Grant funding</b></label>
|
||||
<div class="cols-3">
|
||||
<div class="project-grant-requested">
|
||||
<label xsmall>Requested amount (by Beneficiary)</label>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="project-grant-proposed">
|
||||
<label xsmall>Recommended amount (by Interview panel)</label>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="project-grant-final">
|
||||
<label xsmall>Final amount</label>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-equity">
|
||||
<label large><b>Equity funding</b></label>
|
||||
<div class="cols-3">
|
||||
<div class="project-equity-requested">
|
||||
<label xsmall>Requested amount (by Beneficiary)</label>
|
||||
<span></span>
|
||||
</div>
|
||||
<div class="project-equity-proposed">
|
||||
<label xsmall>Recommended amount (by Interview panel)</label>
|
||||
<span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="project-documents">
|
||||
<hr/>
|
||||
<label xlarge><b>Documents</b></label>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,56 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ProjectFundingInfoView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.components = ui.eicfy(this.el);
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
this.fill(this.project.current)
|
||||
}
|
||||
|
||||
fill(data) {
|
||||
this.find('.project-abstract').innerHTML = data.project.abstract;
|
||||
this.find('.project-keywords span').innerHTML = data.project.keywords.eicKeywords.join(', ');
|
||||
this.find('.project-extra-keywords span').innerHTML = data.project.keywords.freeKeywords.join(', ');
|
||||
|
||||
if(data.project.financingDetails.grant) {
|
||||
this.find('.project-grant-requested span').innerHTML = ui.format.currency(data.project.financingDetails.grant.requestedAmount);
|
||||
this.find('.project-grant-proposed span').innerHTML = ui.format.currency(data.project.financingDetails.grant.proposedAmount);
|
||||
this.find('.project-grant-final span').innerHTML = ui.format.currency(data.project.financingDetails.grant.finalAmount);
|
||||
|
||||
} else {
|
||||
ui.hide(this.find('.project-grant'))
|
||||
}
|
||||
|
||||
if(data.project.financingDetails.equity) {
|
||||
this.find('.project-equity-requested span').innerHTML = ui.format.currency(data.project.financingDetails.equity.requestedAmount);
|
||||
this.find('.project-equity-proposed span').innerHTML = ui.format.currency(data.project.financingDetails.equity.proposedAmount);
|
||||
|
||||
} else {
|
||||
ui.hide(this.find('.project-equity'))
|
||||
|
||||
}
|
||||
|
||||
if(data.project.documents) {
|
||||
let content = this.find('.project-documents');
|
||||
for(let section of data.project.documents) {
|
||||
content.append(ui.create(`<label large><b>${section.category}</b></label>`));
|
||||
let files = ui.create(`<ul bulleted small>`)
|
||||
content.append(files);
|
||||
for(let file of section.attachments) {
|
||||
let url = app.config.api['/files'].get.uri
|
||||
url = url.replace('{id}', file.id)
|
||||
files.append(ui.create(`<li><a href="${url}" noroute target="_blank">${file.label}</a></li>`));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ui.hide(this.find('.project-documents'))
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('ProjectFundingInfoView',ProjectFundingInfoView);
|
||||
@@ -0,0 +1,47 @@
|
||||
<article eiccard class="funding-node progress-meeting">
|
||||
<header>
|
||||
<h1>Progress Meeting</h1>
|
||||
<h2><span eicchip warning>feedback</span></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="cols-2 right">
|
||||
<div>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>Schedule</h1>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Date</label>
|
||||
<span>6 Feb 2024 | 10:00 - 22:00</span>
|
||||
</div>
|
||||
<div>
|
||||
<label>Location</label>
|
||||
<span>skype</span>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>Participants</h1>
|
||||
</header>
|
||||
<section>
|
||||
<label>Attending</label>
|
||||
<ul></ul>
|
||||
<label>Excused</label>
|
||||
<ul></ul>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
<article eiccard class="feedback-form">
|
||||
<header>
|
||||
<h1>Feedback</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,10 @@
|
||||
class ProjectFundingProgressMeetingView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
}
|
||||
|
||||
fill() {}
|
||||
|
||||
refresh(data) {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
<style>
|
||||
.contributors .list {
|
||||
display: flex;
|
||||
flex-flow: wrap;
|
||||
grid-gap: 20px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
<article eiccard class="contributors">
|
||||
<header>
|
||||
<h1>Contributors</h1>
|
||||
<h2>People involved in the project</h2>
|
||||
</header>
|
||||
<section>
|
||||
<ul nonbulleted class="list"></ul>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
*/
|
||||
|
||||
class ProjectFundingTeamView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
this.project.contributors(this.project.current.project.number)
|
||||
.then(this.fill.bind(this))
|
||||
}
|
||||
|
||||
fill(data) {
|
||||
let list = this.find('ul.list');
|
||||
|
||||
for(let member of data) {
|
||||
let roles = member.roles.map(o => this.translateRole(o.role))
|
||||
let el = ui.create(`<li>
|
||||
<div medium><b>${member.information.name.firstName} ${member.information.name.lastName}</b></div>
|
||||
<div xsmall><a href="mailto:${member.information.contacts.email}">${member.information.contacts.email}</a></div>
|
||||
<div small>${roles.join(' | ')}</div>
|
||||
</li>`);
|
||||
|
||||
list.append(el)
|
||||
}
|
||||
}
|
||||
|
||||
translateRole(role) {
|
||||
let s = role.replace('PROJECT_', '');
|
||||
|
||||
switch(s) {
|
||||
case 'TechDDExpert':
|
||||
s = 'TechDD Expert';
|
||||
break;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('ProjectFundingTeamView',ProjectFundingTeamView);
|
||||
@@ -0,0 +1,29 @@
|
||||
<style>
|
||||
.funding-node.tech-dd textarea { height: 120px; }
|
||||
.funding-node.tech-dd .tabs-extended { min-width: 240px; }
|
||||
</style>
|
||||
<article eiccard class="funding-node tech-dd">
|
||||
<header class="cols-2 right">
|
||||
<div>
|
||||
<h1>Technical Due Diligence</h1>
|
||||
<h2></h2>
|
||||
</div>
|
||||
<div class="version"></div>
|
||||
</header>
|
||||
<section>
|
||||
<div class="files"></div>
|
||||
<div class="form"></div>
|
||||
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-2 right">
|
||||
<div>
|
||||
<div class="autosave-control">
|
||||
<input eicinput type="toggler" labelleft="Enable auto-save" name="autosave" data-typ="ignore" truevalue="true" falsevalue="false" value="true" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions"></div>
|
||||
</div>
|
||||
|
||||
</footer>
|
||||
</article>
|
||||
@@ -0,0 +1,625 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
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 = `<span eicchip primary>${versionLabel}</span>`
|
||||
|
||||
if(data.experts.length > 0) {
|
||||
tags.push(`<span eicchip info>reporting assigned to ${data.experts[0].information.name.firstName} ${data.experts[0].information.name.lastName}</span>`)
|
||||
}
|
||||
tags.push(`<span eicchip accent>${app.meta.getItem('icmp-techdd-status', data.statusHistory[0].value).label}</span>`)
|
||||
|
||||
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(`<div eicalert info><a href="${url}" noroute target="_blank">Download report as PDF</a></div>`)
|
||||
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);
|
||||
@@ -0,0 +1,10 @@
|
||||
<style>
|
||||
.project-node-process-confirm {
|
||||
min-width: 40vw;
|
||||
min-height: 40vh;
|
||||
}
|
||||
.project-node-process-confirm textarea { height:320px; }
|
||||
</style>
|
||||
<div class="project-node-process-confirm">
|
||||
<textarea eictextarea name="justification" placeholder="Comments about this action" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,47 @@
|
||||
class ICMPNodeActionConfirmDialog extends EICDialogContent {
|
||||
actions = [
|
||||
{
|
||||
label: 'cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'proceed',
|
||||
onclick: this.process.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'process'
|
||||
}
|
||||
]
|
||||
|
||||
async DOMContentLoaded(options) {
|
||||
this.node = options.models.node;
|
||||
this.action = options.action
|
||||
this.number = options.number
|
||||
this.type = options.type
|
||||
this.nodeId = options.nodeId
|
||||
this.report = options.report
|
||||
console.log(options)
|
||||
console.log(this)
|
||||
}
|
||||
|
||||
DOMContentFocused() {
|
||||
ui.eicfy(this.el);
|
||||
this.form = new Form(this.el);
|
||||
}
|
||||
|
||||
process() {
|
||||
let payload = {
|
||||
status: this.action,
|
||||
form: this.report
|
||||
};
|
||||
|
||||
payload = {...payload, ...this.form.value}
|
||||
|
||||
this.node.process(this.action, this.number, this.type, this.nodeId, payload)
|
||||
.then( this.commit );
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('ICMPNodeActionConfirmDialog', ICMPNodeActionConfirmDialog);
|
||||
@@ -0,0 +1,434 @@
|
||||
<form>
|
||||
<h2>1. Focus on the technology</h2>
|
||||
<div class="cols-2 left tech">
|
||||
<input type="hidden" name="form.version" data-path="" value="1.0" />
|
||||
<input type="hidden" name="status" data-path="" value="" />
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="attributes">Attributes<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="protection">Protection<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="validity">Validity<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="commercialisation">Commercialisation<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="milestones">Milestones<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="production">Value Chain<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="compliance">Compliance<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="feedbacks">Feedbacks<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.1. Attributes</h1>
|
||||
<h2>qualities, shortcomings, use and possible extension</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.2. Protection</h1>
|
||||
<h2>patents, copyrights, trade secret, know-ho</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.3. Validity</h1>
|
||||
<h2>development plan, tests, prototype, demo, scalability</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.4. Commercialisation</h1>
|
||||
<h2>time and costs to develop, barriers and risks</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.5. Milestones</h1>
|
||||
<h2>Expert opinion: clear, complete, realistic? Costs and timing precisely explained?</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.6. Value Chain</h1>
|
||||
<h2>internal / external, supply chain, suppliers</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.7. Compliance</h1>
|
||||
<h2>current / future regulations, industry standards</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.8. Feedbacks</h1>
|
||||
<h2>from tests, lab results, prototypes, customers</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<h2>2. Focus on the market</h2>
|
||||
<div class="cols-2 left market">
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="landscape">Landscape<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="ecosystem">Ecosystem<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="positioning">Positioning<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="competition">Competition<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="opportunities">Opportunities<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.1. Landscape</h1>
|
||||
<h2>market shares, trends, cycles, segments, regulations</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.2. Ecosystem</h1>
|
||||
<h2>actors and interactions (including competitors)</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.3. Positioning</h1>
|
||||
<h2>USP, alternatives, substitutes, comparaison</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.4. Competition</h1>
|
||||
<h2>who, how far, what traction, what funding</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.5. Opportunities</h1>
|
||||
<h2>procurement, expansion, export, trade</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<h2>3. Focus on Strategy and business matters</h2>
|
||||
<div class="cols-2 left strategy">
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="team">Team<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="scalability">Scalability<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>3.1. Team</h1>
|
||||
<h2>Team sizing, key profiles, hiring, critical external collaborations.</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.strategy.team" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.strategy.team" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.strategy.team" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="5000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.strategy.team" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.strategy.team" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.strategy.team" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>3.2. Scalability</h1>
|
||||
<h2>Growth speed, reach profitability</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.strategy.scalability" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.strategy.scalability" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.strategy.scalability" class="required" hint="Please try to provide an answer around 4000 chars length." maxlen="5000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.strategy.scalability" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.strategy.scalability" class="required" hint="Please try to provide an answer around 2000 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.strategy.scalability" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
<h2>General conclusions and recommendations</h2>
|
||||
<div>
|
||||
<label>Is the technology in combination with the company investable or not?</label>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="choice" data-path="form.conclusion.combination" data-type="boolean" class="required">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea eictextarea name="justification" data-path="form.conclusion.combination" class="required" hint="Please try to provide an answer between 3200 and 4000 chars." maxlen="7000" placeholder="please explain"></textarea>
|
||||
<textarea eictextarea name="justification" data-path="comments.conclusion.combination" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Did you identify issues or short comings? Should this be the case can you suggest recommendations to address them.</label>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="choice" data-path="form.conclusion.issues" data-type="boolean" class="required">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea eictextarea name="justification" data-path="form.conclusion.issues" class="required" hint="Please try to provide an answer between 3200 and 4000 chars." maxlen="7000" placeholder="please explain"></textarea>
|
||||
<textarea eictextarea name="justification" data-path="comments.conclusion.issues" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>In your opinion is the project aligned with the objectives of the EC funds on Deep tech, strategic priorities and sovereignity concerns?</label>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="choice" data-path="form.conclusion.alignment" data-type="boolean" class="required">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea eictextarea name="justification" data-path="form.conclusion.alignment" class="required" hint="Please try to provide an answer between 3200 and 4000 chars." maxlen="7000" placeholder="please explain"></textarea>
|
||||
<textarea eictextarea name="justification" data-path="comments.conclusion.alignment" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ICMPFormTechddV1EnhancedView extends ICMPFormTechddV1View {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
super.DOMContentLoaded(options);
|
||||
|
||||
this.tabStrategy = new Tab();
|
||||
this.tabStrategy.addTabs(
|
||||
this.findAll('.strategy .tabs-extended menu li'),
|
||||
this.findAll('.strategy article')
|
||||
);
|
||||
}
|
||||
|
||||
getTriggers() {
|
||||
return [].concat( this.tabTech.triggers, this.tabMarket.triggers, this.tabStrategy.triggers )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('ICMPFormTechddV1EnhancedView',ICMPFormTechddV1EnhancedView);
|
||||
@@ -0,0 +1,460 @@
|
||||
<form>
|
||||
<h2>Focus on the technology</h2>
|
||||
<div class="cols-2 left tech">
|
||||
<input type="hidden" name="form.version" data-path="" value="1.0" />
|
||||
<input type="hidden" name="status" data-path="" value="" />
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="attributes">Attributes<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="protection">Protection<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="validity">Validity<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="commercialisation">Commercialisation<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="milestones">Milestones<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="production">Value Chain<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="compliance">Compliance<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="feedbacks">Feedbacks<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.1. Attributes</h1>
|
||||
<h2>qualities, shortcomings, use and possible extension</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.2. Protection</h1>
|
||||
<h2>patents, copyrights, trade secret, know-ho</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.3. Validity</h1>
|
||||
<h2>development plan, tests, prototype, demo, scalability</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.4. Commercialisation</h1>
|
||||
<h2>time and costs to develop, barriers and risks</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.5. Milestones</h1>
|
||||
<h2>Expert opinion: clear, complete, realistic? Costs and timing precisely explained?</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.6. Value Chain</h1>
|
||||
<h2>internal / external, supply chain, suppliers</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.7. Compliance</h1>
|
||||
<h2>current / future regulations, industry standards</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.8. Feedbacks</h1>
|
||||
<h2>from tests, lab results, prototypes, customers</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 2500 chars length." maxlen="3000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<h2>2. Focus on the market</h2>
|
||||
<div class="cols-2 left market">
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="landscape">Landscape<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="ecosystem">Ecosystem<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="positioning">Positioning<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="competition">Competition<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="opportunities">Opportunities<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.1. Landscape</h1>
|
||||
<h2>market shares, trends, cycles, segments, regulations</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.2. Ecosystem</h1>
|
||||
<h2>actors and interactions (including competitors)</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.3. Positioning</h1>
|
||||
<h2>USP, alternatives, substitutes, comparaison</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.4. Competition</h1>
|
||||
<h2>who, how far, what traction, what funding</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.5. Opportunities</h1>
|
||||
<h2>procurement, expansion, export, trade</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<h2>3. Focus on Strategy and business matters</h2>
|
||||
<div class="cols-2 left strategy">
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="team">Team<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="scalability">Scalability<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="business">Business model<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>3.1. Team</h1>
|
||||
<h2>Team sizing, key profiles, hiring, critical external collaborations.</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.strategy.team" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.strategy.team" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.strategy.team" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.strategy.team" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.strategy.team" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.strategy.team" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>3.2. Scalability</h1>
|
||||
<h2>Growth speed, reach profitability</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.strategy.scalability" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.strategy.scalability" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.strategy.scalability" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.strategy.scalability" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.strategy.scalability" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.strategy.scalability" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>3.3. Business model</h1>
|
||||
<h2>Key revenues, Capex, Cost assumptions in the Business model</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.strategy.business" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6500"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.strategy.business" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.strategy.business" class="required" hint="Please try to provide an answer around 6000 chars length." maxlen="9000"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.strategy.business" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.strategy.business" class="required" hint="Please try to provide an answer around 4500 chars length." maxlen="6000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.strategy.business" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<h2>General conclusions and recommendations</h2>
|
||||
<div>
|
||||
<label>Is the technology in combination with the company investable or not?</label>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="choice" data-path="form.conclusion.combination" data-type="boolean" class="required">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea eictextarea name="justification" data-path="form.conclusion.combination" class="required" hint="Please try to provide an answer between 3200 and 4000 chars." maxlen="7000" placeholder="please explain"></textarea>
|
||||
<textarea eictextarea name="justification" data-path="comments.conclusion.combination" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Did you identify issues or short comings? Should this be the case can you suggest recommendations to address them.</label>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="choice" data-path="form.conclusion.issues" data-type="boolean" class="required">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea eictextarea name="justification" data-path="form.conclusion.issues" class="required" hint="Please try to provide an answer between 3200 and 4000 chars." maxlen="7000" placeholder="please explain"></textarea>
|
||||
<textarea eictextarea name="justification" data-path="comments.conclusion.issues" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>In your opinion is the project aligned with the objectives of the EC funds on Deep tech, strategic priorities and sovereignity concerns?</label>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="choice" data-path="form.conclusion.alignment" data-type="boolean" class="required">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea eictextarea name="justification" data-path="form.conclusion.alignment" class="required" hint="Please try to provide an answer between 3200 and 4000 chars." maxlen="7000" placeholder="please explain"></textarea>
|
||||
<textarea eictextarea name="justification" data-path="comments.conclusion.alignment" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ICMPFormTechddV1FullView extends ICMPFormTechddV1View {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
super.DOMContentLoaded(options);
|
||||
|
||||
this.tabStrategy = new Tab();
|
||||
this.tabStrategy.addTabs(
|
||||
this.findAll('.strategy .tabs-extended menu li'),
|
||||
this.findAll('.strategy article')
|
||||
);
|
||||
}
|
||||
|
||||
getTriggers() {
|
||||
return [].concat( this.tabTech.triggers, this.tabMarket.triggers, this.tabStrategy.triggers )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('ICMPFormTechddV1FullView',ICMPFormTechddV1FullView);
|
||||
@@ -0,0 +1,352 @@
|
||||
<form>
|
||||
<h2>1. Focus on the technology</h2>
|
||||
<div class="cols-2 left tech">
|
||||
<input type="hidden" name="form.version" data-path="" value="1.0" />
|
||||
<input type="hidden" name="status" data-path="" value="" />
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="attributes">Attributes<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="protection">Protection<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="validity">Validity<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="commercialisation">Commercialisation<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="milestones">Milestones<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="production">Value Chain<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="compliance">Compliance<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="feedbacks">Feedbacks<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.1. Attributes</h1>
|
||||
<h2>qualities, shortcomings, use and possible extension</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.attributes" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.attributes" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.2. Protection</h1>
|
||||
<h2>patents, copyrights, trade secret, know-ho</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.protection" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.protection" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.3. Validity</h1>
|
||||
<h2>development plan, tests, prototype, demo, scalability</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.validity" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.validity" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.4. Commercialisation</h1>
|
||||
<h2>time and costs to develop, barriers and risks</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.commercialisation" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.commercialisation" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.5. Milestones</h1>
|
||||
<h2>Expert opinion: clear, complete, realistic? Costs and timing precisely explained?</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.milestones" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.milestones" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.6. Value Chain</h1>
|
||||
<h2>internal / external, supply chain, suppliers</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.production" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.production" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.7. Compliance</h1>
|
||||
<h2>current / future regulations, industry standards</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.compliance" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.compliance" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>1.8. Feedbacks</h1>
|
||||
<h2>from tests, lab results, prototypes, customers</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.technology.feedbacks" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.technology.feedbacks" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<h2>2. Focus on the market</h2>
|
||||
<div class="cols-2 left market">
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="landscape">Landscape<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="ecosystem">Ecosystem<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="positioning">Positioning<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="competition">Competition<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="opportunities">Opportunities<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.1. Landscape</h1>
|
||||
<h2>market shares, trends, cycles, segments, regulations</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.landscape" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.landscape" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.2. Ecosystem</h1>
|
||||
<h2>actors and interactions (including competitors)</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.ecosystem" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.ecosystem" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.3. Positioning</h1>
|
||||
<h2>USP, alternatives, substitutes, comparaison</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.positioning" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.positioning" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.4. Competition</h1>
|
||||
<h2>who, how far, what traction, what funding</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.competition" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.competition" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard>
|
||||
<header>
|
||||
<h1>2.5. Opportunities</h1>
|
||||
<h2>procurement, expansion, export, trade</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="abstract" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expert opinion</label>
|
||||
<textarea eictextarea name="expertOpinion" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 3000 chars length." maxlen="4500"></textarea>
|
||||
<textarea eictextarea name="expertOpinion" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk(s)</label>
|
||||
<textarea eictextarea name="risks" data-path="form.market.opportunities" class="required" hint="Please try to provide an answer around 1500 chars length." maxlen="2000"></textarea>
|
||||
<textarea eictextarea name="risks" data-path="comments.market.opportunities" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</div>
|
||||
<h2>General conclusions and recommendations</h2>
|
||||
<div>
|
||||
<label>Is the technology in combination with the company investable or not?</label>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="combination" data-path="form.conclusion" data-type="boolean" class="required">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<textarea eictextarea name="justification" data-path="form.conclusion" class="required" hint="Please try to provide an answer between 3200 and 4000 chars." maxlen="7000" placeholder="please explain"></textarea>
|
||||
<textarea eictextarea name="justification" data-path="comments.conclusion" class="" hint="" placeholder="Place your comments here"></textarea>
|
||||
</div>
|
||||
</form>
|
||||
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
*
|
||||
*/
|
||||
class ICMPFormTechddV1View extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.components = ui.eicfy(this.el);
|
||||
//for(let model in options.models) this[model] = options.models[model];
|
||||
console.log('tabs')
|
||||
|
||||
this.tabTech = new Tab();
|
||||
this.tabTech.addTabs(this.findAll('.tech .tabs-extended menu li'), this.findAll('.tech article'))
|
||||
this.tabMarket = new Tab();
|
||||
this.tabMarket.addTabs(this.findAll('.market .tabs-extended menu li'), this.findAll('.market article'))
|
||||
}
|
||||
|
||||
fill(data) {
|
||||
this.form = new Form(this.find('form'));
|
||||
this.form.fill(this.components, data);
|
||||
|
||||
}
|
||||
|
||||
getTriggers() {
|
||||
return [].concat( this.tabTech.triggers, this.tabMarket.triggers )
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('ICMPFormTechddV1View',ICMPFormTechddV1View);
|
||||
@@ -0,0 +1,65 @@
|
||||
<style>
|
||||
.soe > header {
|
||||
background: url('/app/assets/images/cards/soe-banner.jpg');
|
||||
background-position: center;
|
||||
background-size: cover !important;
|
||||
background-repeat: no-repeat;
|
||||
border-bottom: 4px solid white !important;
|
||||
}
|
||||
.soe .insights { min-width: 180px; }
|
||||
.soe .insights span {
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
padding-top: var(--eicui-base-spacing-s);
|
||||
}
|
||||
.soe .insights label { text-align: center; }
|
||||
.soe .description [eicdatagrid] .row { grid-template-columns: 110px 2fr 1fr 1fr 110px 2fr 1fr 10px; }
|
||||
.soe .description [eicdatagrid] .dataset .row .cell:nth-child(5),
|
||||
.soe .description [eicdatagrid] .dataset .row .cell:nth-child(6) { text-align: center; }
|
||||
.soe .description [eicdatagrid] .dataset .row .cell:nth-child(8) { text-align: right; }
|
||||
.soe .members > section { max-height:30vh; }
|
||||
.soe .members li { padding: var(--eicui-base-spacing-s) 0 ; }
|
||||
</style>
|
||||
<article eiccard media class="soe">
|
||||
<header>
|
||||
<h1>Seals of Excellence</h1>
|
||||
<h2>${scope}</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<article eiccard class="insights loading" collapsable>
|
||||
<header>
|
||||
<h1>Feedbacks insight</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<div><span xlarge class="items">...</span><label small>FEEDBACKS</label></div>
|
||||
<div><span xlarge class="fundings">...</span><label small>TOTAL FUNDING</label></div>
|
||||
<div><span primary><a href="https://app.powerbi.com/groups/me/apps/15bf1ef3-6d63-451e-8487-b0fda8ec8c7d/reports/2b903b7c-41b0-47f1-b550-3ecc8278238f/ReportSection21eccfd278f3892e1129?experience=power-bi" target="_blank" title="Seals of Excellence on Power BI">Full report (Power BI)</a></span></div>
|
||||
</section>
|
||||
</article>
|
||||
<article eiccard class="members loading" collapsable>
|
||||
<header>
|
||||
<h1>Members</h1>
|
||||
<h2></h2>
|
||||
</header>
|
||||
<section>
|
||||
<ul></ul>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-1 right">
|
||||
<button disabled eicbutton primary xsmall>add member</button>
|
||||
</div>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
<article eiccard class="description">
|
||||
<section>
|
||||
<div eicdatagrid></div>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
*/
|
||||
|
||||
class SoeDashboardView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
this.members = this.find('.members ul')
|
||||
this.list = new DataGrid(this.find('.description [eicdatagrid]'), {
|
||||
headers: [
|
||||
{label: 'PIC', filter: 'text', sortable: true},
|
||||
{label: 'company', filter: 'text', sortable: true},
|
||||
{label: 'instrument', filter: 'list', sortable: true},
|
||||
{label: 'cut-off', type: 'date', filter: 'text', sortable: true},
|
||||
{label: 'proposal number', filter: 'text', sortable: true},
|
||||
{label: 'acronym', filter: 'text', sortable: true},
|
||||
{label: 'funding', type: 'currency', sortable: true},
|
||||
],
|
||||
height: 'calc(100vh - 360px)'
|
||||
})
|
||||
this.list.onRowClick = this.onProjectSelect.bind(this)
|
||||
|
||||
this.soe.list().then( this.onListUpdated.bind(this));
|
||||
|
||||
this.soe.members().then( this.onMembersUpdated.bind(this));
|
||||
|
||||
app.events.channel.addEventListener('soe_update', this.onUpdateRequest.bind(this))
|
||||
}
|
||||
|
||||
onUpdateRequest() { this.soe.list().then(this.onListUpdated.bind(this)); }
|
||||
|
||||
onListUpdated(payload) {
|
||||
|
||||
this.list.clear();
|
||||
|
||||
let totalFB = 0;
|
||||
let totalFunding = 0;
|
||||
|
||||
for(let item of payload) {
|
||||
this.list.addRow( JSON.stringify({ pic: item.company.pic, number: item.proposal.number }), [
|
||||
item.company.pic,
|
||||
item.company.name,
|
||||
item.instrument,
|
||||
item.cutoffDate,
|
||||
item.proposal.number,
|
||||
item.proposal.acronym,
|
||||
item.totalFunding
|
||||
])
|
||||
|
||||
totalFunding += item.totalFunding;
|
||||
totalFB += parseInt(item.fundings);
|
||||
}
|
||||
|
||||
this.find('.insights .items').innerHTML = totalFB;
|
||||
this.find('.insights .fundings').innerHTML = ui.format.currency( totalFunding );
|
||||
this.components.find(o => o.constructor.name == 'Card' && o.el.classList.contains('insights')).loading = false;
|
||||
|
||||
}
|
||||
|
||||
onMembersUpdated(payload) {
|
||||
|
||||
this.members.innerHTML = '';
|
||||
|
||||
let warnings = this.find('.members header h2')
|
||||
warnings.innerHTML = `<span eicchip xsmall success>${payload.length} members</span>`;
|
||||
|
||||
for(let member of payload) {
|
||||
let li = ui.create(`<li class="cols-2 right middle" data-id="${member.uid}">
|
||||
<div>
|
||||
<div><b>${member.lastname} ${member.firstname}</b></div>
|
||||
<div xsmall="" class="props">
|
||||
<a href="mailto:${member.email}">${member.email}</a>
|
||||
</div>
|
||||
</div>
|
||||
<span class="actions"></span>
|
||||
</li>`);
|
||||
this.members.append(li);
|
||||
}
|
||||
this.components.find(o => o.constructor.name == 'Card' && o.el.classList.contains('members')).loading = false;
|
||||
|
||||
}
|
||||
|
||||
onProjectSelect(row) {
|
||||
|
||||
let pic = 0;
|
||||
let number = 0;
|
||||
|
||||
if(row.dataset.id != 0) {
|
||||
let ident = JSON.parse(row.dataset.id)
|
||||
|
||||
pic = ident.pic;
|
||||
number = ident.number
|
||||
}
|
||||
|
||||
app.Router.route(`/soe/companies/${pic}/projects/${number}/fundings`, { });
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SoeDashboardView', SoeDashboardView);
|
||||
@@ -0,0 +1,69 @@
|
||||
<style>
|
||||
.soe-project > header {
|
||||
background: url('/app/assets/images/cards/soe-banner.jpg');
|
||||
background-position: center;
|
||||
background-size: cover !important;
|
||||
background-repeat: no-repeat;
|
||||
border-bottom: 4px solid white !important;
|
||||
}
|
||||
.soe-project .feedbacks .insight {
|
||||
padding: var(--eicui-base-spacing-l);
|
||||
width: fit-content;
|
||||
grid-gap: var(--eicui-base-spacing-l);
|
||||
display: grid;
|
||||
justify-self: center;
|
||||
}
|
||||
.soe-project .feedbacks .insight label { text-align: right; }
|
||||
.soe-project .feedbacks .insight span {
|
||||
text-align: center;
|
||||
display: block;
|
||||
font-weight: bold;
|
||||
}
|
||||
.soe-project .props {
|
||||
min-width: 240px;
|
||||
}
|
||||
</style>
|
||||
<article eiccard media class="soe-project">
|
||||
<header>
|
||||
<h1>...</h1>
|
||||
<h2>Feedbacks on Seal of Excellence</h2>
|
||||
</header>
|
||||
<section>
|
||||
<article eiccard collapsable class="project loading">
|
||||
<header>
|
||||
<h1>Project Information</h1>
|
||||
<h2><span xsmall>click on the right arrow to expand/collapse project details</span></h2>
|
||||
</header>
|
||||
<section>
|
||||
|
||||
<div class="cols-2 left">
|
||||
<div class="props">
|
||||
<label xsmall>Project Number</label><b><span class="number"></span></b>
|
||||
<label xsmall>Company</label><b><span class="company"></span></b>
|
||||
<label xsmall>PIC</label><b><span class="pic"></span></b>
|
||||
<label xsmall>Country</label><b><span class="country"></span></b>
|
||||
</div>
|
||||
<p class="abstract"></p>
|
||||
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
|
||||
<div class="feedbacks">
|
||||
<div class="cols-2 right">
|
||||
<div class="insight">
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<span primary xxlarge class="items">...</span><label small>FEEDBACKS</label>
|
||||
</div>
|
||||
<div>
|
||||
<span primary xxlarge class="fundings">...</span><label small>TOTAL FUNDING</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<button eicbutton class="add" primary small>Add feedback</button>
|
||||
</div>
|
||||
<ul class="list"></ul>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,159 @@
|
||||
class SoeFeedbacksView extends EICDomContent {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
this.pic = options.pic;
|
||||
this.number = options.number;
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.pic = options.pic
|
||||
this.number = options.number
|
||||
|
||||
let btAdd = this.components.find(o => o.el.classList.contains('add'));
|
||||
if(this.soe.hasPrivilege('create')) {
|
||||
btAdd.click = this.onAddFeedback.bind(this);
|
||||
} else {
|
||||
btAdd.disabled = true;
|
||||
}
|
||||
|
||||
this.soe.fundings(this.pic, this.number).then(this.onFundingsUpdate.bind(this));
|
||||
}
|
||||
|
||||
onFundingsUpdate(payload) {
|
||||
let totalItems = 0;
|
||||
let totalFundings = 0;
|
||||
|
||||
this.find('.soe-project > header h1').innerHTML = payload.proposal.acronym;
|
||||
this.find('.soe-project section .number').innerHTML = payload.proposal.number;
|
||||
this.find('.soe-project section .company').innerHTML = payload.company.name;
|
||||
this.find('.soe-project section .pic').innerHTML = payload.company.pic;
|
||||
this.find('.soe-project section .country').innerHTML = payload.company.country;
|
||||
this.find('.soe-project section .abstract').innerHTML = payload.proposal.abstract;
|
||||
|
||||
let list = this.find('.soe-project .list')
|
||||
list.innerHTML = '';
|
||||
|
||||
for(let funding of payload.fundings) {
|
||||
let li = ui.create(`<article eiccard primary>
|
||||
<header>
|
||||
<div class="cols-2 right">
|
||||
<div>
|
||||
<h1>Source(s): ${funding.fundingSource.map(item => app.meta.getItem( 'soe-fundings', item).label).join(' + ')}</h1>
|
||||
<h2>Date: ${ui.format.date(funding.cutoffDate)}</h2>
|
||||
</div>
|
||||
<div eicdropdown>
|
||||
<button eicbutton rounded basic icon="icon-more" small></button>
|
||||
<menu eicmenu></menu>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<section>
|
||||
${funding.fundingAmount ? '<div success>Funding: <b>' + ui.format.currency(funding.fundingAmount) + '</b></div>': '' }
|
||||
<div>Duration: ${funding.duration} month(s)</div>
|
||||
<p>${funding.additionalDetails}</p>
|
||||
<div>
|
||||
<span>Success Story: ${funding.successStory ? 'Yes': 'No'}</span>
|
||||
<p>${funding.successStoryDetails}</p>
|
||||
</div>
|
||||
<div class="cols-2">
|
||||
<div secondary xsmall>creation: <span>${ui.format.date(funding.statusHistory[funding.statusHistory.length - 1].dateTime)}</span> by <span>${funding.statusHistory[funding.statusHistory.length - 1].author}</span></div>
|
||||
<div secondary xsmall>Last update: <span>${ui.format.date(funding.statusHistory[0].dateTime)}</span> by <span>${funding.statusHistory[0].author}</span></div>
|
||||
</div>
|
||||
</section>
|
||||
</article>`);
|
||||
|
||||
if(this.soe.hasPrivilege('update')) {
|
||||
let edit = ui.create(`<li menuitem primary class="menu-link" data-id="${funding.id}"><a href="#" title="edit"><i class="icon-edit"></i><label>edit</label></a></li>`);
|
||||
edit.addEventListener('click', this.onEditFeedback.bind(this));
|
||||
li.querySelector('[eicmenu]').append(edit);
|
||||
}
|
||||
|
||||
let history = ui.create(`<li menuitem primary class="menu-link" data-id="${funding.id}"><a href="#" title="history"><i class="icon-history"></i><label>history</label></a></li>`);
|
||||
history.addEventListener('click', this.onHistoryFeedback.bind(this));
|
||||
li.querySelector('[eicmenu]').append(history);
|
||||
|
||||
ui.eicfy(li);
|
||||
list.append(li);
|
||||
|
||||
totalItems++;
|
||||
totalFundings += funding.fundingAmount;
|
||||
}
|
||||
|
||||
this.find('.insight .items').innerHTML = totalItems
|
||||
this.find('.insight .fundings').innerHTML = ui.format.currency( totalFundings)
|
||||
this.components.find(o => o.constructor.name == 'Card' && o.el.classList.contains('project')).loading = false;
|
||||
}
|
||||
|
||||
async onAddFeedback(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/soe/dialogs/SoeFeedbackFormDialog',
|
||||
{ title: 'Provide a feedback' },
|
||||
{
|
||||
pic: this.pic,
|
||||
number: this.number,
|
||||
models: { soe: this.soe },
|
||||
mode: 'create'
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
this.soe.fundings(this.pic, this.number)
|
||||
.then(this.onFundingsUpdate.bind(this));
|
||||
|
||||
app.events.channel.dispatchEvent(new Event('soe_update'))
|
||||
}
|
||||
}
|
||||
|
||||
async onEditFeedback(event) {
|
||||
//event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
console.log(event.currentTarget.dataset.id)
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/soe/dialogs/SoeFeedbackFormDialog',
|
||||
{ title: 'Editing feedback' },
|
||||
{
|
||||
pic: this.pic,
|
||||
number: this.number,
|
||||
id: event.currentTarget.dataset.id,
|
||||
models: { soe: this.soe },
|
||||
mode: 'edit'
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(result) {
|
||||
this.soe.fundings(this.pic, this.number)
|
||||
.then(this.onFundingsUpdate.bind(this));
|
||||
|
||||
app.events.channel.dispatchEvent(new Event('soe_update'));
|
||||
}
|
||||
}
|
||||
|
||||
async onHistoryFeedback(event) {
|
||||
//event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/soe/dialogs/SoeFeedbackHistoryDialog',
|
||||
{ title: 'Feedbacks History' },
|
||||
{
|
||||
id: event.currentTarget.dataset.id,
|
||||
models: { soe: this.soe }
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SoeFeedbacksView', SoeFeedbacksView);
|
||||
@@ -0,0 +1,62 @@
|
||||
<style>
|
||||
.soe-feedback.form {
|
||||
min-width: 60vw;
|
||||
}
|
||||
.soe-feedback.form label {
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<div class="soe-feedback form">
|
||||
|
||||
<div>
|
||||
<label xsmall>Funding source</label>
|
||||
<select eicselect name="fundingSource" data-path="" multiple class="required" placeholder="Please select funding source(s)">
|
||||
${app.meta.toOptions(app.meta.getCollection('soe-fundings'), null, true).map(o => o.outerHTML).join('')}
|
||||
</select>
|
||||
<input eicinput name="fundingSourceOther" data-path="" type="text" class="" value="" placeholder="please specify source" />
|
||||
</div>
|
||||
<div class="cols-3">
|
||||
<div>
|
||||
<label xsmall>Start date of financial support</label>
|
||||
<input eicinput name="cutoffDate" data-path="" type="date" value="" class="required" placeholder="" />
|
||||
</div>
|
||||
<div>
|
||||
<label xsmall>Funding amount</label>
|
||||
<input eicinput name="fundingAmount" data-path="" data-type="number" min="0" type="currency" value="" placeholder="Leave blank if no funding applies" />
|
||||
</div>
|
||||
<div>
|
||||
<label xsmall>Duration (in month) of the financial support</label>
|
||||
<input eicinput name="duration" data-path="" data-type="number" min="0" type="number" value="" class="required" placeholder="" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label xsmall>Additional Details</label>
|
||||
<ul bulleted xsmall>
|
||||
<li>Name and status of the support scheme/programme (e.g. open, closed, continuously open), overall call budget</li>
|
||||
<li>Co-funding rate (e.g. lower or the same as Horizon Europe)</li>
|
||||
<li>Use of GBER Article 25a (applicable state aid rules for projects awarded Seal of Excellence)</li>
|
||||
<li>Do you support the investment component of the project</li>
|
||||
</ul>
|
||||
<textarea eictextarea name="additionalDetails" data-path="" class="" placeholder="(optional)"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label xsmall>Is this SoE a success story?</label>
|
||||
<ul bulleted xsmall>
|
||||
<li>Do you consider the project to have a high innovative potential or technological impact</li>
|
||||
<li>Project attracts additional investments (e.g. private VC, winner of an innovation prize or other grant/subsidy)</li>
|
||||
</ul>
|
||||
<div class="cols-3">
|
||||
<select eicselect name="successStory" data-path="" class="required" data-type="boolean" placeholder="" >
|
||||
<option value=""></option>
|
||||
<option value="true">yes</option>
|
||||
<option value="false">no</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<textarea eictextarea name="successStoryDetails" data-path="" class="required" placeholder="Explain why"></textarea>
|
||||
</div>
|
||||
<div eicalert danger class="confirmation">
|
||||
<input eiccheckbox type="checkbox" name="confirmDelete" data-type="ignore" label="Yes, I want to remove this feedback" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,151 @@
|
||||
class SoeFeedbackFormDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
},
|
||||
{
|
||||
label: 'Delete',
|
||||
onclick: this.delete.bind(this),
|
||||
severity: 'danger',
|
||||
role: 'delete',
|
||||
disabled: false
|
||||
},
|
||||
{
|
||||
label: 'Send',
|
||||
onclick: this.send.bind(this),
|
||||
severity: 'primary',
|
||||
role: 'send',
|
||||
disabled: false
|
||||
}
|
||||
]
|
||||
|
||||
// meta item ID for 'Other source'
|
||||
ALT_SOURCE_ID = 'dTVTpQG-KRzej86IwE0v7TA';
|
||||
|
||||
constructor(options) { super(options); }
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
this.pic = options.pic;
|
||||
this.number = options.number;
|
||||
this.id = options.id;
|
||||
this.mode = options.mode || 'create';
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
this.source = this.components.find(o => o.el.name == 'fundingSource')
|
||||
this.source.el.addEventListener('change', this.onSourceChange.bind(this))
|
||||
this.sourceExtra = this.components.find(o => o.el.name == 'fundingSourceOther')
|
||||
ui.hide(this.sourceExtra.el)
|
||||
this.scheme = this.components.find(o => o.el.name == 'cutoffDate')
|
||||
this.amount = this.components.find(o => o.el.name == 'fundingAmount')
|
||||
this.details = this.components.find(o => o.el.name == 'additionalDetails')
|
||||
this.duration = this.components.find(o => o.el.name == 'duration')
|
||||
this.successStory = this.components.find(o => o.el.name == 'successStory')
|
||||
this.successStory.el.addEventListener('change', this.onSuccessStoryChange.bind(this))
|
||||
this.successStoryDetails = this.components.find(o => o.el.name == 'successStoryDetails')
|
||||
ui.hide(this.successStoryDetails.container)
|
||||
this.successStoryDetails.el.classList.remove('required')
|
||||
|
||||
this.confirmDelete = this.components.find(component => component.el.name == 'confirmDelete');
|
||||
this.confirmation = this.find('.confirmation');
|
||||
ui.hide(this.confirmation);
|
||||
|
||||
this.form = new Form(this.find('.soe-feedback.form'));
|
||||
}
|
||||
|
||||
DOMContentFocused() {
|
||||
|
||||
this.actions.find(o => o.role == 'delete').button.hide();
|
||||
|
||||
if(this.mode == 'edit') {
|
||||
this.fill(this.soe.getFeedback(this.id));
|
||||
} else {
|
||||
if(!this.soe.hasPrivilege('create')) {
|
||||
this.actions.find(o => o.role == 'send').button.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fill(feedback) {
|
||||
this.source.value = feedback.fundingSource
|
||||
this.sourceExtra.value = feedback.fundingSourceOther
|
||||
this.scheme.value = feedback.cutoffDate
|
||||
this.amount.value = feedback.fundingAmount
|
||||
this.details.value = feedback.additionalDetails
|
||||
this.duration.value = feedback.duration
|
||||
this.successStory.value = feedback.successStory
|
||||
this.successStoryDetails.value = feedback.successStoryDetails
|
||||
|
||||
if(this.soe.hasPrivilege('delete')) {
|
||||
this.actions.find(o => o.role == 'delete').button.show();
|
||||
}
|
||||
|
||||
if(!this.soe.hasPrivilege('update')) {
|
||||
this.actions.find(o => o.role == 'send').button.hide();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onSourceChange(event) {
|
||||
if(this.source.value.includes(this.ALT_SOURCE_ID)) {
|
||||
ui.show(this.sourceExtra.el)
|
||||
} else {
|
||||
this.sourceExtra.value = '';
|
||||
ui.hide(this.sourceExtra.el)
|
||||
}
|
||||
}
|
||||
|
||||
onSuccessStoryChange(event) {
|
||||
if(this.successStory.value == 'true') {
|
||||
ui.show(this.successStoryDetails.container)
|
||||
this.successStoryDetails.el.classList.add('required')
|
||||
} else {
|
||||
ui.hide(this.successStoryDetails.container)
|
||||
this.successStoryDetails.el.classList.remove('required')
|
||||
}
|
||||
}
|
||||
|
||||
send() {
|
||||
ui.hide(this.confirmation);
|
||||
|
||||
if(this.form.validate()) {
|
||||
switch(this.mode) {
|
||||
case 'create':
|
||||
this.actions.find(o => o.role == 'send').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.soe.create(this.pic, this.number, this.form.value)
|
||||
.then( payload => this.commit(true) )
|
||||
break;
|
||||
case 'edit':
|
||||
this.actions.find(o => o.role == 'send').button.loading = true;
|
||||
this.actions.find(o => o.role == 'delete').button.disabled = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.soe.update(this.pic, this.number, this.id, this.form.value)
|
||||
.then( payload => this.commit(true) )
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
delete() {
|
||||
ui.show(this.confirmation);
|
||||
|
||||
if(this.confirmDelete.el.checked) {
|
||||
this.actions.find(o => o.role == 'send').button.disabled = true;
|
||||
this.actions.find(o => o.role == 'delete').button.loading = true;
|
||||
this.actions.find(o => o.role == 'cancel').button.disabled = true;
|
||||
this.soe.delete( this.pic, this.number, this.id )
|
||||
.then( payload => this.commit(true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SoeFeedbackFormDialog', SoeFeedbackFormDialog);
|
||||
@@ -0,0 +1,6 @@
|
||||
<style>
|
||||
.soe-feedback.history { min-width: 60vw; }
|
||||
</style>
|
||||
<div class="soe-feedback history">
|
||||
<div eicdatagrid></div>
|
||||
</div>
|
||||
@@ -0,0 +1,48 @@
|
||||
class SoeFeedbackHistoryDialog extends EICDialogContent {
|
||||
|
||||
actions = [
|
||||
{
|
||||
label: 'Cancel',
|
||||
onclick: this.cancel.bind(this),
|
||||
severity: 'secondary',
|
||||
role: 'cancel'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
constructor(options) { super(options); }
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
this.id = options.id;
|
||||
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.components = ui.eicfy(this.el);
|
||||
|
||||
console.log('', this.soe.getFeedback(this.id))
|
||||
let list = new DataGrid(this.find('[eicdatagrid]'), {
|
||||
headers: [
|
||||
{ label:'Date', type: 'dateTime'},
|
||||
{ label:'Action'},
|
||||
{ label:'User'},
|
||||
]
|
||||
})
|
||||
|
||||
for(let item of this.soe.getFeedback(this.id).statusHistory) {
|
||||
list.addRow(0, [
|
||||
item.dateTime,
|
||||
item.value,
|
||||
item.author
|
||||
]);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fill() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('SoeFeedbackHistoryDialog', SoeFeedbackHistoryDialog);
|
||||
@@ -0,0 +1,74 @@
|
||||
<style>
|
||||
.submission.short-form > header {
|
||||
background: url('/app/assets/images/cards/submission-card.jpg');
|
||||
background-position: center;
|
||||
background-size: cover !important;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.submission.short-form textarea { min-height: 240px; }
|
||||
.submission.short-form input[data-path=""][name="PICNumber"],
|
||||
.submission.short-form input[data-path=""][name="CEOPhoneNumberCode"],
|
||||
.submission.short-form input[data-path=""][name="CompanyPostalCode"] { min-width: 180px; }
|
||||
.submission.short-form .tools .contributors { white-space: nowrap; }
|
||||
.submission.short-form .tools .contributors [eicchip],
|
||||
.submission.short-form .tools .contributors [eicchip] label { cursor: pointer; }
|
||||
</style>
|
||||
<article eiccard media class="submission short-form legacy">
|
||||
<header>
|
||||
<h1>Short proposal ${models.submission.data.ProposalId}</h1>
|
||||
<h2>
|
||||
<span eicchip primary>short ${models.submission.data.version}</span>
|
||||
<span eicchip primary>${models.submission.data.status}</span>
|
||||
${models.submission.data.resubmitted ? `<span eicchip accent>resubmission</span>`:''}
|
||||
</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicalert danger>
|
||||
<p small><span danger>Please note that after <b>December 31st 2023 at 23:59 (GMT +1)</b>, it will no longer be possible to submit a short proposal via the current EIC Platform.</span></p>
|
||||
<div small>
|
||||
Nonetheless, the platform will remain accessible to consult previous proposals, drafts, results and to access the coaching module.
|
||||
Should you intend to submit a new short proposal as of <b>January 3, 2024</b> you can do so via the <a href="https://ec.europa.eu/info/funding-tenders/opportunities/portal/" target="_blank" title="Funding and Tenders Portal">Funding and Tenders Portal</a>.
|
||||
Please note that the templates and rules for the short proposal will change under the <a href="https://eic.ec.europa.eu/eic-2024-work-programme_en" target="_blank" title="EIC Work Programme 2024">EIC Work Programme 2024</a>.
|
||||
We invite you to read them carefully. The new rules will come into force from <b>January 1, 2024</b>.
|
||||
More detailed information will be available shortly on our <a href="https://eic.ec.europa.eu/eic-accelerator-application-platform-frequently-asked-questions_en" target="_blank" title="EIC Website">website</a>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="tools cols-2 right">
|
||||
<div>
|
||||
<div class="updated">last saved: <span class="date"></span> by <span class="contributor"></span></div>
|
||||
<div class="created">created: <span class="date"></span> by <span class="contributor"></span></div>
|
||||
</div>
|
||||
<div class="contributors"></div>
|
||||
</div>
|
||||
<div eicalert info>
|
||||
This form has been extracted from the previous submission platform and is filled a deprecated submission template.
|
||||
You cannot make any more changes to it or use it for submission. If it was not submitted at the time, please start over your proposal using the latest template. If you submitted your proposal at the time with this template, you are still able to see the result of the evaluation through the "submission" tab. Thank you for your understanding.
|
||||
</div>
|
||||
<div class="form cols-2 left">
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="what">What</li>
|
||||
<li data-target="why">Why</li>
|
||||
<li data-target="who">Who</li>
|
||||
<li data-target="how">How</li>
|
||||
<li data-target="amount">How much</li>
|
||||
<li data-target="whom">For whom</li>
|
||||
<li data-target="impact">Impact</li>
|
||||
<li data-target="documents">Documents</li>
|
||||
<li data-target="status">Submission</li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div class="content" data-part="what"></div>
|
||||
<div class="content" data-part="why"></div>
|
||||
<div class="content" data-part="who"></div>
|
||||
<div class="content" data-part="how"></div>
|
||||
<div class="content" data-part="amount"></div>
|
||||
<div class="content" data-part="whom"></div>
|
||||
<div class="content" data-part="impact"></div>
|
||||
<div class="content" data-part="documents"></div>
|
||||
<div class="content" data-part="status"></div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,175 @@
|
||||
class SubmissionShortForm2022View extends EICDomContent {
|
||||
|
||||
options = {
|
||||
mode: 'read',
|
||||
}
|
||||
|
||||
chunks = [];
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
|
||||
// forcing read mode for legacy proposals
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
ui.eicfy(this.el);
|
||||
|
||||
this.tabs = new Tab();
|
||||
this.tabs.addTabs(this.findAll('.tabs-extended menu li'), this.findAll('.content'))
|
||||
|
||||
this.pic = options.profile.pic;
|
||||
this.number = options.profile.number;
|
||||
|
||||
this.form = new Form(this.find('.form'));
|
||||
|
||||
this.members.list(this.pic,this.number).then(this.fillContributors.bind(this))
|
||||
|
||||
this.fill(this.options.mode);
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
let myTabsPath = 'projects/submissions/2022/tabs/'
|
||||
|
||||
this.find('.tools .created .date').innerText = (new Date(this.submission.data.CreatedAt)).toLocaleString("en-DE");
|
||||
this.find('.tools .created .contributor').innerText = `(unknown user)`;
|
||||
|
||||
let queue = [
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022WhatView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('what', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022WhyView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('why', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022WhoView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('who', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022HowView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('how', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022AmountView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('amount', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022WhomView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('whom', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022ImpactView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('impact', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022DocumentsView', {}, { model: this.submission })
|
||||
.then( view => this.appendTab('documents', view)),
|
||||
]
|
||||
|
||||
if(this.submission.data.status == 'draft') {
|
||||
|
||||
} else {
|
||||
queue.push(
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2022StatusView', {}, { model: this.submission } )
|
||||
.then( view => {
|
||||
this.appendTab('status', view);
|
||||
this.statusTab = view;
|
||||
view.el.addEventListener('withdraw', this.withdraw.bind(this));
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
Promise.allSettled(queue)
|
||||
.then((results) => {
|
||||
this.setupMode(mode)
|
||||
this.loaded = true;
|
||||
});
|
||||
|
||||
this.refreshUpdated({
|
||||
date: this.submission.data.UpdatedAt,
|
||||
contributor: {
|
||||
firstname: 'import',
|
||||
lastname: ''
|
||||
}})
|
||||
}
|
||||
|
||||
setupMode(mode) {
|
||||
|
||||
this.mode = mode;
|
||||
|
||||
for(let chunk of this.chunks) {
|
||||
chunk.view.options.mode = mode;
|
||||
chunk.view.fill(mode);
|
||||
}
|
||||
}
|
||||
|
||||
appendTab(target, view) {
|
||||
this.chunks.push( {target: target, view: view} );
|
||||
this.find(`.content[data-part="${target}"]`).append(view.el)
|
||||
}
|
||||
|
||||
fillContributors(data) {
|
||||
let metrics = { contributors: 0, pending: 0 }
|
||||
this.find('.contributors').innerHTML = '';
|
||||
for(let item of data) {
|
||||
if(item.status == 'active') metrics.contributors++;
|
||||
if(item.status == 'pending') metrics.pending++;
|
||||
}
|
||||
this.find('.contributors').innerHTML = '';
|
||||
if(metrics.contributors > 0) {
|
||||
let chip = new Chip(null, {label: 'contributors', severity: 'primary', badge: metrics.contributors});
|
||||
chip.el.addEventListener('click', this.onContributorsEdit.bind(this))
|
||||
this.find('.contributors').append(chip.el);
|
||||
}
|
||||
if(metrics.pending > 0) {
|
||||
let chip = new Chip(null, {label: 'pending requests', severity: 'warning', badge: metrics.pending})
|
||||
chip.el.addEventListener('click', this.onContributorsEdit.bind(this))
|
||||
this.find('.contributors').append(chip.el);
|
||||
}
|
||||
}
|
||||
|
||||
async withdraw(event) {
|
||||
this.submission.withdraw(this.pic, this.number, this.form.value)
|
||||
.then(async payload => {
|
||||
this.dispatchUpdate();
|
||||
ui.growl.append('Your proposal has been successfully withdrawn', 'success');
|
||||
this.unload();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
async onContributorsEdit(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/submissions/dialogs/SubmissionFormAccessDialog',
|
||||
{ title: 'Contributors to this proposal' }, {
|
||||
pic: this.pic,
|
||||
number: this.number,
|
||||
models: {
|
||||
proposal: this.members,
|
||||
organisation: new ApplicantMembersModel(['list'])
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
this.members.list(this.pic,this.number)
|
||||
.then(this.fillContributors.bind(this));
|
||||
|
||||
}
|
||||
|
||||
refreshUpdated(data) {
|
||||
this.find('.tools .updated .date').innerText = (new Date(data.date)).toLocaleString("en-DE");
|
||||
this.find('.tools .updated .contributor').innerText = `${data.contributor.firstname} ${data.contributor.lastname}`;
|
||||
}
|
||||
|
||||
DOMContentResized() {
|
||||
super.DOMContentResized();
|
||||
|
||||
let device = this.el.getAttribute('device');
|
||||
let tabs = this.find('.tabs-extended');
|
||||
|
||||
switch(device) {
|
||||
case 'desktop':
|
||||
ui.show(tabs);
|
||||
let current = tabs.querySelector('.tab-selected');
|
||||
let part = current.getAttribute('data-target');
|
||||
this.findAll('.content[data-part]').forEach(content => { if(content.getAttribute('data-part') != part) ui.hide(content) });
|
||||
break;
|
||||
default:
|
||||
ui.hide(tabs);
|
||||
this.findAll('.content[data-part]').forEach(content => { ui.show(content) })
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022View', SubmissionShortForm2022View);
|
||||
@@ -0,0 +1,40 @@
|
||||
<h2>How much?</h2>
|
||||
<div>
|
||||
<label>Please indicate which EIC Support you intend to request</label>
|
||||
<select eicselect name="DiagnosticBudgetType" data-path="" data-type="text" class="required">
|
||||
<option></option>
|
||||
<option value="0">Blended Finance</option>
|
||||
<option value="1">Grant First</option>
|
||||
<option value="2">Grant Only</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="cols-2">
|
||||
<div>
|
||||
<h3>Innovative activities</h3>
|
||||
<div>
|
||||
<label>How much do you think that the innovation activities related to your project will cost ?</label>
|
||||
<input eicinput type="currency" name="TotalEstimatedInnovationCostProject" data-path="" data-type="text" maxlen="100" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Please detail your other funding sources</label>
|
||||
<textarea eictextarea name="OtherSourcesInnovation" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Market Deployment / Scale-Up activities</h3>
|
||||
<div>
|
||||
<label>How much do you think the EIC should provide you in the form of investment for the market deployment/scale up activities related to your project ?</label>
|
||||
<input eicinput type="currency" name="InvestmentMarketComponent" data-path="" data-type="text" maxlen="100" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>If you have other ressources for market deployment, please specify the amount</label>
|
||||
<input eicinput type="currency" name="TotalInvestmentMarketFromOtherSources" data-path="" data-type="text" maxlen="100" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Please detail your other funding sources</label>
|
||||
<textarea eictextarea name="OtherSourcesMarket" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
class SubmissionShortForm2022AmountView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022AmountView', SubmissionShortForm2022AmountView);
|
||||
@@ -0,0 +1,34 @@
|
||||
<style>
|
||||
div[data-part="documents"] div.documentFiles button {
|
||||
margin: 1vw;
|
||||
flex: auto;
|
||||
}
|
||||
div[data-part="documents"] div.documentFiles button i[class*="icon"] {
|
||||
margin-left: 1vw;
|
||||
font-weight: bold;
|
||||
font-size: var(--eicui-base-font-size-xl);
|
||||
}
|
||||
div[data-part="documents"] embed.EmbedDocument {
|
||||
min-height: 50vw;
|
||||
display: none;
|
||||
}
|
||||
div[data-part="documents"] embed.EmbedVideo {
|
||||
min-height: 20vw;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||||
<h2>Your uploaded documents</h2>
|
||||
<article eiccard class="documents">
|
||||
<section>
|
||||
<div class="documentFiles cols-2"></div>
|
||||
<embed type="application/pdf" class="EmbedDocument" src=""/>
|
||||
<div class="videoStage">
|
||||
<video id="video" width="675" height="380" controls autoplay class="EmbedVideo"></video>
|
||||
<div eicalert warning class="noStreamingWarn">Looks like your browser does not support video streaming.</div>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<div class="cols-2"></div>
|
||||
</footer>
|
||||
</article>
|
||||
@@ -0,0 +1,102 @@
|
||||
class SubmissionShortForm2022DocumentsView extends SubmissionShortFormTabView {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
let components = ui.eicfy(this.el);
|
||||
this.model = options.model;
|
||||
|
||||
this.AwsDownload = new AwsFileDownload({
|
||||
pdf : { url: app.config.api['/S3Files'].viewDocument.uri,
|
||||
method: app.config.api['/S3Files'].viewDocument.method,
|
||||
headers: { 'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include'
|
||||
},
|
||||
})
|
||||
|
||||
this.embedDocument = this.find('.EmbedDocument')
|
||||
this.videoStage = this.find('.videoStage')
|
||||
this.embedVideo = this.find('.EmbedVideo')
|
||||
this.noStreamingWarn = this.find('.noStreamingWarn')
|
||||
|
||||
this.videoStage.style.display = 'none'
|
||||
this.embedDocument.style.display = 'none'
|
||||
this.noStreamingWarn.style.display = 'none'
|
||||
this.filesContainer = this.find('.documentFiles')
|
||||
|
||||
if(this.model.data.PdfUploadedFilename || this.model.data.VideoUploadedFilename) {
|
||||
if(this.model.data.PdfUploadedFilename) {
|
||||
this.pdfButton = new Button(null, {
|
||||
icon: 'icon-pdf',
|
||||
label: this.model.data.PdfOriginalFilename,
|
||||
severity: 'primary',
|
||||
disabled: false,
|
||||
badge: false,
|
||||
size: 'small',
|
||||
hint: 'View the PDF',
|
||||
})
|
||||
this.pdfButton.el.addEventListener('click', this.showPdf.bind(this))
|
||||
this.find('footer div').append(this.pdfButton.el)
|
||||
}
|
||||
if(this.model.data.VideoUploadedFilename) {
|
||||
this.vodButton = new Button(null, {
|
||||
icon: 'icon-youtube',
|
||||
label: this.model.data.VideoOriginalFilename,
|
||||
severity: 'primary',
|
||||
disabled: false,
|
||||
badge: false,
|
||||
size: 'small',
|
||||
hint: 'View the video',
|
||||
})
|
||||
this.vodButton.el.addEventListener('click', this.showVideo.bind(this))
|
||||
this.find('footer div').append(this.vodButton.el)
|
||||
}
|
||||
} else {
|
||||
this.filesContainer.innerHtml = 'No uploaded Documents'
|
||||
}
|
||||
}
|
||||
|
||||
async showPdf(event) {
|
||||
this.pdfButton.loading = true
|
||||
let awsUrl = await this.AwsDownload.getDownloadUrl('pdf', this.model.data.PdfUploadedFilename)
|
||||
this.pdfButton.loading = false
|
||||
if(!awsUrl) return
|
||||
ui.hide(this.videoStage)
|
||||
|
||||
// Safari workaround : You can't just replace the src like with every other browser,
|
||||
// you need to create a new embed node with the src already in.
|
||||
let clone = this.embedDocument.cloneNode()
|
||||
clone.setAttribute('src',awsUrl)
|
||||
this.embedDocument.replaceWith(clone)
|
||||
this.embedDocument=clone
|
||||
ui.show(this.embedDocument)
|
||||
}
|
||||
|
||||
async showVideo(event) {
|
||||
this.videoStage.style.display = 'block'
|
||||
this.embedDocument.style.display = 'none'
|
||||
if(!Hls.isSupported()) {
|
||||
this.noStreamingWarn.style.display = 'block'
|
||||
return
|
||||
}
|
||||
this.noStreamingWarn.style.display = 'none'
|
||||
let noextName = this.model.data.VideoUploadedFilename.substr(0,this.model.data.VideoUploadedFilename.lastIndexOf('.'))
|
||||
let videoUrl = app.config.api['/S3Files'].viewVideo.uri+`${noextName}.m3u8`
|
||||
let config = {
|
||||
debug: true,
|
||||
xhrSetup: function (xhr,url) {
|
||||
xhr.withCredentials = true; // do send cookie
|
||||
}
|
||||
};
|
||||
var hls = new Hls(config);
|
||||
hls.loadSource(videoUrl)
|
||||
hls.attachMedia(this.embedVideo);
|
||||
hls.on(Hls.Events.MANIFEST_PARSED,(() => {
|
||||
this.embedVideo.play();
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
fill() {}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022DocumentsView', SubmissionShortForm2022DocumentsView);
|
||||
@@ -0,0 +1,53 @@
|
||||
<h2>How?</h2>
|
||||
<!--
|
||||
<div>
|
||||
<label>Idea or technology based</label>
|
||||
<input eiccheckbox type="checkbox" name="ideaBased" value="true" label="Idea based" />
|
||||
<input eiccheckbox type="checkbox" name="techBased" value="true" label="Technology based" />
|
||||
</div>
|
||||
-->
|
||||
<div class="Use case(s)">
|
||||
<div class="cases"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Describe the deliverables</label>
|
||||
<textarea eictextarea name="Deliverables" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>What are the risks of failure?</label>
|
||||
<div class="risks"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Why can't you do it without the EIC? Why not other funding sources including National/Regional public programmes?</label>
|
||||
<textarea eictextarea name="WhyCantWithoutEIC" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Current Technology Readiness Level</label>
|
||||
<select eicselect name="CurrentTrl" data-path="" data-type="text" class="required">
|
||||
<option></option>
|
||||
<option value="1">TRL1: Basic Research</option>
|
||||
<option value="2">TRL2: Technology Formulation</option>
|
||||
<option value="3">TRL3: Needs Validation</option>
|
||||
<option value="4">TRL4: Small Scale Prototype</option>
|
||||
<option value="5">TRL5: Large Scale Prototype</option>
|
||||
<option value="6">TRL6: Prototype System</option>
|
||||
<option value="7">TRL7: Demonstration System</option>
|
||||
<option value="8">TRL8: First Of A Kind Commercial System</option>
|
||||
<option value="9">TRL9: Full Commercial Application</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Expected Technology Readiness Level</label>
|
||||
<select eicselect name="ExpectedTrl" data-path="" data-type="text" class="required">
|
||||
<option></option>
|
||||
<option value="1">TRL1: Basic Research</option>
|
||||
<option value="2">TRL2: Technology Formulation</option>
|
||||
<option value="3">TRL3: Needs Validation</option>
|
||||
<option value="4">TRL4: Small Scale Prototype</option>
|
||||
<option value="5">TRL5: Large Scale Prototype</option>
|
||||
<option value="6">TRL6: Prototype System</option>
|
||||
<option value="7">TRL7: Demonstration System</option>
|
||||
<option value="8">TRL8: First Of A Kind Commercial System</option>
|
||||
<option value="9">TRL9: Full Commercial Application</option>
|
||||
</select>
|
||||
</div>
|
||||
@@ -0,0 +1,76 @@
|
||||
class SubmissionShortForm2022HowView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {}
|
||||
|
||||
fill() {
|
||||
super.fill();
|
||||
|
||||
if(this.model.data.RisksOfFailure) {
|
||||
for(let item of this.model.data.RisksOfFailure)
|
||||
this.find('.risks').append(this.createFailure(item));
|
||||
}
|
||||
|
||||
if(this.model.data.DiagnosticFunctions) {
|
||||
for(let item of this.model.data.DiagnosticFunctions)
|
||||
this.find('.cases').append(this.createCase(item));
|
||||
}
|
||||
}
|
||||
|
||||
createFailure(item) {
|
||||
let card = ui.create(`<div class="risk">
|
||||
<div>
|
||||
<label>Risk type</label>
|
||||
<select eicselect disabled name="Type" data-path="RisksOfFailure[]" data-type="text">
|
||||
<option></option>
|
||||
<option value="0">Technological</option>
|
||||
<option value="1">Financial</option>
|
||||
<option value="2">Commercial</option>
|
||||
<option value="3">Regulatory</option>
|
||||
<option value="4">Societal acceptance</option>
|
||||
<option value="5">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Risk description</label>
|
||||
<textarea eictextarea disabled name="Description" data-path="RisksOfFailure[]" data-type="text"></textarea>
|
||||
</div>
|
||||
</div>`);
|
||||
|
||||
let components = ui.eicfy(card);
|
||||
components.find(item => item.el.name=="Type").value = item.Type;
|
||||
components.find(item => item.el.name=="Description").value = item.Description;
|
||||
|
||||
return card;
|
||||
}
|
||||
|
||||
createCase(item) {
|
||||
let card = ui.create(`<div class="risk">
|
||||
<div>
|
||||
<label>Function</label>
|
||||
<input eicinput disabled name="Function" data-path="" data-type="text" value="${item.DiagnosticFunction.Name}" />
|
||||
</div>
|
||||
${ item.DiagnosticFunctionFeature.map(feature =>
|
||||
`<div>
|
||||
<label>Feature name</label>
|
||||
<input eicinput disabled name="Feature" data-path="" data-type="text" value="${feature.Name}" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Needed knowledge</label>
|
||||
<textarea eictextarea disabled name="NeededKnowledge" data-path="" data-type="text">${feature.NeededKnowledge}</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Technology</label>
|
||||
<textarea eictextarea disabled name="Technology" data-path="" data-type="text">${feature.Technology}</textarea>
|
||||
</div>`
|
||||
).join('')
|
||||
}
|
||||
|
||||
</div>`);
|
||||
|
||||
ui.eicfy(card);
|
||||
|
||||
return card;
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022HowView', SubmissionShortForm2022HowView);
|
||||
@@ -0,0 +1,25 @@
|
||||
<h2>What impact?</h2>
|
||||
<div>
|
||||
<label>Current Business Readiness Level</label>
|
||||
<select eicselect name="CurrentBrl" data-path="" data-type="text" class="required">
|
||||
<option></option>
|
||||
<option value="1">Basic Research</option>
|
||||
<option value="2">Needs formulation</option>
|
||||
<option value="3">Needs Validation</option>
|
||||
<option value="4">Small scale stakeholder campaign</option>
|
||||
<option value="5">Large scale early adopter campaign</option>
|
||||
<option value="6">Proof of traction</option>
|
||||
<option value="7">Proof of satisfaction</option>
|
||||
<option value="8">Proof of scalability</option>
|
||||
<option value="9">Proof of stability</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Can you describe potential societal or industrial applications? Will your innovation have broader societal, economic, environmental or climate impacts?</label>
|
||||
<textarea eictextarea name="Application" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>What are the payoffs for your company in case of success? How do you see your company in 5 years</label>
|
||||
<textarea eictextarea name="Payoffs" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
class SubmissionShortForm2022ImpactView extends SubmissionShortFormTabView {}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022ImpactView', SubmissionShortForm2022ImpactView);
|
||||
@@ -0,0 +1,114 @@
|
||||
<h2>Submission status</h2>
|
||||
<div class="cols-1 withdrawing">
|
||||
<h5>Your proposal is on the way</h5>
|
||||
<div eicalert success>
|
||||
<p>We acknowledge the successful submission of your proposal on <span class="date"></span>. Thank you for taking the time to prepare and submit it to us.</p>
|
||||
</div>
|
||||
<p>At this stage, no further action is required from your side. Our team will now proceed with the evaluation process. Once the evaluation is completed, we will notify you about the outcome.</p>
|
||||
<h5>Withdrawing your proposal</h5>
|
||||
<p>If you choose to withdraw your proposal, it is taken out of the ongoing evaluation process without affecting the resubmission limits as per the current <a href="https://eic.ec.europa.eu/eic-2023-work-programme_en" target="_blank">EIC 2023 work programme</a>. As long as you remain eligible, you will still have the opportunity to resubmit another proposal in the future.</p>
|
||||
<div eicalert danger class="withdraw-confirm">
|
||||
<input eiccheckbox class="" type="checkbox" name="withdrawConfirmation" data-path="" data-type="ignore" value="true" label="Hereby, I confirm that I want to withdraw my proposal from the evaluation process" />
|
||||
</div>
|
||||
<div class="cols-1 center"><button eicbutton danger class="withdraw">Withdraw my proposal</button></div>
|
||||
<hr/>
|
||||
</div>
|
||||
<div class="withdrawn">
|
||||
<div eicalert warning>You withdrawn this proposal</div>
|
||||
</div>
|
||||
<div class="cols-1 fast-track">
|
||||
<h5>Your proposal is on the fast tracks</h5>
|
||||
<p>
|
||||
Your proposal has been selected for a Fast-Track process.
|
||||
This means that you will now skip the short proposal submission and proceed directly to the full proposal submission and evaluation.
|
||||
</p>
|
||||
</div>
|
||||
<div class="cols-1 evaluation">
|
||||
|
||||
<div class="go">
|
||||
<div eicalert success>
|
||||
<h5>Congratulations! Your proposal has been evaluated</h5>
|
||||
<p>We are happy to inform you that your proposal submitted on <span class="date">${(new Date(model.data.UpdatedAt)).toLocaleDateString("en-DE")}</span> has been awarded an overall GO by our experts.</p>
|
||||
<div class="cols-2">
|
||||
<!--<button eicbutton class="view-submission-go" icon="icon-pdf"><span>Download the submission</span></button>-->
|
||||
<button eicbutton primary class="view-evaluation-go" icon="icon-pdf"><span>Download the evaluation</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<p>You are on the eve of a new adventure. You now benefit of a new batch of services including:</p>
|
||||
<ul bulleted>
|
||||
<li>access to the full proposal submission (you have a full year to submit)</li>
|
||||
<li>3 days of coaching</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="no-go">
|
||||
<div eicalert danger>
|
||||
<p>We regret to inform you that your proposal submitted on <span class="date">${(new Date(model.data.updated.date)).toLocaleDateString("en-DE")}</span> has received an overall NO GO.</p>
|
||||
<div class="cols-2">
|
||||
<!--<button eicbutton class="view-submission-no-go" icon="icon-pdf"><span>Download the submission</span></button>-->
|
||||
<button eicbutton primary class="view-evaluation-no-go" icon="icon-pdf"><span>Download the evaluation</span></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="first-submission">
|
||||
<p>
|
||||
Please review the proposal evaluation comments as you will have the opportunity to rebut the experts' comments and summarise any changes made to the proposal, in the event that you decide to resubmit.
|
||||
Please note, that as per the <a href="https://ec.europa.eu/info/funding-tenders/opportunities/docs/2021-2027/horizon/wp-call/2023/wp_horizon-eic-2023_en.pdf" target="_blank" title="EIC Work Programme 2023">Work Programme 2023</a> (Table 8), applicants are limited to two unsuccessful attempts at this step, even if the innovations/ideas are different between attempts. You may start over with a blank template from scratch by returning to the dashboard and creating a new version or you can reuse this proposal as a basis for your next attempt. If you wish to reuse the proposal please click on the button below:
|
||||
</p>
|
||||
</div>
|
||||
<div class="second-submission">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="go-full">
|
||||
<h5>How to proceed to the Full Proposal?</h5>
|
||||
<p>
|
||||
Your next step is to submit, within exactly 12 months from the date of receiving your positive short proposal result, a deeper detailed description of your proposal (known as the full proposal).
|
||||
In order to do so, please follow one of the links below.
|
||||
Each link refers to a different topic of which more details is provided. Once the desired topic is identified, follow the 'apply to this topic' related link:
|
||||
</p>
|
||||
<ul bulleted>
|
||||
<li>
|
||||
<label><a href="https://ec.europa.eu/info/funding-tenders/opportunities/docs/2021-2027/horizon/wp-call/2023/wp_horizon-eic-2023_en.pdf#page" target="_blank" title="EIC Accelerator Open 2023">EIC Accelerator Open 2023</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATOROPEN-01" target="_blank" title="EIC Accelerator Open 2023">Apply to this topic</a></div>
|
||||
</li>
|
||||
<li>
|
||||
<label><a href="https://eic.ec.europa.eu/events/eic-accelerator-energy-storage-challenge-information-day-2023-02-01_en" target="_blank" title="EIC Accelerator Challenge: Energy storage">EIC Accelerator Challenge: Energy storage</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATORCHALLENGES-03" target="_blank" title="EIC Accelerator Challenge: Energy storage">Apply to this topic</a></div>
|
||||
</li>
|
||||
<li>
|
||||
<label><a href="https://eic.ec.europa.eu/events/eic-accelerator-challenge-novel-technologies-resilient-agriculture-information-day-2023-01-31_en" target="_blank" title="EIC Accelerator Challenge: Novel technologies for resilient agriculture">EIC Accelerator Challenge: Novel technologies for resilient agriculture</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATORCHALLENGES-06" target="_blank" title="EIC Accelerator Challenge: Novel technologies for resilient agriculture">Apply to this topic</a></div>
|
||||
</li>
|
||||
<li>
|
||||
<label><a href="https://eic.ec.europa.eu/events/accelerator-challenge-emerging-semiconductor-and-quantum-technology-components-information-day-2023-02-09_en" target="_blank" title="EIC Accelerator Challenge: Emerging semiconductor or quantum technology components">EIC Accelerator Challenge: Emerging semiconductor or quantum technology components</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATORCHALLENGES-05" target="_blank" title="EIC Accelerator Challenge: Emerging semiconductor or quantum technology components">Apply to this topic</a></div>
|
||||
</li>
|
||||
<li>
|
||||
<label><a href="https://eic.ec.europa.eu/events/accelerator-challenge-aerosol-and-surface-decontamination-pandemic-management-information-day-2023-02-02_en" target="_blank" title="EIC Accelerator Challenge: Aerosol and surface decontamination for pandemic management">EIC Accelerator Challenge: Aerosol and surface decontamination for pandemic management</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATORCHALLENGES-02" target="_blank" title="EIC Accelerator Challenge: Aerosol and surface decontamination for pandemic management">Apply to this topic</a></div>
|
||||
</li>
|
||||
<li>
|
||||
<label><a href="https://eic.ec.europa.eu/events/eic-accelerator-space-challenge-information-day-2023-01-26_en" target="_blank" title="EIC Accelerator Challenge: Customer-driven, innovative space technologies and services">EIC Accelerator Challenge: Customer-driven, innovative space technologies and services</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATORCHALLENGES-07" target="_blank" title="EIC Accelerator Challenge: Customer-driven, innovative space technologies and services">Apply to this topic</a></div>
|
||||
</li>
|
||||
<li>
|
||||
<label><a href="https://eic.ec.europa.eu/events/eic-accelerator-challenge-novel-biomarker-based-assays-guide-personalised-cancer-treatment-2023-02-03_en" target="_blank" title="EIC Accelerator Challenge: Novel biomarker-based assays to guide personalised cancer treatment">EIC Accelerator Challenge: Novel biomarker-based assays to guide personalised cancer treatment</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATORCHALLENGES-01" target="_blank" title="EIC Accelerator Challenge: Novel biomarker-based assays to guide personalised cancer treatment">Apply to this topic</a></div>
|
||||
</li>
|
||||
<li>
|
||||
<label><a href="https://eic.ec.europa.eu/events/accelerator-challenge-new-european-bauhaus-and-architecture-engineering-and-construction-2023-02-02_en" target="_blank" title="EIC Accelerator Challenge: New European Bauhaus and Architecture, Engineering and Construction digitalization for decarbonisation">EIC Accelerator Challenge: New European Bauhaus and Architecture, Engineering and Construction digitalization for decarbonisation</a></label>
|
||||
<div><a href="https://ec.europa.eu/research/participants/submission/manage/screen/submission/create-draft/30648?topic=HORIZON-EIC-2023-ACCELERATORCHALLENGES-04" target="_blank" title="EIC Accelerator Challenge: New European Bauhaus and Architecture, Engineering and Construction digitalization for decarbonisation">Apply to this topic</a></div>
|
||||
</li>
|
||||
</ul>
|
||||
<p>We advise you also to consult the following documents to assist your preparations for the next step of your application:</p>
|
||||
<ul>
|
||||
<label><a href="https://eic.ec.europa.eu/guide-applicants_en" target="_blank" title="EIC Accelerator Guide for Applicants">EIC Accelerator Guide for Applicants</a></label>
|
||||
<label><a href="https://eic.ec.europa.eu/eic-frequently-asked-questions_en#eic-accelerator" target="_blank" title="EIC Accelerator General FAQs">EIC Accelerator General FAQs</a></label>
|
||||
<label><a href="https://eic.ec.europa.eu/eic-accelerator-application-platform-frequently-asked-questions_en" target="_blank" title="EIC Accelerator Platform related FAQs">EIC Accelerator Platform related FAQs</a></label>
|
||||
<label><a href="https://ec.europa.eu/info/funding-tenders/opportunities/docs/2021-2027/horizon/wp-call/2023/wp_horizon-eic-2023_en.pdf" target="_blank" title="EIC Work Programme 2023">EIC Work Programme 2023</a></label>
|
||||
<label><a href="https://eic.ec.europa.eu/system/files/2021-05/EIC%20Fund%20Investment%20Guidelines%20-%20Horizon%20Europe.pdf" target="_blank" title="EIC Fund Investment Guidelines">EIC Fund Investment Guidelines</a></label>
|
||||
<label><a href="" target="_blank" title="Model Grant Agreement used for EIC Actions under Horizon Europe">Model Grant Agreement used for EIC Actions under Horizon Europe</a></label>
|
||||
<label><a href="https://ec.europa.eu/info/funding-tenders/opportunities/docs/2021-2027/common/guidance/om_en.pdf" target="_blank" title="Funding & Tenders Portal Online Manual">Funding & Tenders Portal Online Manual</a></label>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,119 @@
|
||||
class SubmissionShortForm2022StatusView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {
|
||||
|
||||
this.AwsDownload = new AwsFileDownload({
|
||||
pdf : { url: app.config.api['/S3Files'].viewDocument.uri,
|
||||
method: app.config.api['/S3Files'].viewDocument.method,
|
||||
headers: { 'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include'
|
||||
},
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
super.fill(mode);
|
||||
|
||||
this.switchStatus(this.model.data.status);
|
||||
}
|
||||
|
||||
switchStatus(status) {
|
||||
ui.hide(this.find('.fast-track'));
|
||||
ui.hide(this.find('.withdrawing'));
|
||||
ui.hide(this.find('.evaluation'));
|
||||
ui.hide(this.find('.go-full'));
|
||||
ui.hide(this.find('.withdrawn'));
|
||||
|
||||
if(status == 'submitted') {
|
||||
this.withdraw = this.components.find(item => item.el.classList.contains('withdraw'));
|
||||
this.withdraw.addEventListener('click', this.onWithdraw.bind(this))
|
||||
this.withdrawConfirm = this.components.find(item => item.el.name == 'withdrawConfirmation');
|
||||
this.withdrawConfirm.disabled = false;
|
||||
|
||||
ui.show(this.find('.withdrawing'));
|
||||
ui.hide(this.find('.withdraw-confirm'));
|
||||
|
||||
if(!this.model.hasPrivilege('withdraw')) {
|
||||
ui.hide(this.find('button.withdraw'))
|
||||
}
|
||||
|
||||
this.find('.withdrawing span.date').innerText = (new Date(this.model.data.updated.date)).toLocaleDateString("en-DE")
|
||||
}
|
||||
|
||||
if(status == 'withdrawn') {
|
||||
ui.show(this.find('.withdrawn'));
|
||||
}
|
||||
|
||||
if(status == 'fast-track') {
|
||||
ui.show(this.find('.fast-track'));
|
||||
ui.show(this.find('.go-full'));
|
||||
}
|
||||
|
||||
if(status == 'evaluated') {
|
||||
|
||||
ui.show(this.find('.evaluation'));
|
||||
ui.hide(this.find('.evaluation .go'));
|
||||
ui.hide(this.find('.evaluation .no-go'));
|
||||
|
||||
if(this.model.data.evaluation) {
|
||||
|
||||
if(this.model.data.evaluation.outcome == 'go') {
|
||||
|
||||
let evalBt = this.components.find(item => item.el.classList.contains('view-evaluation-go'));
|
||||
evalBt.addEventListener('click', this.onDocumentDownload.bind(this, this.model.data.documents.esrShort.fileName))
|
||||
|
||||
ui.show(this.find('.evaluation .go'));
|
||||
ui.show(this.find('.go-full'));
|
||||
}
|
||||
|
||||
if(this.model.data.evaluation.outcome == 'no-go') {
|
||||
let evalBt = this.components.find(item => item.el.classList.contains('view-evaluation-no-go'));
|
||||
evalBt.addEventListener('click', this.onDocumentDownload.bind(this, this.model.data.documents.esrShort.fileName))
|
||||
|
||||
ui.hide(this.find('.evaluation .first-submission'));
|
||||
ui.hide(this.find('.evaluation .second-submission'));
|
||||
ui.show(this.find('.evaluation .no-go'));
|
||||
|
||||
if(this.model.data.resubmitted) {
|
||||
ui.show(this.find('.evaluation .second-submission'));
|
||||
} else {
|
||||
ui.show(this.find('.evaluation .first-submission'));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.error('No evaluation provided')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async onWithdraw(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
ui.show(this.find('.withdraw-confirm'));
|
||||
if(this.withdrawConfirm.el.checked) {
|
||||
this.el.dispatchEvent(new Event('withdraw'));
|
||||
}
|
||||
}
|
||||
|
||||
async onDocumentDownload(file, event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
if(!file) return
|
||||
let awsUrl = await this.AwsDownload.getDownloadUrl('pdf', file)
|
||||
console.log('', awsUrl)
|
||||
|
||||
if(!awsUrl) {
|
||||
ui.growl.append('This document is unavailable', 'danger');
|
||||
return;
|
||||
}
|
||||
|
||||
window.open(awsUrl, '_blank');
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022StatusView', SubmissionShortForm2022StatusView);
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
<h2>What?</h2>
|
||||
<div>
|
||||
<label>Acronym</label>
|
||||
<input eicinput name="Acronym" data-path="" data-type="text" maxlen="50" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Title</label>
|
||||
<input eicinput name="ProposalTitle" data-path="" data-type="text" maxlen="200" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Description of the solution</label>
|
||||
<textarea eictextarea name="Solution" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<label>Keywords</label>
|
||||
<div class="cols-3">
|
||||
<div>
|
||||
<label>1st Keyword</label>
|
||||
<select eicselect name="Keyword1Code" data-path="" data-type="array" class="required">
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>2nd Keyword</label>
|
||||
<select eicselect name="Keyword2Code" data-path="" data-type="array" class="required">
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>3rd Keyword</label>
|
||||
<select eicselect name="Keyword3Code" data-path="" data-type="array" class="required">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Free Keywords</label>
|
||||
<select eicselect editable name="FreeKeywords" data-path="" data-type="text" class="required"></select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Type of solution</label>
|
||||
<select eicselect multiple name="SolutionTypes" data-path="" data-type="array" class="required">
|
||||
<option value="0">product</option>
|
||||
<option value="1">service</option>
|
||||
<option value="2">process</option>
|
||||
<option value="3">marketing method</option>
|
||||
<option value="4">organisational method</option>
|
||||
<option value="5">consulting services</option>
|
||||
<option value="6">other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Type of innovation</label>
|
||||
<select eicselect name="InnovationType" data-path="" data-type="text" class="required">
|
||||
<option></option>
|
||||
<option value="0">Significant Improvement Of Existing</option>
|
||||
<option value="1">Brand New Thing</option>
|
||||
<option value="2">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Justify your choice of type of innovation</label>
|
||||
<textarea eictextarea name="InnovationTypeJustification" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Level of innovation</label>
|
||||
<select eicselect name="InnovationLevel" data-path="" data-type="text" class="required">
|
||||
<option></option>
|
||||
<option value="0">Some distinct, probably minor, improvement over existing products</option>
|
||||
<option value="1">Innovative but could be difficult to convert customers</option>
|
||||
<option value="2">Obviously innovative and easily appreciated advantages to customer</option>
|
||||
<option value="3">Very innovative</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Justify your choice of level of innovation</label>
|
||||
<textarea eictextarea name="Problem" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>What are the existing solutions and what are their limits?</label>
|
||||
<textarea eictextarea name="Solutions" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,19 @@
|
||||
class SubmissionShortForm2022WhatView extends SubmissionShortFormTabView {
|
||||
|
||||
fill(data) {
|
||||
let keywords = [];
|
||||
|
||||
keywords = app.meta.toOptions('eic-keywords', null, true);
|
||||
keywords.forEach(item => this.find('select[name="Keyword1Code"]').append(item));
|
||||
|
||||
keywords = app.meta.toOptions('eic-keywords', null, true);
|
||||
keywords.forEach(item => this.find('select[name="Keyword2Code"]').append(item));
|
||||
|
||||
keywords = app.meta.toOptions('eic-keywords', null, true);
|
||||
keywords.forEach(item => this.find('select[name="Keyword3Code"]').append(item));
|
||||
|
||||
super.fill(data);
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022WhatView', SubmissionShortForm2022WhatView);
|
||||
@@ -0,0 +1,150 @@
|
||||
<style>
|
||||
.submission.short-form .members {
|
||||
display: grid;
|
||||
grid-gap: 10px;
|
||||
grid-template-columns: repeat( auto-fill, 280px);
|
||||
grid-auto-rows: 1fr;
|
||||
margin-bottom: var(--eicui-base-spacing-l);
|
||||
}
|
||||
.submission.short-form .members article {
|
||||
width: 280px;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<h2>Who?</h2>
|
||||
<div>
|
||||
<label>Are you an investor who supports and submits on behalf of an SME, a small mid-cap or a natural person willing to establish a start-up/SME in an EU Member State or an Associated country? NB: Investors cannot include consultants or organisations in the capacity of intermediaries of Horizon Europe (EEN, NCP)</label>
|
||||
<select eicselect name="IsSupportingSme" data-path="" data-type="text">
|
||||
<option></option>
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<label>PIC</label>
|
||||
<input eicinput disabled name="PICNumber" data-path="" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Legal entity name (which can be the name of a person)</label>
|
||||
<input eicinput name="LegalEntityName" data-path="" data-type="text" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Business name</label>
|
||||
<input eicinput name="BusinessName" data-path="" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Address</label>
|
||||
<input eicinput name="CompanyStreetAndNumber" data-path="" data-type="text" placeholder="Street" />
|
||||
</div>
|
||||
<div class="cols-2 left">
|
||||
<input eicinput name="CompanyPostalCode" data-path="" data-type="text" placeholder="Postal Code" />
|
||||
<input eicinput name="CompanyCity" data-path="" data-type="text" placeholder="City" />
|
||||
</div>
|
||||
<div>
|
||||
<input eicinput name="CompanyCountry" data-path="" data-type="text" placeholder="Country" />
|
||||
</div>
|
||||
<div>
|
||||
<label>What is your type of entity ?</label>
|
||||
<select eicselect name="OrganisationSubmissionType" data-path="" data-type="text">
|
||||
<option></option>
|
||||
<option value="0">Public Body</option>
|
||||
<option value="1">Private Organisation</option>
|
||||
<option value="2">NaturalPerson</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Size of organisation ?</label>
|
||||
<select eicselect name="OrganisationSubmissionSecondaryType" data-path="" data-type="text">
|
||||
<option></option>
|
||||
<option value="0">An SME</option>
|
||||
<option value="1">A Small-mid cap</option>
|
||||
<option value="2">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Are you a for-profit organisation?</label>
|
||||
<select eicselect name="IsProfitMaking" data-path="" data-type="text">
|
||||
<option></option>
|
||||
<option value="false">No</option>
|
||||
<option value="true">Yes</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>What is the purpose of your project ?</label>
|
||||
<select eicselect name="SectorOfActivityType" data-path="" data-type="array">
|
||||
<option></option>
|
||||
<option value="Research">Research</option>
|
||||
<option value="Education">Education</option>
|
||||
<option value="ResearchEducation">Research & Education</option>
|
||||
<option value="Industry">Industry</option>
|
||||
<option value="Other">Other</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>CEO</label>
|
||||
</div>
|
||||
<div class="cols-3">
|
||||
<div>
|
||||
<label>Gender</label>
|
||||
<select eicselect name="CEOGender" data-path="" data-type="text">
|
||||
<option></option>
|
||||
<option value="1">Ms</option>
|
||||
<option value="0">M</option>
|
||||
<option value="2">I rather not say</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>First name</label>
|
||||
<input eicinput name="CEOFirstName" data-path="" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Last name</label>
|
||||
<input eicinput name="CEOLastName" data-path="" data-type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Email</label>
|
||||
<input eicinput name="CEOEmail" data-path="" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Phone</label>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<div>
|
||||
<input eicinput name="CEOPhoneNumberCode" data-path="" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<input eicinput name="CEOPhoneNumber" data-path="" data-type="text" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Your team</label>
|
||||
<div class="members"></div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Do you have the team you need to implement the action?</label>
|
||||
<select eicselect name="TeamYouNeed" data-path="" data-type="array">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Please justify your choice</label>
|
||||
<textarea eictextarea name="MissingSkills" data-path="" data-type="text" maxlen="1000"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Will you need cooperation with other stakeholders of your value chain (research and innovation, industrial, financial, suppliers, distributors, ... ) to implement the action?</label>
|
||||
<select eicselect name="PartnersYouNeed" data-path="" data-type="array">
|
||||
<option></option>
|
||||
<option value="true">Yes</option>
|
||||
<option value="false">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Please justify your choice</label>
|
||||
<textarea eictextarea name="MissingPartnerships" data-path="" data-type="text" maxlen="1000"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,76 @@
|
||||
class SubmissionShortForm2022WhoView extends SubmissionShortFormTabView {
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
super.DOMContentLoaded(options);
|
||||
|
||||
app.meta.collections['organisation-genders-legacy'] = {
|
||||
content: [
|
||||
{
|
||||
"id": "0",
|
||||
"label": "M"
|
||||
},
|
||||
{
|
||||
"id": "1",
|
||||
"label": "Ms"
|
||||
},
|
||||
{
|
||||
"id": "2",
|
||||
"label": ""
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
this.initTeamEditing();
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
super.fill(mode);
|
||||
let type = this.find('[name="OrganisationSubmissionType"]')
|
||||
if(type != "1") {
|
||||
ui.hide(this.find('[name="OrganisationSubmissionSecondaryType"]').parentElement.parentElement)
|
||||
}
|
||||
if(type == "2") {
|
||||
ui.hide(this.find('[name="IsProfitMaking"]').parentElement.parentElement)
|
||||
}
|
||||
|
||||
let team = this.find('[name="TeamYouNeed"]')
|
||||
if(team == "false") {
|
||||
ui.hide(this.find('[name="MissingSkills"]').parentElement.parentElement)
|
||||
}
|
||||
|
||||
let partner = this.find('[name="PartnersYouNeed"]')
|
||||
if(partner == "true") {
|
||||
ui.hide(this.find('[name="MissingSkills"]').parentElement.parentElement)
|
||||
}
|
||||
}
|
||||
|
||||
setupRules() {}
|
||||
|
||||
initTeamEditing() {
|
||||
for(let member of this.model.data.team) {
|
||||
this.addMember(member);
|
||||
}
|
||||
}
|
||||
|
||||
addMember(member) {
|
||||
let severity = member.Position && member.Position.indexOf('CEO') == 0 ? 'primary': ''
|
||||
let card = ui.create(`<article eiccard ${severity} data-id="${member.Id}" data-index="${this.findAll('.members article').length}">
|
||||
<header>
|
||||
<h1>${app.meta.toString('organisation-genders-legacy', member.Gender)} ${member.FirstName} ${member.LastName}</h1>
|
||||
<h2>${member.Position}</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div>
|
||||
<label>Area of Expertise(s)</label>
|
||||
<span>${member.AreasOfExpertise.join(', ')}</span>
|
||||
</div>
|
||||
<p><i>"${member.Why}"</i></p>
|
||||
</section>
|
||||
<footer></footer>
|
||||
</article`);
|
||||
ui.hide(card.querySelector('footer'));
|
||||
this.find('.members').append(card);
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022WhoView', SubmissionShortForm2022WhoView);
|
||||
@@ -0,0 +1,52 @@
|
||||
<h2>For whom?</h2>
|
||||
<div>
|
||||
<label>Who will use the innovation ?</label>
|
||||
<select eicselect name="InnovationWho" data-path="" data-type="array" class="required">
|
||||
<option></option>
|
||||
<option value="0">Current Customers</option>
|
||||
<option value="1">New Customers</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Please justify your choice</label>
|
||||
<textarea eictextarea name="InnovationWhoJustification" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Describe your targeted market</label>
|
||||
<textarea eictextarea name="Market" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Would you say that the market targeted by this innovation is ...</label>
|
||||
<select eicselect name="InnovationTarget" data-path="" data-type="array" class="required">
|
||||
<option></option>
|
||||
<option value="0">Not yet existing and it is not yet clear that the innovation can create it</option>
|
||||
<option value="1">Not yet existing but the innovation can create it (market-creating)</option>
|
||||
<option value="2">Emerging: there is a growing demand and few offerings are available</option>
|
||||
<option value="3">Mature: the market is already supplied with many products of the type proposed</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Please justify your choice</label>
|
||||
<textarea eictextarea name="InnovationTargetJustification" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>How will the innovation be exploited ?</label>
|
||||
<select eicselect name="InnovationExploited" data-path="" data-type="array" class="required">
|
||||
<option></option>
|
||||
<option value="0">Introduced as new to the market (commercial exploitation)</option>
|
||||
<option value="1">Only new to the organisation</option>
|
||||
<option value="2">No exploitation</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Please justify your choice</label>
|
||||
<textarea eictextarea name="InnovationExploitedJustification" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Who cares about your innovation?</label>
|
||||
<textarea eictextarea name="WhoCares" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Indicate your Time-to-Market in years ?</label>
|
||||
<input eicinput type="number" min="1" max="10" name="Ttm" data-path="" data-type="text" maxlen="150" class="required" />
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
class SubmissionShortForm2022WhomView extends SubmissionShortFormTabView {
|
||||
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022WhomView', SubmissionShortForm2022WhomView);
|
||||
@@ -0,0 +1,30 @@
|
||||
<h2>Why?</h2>
|
||||
<div>
|
||||
<label>Describe the problem to be solved or the need(s) to be satisfied</label>
|
||||
<textarea eictextarea name="InnovationLevelJustification" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<!--
|
||||
<div>
|
||||
<div eicalert warning>MFA: this field is related to a sideline service called BottomUpTopic. NOT USED IN project sheet</div>
|
||||
<label>What is the purpose of your project ?</label>
|
||||
<select eicselect multiple name="BottomUpTopics" data-path="" data-type="array" class="required">
|
||||
<option value="3">R&D Infrastructure</option>
|
||||
<option value="4">Training and mobility</option>
|
||||
<option value="5">Basic Research</option>
|
||||
<option value="1">Applied research and/or technological innovation</option>
|
||||
<option value="2">Non-technological innovation</option>
|
||||
</select>
|
||||
</div>
|
||||
-->
|
||||
<div>
|
||||
<label>Why it is new compared to the existing solutions?</label>
|
||||
<textarea eictextarea name="WhyNew" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Why do you think it will be successful?</label>
|
||||
<textarea eictextarea name="WhySuccessful" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Why now ?</label>
|
||||
<textarea eictextarea name="WhyNow" data-path="" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
class SubmissionShortForm2022WhyView extends SubmissionShortFormTabView {}
|
||||
|
||||
app.registerClass('SubmissionShortForm2022WhyView', SubmissionShortForm2022WhyView);
|
||||
@@ -0,0 +1,86 @@
|
||||
<style>
|
||||
.submission.short-form > header {
|
||||
background: url('/app/assets/images/cards/submission-card.jpg');
|
||||
background-position: center;
|
||||
background-size: cover !important;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
.submission.short-form .tools {
|
||||
padding-bottom: 8px;
|
||||
}
|
||||
.submission.short-form input[data-path="organisation"][name="pic"],
|
||||
.submission.short-form input[data-path="organisation.address"][name="postcode"],
|
||||
.submission.short-form input[data-path="proposal"][name="number"] { min-width: 180px; }
|
||||
.submission.short-form textarea { min-height: 120px; }
|
||||
.submission.short-form [data-part="funding"] textarea[name="needs"],
|
||||
.submission.short-form textarea[name="genderBalance"],
|
||||
.submission.short-form [data-part="problem"] textarea,
|
||||
.submission.short-form [data-part="solution"] textarea,
|
||||
.submission.short-form [data-part="competition"] textarea,
|
||||
.submission.short-form [data-part="impacts"] textarea { min-height: 430px; }
|
||||
.submission.short-form [data-part="general"] select[name="estimatedTRL"]+div.eicui-select-selection span { white-space: normal; }
|
||||
.submission.short-form .tools .contributors { white-space: nowrap; }
|
||||
.submission.short-form .tools .contributors [eicchip],
|
||||
.submission.short-form .tools .contributors [eicchip] label { cursor: pointer; }
|
||||
.submission.short-form [data-part] h2 { white-space: normal; }
|
||||
</style>
|
||||
<article eiccard media class="submission short-form">
|
||||
<header>
|
||||
<h1>Short proposal ${models.submission.data.proposal.number}</h1>
|
||||
<h2>
|
||||
<span eicchip primary>short ${models.submission.data.version}</span>
|
||||
<span eicchip primary>${models.submission.data.status}</span>
|
||||
${models.submission.data.resubmitted ? `<span eicchip accent>resubmission</span>`:''}
|
||||
</h2>
|
||||
</header>
|
||||
<section>
|
||||
<div eicalert danger class="display:none;">
|
||||
<p small><span danger>Please note that after <b>December 31st 2023 at 23:59 (GMT +1)</b>, it will no longer be possible to submit a short proposal via the current EIC Platform.</span></p>
|
||||
<div small>
|
||||
Nonetheless, the platform will remain accessible to consult previous proposals, drafts, results and to access the coaching module.
|
||||
Should you intend to submit a new short proposal as of <b>January 3, 2024</b> you can do so via the <a href="https://ec.europa.eu/info/funding-tenders/opportunities/portal/" target="_blank" title="Funding and Tenders Portal">Funding and Tenders Portal</a>.
|
||||
Please note that the templates and rules for the short proposal will change under the <a href="https://eic.ec.europa.eu/eic-2024-work-programme_en" target="_blank" title="EIC Work Programme 2024">EIC Work Programme 2024</a>.
|
||||
We invite you to read them carefully. The new rules will come into force from <b>January 1, 2024</b>.
|
||||
More detailed information will be available shortly on our <a href="https://eic.ec.europa.eu/eic-accelerator-application-platform-frequently-asked-questions_en" target="_blank" title="EIC Website">website</a>.
|
||||
</div>
|
||||
</div>
|
||||
<div class="tools cols-2 right">
|
||||
<div class="cols-2">
|
||||
<div class="updated"><label small>last saved</label><span class="date" small></span> by <span class="contributor"></span></div>
|
||||
<div class="created"><label small>created</label><span class="date" small></span> by <span class="contributor"></span></div>
|
||||
</div>
|
||||
<div class="contributors"></div>
|
||||
</div>
|
||||
<div class="form cols-2 left">
|
||||
<input type="hidden" name="version" data-path="" value="1.0" />
|
||||
<div class="tabs-extended vertical">
|
||||
<section>
|
||||
<menu eictab>
|
||||
<li data-target="general">General<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="problem">Opportunity<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="solution">Solution<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="competition">Competition<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="impacts">Impacts<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="company">Company<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="team">Team<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="funding">Funding<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="documents">Documents<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="status">Submission<span xxsmall eicbadge danger></span></li>
|
||||
<li data-target="consent">Data sharing<span xxsmall eicbadge danger></span></li>
|
||||
</menu>
|
||||
</section>
|
||||
</div>
|
||||
<div class="content" data-part="general"></div>
|
||||
<div class="content" data-part="problem"></div>
|
||||
<div class="content" data-part="solution"></div>
|
||||
<div class="content" data-part="competition"></div>
|
||||
<div class="content" data-part="impacts"></div>
|
||||
<div class="content" data-part="company"></div>
|
||||
<div class="content" data-part="team"></div>
|
||||
<div class="content" data-part="funding"></div>
|
||||
<div class="content" data-part="documents"></div>
|
||||
<div class="content" data-part="status"></div>
|
||||
<div class="content" data-part="consent"></div>
|
||||
</div>
|
||||
</section>
|
||||
</article>
|
||||
@@ -0,0 +1,410 @@
|
||||
/**
|
||||
* Short Proposal Form
|
||||
*
|
||||
* @version 2023
|
||||
* @author Michael Fallise
|
||||
*
|
||||
* Options:
|
||||
* * modeAc
|
||||
*
|
||||
*/
|
||||
class SubmissionShortForm2023View extends EICDomContent {
|
||||
|
||||
options = {
|
||||
mode: 'read',
|
||||
autoSaveInterval: 15 * 1000,
|
||||
validationInterval: 2 * 1000,
|
||||
}
|
||||
|
||||
chunks = [];
|
||||
|
||||
DOMContentLoaded(options) {
|
||||
this.components = ui.eicfy(this.el)
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
|
||||
this.tabs = new Tab();
|
||||
this.tabs.addTabs(this.findAll('.tabs-extended menu li'), this.findAll('.content'))
|
||||
|
||||
this.form = new Form(this.find('.form'));
|
||||
|
||||
this.pic = options.profile.pic;
|
||||
this.number = options.profile.number;
|
||||
this.members.list(this.pic,this.number).then(this.fillContributors.bind(this))
|
||||
this.mode = options.profile.mode || this.options.mode;
|
||||
this.checkConcurrency(options.profile.mode || this.options.mode);
|
||||
}
|
||||
|
||||
async checkConcurrency(mode) {
|
||||
if(mode == 'edit') {
|
||||
let diff = (new Date()).getTime() - (new Date(this.submission.data.updated.date)).getTime()
|
||||
let created = (new Date()).getTime() - (new Date(this.submission.data.created.date)).getTime()
|
||||
if(diff < this.options.autoSaveInterval && created > this.options.autoSaveInterval) {
|
||||
// someone is editing right now
|
||||
let goReadOnly = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/submissions/dialogs/SubmissionFormConcurrencyDialog',
|
||||
{ title: 'Someone is already at work' }, {
|
||||
contributor: this.submission.data.updated.contributor
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
mode = 'read';
|
||||
|
||||
if(!goReadOnly) {
|
||||
this.mode = 'edit';
|
||||
this.unload();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.fill(mode);
|
||||
}
|
||||
|
||||
DOMContentRemoved() {
|
||||
if(!this.loaded) return;
|
||||
if(this.autoSave) {
|
||||
clearInterval(this.autoSave)
|
||||
this.autoSave = null;
|
||||
};
|
||||
if(this.validationCheck) {
|
||||
clearInterval(this.validationCheck);
|
||||
this.validationCheck = null;
|
||||
}
|
||||
if((this.mode == 'edit')) this.save();
|
||||
|
||||
for(let chunk of this.chunks) { chunk.view.DOMContentRemoved(); }
|
||||
}
|
||||
|
||||
DOMContentBlured() {
|
||||
if(!this.loaded) return;
|
||||
if(this.autoSave) {
|
||||
clearInterval(this.autoSave);
|
||||
this.autoSave = null;
|
||||
}
|
||||
if(this.validationCheck) {
|
||||
clearInterval(this.validationCheck);
|
||||
this.validationCheck = null;
|
||||
}
|
||||
}
|
||||
|
||||
DOMContentFocused(options) {
|
||||
if(!this.loaded) return;
|
||||
|
||||
//By Nike : if options, means rerouted, then new models, then switch to fresh models !
|
||||
if(options) {
|
||||
for(let model in options.models) this[model] = options.models[model];
|
||||
}
|
||||
|
||||
//By Nike: propagate DOMContentFocused to tabs. Caution before setupMode which calls fill, which must have fresh model.data
|
||||
for(let chunk of this.chunks) chunk.view.DOMContentFocused(options)
|
||||
|
||||
if(
|
||||
options &&
|
||||
options.profile &&
|
||||
options.profile.mode &&
|
||||
options.profile.mode != this.mode
|
||||
) this.setupMode(options.profile.mode);
|
||||
|
||||
if(!this.autoSave && this.mode == 'edit') {
|
||||
this.autoSave = setInterval(this.save.bind(this), this.options.autoSaveInterval);
|
||||
}
|
||||
if(!this.validationCheck && this.mode == 'edit') {
|
||||
this.validationCheck = setInterval(this.validate.bind(this), this.options.validationCheck);
|
||||
}
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
let myTabsPath = 'projects/submissions/2023/tabs/'
|
||||
|
||||
this.find('.tools .created .date').innerText = (new Date(this.submission.data.created.date)).toLocaleString("en-DE");
|
||||
this.find('.tools .created .contributor').innerText = `${this.submission.data.created.contributor.firstname} ${this.submission.data.created.contributor.lastname}`;
|
||||
|
||||
this.refreshUpdated(this.submission.data.updated);
|
||||
|
||||
let queue = [
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023GeneralView', {}, { model: this.submission } )
|
||||
.then( view => this.appendTab('general', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023CompanyView', {}, { model: this.submission } )
|
||||
.then( view => this.appendTab('company', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023TeamView', {}, { model: this.submission, team: this.team, pic: this.pic, number: this.number } )
|
||||
.then( view => this.appendTab('team', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023ProblemView', {}, { model: this.submission } )
|
||||
.then( view => this.appendTab('problem', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023SolutionView', {}, { model: this.submission } )
|
||||
.then( view => this.appendTab('solution', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023CompetitionView', {}, { model: this.submission } )
|
||||
.then( view => this.appendTab('competition', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023FundingView', {}, { model: this.submission } )
|
||||
.then( view => this.appendTab('funding', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023ImpactView', {}, { model: this.submission } )
|
||||
.then( view => this.appendTab('impacts', view)),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023DocumentsView', {}, { model: this.submission } )
|
||||
.then( view => {
|
||||
this.appendTab('documents', view);
|
||||
view.el.addEventListener('save', this.save.bind(this) )
|
||||
}),
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023ConsentView', {}, { model: this.submission, team: this.team, pic: this.pic, number: this.number } )
|
||||
.then( view => this.appendTab('consent', view))
|
||||
];
|
||||
|
||||
if(['draft', 'frozen'].includes(this.submission.data.status)) {
|
||||
queue.push(
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023DisclaimerView', {}, { model: this.submission } )
|
||||
.then( view => {
|
||||
this.appendTab('status', view);
|
||||
this.statusTab = view;
|
||||
view.el.addEventListener('submit', this.submit.bind(this));
|
||||
})
|
||||
)
|
||||
} else {
|
||||
queue.push(
|
||||
this.loadContent( myTabsPath+'SubmissionShortForm2023StatusView', {}, { model: this.submission } )
|
||||
.then( view => {
|
||||
this.appendTab('status', view);
|
||||
this.statusTab = view;
|
||||
view.el.addEventListener('withdraw', this.withdraw.bind(this));
|
||||
view.el.addEventListener('clone', this.clone.bind(this));
|
||||
view.el.addEventListener('complain', this.complain.bind(this));
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
Promise.allSettled(queue)
|
||||
.then((results) => {
|
||||
this.setupMode(mode);
|
||||
this.loaded = true;
|
||||
});
|
||||
}
|
||||
|
||||
setupMode(mode) {
|
||||
|
||||
this.mode = mode;
|
||||
|
||||
for(let chunk of this.chunks) {
|
||||
chunk.view.options.mode = mode;
|
||||
chunk.view.fill(mode);
|
||||
}
|
||||
|
||||
if(this.mode == 'read' && this.autoSave) {
|
||||
clearInterval(this.autoSave);
|
||||
this.autoSave = null
|
||||
}
|
||||
|
||||
if(this.mode == 'read' && this.validationCheck) {
|
||||
clearInterval(this.validationCheck);
|
||||
this.validationCheck = null
|
||||
}
|
||||
|
||||
if(this.mode == 'edit' && !this.autoSave) {
|
||||
this.autoSave = setInterval(this.save.bind(this), this.options.autoSaveInterval);
|
||||
this.save(); // perform a save on startup to mark editing start
|
||||
}
|
||||
|
||||
if(this.mode == 'edit' && !this.validationCheck) {
|
||||
this.validationCheck = setInterval(this.validate.bind(this), this.options.validationInterval);
|
||||
}
|
||||
|
||||
if(!['draft', 'frozen'].includes(this.submission.data.status)) {
|
||||
this.tabs.selectByIndex(this.tabs.triggers.length - 2);
|
||||
}
|
||||
}
|
||||
|
||||
appendTab(target, view) {
|
||||
let chunk = {target: target, view: view};
|
||||
this.chunks.push( chunk );
|
||||
this.find(`.content[data-part="${target}"]`).append(view.el);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
fillContributors(data) {
|
||||
let metrics = { contributors: 0, pending: 0 }
|
||||
this.find('.contributors').innerHTML = '';
|
||||
for(let item of data) {
|
||||
if(item.status == 'active') metrics.contributors++;
|
||||
if(item.status == 'pending') metrics.pending++;
|
||||
}
|
||||
this.find('.contributors').innerHTML = '';
|
||||
if(metrics.contributors > 0) {
|
||||
let chip = new Chip(null, {label: 'contributors', severity: 'primary', badge: metrics.contributors});
|
||||
chip.el.addEventListener('click', this.onContributorsEdit.bind(this))
|
||||
this.find('.contributors').append(chip.el);
|
||||
}
|
||||
if(metrics.pending > 0) {
|
||||
let chip = new Chip(null, {label: 'pending requests', severity: 'warning', badge: metrics.pending})
|
||||
chip.el.addEventListener('click', this.onContributorsEdit.bind(this))
|
||||
this.find('.contributors').append(chip.el);
|
||||
}
|
||||
}
|
||||
|
||||
async onContributorsEdit(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/submissions/dialogs/SubmissionFormAccessDialog',
|
||||
{ title: 'Contributors to this proposal' }, {
|
||||
pic: this.pic,
|
||||
number: this.number,
|
||||
models: {
|
||||
proposal: this.members,
|
||||
organisation: new ApplicantMembersModel(['list'])
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
this.members.list(this.pic,this.number)
|
||||
.then(this.fillContributors.bind(this));
|
||||
|
||||
}
|
||||
|
||||
validate() {
|
||||
let valid = true;
|
||||
|
||||
for(let chunk of this.chunks) {
|
||||
let report = chunk.view.validate(true);
|
||||
let badge = this.components.find(item => item.el.hasAttribute('eicbadge') && item.el.parentElement.dataset.target == chunk.target)
|
||||
badge.value = report.issues;
|
||||
|
||||
valid = valid && report.valid;
|
||||
}
|
||||
this.statusTab.switchSubmit(!valid);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
refreshUpdated(data) {
|
||||
this.find('.tools .updated .date').innerText = (new Date(data.date)).toLocaleString("en-DE");
|
||||
this.find('.tools .updated .contributor').innerText = `${data.contributor.firstname} ${data.contributor.lastname}`;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.validate();
|
||||
this.submission.save(this.pic, this.number, this.form.value)
|
||||
.then(
|
||||
async payload => {
|
||||
this.refreshUpdated(payload.updated);
|
||||
this.dispatchUpdate();
|
||||
},
|
||||
async error => {
|
||||
if(error.code == 504) {
|
||||
ui.growl('Auto save encountered some issue.', 'danger');
|
||||
this.setupMode('read');
|
||||
} else {
|
||||
ui.growl.append(error.displayMessage, 'danger');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async submit(event) {
|
||||
if(this.validate()) {
|
||||
if(this.autoSave) {
|
||||
clearInterval(this.autoSave)
|
||||
this.autoSave = null;
|
||||
};
|
||||
if(this.validationCheck) {
|
||||
clearInterval(this.validationCheck);
|
||||
this.validationCheck = null;
|
||||
}
|
||||
this.submission.submit(this.pic, this.number, this.form.value)
|
||||
.then(async payload => {
|
||||
|
||||
this.dispatchUpdate();
|
||||
this.mode = 'read'; // exiting edit mode, avoiding save on close
|
||||
ui.growl.append('Your proposal has been successfully submitted', 'success');
|
||||
this.unload();
|
||||
|
||||
},
|
||||
() => {
|
||||
if(!this.autoSave && this.mode == 'edit') {
|
||||
this.autoSave = setInterval(this.save.bind(this), this.options.autoSaveInterval);
|
||||
}
|
||||
if(!this.validationCheck && this.mode == 'edit') {
|
||||
this.validationCheck = setInterval(this.validate.bind(this), this.options.validationCheck);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async withdraw(event) {
|
||||
this.submission.withdraw(this.pic, this.number, this.form.value)
|
||||
.then(async payload => {
|
||||
this.dispatchUpdate();
|
||||
ui.growl.append('Your proposal has been successfully withdrawn', 'success');
|
||||
this.unload();
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
async clone(event) {
|
||||
this.submission.clone(this.pic, this.number)
|
||||
.then(
|
||||
async payload => {
|
||||
this.dispatchUpdate();
|
||||
ui.growl.append('A new proposal is available', 'success');
|
||||
this.unload();
|
||||
},
|
||||
async () => {
|
||||
ui.growl.append('Sorry, you don\'t have the rights to clone this proposal', 'danger')
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
async complain(event) {
|
||||
|
||||
let complaint = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/submissions/dialogs/SubmissionFormComplaintDialog',
|
||||
{ title: 'File a complaint about the evaluation' }, {
|
||||
pic: this.pic,
|
||||
number: this.number,
|
||||
proposal: this.submission
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
if(complaint) this.unload();
|
||||
}
|
||||
|
||||
|
||||
|
||||
DOMContentResized() {
|
||||
super.DOMContentResized();
|
||||
|
||||
let device = this.el.getAttribute('device');
|
||||
let tabs = this.find('.tabs-extended');
|
||||
|
||||
switch(device) {
|
||||
case 'desktop':
|
||||
ui.show(tabs);
|
||||
let current = tabs.querySelector('.tab-selected');
|
||||
let part = current.getAttribute('data-target');
|
||||
this.findAll('.content[data-part]').forEach(content => { if(content.getAttribute('data-part') != part) ui.hide(content) });
|
||||
break;
|
||||
default:
|
||||
ui.hide(tabs);
|
||||
this.findAll('.content[data-part]').forEach(content => { ui.show(content) })
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
dispatchUpdate() {
|
||||
/*
|
||||
// MFA: if ever needed to perform local updates
|
||||
let data = {
|
||||
number: this.submission.data.proposal.number,
|
||||
acronym: this.submission.data.proposal.acronym,
|
||||
status: this.submission.data.status,
|
||||
version: this.submission.data.version
|
||||
}
|
||||
*/
|
||||
let data = null;
|
||||
|
||||
app.events.trigger('proposal_updated', data);
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023View', SubmissionShortForm2023View);
|
||||
@@ -0,0 +1,57 @@
|
||||
<h2>Company</h2>
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<label>PIC</label>
|
||||
<input eicinput disabled name="pic" data-path="organisation" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Legal entity name (which can be the name of a person)</label>
|
||||
<input eicinput disabled name="legalname" data-path="organisation" data-type="text" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label>Business name</label>
|
||||
<input eicinput disabled name="businessName" data-path="organisation" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Address</label>
|
||||
<input eicinput disabled name="street" data-path="organisation.address" data-type="text" placeholder="Street" />
|
||||
</div>
|
||||
<div class="cols-2 left">
|
||||
<input eicinput disabled name="postcode" data-path="organisation.address" data-type="text" placeholder="Postal Code" />
|
||||
<input eicinput disabled name="city" data-path="organisation.address" data-type="text" placeholder="City" />
|
||||
</div>
|
||||
<div>
|
||||
<input eicinput disabled name="country" data-path="organisation.address" data-type="text" placeholder="Country" />
|
||||
</div>
|
||||
<div>
|
||||
<label>VAT Number</label>
|
||||
<input eicinput disabled name="VAT" data-path="organisation" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<div eicalert info>
|
||||
The company information above is collected from the Funding & tender opportunities Portal. Should your company data be missing or incorrect please update your company information on the Funding & tender opportunities portal directly following
|
||||
<a class="fundingportal-link" href="https://ec.europa.eu/info/funding-tenders/opportunities/portal/screen/home" target="_blank" title="Funding & tender opportunities">this link</a>.
|
||||
Once done, use the refresh button below to update the organisation info in your proposal.</div>
|
||||
<div class="cols-1 right">
|
||||
<button eicbutton small primary class="company-update">refresh</button>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Website</label>
|
||||
<input eicinput name="website" data-path="organisation" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>What is your type of entity ?</label>
|
||||
<select eicselect name="companyType" data-path="organisation" class="required">
|
||||
<option value=""></option>
|
||||
<option value="sme">SME</option>
|
||||
<option value="midcap">Small mid-cap</option>
|
||||
<option value="person">Natural person</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Describe your company history, your vision and ambitions.</label>
|
||||
<textarea eictextarea name="description" data-path="organisation" data-type="text" maxlen="5000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,65 @@
|
||||
class SubmissionShortForm2023CompanyView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {
|
||||
this.companyUpdate = this.components.find(component => component.el.classList.contains('company-update'));
|
||||
this.companyUpdate.el.addEventListener('click', this.onCompanyUpdate.bind(this));
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
super.fill(mode);
|
||||
|
||||
this.find('[name="pic"]').setAttribute('disabled', '');
|
||||
this.find('[name="legalname"]').setAttribute('disabled', '');
|
||||
this.find('[name="businessName"]').setAttribute('disabled', '');
|
||||
this.find('[name="street"]').setAttribute('disabled', '');
|
||||
this.find('[name="postcode"]').setAttribute('disabled', '');
|
||||
this.find('[name="city"]').setAttribute('disabled', '');
|
||||
this.find('[name="country"]').setAttribute('disabled', '');
|
||||
this.find('[name="VAT"]').setAttribute('disabled', '');
|
||||
|
||||
if(mode == 'edit') {
|
||||
ui.show(this.companyUpdate.el)
|
||||
} else {
|
||||
ui.hide(this.companyUpdate.el)
|
||||
}
|
||||
|
||||
// Nike to MFA: was ${model.data.organisation.pic} straight in the markup.
|
||||
// Unfortunaly, it happens that organisation is not (yet?) in data.
|
||||
// Then it crashes the tab on a templating error (actually it makes empty thx to the template-error-catching)
|
||||
// Also the pic can be empty, then you have a "bad pic" arriving on the F&TP.
|
||||
// So defaut link in markup is your F&TP home, the we replace here by more precise if all ok.
|
||||
if(this.model.data.organisation && this.model.data.organisation.pic) {
|
||||
this.find('.fundingportal-link').setAttribute('href', `https://ec.europa.eu/info/funding-tenders/opportunities/portal/participant/${this.model.data.organisation.pic}/organisation-data`);
|
||||
}
|
||||
}
|
||||
|
||||
onCompanyUpdate(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.companyUpdate.loading = true;
|
||||
|
||||
this.model.getOrganisationInfo(this.model.data.organisation.pic)
|
||||
.then(this.onSuccess.bind(this), this.onFailure.bind(this))
|
||||
}
|
||||
|
||||
async onSuccess(data) {
|
||||
this.companyUpdate.loading = false;
|
||||
|
||||
this.find('[name="legalname"]').value = data.legalName;
|
||||
// currently missing in service
|
||||
if(data.businessName) this.find('[name="businessName"]').value = data.businessName;
|
||||
this.find('[name="street"]').value = data.street;
|
||||
this.find('[name="postcode"]').value = data.postalCode;
|
||||
this.find('[name="city"]').value = data.city;
|
||||
this.find('[name="country"]').value = data.country;
|
||||
this.find('[name="VAT"]').value = data.vat;
|
||||
}
|
||||
|
||||
async onFailure() {
|
||||
this.companyUpdate.loading = false;
|
||||
ui.growl.append('We could not update your organisation info', 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023CompanyView', SubmissionShortForm2023CompanyView);
|
||||
@@ -0,0 +1,5 @@
|
||||
<h2>Market and Competition analysis</h2>
|
||||
<div>
|
||||
<label>Describe your business model and the target market (segmentations, size, growth and drivers), explain why customers would be willing to pay, outline the advantages and disadvantages of your solution compared to competitors, and explain how the company's growth will be impacted.</label>
|
||||
<textarea eictextarea name="competition" data-path="narrative" data-type="text" maxlen="10000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,3 @@
|
||||
class SubmissionShortForm2023CompetitionView extends SubmissionShortFormTabView {}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023CompetitionView', SubmissionShortForm2023CompetitionView);
|
||||
@@ -0,0 +1,8 @@
|
||||
<h2>Data sharing consent</h2>
|
||||
<p>Your National Contact Point, Enterprise Europe Network (EEN) Members and Knowledge Innovation Communities of the European Institute of Innovation and Technologies (EIT KICs) may be a useful source of information and support in preparation of your full proposal. Therefore, we encourage you to share your data with them.</p>
|
||||
<p>I consent to sharing the following data with my relevant National Contact Point, relevant EEN Member and EIT KICs:</p>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox type="checkbox" name="sharingConsent" data-path="declarations" value="true" />
|
||||
<div>basic information about my proposal limited to the name, phone, email of the CEO or person in charge of managing innovation/company, proposal acronym, title, abstract, evaluation result (GO/NO GO).</div>
|
||||
</div>
|
||||
<p>The data will be made available subject to confidentiality obligations of the NCPs, KICs and EEN members.</p>
|
||||
@@ -0,0 +1,3 @@
|
||||
class SubmissionShortForm2023ConsentView extends SubmissionShortFormTabView {}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023ConsentView', SubmissionShortForm2023ConsentView);
|
||||
@@ -0,0 +1,139 @@
|
||||
<h2>Submit your proposal</h2>
|
||||
<div class="resubmission">
|
||||
<div>
|
||||
<label>Summarise the main changes compared to the previous (rejected) short proposal which you submitted</label>
|
||||
<textarea eictextarea name="changes" data-path="resubmission" data-type="text" maxlen="10000" class=""></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Provide your rebuttal (if any) to the experts' comments to the previous (rejected) short proposal which you submitted</label>
|
||||
<textarea eictextarea name="rebuttal" data-path="resubmission" data-type="text" maxlen="10000" class=""></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="declaration">
|
||||
<h5>Declarations</h5>
|
||||
<div eicalert warning>All declarations have to be agreed when submitting the form</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="consentOfApplicants" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
We declare to have the explicit consent of all applicants on their
|
||||
participation and on the content of this proposal.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="correctInformation" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
We confirm that the information contained in this proposal is correct
|
||||
and complete and that none of the project activities have started
|
||||
before the proposal was submitted (unless explicitly authorised in
|
||||
the call conditions).
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="elligible" data-path="declarations" data-type="ignore" value="true" />
|
||||
|
||||
<div>
|
||||
We declare:
|
||||
<ul bulleted>
|
||||
<li>to be fully compliant with the eligibility criteria set out in the call</li>
|
||||
<li>not to be subject to any exclusion grounds under the EU Financial Regulation 2018/1046</li>
|
||||
<li>to have the financial and operational capacity to carry out the proposed project.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="useOfFToP" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
We acknowledge that all communication will be made through the Funding & Tenders opportunities
|
||||
Portal electronic exchange system and that access and use of this system is subject
|
||||
to the Funding & Tenders opportunities Portal Terms & Conditions.
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="acceptTerms" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
We have read, understood and accepted the Funding & Tenders Portal Terms & Conditions
|
||||
and Privacy Statement that set out the conditions of use of the Portal and the scope,
|
||||
purposes, retention periods, etc. for the processing of personal data of all data
|
||||
subjects whose data we communicate for the purpose of the application, evaluation,
|
||||
award and subsequent management of our grant, prizes and contracts (including financial
|
||||
transactions and audits).
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="ethicalPrinciples" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
<p>We declare that the proposal complies with ethical principles (including the highest
|
||||
standards of research integrity as set out in the ALLEA European Code of Conduct for
|
||||
Research Integrity, as well as applicable international and national law, including the
|
||||
Charter of Fundamental Rights of the European Union and the European Convention on
|
||||
Human Rights and its Supplementary Protocols).</p>
|
||||
<p>Appropriate procedures, policies and
|
||||
structures are in place to foster responsible research practices, to
|
||||
prevent questionable research practices and research misconduct, and to
|
||||
handle allegations of breaches of the principles and standards in the Code of Conduct.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="exclusiveCivilApplications" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
We declare that the proposal has an exclusive focus on civil applications (activities
|
||||
intended to be used in military application or aiming to serve military purposes
|
||||
cannot be funded). If the project involves dual-use items in the sense of Regulation
|
||||
2021/821, or other items for which authorisation is required, we confirm that we will
|
||||
comply with the applicable regulatory framework (e.g. obtain export/import licences
|
||||
before these items are used).
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="complyGeneticsRegulations" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
We confirm that the activities proposed do not
|
||||
<ul bulleted>
|
||||
<li>aim at human cloning for reproductive purposes;</li>
|
||||
<li>
|
||||
intend to modify the genetic heritage of human beings which could make such changes
|
||||
heritable (with the exception of research relating to cancer treatment of the gonads,
|
||||
which may be financed), or
|
||||
</li>
|
||||
<li>
|
||||
intend to create human embryos solely for the purpose of research or for the purpose of
|
||||
stem cell procurement, including by means of somatic cell nuclear transfer.
|
||||
</li>
|
||||
<li>lead to the destruction of human embryos (for example, for obtaining stem cells) These activities are excluded from funding.</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="cols-2 left noflex">
|
||||
<input eiccheckbox class="required" type="checkbox" name="complyLegalObligations" data-path="declarations" data-type="ignore" value="true" />
|
||||
<div>
|
||||
We confirm that for activities carried out outside the Union, the same activities would
|
||||
have been allowed in at least one EU Member State
|
||||
</div>
|
||||
</div>
|
||||
<hr/>
|
||||
</div>
|
||||
|
||||
<div class="submitting">
|
||||
<div eicalert info>
|
||||
Please note that only one Submission form can be submitted per Participant Identification Code
|
||||
(PIC). You will therefore not be able to submit another proposal with the same Participant
|
||||
Identification Code (PIC).
|
||||
</div>
|
||||
<div eicalert danger class="incomplete">Your form still contains some issues preventing submission</div>
|
||||
<div class="cols-1 center ">
|
||||
<button eicbutton large disabled success class="submit">Submit my proposal</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- For Mike: disabled after Val tickets from confused users & discussion with Mikekel.
|
||||
Nobody knows for sure if it was a temp msg from the release before (when we couldn't show ESRs) and can be kicked now,
|
||||
Or if it should stay and be workflow-related ? Because for now it shows when it shouldn't.
|
||||
=> Possible side-effect of some ui.show(this.find('.evaluation')) because class ".evaluation" used in different places
|
||||
|
||||
Consensus is to kick it for now.
|
||||
|
||||
<div class="cols-1 center evaluation">
|
||||
Evaluation report will be available soon
|
||||
</div>
|
||||
|
||||
-->
|
||||
@@ -0,0 +1,44 @@
|
||||
class SubmissionShortForm2023DisclaimerView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {
|
||||
if(this.model.data.resubmitted) {
|
||||
ui.show(this.find('.resubmission'));
|
||||
this.find('h2').innerHTML = 'Resubmission';
|
||||
this.find('textarea[name="changes"]').classList.add('required');
|
||||
this.find('textarea[name="changes"]').setAttribute('data-type', 'text');
|
||||
this.find('textarea[name="rebuttal"]').classList.add('required');
|
||||
this.find('textarea[name="rebuttal"]').setAttribute('data-type', 'text');
|
||||
|
||||
} else {
|
||||
ui.hide(this.find('.resubmission'));
|
||||
this.find('h2').innerHTML = 'Submission';
|
||||
this.find('textarea[name="changes"]').classList.remove('required');
|
||||
this.find('textarea[name="changes"]').setAttribute('data-type', 'ignore');
|
||||
this.find('textarea[name="rebuttal"]').classList.remove('required');
|
||||
this.find('textarea[name="rebuttal"]').setAttribute('data-type', 'ignore');
|
||||
}
|
||||
|
||||
this.submit = this.components.find(item => item.el.classList.contains('submit'));
|
||||
this.submit.addEventListener('click', this.onSubmit.bind(this))
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
super.fill(mode);
|
||||
|
||||
if(this.mode == 'edit') ui.show(this.find('.submitting'));
|
||||
}
|
||||
|
||||
switchSubmit(hasIssues) {
|
||||
if(hasIssues) {
|
||||
ui.show(this.find('.incomplete'))
|
||||
this.submit.disabled = true;
|
||||
} else {
|
||||
this.submit.disabled = false;
|
||||
ui.hide(this.find('.incomplete'))
|
||||
}
|
||||
}
|
||||
|
||||
async onSubmit(event) { this.el.dispatchEvent(new Event('submit')); }
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023DisclaimerView', SubmissionShortForm2023DisclaimerView);
|
||||
@@ -0,0 +1,72 @@
|
||||
<style>
|
||||
.short-form [data-part="documents"] embed.EmbedDocument {
|
||||
min-height: 50vw;
|
||||
display: none;
|
||||
width: 100%;
|
||||
}
|
||||
.short-form [data-part="documents"] embed.EmbedVideo {
|
||||
min-height: 20vw;
|
||||
display: none;
|
||||
}
|
||||
.documentFiles {
|
||||
grid-template-rows: 1fr;
|
||||
}
|
||||
.documentFiles .deck, .documentFiles .video {
|
||||
min-height: 8vw;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
|
||||
<h2>Supporting documents</h2>
|
||||
|
||||
<div class="documentFiles cols-2">
|
||||
<input eicinput type="hidden" name="pitchName" data-path="documents.pitchDeck" value="" class="required" />
|
||||
<input eicinput type="hidden" name="fileName" data-path="documents.pitchDeck" value="" class="required" />
|
||||
<input eicinput type="hidden" name="pitchName" data-path="documents.pitchVideo" value="" class="required" />
|
||||
<input eicinput type="hidden" name="fileName" data-path="documents.pitchVideo" value="" class="required" />
|
||||
<article eiccard class="deck">
|
||||
<header>
|
||||
<h1>Pitch Deck</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>Make sure your pitch summarises your proposal, but more importantly, engage us with your story! Convey your message in a compelling manner and persuade us to support your innovative idea.
|
||||
(It should be provided in PDF and not exceed 10 pages)</p>
|
||||
<p>Be assured you will have the opportunity to upload the latest version of your pitch as part of the full (step 2) proposal.</p>
|
||||
<div eicalert danger>The file is missing.</div>
|
||||
<button eicbutton primary rounded small class="view-pdf" icon="icon-pdf"></button>
|
||||
</section>
|
||||
<footer>
|
||||
<button eicbutton primary small class="upload-pdf"></button>
|
||||
</footer>
|
||||
</article>
|
||||
<article eiccard class="video">
|
||||
<header>
|
||||
<h1>Pitch Video</h1>
|
||||
</header>
|
||||
<section>
|
||||
<p>Describe your company and your project in a 3 minutes video with up to 3 team members</p>
|
||||
<div eicalert danger class="missing">The file is missing.</div>
|
||||
<div eicalert warning class="notready">
|
||||
Your video has been uploaded and is being converted.
|
||||
This process can take a few minutes.
|
||||
You'll be able to view it once the conversion is complete.
|
||||
In order to be able to submit your proposal, you video must be converted.
|
||||
</div>
|
||||
<div eicalert danger class="convert-error">
|
||||
Your video could not be converted.
|
||||
This can be due to unsupported codecs.
|
||||
Please retry using a different format / encoding.
|
||||
</div>
|
||||
<button eicbutton primary rounded small class="view-video" icon="icon-youtube"></button>
|
||||
</section>
|
||||
<footer>
|
||||
<button eicbutton primary small class="upload-video"></button>
|
||||
</footer>
|
||||
</article>
|
||||
</div>
|
||||
<embed type="application/pdf" class="EmbedDocument" src=""/>
|
||||
<div class="videoStage">
|
||||
<video id="video" width="100%" controls autoplay class="EmbedVideo"></video>
|
||||
<div eicalert warning class="noStreamingWarn">Looks like your browser does not support video streaming.</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,314 @@
|
||||
class SubmissionShortForm2023DocumentsView extends SubmissionShortFormTabView {
|
||||
|
||||
options = {
|
||||
mode: 'read',
|
||||
conversionCheckDelay: 10 // seconds between each conversion check
|
||||
}
|
||||
setupRules() {
|
||||
|
||||
this.AwsDownload = new AwsFileDownload({
|
||||
pdf : { url: app.config.api['/S3Files'].viewDocument.uri,
|
||||
method: app.config.api['/S3Files'].viewDocument.method,
|
||||
headers: { 'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
credentials: 'include'
|
||||
},
|
||||
})
|
||||
|
||||
this.embedDocument = this.find('.EmbedDocument')
|
||||
this.videoStage = this.find('.videoStage')
|
||||
this.embedVideo = this.find('.EmbedVideo')
|
||||
this.noStreamingWarn = this.find('.noStreamingWarn')
|
||||
|
||||
// Tri-state: false = not yet or we don't know, true => converted,
|
||||
// 'error' => conversion crash => show bad format msg, stop checking.
|
||||
this.videoConverted = false
|
||||
|
||||
this.pdfViewButton = this.components.find(o => o.el.classList.contains('view-pdf'));
|
||||
this.pdfViewButton.el.addEventListener('click', this.showPdf.bind(this))
|
||||
this.pdfUploadButton = this.components.find(o => o.el.classList.contains('upload-pdf'));
|
||||
this.pdfUploadButton.el.addEventListener('click', this.uploadPdf.bind(this))
|
||||
|
||||
this.videoViewButton = this.components.find(o => o.el.classList.contains('view-video'));
|
||||
this.videoViewButton.el.addEventListener('click', this.showVideo.bind(this))
|
||||
this.videoUploadButton = this.components.find(o => o.el.classList.contains('upload-video'));
|
||||
this.videoUploadButton.el.addEventListener('click', this.uploadVideo.bind(this))
|
||||
}
|
||||
|
||||
DOMContentRemoved() { this.stopCheckVideoConvertion(); }
|
||||
|
||||
validate() {
|
||||
let report = { valid: true, issues:0}
|
||||
if(this.find('[name="pitchName"][data-path="documents.pitchDeck"]').value == '') {
|
||||
report.valid = false
|
||||
report.issues ++
|
||||
ui.show(this.find('.deck [eicalert]'))
|
||||
} else ui.hide(this.find('.deck [eicalert]'))
|
||||
|
||||
if(this.find('[name="pitchName"][data-path="documents.pitchVideo"]').value == '') {
|
||||
report.valid = false
|
||||
report.issues ++
|
||||
ui.show(this.find('.video [eicalert].missing'))
|
||||
} else ui.hide(this.find('.video [eicalert].missing'))
|
||||
|
||||
if(this.videoConverted!=true) {
|
||||
report.valid = false
|
||||
report.issues ++
|
||||
}
|
||||
|
||||
return(report)
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
|
||||
super.fill(mode);
|
||||
|
||||
this.findAll('[eicalert]').forEach(element => { ui.hide(element) });
|
||||
ui.hide(this.videoStage);
|
||||
ui.hide(this.embedDocument);
|
||||
ui.hide(this.noStreamingWarn);
|
||||
|
||||
//Temporary => change checkconverted mechanics for rel4
|
||||
ui.hide(this.find('.video [eicalert].convert-error'));
|
||||
|
||||
this.hasPitchDeck = this.model.data.documents.pitchDeck &&
|
||||
this.model.data.documents.pitchDeck.fileName &&
|
||||
this.model.data.documents.pitchDeck.pitchName
|
||||
|
||||
this.hasPitchVideo = this.model.data.documents.pitchVideo &&
|
||||
this.model.data.documents.pitchVideo.fileName &&
|
||||
this.model.data.documents.pitchVideo.pitchName
|
||||
|
||||
if(this.hasPitchVideo) this.checkVideoConvertion();
|
||||
|
||||
this.refreshButtons();
|
||||
}
|
||||
|
||||
refreshButtons() {
|
||||
if(this.hasPitchDeck) {
|
||||
ui.show(this.pdfViewButton.el);
|
||||
this.pdfViewButton.label = this.model.data.documents.pitchDeck.pitchName;
|
||||
} else {
|
||||
ui.hide(this.pdfViewButton.el);
|
||||
}
|
||||
|
||||
if(this.hasPitchVideo) {
|
||||
ui.show(this.videoViewButton.el);
|
||||
this.videoViewButton.disabled = false;
|
||||
this.videoViewButton.label = this.model.data.documents.pitchVideo.pitchName;
|
||||
|
||||
if(this.videoConverted==false) { // don't know or not converted
|
||||
this.startCheckVideoConvertion()
|
||||
this.videoViewButton.disabled = true
|
||||
ui.show(this.find('.video [eicalert].notready'))
|
||||
ui.hide(this.find('.video [eicalert].convert-error'))
|
||||
} else if(this.videoConverted=='error') { // conversion crash
|
||||
this.stopCheckVideoConvertion()
|
||||
ui.show(this.find('.video [eicalert].convert-error'))
|
||||
ui.hide(this.find('.video [eicalert].notready'))
|
||||
} else { // conversion success
|
||||
ui.hide(this.find('.video [eicalert].notready'))
|
||||
ui.hide(this.find('.video [eicalert].convert-error'))
|
||||
}
|
||||
|
||||
} else {
|
||||
this.stopCheckVideoConvertion();
|
||||
ui.hide(this.videoViewButton.el);
|
||||
}
|
||||
|
||||
switch(this.mode) {
|
||||
case 'read':
|
||||
ui.hide(this.find('.deck footer'))
|
||||
ui.hide(this.find('.video footer'))
|
||||
break;
|
||||
case 'edit':
|
||||
ui.show(this.find('.deck footer'))
|
||||
this.pdfUploadButton.label = this.hasPitchDeck ? 'replace document' : 'Upload document'
|
||||
this.pdfUploadButton.hint = this.hasPitchDeck ? 'Replace the current PDF document by a new version' : 'Upload your PDF document'
|
||||
ui.show(this.find('.video footer'))
|
||||
this.videoUploadButton.label = this.hasPitchVideo ? 'replace video': 'upload video';
|
||||
this.videoUploadButton.hint = this.hasPitchVideo ? 'Replace the current video by a new version' : 'Upload your video';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
startCheckVideoConvertion() {
|
||||
if((this.videoConverted==true) || this.checkVideoConvertTO) return;
|
||||
this.checkVideoConvertTO = setTimeout(this.checkVideoConvertion.bind(this), 1000 * this.options.conversionCheckDelay)
|
||||
}
|
||||
|
||||
stopCheckVideoConvertion() {
|
||||
clearTimeout(this.checkVideoConvertTO)
|
||||
this.checkVideoConvertTO = null;
|
||||
}
|
||||
|
||||
reStartCheckVideoConvertion() {
|
||||
this.stopCheckVideoConvertion()
|
||||
this.startCheckVideoConvertion()
|
||||
}
|
||||
|
||||
checkVideoConvertion() {
|
||||
fetch(app.config.api['/S3Files'].checkVideoConversion.uri+'?'+crypto.randomUUID(),{
|
||||
method: app.config.api['/S3Files'].checkVideoConversion.method,
|
||||
body: `{ "fileName" : "${this.model.data.documents.pitchVideo.fileName}" }`,
|
||||
credentials: 'include'
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(resp => {
|
||||
if(resp.payload.conversionStatus == 'COMPLETE') this.videoConverted = true
|
||||
else if(resp.payload.conversionStatus == 'ERROR') this.videoConverted = 'error'
|
||||
else this.videoConverted = false
|
||||
|
||||
if((this.videoConverted==true) || (this.videoConverted == 'error')) {
|
||||
this.refreshButtons();
|
||||
this.stopCheckVideoConvertion()
|
||||
} else {
|
||||
this.reStartCheckVideoConvertion()
|
||||
}
|
||||
},
|
||||
err => {
|
||||
// Checking a file that exists in ML, but not on S3 makes a 404, which arrives here (but could also be any Lambda crash of course)
|
||||
// Do as if we don't know, continue checking (there are often recoverable Lambda crashes).
|
||||
// Maybe in the future : if(err.code==404) => switch back to the 'error' mechanism ?
|
||||
this.videoConverted = false
|
||||
this.reStartCheckVideoConvertion()
|
||||
this.refreshButtons();
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
async showPdf(event) {
|
||||
if(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if(this.hls && this.embedVideo) {
|
||||
this.embedVideo.pause();
|
||||
this.hls.stopLoad()
|
||||
this.hls.detachMedia()
|
||||
this.hls.destroy()
|
||||
}
|
||||
this.pdfViewButton.loading = true
|
||||
let awsUrl = await this.AwsDownload.getDownloadUrl('pdf', this.model.data.documents.pitchDeck.fileName)
|
||||
this.pdfViewButton.loading = false
|
||||
if(!awsUrl) return
|
||||
ui.hide(this.videoStage)
|
||||
|
||||
// Safari workaround : You can't just replace the src like with every other browser,
|
||||
// you need to create a new embed node with the src already in.
|
||||
let clone = this.embedDocument.cloneNode()
|
||||
clone.setAttribute('src',awsUrl)
|
||||
this.embedDocument.replaceWith(clone)
|
||||
this.embedDocument=clone
|
||||
ui.show(this.embedDocument)
|
||||
}
|
||||
|
||||
async showVideo(event) {
|
||||
if(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
ui.show(this.videoStage);
|
||||
ui.hide(this.embedDocument);
|
||||
if(!Hls.isSupported()) {
|
||||
ui.show(this.noStreamingWarn);
|
||||
return
|
||||
}
|
||||
ui.hide(this.noStreamingWarn);
|
||||
let noextName = this.model.data.documents.pitchVideo.fileName.substr(0,this.model.data.documents.pitchVideo.fileName.lastIndexOf('.'))
|
||||
let videoUrl = app.config.api['/S3Files'].viewVideo.uri+`${noextName}.m3u8`
|
||||
let config = {
|
||||
debug: false,
|
||||
xhrSetup: function (xhr,url) {
|
||||
xhr.withCredentials = true; // do send cookie
|
||||
}
|
||||
};
|
||||
this.hls = new Hls(config);
|
||||
this.hls.loadSource(videoUrl)
|
||||
this.hls.attachMedia(this.embedVideo);
|
||||
this.hls.on(Hls.Events.MANIFEST_PARSED,(() => {
|
||||
this.embedVideo.play();
|
||||
}).bind(this));
|
||||
}
|
||||
|
||||
async uploadPdf(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let filename = this.hasPitchDeck ? this.model.data.documents.pitchDeck.fileName: '';
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/submissions/dialogs/SubmissionFormUploadDialog',
|
||||
{ title: 'Upload a pitch deck PDF...' },
|
||||
{ filename: filename,
|
||||
pic: this.model.data.organisation.pic,
|
||||
pid: this.model.data.proposal.number,
|
||||
allowedExtensions: ['pdf'],
|
||||
allowedMimes: ['application/pdf'],
|
||||
getSignedUrl : app.config.api['/S3Files'].uploadPdf.getSignedUrl,
|
||||
getSignedMethod : app.config.api['/S3Files'].uploadPdf.getSignedMethod,
|
||||
uploadUrl : app.config.api['/S3Files'].uploadPdf.uploadUrl,
|
||||
uploadMethod : app.config.api['/S3Files'].uploadPdf.uploadMethod,
|
||||
message: 'Drop your pitch deck PDF here !',
|
||||
}
|
||||
)
|
||||
);
|
||||
if(result) {
|
||||
this.find('[name="pitchName"][data-path="documents.pitchDeck"]').value = result.pitchName
|
||||
this.find('[name="fileName"][data-path="documents.pitchDeck"]').value = result.fileName
|
||||
this.model.data.documents.pitchDeck = {
|
||||
fileName : result.fileName,
|
||||
pitchName : result.pitchName
|
||||
}
|
||||
this.hasPitchDeck = true;
|
||||
this.refreshButtons()
|
||||
this.validate();
|
||||
this.el.dispatchEvent(new Event('save'))
|
||||
}
|
||||
}
|
||||
|
||||
async uploadVideo(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
let filename = this.hasPitchVideo ? this.model.data.documents.pitchVideo.fileName : '';
|
||||
|
||||
let result = await this.openDialog(
|
||||
await this.loadContent(
|
||||
'projects/submissions/dialogs/SubmissionFormUploadDialog',
|
||||
{ title: 'Upload a pitch video...' },
|
||||
{ filename: filename,
|
||||
pic: this.model.data.organisation.pic,
|
||||
pid: this.model.data.proposal.number,
|
||||
allowedExtensions: ['mp4', '3gp', 'mov', 'qt', 'avi', 'wmv', 'webm', '3g2', 'ogv', 'm4v'],
|
||||
allowedMimes: [ 'video/mp4', 'video/quicktime', 'video/3gpp', 'video/3gpp2', 'video/3gp2', 'video/x-msvideo', 'video/avi', 'video/x-ms-wmv',
|
||||
'video/webm', 'video/x-m4v'],
|
||||
getSignedUrl : app.config.api['/S3Files'].uploadVideo.getSignedUrl,
|
||||
getSignedMethod : app.config.api['/S3Files'].uploadVideo.getSignedMethod,
|
||||
uploadUrl : app.config.api['/S3Files'].uploadPdf.uploadUrl,
|
||||
uploadMethod : app.config.api['/S3Files'].uploadPdf.uploadMethod,
|
||||
message: 'Drop your pitch video here !'
|
||||
}
|
||||
)
|
||||
);
|
||||
if(result) {
|
||||
this.find('[name="pitchName"][data-path="documents.pitchVideo"]').value = result.pitchName
|
||||
this.find('[name="fileName"][data-path="documents.pitchVideo"]').value = result.fileName
|
||||
this.model.data.documents.pitchVideo = {
|
||||
fileName : result.fileName,
|
||||
pitchName : result.pitchName
|
||||
}
|
||||
this.hasPitchVideo = true;
|
||||
this.videoConverted = false;
|
||||
this.startCheckVideoConvertion();
|
||||
this.refreshButtons();
|
||||
this.validate();
|
||||
this.el.dispatchEvent(new Event('save'))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023DocumentsView', SubmissionShortForm2023DocumentsView);
|
||||
@@ -0,0 +1,33 @@
|
||||
<h2>Funding request</h2>
|
||||
<div>
|
||||
<label>Please indicate which EIC Support you intend to request</label>
|
||||
<select eicselect name="budgetType" data-path="funding" data-type="text" class="required">
|
||||
<option value=""></option>
|
||||
<option value="0">Blended Finance</option>
|
||||
<option value="1">Grant First</option>
|
||||
<option value="2">Grant Only</option>
|
||||
<option value="3">I don't know yet</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>Total cost (up to TRL 8)</label>
|
||||
<input eicinput type="currency" name="totalCost" data-path="funding" data-type="text" min="0" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Grant amount requested from EIC (maximum 70% of total cost and less than 2.5M€)</label>
|
||||
<input eicinput type="currency" name="requestedAmount" data-path="funding" data-type="text" min="2" max="2499999" class="required" />
|
||||
<div eicalert info>This amount is purely indicative and does not represent any obligation.</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Grant amount from other funding sources</label>
|
||||
<input eicinput type="currency" name="otherFunding" data-path="funding" data-type="ignore" disabled />
|
||||
<div eicalert info>This amount is the funding not covered by the EIC</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Investment amount requested from EIC ( From 500 000 euros up to 15 000 000 euros)</label>
|
||||
<input eicinput type="currency" name="InvestmentAmount" data-path="funding" data-type="text" min="500000" max="15000000" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Describe your financial needs for grants and investment, explain why you have not been able to raise sufficient investment to carry out the project, and why you need the support of the EIC. Please note that the figures are indicative at this stage, and you will have the possibility to modify this within your full (step 2) proposal.</label>
|
||||
<textarea eictextarea name="needs" data-path="funding" data-type="text" maxlen="10000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,125 @@
|
||||
class SubmissionShortForm2023FundingView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {
|
||||
|
||||
this.totalCost = this.find('[name="totalCost"]');
|
||||
this.requestedAmount = this.find('[name="requestedAmount"]');
|
||||
this.otherFunding = this.find('[name="otherFunding"]');
|
||||
this.InvestmentAmount = this.find('[name="InvestmentAmount"]');
|
||||
|
||||
this.type = this.find('[name="budgetType"]');
|
||||
this.type.addEventListener('change', this.onTypeChange.bind(this));
|
||||
|
||||
this.total = this.find('[name="totalCost"]');
|
||||
this.invest = this.find('[name="InvestmentAmount"]');
|
||||
this.other = this.find('[name="otherFunding"]');
|
||||
this.requested = this.find('[name="requestedAmount"]');
|
||||
this.total.addEventListener('change', this.onTotalChange.bind(this));
|
||||
this.requested.addEventListener('change', this.onRequestedChange.bind(this));
|
||||
this.invest.addEventListener('change', this.onInvestChange.bind(this));
|
||||
|
||||
this.type.value = "";
|
||||
this.onTypeChange();
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
super.fill(mode);
|
||||
|
||||
this.maxRequested = Math.floor(Math.min(parseInt(this.total.value) * 0.7, 2499999));
|
||||
this.other.value = parseInt(this.total.value) - parseInt(this.requested.value);
|
||||
|
||||
this.other.disabled = true;
|
||||
|
||||
}
|
||||
|
||||
onTotalChange(event) {
|
||||
if(this.total.min && (parseInt(this.total.value) < this.total.min))
|
||||
this.total.value = this.total.min;
|
||||
|
||||
if(this.total.max && (parseInt(this.total.value) > this.total.max))
|
||||
this.total.value = this.total.max;
|
||||
|
||||
this.maxRequested = Math.floor(Math.min(parseInt(this.total.value) * 0.7, 2499999));
|
||||
|
||||
if(parseInt(this.requested.value) >= this.maxRequested) {
|
||||
this.requested.value = Math.floor(this.maxRequested);
|
||||
this.requested.max = this.maxRequested;
|
||||
}
|
||||
this.onRequestedChange(event);
|
||||
|
||||
this.other.value = parseInt(this.total.value) - parseInt(this.requested.value);
|
||||
}
|
||||
|
||||
onRequestedChange(event) {
|
||||
if(parseInt(this.requested.value) < this.requested.min)
|
||||
this.requested.value = this.requested.min;
|
||||
|
||||
if(parseInt(this.requested.value) > this.maxRequested) {
|
||||
this.requested.value = Math.floor(this.maxRequested);
|
||||
this.requested.max = this.maxRequested;
|
||||
}
|
||||
|
||||
this.other.value = parseInt(this.total.value) - parseInt(this.requested.value);
|
||||
}
|
||||
|
||||
onInvestChange(event) {
|
||||
if(parseInt(this.invest.value) > 15000000) this.invest.value = 15000000;
|
||||
if(parseInt(this.invest.value) < 500000) this.invest.value = 500000;
|
||||
}
|
||||
|
||||
onTypeChange(event) {
|
||||
if(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
let value = '';
|
||||
let option = this.type.querySelector('option[selected="selected"], option[selected]') // Safari shit
|
||||
if(option) value = option.value
|
||||
switch(value) {
|
||||
case '':
|
||||
ui.hide(this.totalCost.parentElement.parentElement);
|
||||
this.totalCost.classList.remove('required');
|
||||
ui.hide(this.requestedAmount.parentElement.parentElement);
|
||||
this.requestedAmount.classList.remove('required');
|
||||
ui.hide(this.otherFunding.parentElement.parentElement);
|
||||
ui.hide(this.InvestmentAmount.parentElement.parentElement);
|
||||
this.InvestmentAmount.classList.remove('required');
|
||||
|
||||
break;
|
||||
case '0':
|
||||
ui.show(this.totalCost.parentElement.parentElement);
|
||||
this.totalCost.classList.add('required');
|
||||
ui.show(this.requestedAmount.parentElement.parentElement);
|
||||
this.requestedAmount.classList.add('required');
|
||||
ui.show(this.otherFunding.parentElement.parentElement);
|
||||
ui.show(this.InvestmentAmount.parentElement.parentElement);
|
||||
this.InvestmentAmount.classList.add('required');
|
||||
|
||||
break;
|
||||
case '1':
|
||||
case '2':
|
||||
ui.show(this.totalCost.parentElement.parentElement);
|
||||
this.totalCost.classList.add('required');
|
||||
ui.show(this.requestedAmount.parentElement.parentElement);
|
||||
this.requestedAmount.classList.add('required');
|
||||
ui.show(this.otherFunding.parentElement.parentElement);
|
||||
ui.hide(this.InvestmentAmount.parentElement.parentElement);
|
||||
this.InvestmentAmount.classList.remove('required');
|
||||
|
||||
break;
|
||||
case '3':
|
||||
ui.hide(this.totalCost.parentElement.parentElement);
|
||||
this.totalCost.classList.remove('required');
|
||||
ui.hide(this.requestedAmount.parentElement.parentElement);
|
||||
this.requestedAmount.classList.remove('required');
|
||||
ui.hide(this.otherFunding.parentElement.parentElement);
|
||||
ui.hide(this.InvestmentAmount.parentElement.parentElement);
|
||||
this.InvestmentAmount.classList.remove('required');
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023FundingView', SubmissionShortForm2023FundingView);
|
||||
@@ -0,0 +1,60 @@
|
||||
<h2>General Information</h2>
|
||||
<div class="cols-2 left">
|
||||
<div>
|
||||
<label>Proposal Number</label>
|
||||
<input eicinput disabled name="number" data-path="proposal" data-type="text" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Acronym</label>
|
||||
<input eicinput name="acronym" data-path="proposal" data-type="text" maxlen="50" class="required" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Full Title</label>
|
||||
<input eicinput name="title" data-path="proposal" data-type="text" maxlen="200" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Abstract</label>
|
||||
<textarea eictextarea name="abstract" data-path="proposal" data-type="text" maxlen="1000" class="required"></textarea>
|
||||
</div>
|
||||
<div>
|
||||
<label>Estimated duration of the project (in months)</label>
|
||||
<input eicinput type="number" name="estimatedDuration" data-path="proposal" data-type="number" min="1" max="60" class="required" />
|
||||
</div>
|
||||
<div>
|
||||
<label>Could you estimate the current TRL of your project ?</label>
|
||||
<select eicselect name="estimatedTRL" data-path="proposal" data-type="number" class="required">
|
||||
<option></option>
|
||||
<option value="1"><span><h5>TRL1: Basic Research</h5>Basic principles observed </span></option>
|
||||
<option value="2"><span><h5>TRL2: Technology Formulation</h5>Technology concept formulated </span></option>
|
||||
<option value="3"><span><h5>TRL3: Needs Validation</h5>Experimental proof of concept</span></option>
|
||||
<option value="4"><span><h5>TRL4: Small Scale Prototype</h5>Technology validated in lab</span></option>
|
||||
<option value="5"><span><h5>TRL5: Large Scale Prototype</h5>Technology validated in relevant environment (industrially relevant environment in the case of key enabling technologies) </span></option>
|
||||
<option value="6"><span><h5>TRL6: Prototype System</h5>Technology demonstrated in relevant environment (industrially relevant environment in the case of key enabling technologies) </span></option>
|
||||
<option value="7"><span><h5>TRL7: Demonstration System</h5>System prototype demonstration in operational environment </span></option>
|
||||
<option value="8"><span><h5>TRL8: First Of A Kind Commercial System</h5>System complete and qualified </span></option>
|
||||
<option value="9"><span><h5>TRL9: Full Commercial Application</h5>Actual system proven in operational environment (competitive manufacturing in the case of key enabling technologies; or in space) </span></option>
|
||||
</select>
|
||||
</div>
|
||||
<div><label>Relevant keywords for the proposal</label></div>
|
||||
<div class="cols-3">
|
||||
<div>
|
||||
<label>1st Keyword</label>
|
||||
<select eicselect name="keyword1Code" lookup data-path="proposal" data-type="array" class="required">
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>2nd Keyword</label>
|
||||
<select eicselect name="keyword2Code" lookup data-path="proposal" data-type="array" class="required">
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label>3rd Keyword</label>
|
||||
<select eicselect name="keyword3Code" lookup data-path="proposal" data-type="array" class="required">
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Additional free keywords</label>
|
||||
<select eicselect name="freeKeywords" editable data-path="proposal" data-type="array" class="" placeholder="Type in your keyword and press enter" />
|
||||
</div>
|
||||
@@ -0,0 +1,31 @@
|
||||
class SubmissionShortForm2023GeneralView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {
|
||||
this.find('[name="estimatedDuration"]').addEventListener('keypress', this.onDurationCheck.bind(this))
|
||||
}
|
||||
|
||||
fill(mode) {
|
||||
let keywords = [];
|
||||
|
||||
keywords = app.meta.toOptions('eic-keywords', null, true);
|
||||
keywords.forEach(item => this.find('select[name="keyword1Code"]').append(item));
|
||||
|
||||
keywords = app.meta.toOptions('eic-keywords', null, true);
|
||||
keywords.forEach(item => this.find('select[name="keyword2Code"]').append(item));
|
||||
|
||||
keywords = app.meta.toOptions('eic-keywords', null, true);
|
||||
keywords.forEach(item => this.find('select[name="keyword3Code"]').append(item));
|
||||
|
||||
super.fill(mode);
|
||||
|
||||
this.find('[name="number"]').setAttribute('disabled', '');
|
||||
|
||||
}
|
||||
|
||||
onDurationCheck(event) {
|
||||
if(!(/^[0-9]$/i.test(event.key))) event.preventDefault();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023GeneralView', SubmissionShortForm2023GeneralView);
|
||||
@@ -0,0 +1,5 @@
|
||||
<h2>Broad impacts</h2>
|
||||
<div>
|
||||
<label>Describe and quantify, if possible, the broad expected impact of your innovation on society, the environment and climate, the UN Sustainable Development Goals and on job creation. Refer to any relevant EU policy. </label>
|
||||
<textarea eictextarea name="impacts" data-path="narrative" data-type="text" maxlen="10000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
class SubmissionShortForm2023ImpactView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023ImpactView', SubmissionShortForm2023ImpactView);
|
||||
@@ -0,0 +1,5 @@
|
||||
<h2>The problem/market opportunity</h2>
|
||||
<div>
|
||||
<label>Describe the problem you are trying to address from the customer/user point of view.</label>
|
||||
<textarea eictextarea name="opportunity" data-path="narrative" data-type="text" maxlen="5000" class="required"></textarea>
|
||||
</div>
|
||||
@@ -0,0 +1,6 @@
|
||||
class SubmissionShortForm2023ProblemView extends SubmissionShortFormTabView {
|
||||
|
||||
setupRules() {}
|
||||
}
|
||||
|
||||
app.registerClass('SubmissionShortForm2023ProblemView', SubmissionShortForm2023ProblemView);
|
||||
@@ -0,0 +1,5 @@
|
||||
<h2>The innovation: Solution/Product or Services (USP)</h2>
|
||||
<div>
|
||||
<label>Present the solution; explain how it works in practice, what it changes for potential users, the way(s) in which it is unique, why it has breakthrough potential, why it is better than existing solutions available on the market, explain concretely how you have achieved the current TRL level, and describe why now is the right time to bring it to the market.</label>
|
||||
<textarea eictextarea name="solution" data-path="narrative" data-type="text" maxlen="20000" class="required"></textarea>
|
||||
</div>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user