unclean SPARC

This commit is contained in:
STEINNI
2025-08-27 07:03:09 +00:00
commit f308460931
430 changed files with 54426 additions and 0 deletions
@@ -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>
+102
View File
@@ -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>
+159
View File
@@ -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);