Skip to content

Commit 5169b74

Browse files
authored
Merge pull request #1946 from OpenEnergyPlatform/feature/1941-group-metadata-fields-on-summary-page
Group metadata fields for summary
2 parents 58b8e65 + 28af742 commit 5169b74

File tree

4 files changed

+364
-262
lines changed

4 files changed

+364
-262
lines changed

dataedit/static/peer_review/opr_contributor.js

Lines changed: 179 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -359,65 +359,13 @@ function selectState(state) { // eslint-disable-line no-unused-vars
359359
/**
360360
* Displays fields based on selected category
361361
*/
362-
function renderSummaryPageFields() {
363-
const acceptedFields = [];
364-
const rejectedFields = [];
365-
const missingFields = [];
366-
const emptyFields = [];
367-
368-
const processedFields = new Set();
369-
370-
if (state_dict && Object.keys(state_dict).length > 0) {
371-
const fields = document.querySelectorAll('.field');
372-
for (let field of fields) {
373-
let field_id = field.id.slice(6);
374-
const fieldValue = $(field).find('.value').text().replace(/\s+/g, ' ').trim();
375-
const fieldState = getFieldState(field_id);
376-
const fieldCategory = field.getAttribute('data-category');
377-
let fieldName = field_id.replace(/\./g, ' ');
378-
const uniqueFieldIdentifier = `${fieldName}-${fieldCategory}`;
379-
if (isEmptyValue(fieldValue)) {
380-
emptyFields.push({ fieldName, fieldValue, fieldCategory: "emptyFields" });
381-
processedFields.add(uniqueFieldIdentifier);
382-
} else if (fieldState === 'ok' ) {
383-
acceptedFields.push({ fieldName, fieldValue, fieldCategory });
384-
processedFields.add(uniqueFieldIdentifier);
385-
}
386-
else if (fieldState === 'rejected') {
387-
rejectedFields.push({ fieldName, fieldValue, fieldCategory });
388-
processedFields.add(uniqueFieldIdentifier);
389-
}
390-
}
391-
}
392-
393-
for (const review of current_review.reviews) {
394-
const field_id = `#field_${review.key}`.replaceAll(".", "\\.");
395-
const fieldValue = $(field_id).find('.value').text().replace(/\s+/g, ' ').trim();
396-
const isAccepted = review.fieldReview.some((fieldReview) => fieldReview.state === 'ok');
397-
const isRejected = review.fieldReview.some((fieldReview) => fieldReview.state === 'rejected');
398-
const fieldCategory = review.category;
399-
let fieldName = review.key.replace(/\./g, ' ');
400-
401-
const uniqueFieldIdentifier = `${fieldName}-${fieldCategory}`;
402362

403-
if (processedFields.has(uniqueFieldIdentifier)) {
404-
continue; // Skipp fields that have already been processed from state_dict
405-
}
406-
407-
408-
if (isEmptyValue(fieldValue)) {
409-
emptyFields.push({ fieldName, fieldValue, fieldCategory: "emptyFields" });
410-
} else if (isAccepted) {
411-
acceptedFields.push({ fieldName, fieldValue, fieldCategory });
412-
} else if (isRejected) {
413-
rejectedFields.push({ fieldName, fieldValue, fieldCategory });
414-
}
415-
}
416-
417-
const categories = document.querySelectorAll(".tab-pane");
363+
function renderSummaryPageFields() {
364+
const categoriesMap = {};
418365

419-
for (const category of categories) {
420-
const category_name = category.id.slice(0);
366+
function addFieldToCategory(category, field) {
367+
if (!categoriesMap[category]) categoriesMap[category] = [];
368+
categoriesMap[category].push(field);
421369

422370
if (category_name === "summary") {
423371
continue;
@@ -441,73 +389,197 @@ function renderSummaryPageFields() {
441389
}
442390
}
443391

444-
const summaryContainer = document.getElementById("summary");
445-
446-
function clearSummaryTable() {
447-
while (summaryContainer.firstChild) {
448-
summaryContainer.firstChild.remove();
392+
const fields = document.querySelectorAll('.field');
393+
fields.forEach(field => {
394+
const field_id = field.id.slice(6);
395+
const fieldValue = $(field).find('.value').text().trim();
396+
const fieldState = getFieldState(field_id);
397+
const fieldCategory = field.getAttribute('data-category');
398+
let fieldName = field_id.replace(/\./g, ' ');
399+
if (fieldCategory !== "general") {
400+
fieldName = fieldName.split(' ').slice(1).join(' ');
449401
}
450-
}
451402

452-
function generateTable(data) {
453-
let table = document.createElement('table');
454-
table.className = 'table review-summary';
403+
let fieldStatus = isEmptyValue(fieldValue) ? 'Empty' :
404+
fieldState === 'ok' ? 'Accepted' :
405+
fieldState === 'rejected' ? 'Rejected' : 'Missing';
455406

456-
let thead = document.createElement('thead');
457-
let header = document.createElement('tr');
458-
header.innerHTML = '<th scope="col">Status</th><th scope="col">Field Category</th><th scope="col">Field Name</th><th scope="col">Field Value</th>';
459-
thead.appendChild(header);
460-
table.appendChild(thead);
461-
462-
let tbody = document.createElement('tbody');
407+
addFieldToCategory(fieldCategory, { fieldName, fieldValue, fieldStatus });
408+
});
463409

464-
data.forEach((item) => {
465-
let row = document.createElement('tr');
410+
const summaryContainer = document.getElementById("summary");
411+
summaryContainer.innerHTML = '';
412+
413+
const tabsNav = document.createElement('ul');
414+
tabsNav.className = 'nav nav-tabs';
415+
416+
const tabsContent = document.createElement('div');
417+
tabsContent.className = 'tab-content';
418+
419+
let firstTab = true;
420+
421+
for (const category in categoriesMap) {
422+
const tabId = `tab-${category}`;
423+
424+
const navItem = document.createElement('li');
425+
navItem.className = 'nav-item';
426+
navItem.innerHTML = `<button class="nav-link${firstTab ? ' active' : ''}" data-bs-toggle="tab" data-bs-target="#${tabId}">${category}</button>`;
427+
tabsNav.appendChild(navItem);
428+
429+
const tabPane = document.createElement('div');
430+
tabPane.className = `tab-pane fade${firstTab ? ' show active' : ''}`;
431+
tabPane.id = tabId;
432+
433+
const fields = categoriesMap[category];
434+
const singleFields = [];
435+
const groupedFields = {};
436+
437+
fields.forEach(field => {
438+
const words = field.fieldName.split(' ');
439+
if (words.length === 1) {
440+
singleFields.push(field);
441+
} else {
442+
const prefix = words[0];
443+
const rest = words.slice(1);
444+
const indices = rest.filter(word => !isNaN(word));
445+
const nameWithoutIndices = rest.filter(word => isNaN(word)).join(' ');
446+
447+
if (!groupedFields[prefix]) groupedFields[prefix] = { indexed: {}, noIndex: [] };
448+
449+
if (indices.length > 0) {
450+
const indexKey = indices.map(num => (parseInt(num, 10) + 1)).join('.');
451+
if (!groupedFields[prefix].indexed[indexKey]) groupedFields[prefix].indexed[indexKey] = [];
452+
groupedFields[prefix].indexed[indexKey].push({ ...field, fieldName: nameWithoutIndices });
453+
} else {
454+
groupedFields[prefix].noIndex.push({ ...field, fieldName: nameWithoutIndices });
455+
}
466456

467457
let th = document.createElement('th');
468458
th.scope = "row";
469459
th.className = "status";
470460
if (item.fieldStatus === "Pending") {
471461
th.className = "status missing";
472462
}
473-
th.textContent = item.fieldStatus;
474-
row.appendChild(th);
475-
476-
let tdFieldCategory = document.createElement('td');
477-
tdFieldCategory.textContent = item.fieldCategory;
478-
row.appendChild(tdFieldCategory);
479-
480-
let tdFieldId = document.createElement('td');
481-
tdFieldId.textContent = item.fieldName;
482-
row.appendChild(tdFieldId);
483-
484-
let tdFieldValue = document.createElement('td');
485-
tdFieldValue.textContent = item.fieldValue;
486-
row.appendChild(tdFieldValue);
487-
488-
tbody.appendChild(row);
489463
});
490464

491-
table.appendChild(tbody);
492-
493-
return table;
494-
}
495-
496-
function updateSummaryTable() {
497-
clearSummaryTable();
465+
if (singleFields.length > 0) {
466+
const table = document.createElement('table');
467+
table.className = 'table review-summary';
468+
table.innerHTML = `
469+
<thead><tr><th>Status</th><th>Field Name</th><th>Field Value</th></tr></thead>
470+
<tbody>${singleFields.map(f => `
471+
<tr>
472+
<td class="status ${f.fieldStatus.toLowerCase()}">${f.fieldStatus}</td>
473+
<td>${f.fieldName}</td>
474+
<td>${f.fieldValue}</td>
475+
</tr>`).join('')}
476+
</tbody>`;
477+
tabPane.appendChild(table);
478+
}
498479

499-
let allData = [];
500-
allData.push(...missingFields.map((item) => ({...item, fieldStatus: 'Pending'})));
501-
allData.push(...rejectedFields.map((item) => ({...item, fieldStatus: 'Rejected'})));
502-
allData.push(...acceptedFields.map((item) => ({...item, fieldStatus: 'Accepted'})));
480+
if (Object.keys(groupedFields).length > 0) {
481+
const accordionContainer = document.createElement('div');
482+
accordionContainer.className = 'accordion';
483+
accordionContainer.id = `accordion-${category}`;
484+
485+
let accordionIndex = 0;
486+
for (const prefix in groupedFields) {
487+
const accordionItem = document.createElement('div');
488+
accordionItem.className = 'accordion-item';
489+
const headingId = `heading-${category}-${accordionIndex}`;
490+
const collapseId = `collapse-${category}-${accordionIndex}`;
491+
492+
const { noIndex, indexed } = groupedFields[prefix];
493+
494+
let innerHTML = '';
495+
496+
if (noIndex.length > 0) {
497+
innerHTML += `
498+
<table class="table table-sm table-bordered">
499+
<thead><tr><th>Status</th><th>Field Name</th><th>Field Value</th></tr></thead>
500+
<tbody>${noIndex.map(f => `
501+
<tr>
502+
<td class="status ${f.fieldStatus.toLowerCase()}">${f.fieldStatus}</td>
503+
<td>${f.fieldName}</td>
504+
<td>${f.fieldValue}</td>
505+
</tr>`).join('')}
506+
</tbody>
507+
</table>`;
508+
}
503509

504-
allData.push(...emptyFields.map((item) => ({...item, fieldStatus: 'Empty'})));
510+
if (Object.keys(indexed).length > 0) {
511+
const subAccordionId = `subAccordion-${category}-${accordionIndex}`;
512+
innerHTML += `<div class="accordion" id="${subAccordionId}">`;
513+
514+
Object.entries(indexed).forEach(([idx, idxFields], idxAccordionIndex) => {
515+
const idxHeadingId = `idxHeading-${category}-${accordionIndex}-${idxAccordionIndex}`;
516+
const idxCollapseId = `idxCollapse-${category}-${accordionIndex}-${idxAccordionIndex}`;
517+
518+
const tabLabel = ['source', 'license'].includes(category) ? 'fields' : `${prefix} ${idx}`;
519+
520+
innerHTML += `
521+
<div class="accordion-item">
522+
<h2 class="accordion-header" id="${idxHeadingId}">
523+
<button class="accordion-button collapsed" data-bs-toggle="collapse" data-bs-target="#${idxCollapseId}">
524+
${tabLabel}
525+
</button>
526+
</h2>
527+
<div id="${idxCollapseId}" class="accordion-collapse collapse" data-bs-parent="#${subAccordionId}">
528+
<div class="accordion-body">
529+
<table class="table table-sm table-bordered">
530+
<thead><tr><th>Status</th><th>Field Name</th><th>Field Value</th></tr></thead>
531+
<tbody>${idxFields.map(f => `
532+
<tr>
533+
<td class="status ${f.fieldStatus.toLowerCase()}">${f.fieldStatus}</td>
534+
<td>${f.fieldName}</td>
535+
<td>${f.fieldValue}</td>
536+
</tr>`).join('')}
537+
</tbody>
538+
</table>
539+
</div>
540+
</div>
541+
</div>`;
542+
});
543+
544+
innerHTML += `</div>`;
545+
}
505546

506-
let table = generateTable(allData);
507-
summaryContainer.appendChild(table);
547+
tabsContent.appendChild(tabPane);
548+
firstTab = false;
508549
}
509-
510-
updateSummaryTable();
550+
const viewsNavItem = document.createElement('li');
551+
viewsNavItem.className = 'nav-item';
552+
viewsNavItem.innerHTML = <button class="nav-link" data-bs-toggle="tab" data-bs-target="#tab-views">views</button>;
553+
554+
555+
tabsNav.appendChild(viewsNavItem);
556+
557+
const viewsPane = document.createElement('div');
558+
viewsPane.className = 'tab-pane fade';
559+
viewsPane.id = 'tab-views';
560+
561+
const allFields = Object.entries(categoriesMap).flatMap(([category, fields]) =>
562+
fields.map(f => ({...f, category}))
563+
);
564+
565+
viewsPane.innerHTML =
566+
<table class="table review-summary">
567+
<thead>
568+
<tr><th>Status</th><th>Category</th><th>Field Name</th><th>Field Value</th></tr>
569+
</thead>
570+
<tbody>${allFields.map(f =>
571+
<tr>
572+
<td class="status ${f.fieldStatus.toLowerCase()}">${f.fieldStatus}</td>
573+
<td>${f.category}</td>
574+
<td>${f.fieldName}</td>
575+
<td>${f.fieldValue}</td>
576+
</tr>).join('')}
577+
</tbody>
578+
</table>;
579+
580+
tabsContent.appendChild(viewsPane);
581+
summaryContainer.appendChild(tabsNav);
582+
summaryContainer.appendChild(tabsContent);
511583
updateTabProgressIndicatorClasses();
512584
}
513585

0 commit comments

Comments
 (0)