From 2a40bdf99ea30e482efefa941da812c26ed3b0a7 Mon Sep 17 00:00:00 2001
From: Benedikt Seeger <benedikt.seeger@ptb.de>
Date: Thu, 27 Feb 2025 16:56:31 +0100
Subject: [PATCH] added used methodes :)

---
 src/renderers/MeasurementRenderer.js | 226 ++++++++++++++++++++-------
 1 file changed, 172 insertions(+), 54 deletions(-)

diff --git a/src/renderers/MeasurementRenderer.js b/src/renderers/MeasurementRenderer.js
index 306213b..edaaa3d 100644
--- a/src/renderers/MeasurementRenderer.js
+++ b/src/renderers/MeasurementRenderer.js
@@ -1,5 +1,7 @@
 import Plotly from 'plotly.js-dist';
 import { DCCRealListQuantity, DCCConformity } from '../dccQuantity.js';
+import JSONEditor from 'jsoneditor';
+import 'jsoneditor/dist/jsoneditor.css';
 
 const palette = [
   '#1f77b4',
@@ -36,15 +38,17 @@ const conformityColors = {
   nofail: '#9e9e9e'
 };
 
+// Render all measurement results in a tabbed layout
 export function renderMeasurementResults(measurementResults, language) {
   console.debug('renderMeasurementResults called with:', measurementResults);
   const container = document.getElementById('measurementResults');
   container.innerHTML = '';
 
-  if (!measurementResults) {
-    console.error('No measurementResults provided.');
-    return;
-  }
+  // Create tab headers and content container
+  const tabHeader = document.createElement('ul');
+  tabHeader.className = 'tab-header';
+  const tabContent = document.createElement('div');
+  tabContent.className = 'tab-content';
 
   let results = measurementResults['dcc:measurementResult'];
   if (!results) {
@@ -52,26 +56,131 @@ export function renderMeasurementResults(measurementResults, language) {
     return;
   }
   if (!Array.isArray(results)) { results = [results]; }
-  const measurementResult = results[0];
 
-  let resultObj = measurementResult['dcc:results'] && measurementResult['dcc:results']['dcc:result'];
-  if (!resultObj) {
-    console.error("Missing 'dcc:results' or 'dcc:result' in measurementResult:", measurementResult);
-    return;
+  results.forEach((result, index) => {
+    // Determine tab title from dcc:name
+    let resultName = 'Measurement Result ' + (index + 1);
+    if (result['dcc:name'] && result['dcc:name']['dcc:content']) {
+      let content = result['dcc:name']['dcc:content'];
+      if (Array.isArray(content)) {
+        const match = content.find(item => item.$ && item.$.lang === language) || content[0];
+        resultName = match._ || match;
+      } else {
+        resultName = content._ || content;
+      }
+    }
+    // Create tab header item
+    const tabItem = document.createElement('li');
+    tabItem.textContent = resultName;
+    if (index === 0) tabItem.classList.add('active');
+
+    // Create tab panel for this measurement result
+    const tabPanel = document.createElement('div');
+    tabPanel.className = 'tab-panel';
+    tabPanel.style.display = (index === 0) ? 'block' : 'none';
+
+    tabItem.addEventListener('click', () => {
+      document.querySelectorAll('.tab-header li').forEach(li => li.classList.remove('active'));
+      document.querySelectorAll('.tab-content > div').forEach(div => div.style.display = 'none');
+      tabItem.classList.add('active');
+      tabPanel.style.display = 'block';
+      // Force a resize event for Plotly to recalc dimensions
+      window.dispatchEvent(new Event('resize'));
+    });
+    tabHeader.appendChild(tabItem);
+
+    // Extract the single dcc:result from this measurement result and render it
+    let resultObj = result['dcc:results'] && result['dcc:results']['dcc:result'];
+    if (!resultObj) {
+      console.error("Missing 'dcc:results' or 'dcc:result' in measurementResult:", result);
+    } else {
+      let singleResult = Array.isArray(resultObj) ? resultObj[0] : resultObj;
+      renderSingleMeasurementResult(singleResult, language, tabPanel);
+    }
+
+    // Add expandable section for Used Methods using JSONEditor
+    const methodsDetails = document.createElement('details');
+    methodsDetails.open = false;
+    const methodsSummary = document.createElement('summary');
+    methodsSummary.textContent = 'Used Methods';
+    methodsDetails.appendChild(methodsSummary);
+    methodsDetails.appendChild(renderUsedMethods(result, language));
+    tabPanel.appendChild(methodsDetails);
+
+    // Add expandable section for Influence Conditions using JSONEditor
+    const influenceDetails = document.createElement('details');
+    influenceDetails.open = false;
+    const influenceSummary = document.createElement('summary');
+    influenceSummary.textContent = 'Influence Conditions';
+    influenceDetails.appendChild(influenceSummary);
+    influenceDetails.appendChild(renderInfluenceConditions(result, language));
+    tabPanel.appendChild(influenceDetails);
+
+    tabContent.appendChild(tabPanel);
+  });
+
+  container.appendChild(tabHeader);
+  container.appendChild(tabContent);
+}
+
+// Helper to render used methods as JSON tree via JSONEditor
+function renderUsedMethods(measurementResult, language) {
+  const container = document.createElement('div');
+  container.style.maxHeight = '200px';
+  container.style.overflowY = 'auto';
+  const options = {
+    mode: 'view',
+    mainMenuBar: false,
+    navigationBar: false,
+    statusBar: false
+  };
+  const editor = new JSONEditor(container, options);
+  if (measurementResult['dcc:usedMethods']) {
+    editor.set(measurementResult['dcc:usedMethods']);
+  } else {
+    container.textContent = 'No used methods data available.';
+  }
+  return container;
+}
+
+// Helper to render influence conditions as JSON tree via JSONEditor
+function renderInfluenceConditions(measurementResult, language) {
+  const container = document.createElement('div');
+  container.style.maxHeight = '200px';
+  container.style.overflowY = 'auto';
+  const options = {
+    mode: 'view',
+    mainMenuBar: false,
+    navigationBar: false,
+    statusBar: false
+  };
+  const editor = new JSONEditor(container, options);
+  if (measurementResult['dcc:influenceConditions']) {
+    editor.set(measurementResult['dcc:influenceConditions']);
+  } else {
+    container.textContent = 'No influence conditions data available.';
   }
-  if (Array.isArray(resultObj)) { resultObj = resultObj[0]; }
+  return container;
+}
+
+// Helper to render a single measurement result (charts, tables, etc.)
+export function renderSingleMeasurementResult(resultObj, language, tabPanel) {
+  console.debug('renderSingleMeasurementResult called with:', resultObj);
+  tabPanel.innerHTML = '';
 
   let resultName = 'Measurement Result';
-  if (measurementResult['dcc:name'] && measurementResult['dcc:name']['dcc:content']) {
-    let content = measurementResult['dcc:name']['dcc:content'];
+  if (resultObj['dcc:name'] && resultObj['dcc:name']['dcc:content']) {
+    let content = resultObj['dcc:name']['dcc:content'];
     if (Array.isArray(content)) {
       const match = content.find(item => item.$ && item.$.lang === language) || content[0];
       resultName = match._ || match;
-    } else { resultName = content._ || content; }
+    } else {
+      resultName = content._ || content;
+    }
   }
-  const tabTitle = document.createElement('h2');
-  tabTitle.textContent = resultName;
-  container.appendChild(tabTitle);
+  const header = document.createElement('h2');
+  header.textContent = resultName;
+  tabPanel.appendChild(header);
 
   if (!resultObj['dcc:data'] || !resultObj['dcc:data']['dcc:list']) {
     console.error("Missing 'dcc:data' or 'dcc:list' in result object:", resultObj);
@@ -99,7 +208,6 @@ export function renderMeasurementResults(measurementResults, language) {
 
   const indexQuantities = [];
   const dataQuantities = [];
-  // extraInfo now stores { uncertainty, conformity } for each data quantity.
   const extraInfo = [];
   quantityJSONs.forEach(q => {
     if (q.$ && q.$.refType && q.$.refType.match(/basic_tableIndex/)) {
@@ -122,15 +230,15 @@ export function renderMeasurementResults(measurementResults, language) {
   });
 
   const dataHeaders = dataQuantities.map((q, idx) => {
-    let header = q.getName(language);
+    let headerText = q.getName(language);
     let unit = q.getUnit();
-    if (!header.toLowerCase().includes(" in ")) {
-      header = header + " in " + unit;
+    if (!headerText.toLowerCase().includes(" in ")) {
+      headerText = headerText + " in " + unit;
     }
-    return header;
+    return headerText;
   });
 
-  // Create scaling toggles for log axes.
+  // Create scaling toggles and X-axis selector controls
   const scalingContainer = document.createElement('div');
   scalingContainer.innerHTML = '<strong>Scaling:</strong> ';
   const logXToggle = document.createElement('input');
@@ -149,7 +257,7 @@ export function renderMeasurementResults(measurementResults, language) {
   logYLabel.textContent = 'Log Y';
   scalingContainer.appendChild(logYToggle);
   scalingContainer.appendChild(logYLabel);
-  container.appendChild(scalingContainer);
+  tabPanel.appendChild(scalingContainer);
 
   const xAxisContainer = document.createElement('div');
   xAxisContainer.innerHTML = '<strong>Select X-Axis:</strong> ';
@@ -166,7 +274,7 @@ export function renderMeasurementResults(measurementResults, language) {
     xAxisContainer.appendChild(radio);
     xAxisContainer.appendChild(label);
   });
-  container.appendChild(xAxisContainer);
+  tabPanel.appendChild(xAxisContainer);
 
   const toleranceToggle = document.createElement('input');
   toleranceToggle.type = 'checkbox';
@@ -177,17 +285,18 @@ export function renderMeasurementResults(measurementResults, language) {
   const tolContainer = document.createElement('div');
   tolContainer.appendChild(toleranceToggle);
   tolContainer.appendChild(tolLabel);
-  container.appendChild(tolContainer);
+  tabPanel.appendChild(tolContainer);
 
+  // Create containers for plots and table.
   const subplotsContainer = document.createElement('div');
   subplotsContainer.id = 'subplotsContainer';
-  container.appendChild(subplotsContainer);
+  tabPanel.appendChild(subplotsContainer);
   const tableContainer = document.createElement('div');
   tableContainer.id = 'tableContainer';
-  container.appendChild(tableContainer);
+  tabPanel.appendChild(tableContainer);
 
   function updateVisualization() {
-    const selectedRadio = document.querySelector('input[name="xAxisSelect"]:checked');
+    const selectedRadio = tabPanel.querySelector('input[name="xAxisSelect"]:checked');
     if (!selectedRadio) { console.error('No X-Axis selection found.'); return; }
     const selectedIndex = selectedRadio.value;
     const xQuantity = indexQuantities[selectedIndex];
@@ -228,7 +337,7 @@ export function renderMeasurementResults(measurementResults, language) {
       row.push(extraInfo.map(info => info.comment || '').filter(c => c).join(' ; '));
       tableData.push(row);
     }
-    renderTable(tableData);
+    renderTable(tableData, computeConformityMapping());
 
     const unitGroups = {};
     dataQuantities.forEach((q, idx) => {
@@ -258,9 +367,7 @@ export function renderMeasurementResults(measurementResults, language) {
       graphDiv.style.height = '300px';
       subplotsContainer.appendChild(graphDiv);
       plotDivs.push(graphDiv);
-
-      // Build main traces for this unit group.
-      let groupTraces = group.map(trace => {
+      const groupTraces = group.map(trace => {
         let tooltip = 'X: %{x} ' + xUnit + ' | ' + trace.name + ': %{y}';
         if (trace.conformity && trace.conformity.length > 0) {
           tooltip += ' | Conformity: %{customdata}';
@@ -289,9 +396,8 @@ export function renderMeasurementResults(measurementResults, language) {
         };
       });
 
-      // If tolerance markings are enabled and conformity data exists, add tolerance traces.
       const toleranceTraces = [];
-      if (document.getElementById('toleranceToggle').checked) {
+      if (tabPanel.querySelector('#toleranceToggle').checked) {
         group.forEach(trace => {
           const confObj = extraInfo[trace.index].conformity;
           if (confObj) {
@@ -322,7 +428,6 @@ export function renderMeasurementResults(measurementResults, language) {
           }
         });
       }
-      // Combine main traces with tolerance traces.
       const allTraces = groupTraces.concat(toleranceTraces);
 
       let xaxisTitle = '';
@@ -330,8 +435,8 @@ export function renderMeasurementResults(measurementResults, language) {
         let xLabel = 'X: ' + xQuantity.getName(language);
         xaxisTitle = '<b>' + xLabel + ' in ' + xUnit + '</b>';
       }
-      const logX = document.getElementById('logXToggle').checked;
-      const logY = document.getElementById('logYToggle').checked;
+      const logX = tabPanel.querySelector('#logXToggle').checked;
+      const logY = tabPanel.querySelector('#logYToggle').checked;
       const layout = {
         xaxis: {
           title: { text: xaxisTitle, font: { size: 36, family: 'Arial', color: 'black' } },
@@ -343,7 +448,6 @@ export function renderMeasurementResults(measurementResults, language) {
           tickfont: { family: 'Arial', size: 14, color: 'black' },
           type: logY ? 'log' : 'linear'
         },
-        // Place legend on top over the plot without shifting x-axis scaling.
         legend: {
           orientation: 'h',
           x: 0.5,
@@ -355,6 +459,8 @@ export function renderMeasurementResults(measurementResults, language) {
         margin: { t: 20, b: 40 }
       };
       Plotly.newPlot(graphDiv, allTraces, layout).then(() => {
+        // Force a resize after a short delay to ensure full width
+        setTimeout(() => { Plotly.Plots.resize(graphDiv); }, 100);
         const caption = document.createElement('div');
         caption.innerHTML = '<b>' + group[0].name + '</b>';
         caption.style.textAlign = 'center';
@@ -380,8 +486,8 @@ export function renderMeasurementResults(measurementResults, language) {
     });
   }
 
-  function renderTable(tableData) {
-    const tableContainer = document.getElementById('tableContainer');
+  function renderTable(tableData, conformityMapping) {
+    const tableContainer = tabPanel.querySelector('#tableContainer');
     tableContainer.innerHTML = '';
     const table = document.createElement('table');
     tableData.forEach((rowData, rowIndex) => {
@@ -390,17 +496,13 @@ export function renderMeasurementResults(measurementResults, language) {
         const cell = document.createElement(rowIndex === 0 ? 'th' : 'td');
         if (rowIndex === 0) {
           cell.innerHTML = cellData;
-          if (cellData !== 'Comments' && cellIndex > 0) {
-            const qtyIndex = Math.floor((cellIndex - 1) / 2);
-            cell.style.backgroundColor = lightPalette[qtyIndex % lightPalette.length];
+          if (cellData !== 'Comments' && cellIndex > 0 && conformityMapping[cellIndex] !== undefined) {
+            cell.style.backgroundColor = conformityMapping[cellIndex];
           }
         } else {
           cell.textContent = cellData;
-          if (tableData[0][cellIndex] && tableData[0][cellIndex].toLowerCase().includes('conformity')) {
-            const confVal = cellData.toLowerCase();
-            if (confVal in conformityColors) {
-              cell.style.backgroundColor = conformityColors[confVal];
-            }
+          if (tableData[0][cellIndex] && tableData[0][cellIndex].toLowerCase().includes('conformity') && conformityMapping[cellIndex] !== undefined) {
+            cell.style.backgroundColor = conformityMapping[cellIndex];
           }
         }
         cell.style.padding = '4px';
@@ -414,20 +516,36 @@ export function renderMeasurementResults(measurementResults, language) {
     tableContainer.appendChild(table);
   }
 
+  function computeConformityMapping() {
+    let mapping = {};
+    let col = 1; // Column 0 is X-Axis.
+    for (let i = 0; i < dataHeaders.length; i++) {
+      col++; // value column.
+      if (extraInfo[i] && extraInfo[i].conformity) {
+        mapping[col] = palette[i % palette.length];
+        col++;
+      }
+    }
+    return mapping;
+  }
+
   function highlightTableRow(rowIndex) {
-    const rows = document.getElementById('tableContainer').querySelectorAll('tr');
+    const rows = tabPanel.querySelector('#tableContainer').querySelectorAll('tr');
     if (rows[rowIndex]) { rows[rowIndex].style.backgroundColor = '#fee'; }
   }
 
   function clearTableRowHighlights() {
-    const rows = document.getElementById('tableContainer').querySelectorAll('tr');
+    const rows = tabPanel.querySelector('#tableContainer').querySelectorAll('tr');
     rows.forEach(row => row.style.backgroundColor = '');
   }
 
   updateVisualization();
-  const radios = document.querySelectorAll('input[name="xAxisSelect"]');
+  const radios = tabPanel.querySelectorAll('input[name="xAxisSelect"]');
   radios.forEach(radio => { radio.addEventListener('change', updateVisualization); });
-  document.getElementById('logXToggle').addEventListener('change', updateVisualization);
-  document.getElementById('logYToggle').addEventListener('change', updateVisualization);
-  toleranceToggle.addEventListener('change', () => { console.log('Tolerance toggle:', toleranceToggle.checked); updateVisualization(); });
+  tabPanel.querySelector('#logXToggle').addEventListener('change', updateVisualization);
+  tabPanel.querySelector('#logYToggle').addEventListener('change', updateVisualization);
+  tabPanel.querySelector('#toleranceToggle').addEventListener('change', () => {
+    console.log('Tolerance toggle:', tabPanel.querySelector('#toleranceToggle').checked);
+    updateVisualization();
+  });
 }
-- 
GitLab