From ae41ae459c433b2d5acea0457190da922d51e9b8 Mon Sep 17 00:00:00 2001
From: Benedikt Seeger <benedikt.seeger@ptb.de>
Date: Fri, 21 Mar 2025 10:36:54 +0100
Subject: [PATCH] admindata aktualisiert

---
 package.json                   |   2 +-
 src/app.js                     |  17 ++++-
 src/renderers/AdminRenderer.js | 133 +++++++++++++++++++++++++++------
 src/styles.css                 |  17 +++++
 4 files changed, 144 insertions(+), 25 deletions(-)

diff --git a/package.json b/package.json
index 61e981a..be6e561 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "dccviewer-js",
-  "version": "0.1.4",
+  "version": "0.1.5",
   "description": "A JS application for displaying digital calibration certificates.",
   "main": "dist/dccviewer-js.bundle.js",
   "files": [
diff --git a/src/app.js b/src/app.js
index 22c847f..a4a70e5 100644
--- a/src/app.js
+++ b/src/app.js
@@ -50,14 +50,29 @@ export function init(xmlStr, options = {}) {
         : (languages.length ? languages[0] : 'en');
 
     // Build a dynamic language dropdown based on the extracted languages
+// Build a dynamic language dropdown based on the extracted languages
     const langSelect = document.createElement('select');
+    langSelect.classList.add('lang-select');
+
+// Mapping for custom option text with flag symbols for language codes
+    const languageOptionText = {
+      de: '🇩🇪 DE Sprache wechseln',
+      en: '🇺🇸 EN Change Language',
+      fr: '🇫🇷 FR Changer de langue',
+      es: '🇪🇸 ES Cambiar idioma',
+      ru: '🇷🇺 RU Изменить язык',
+      cn: '🇨🇳 CN 切换语言'
+    };
+
     languages.forEach(lang => {
       const option = document.createElement('option');
       option.value = lang;
-      option.textContent = lang;
+      // Use custom text if available; otherwise, fallback to the language code.
+      option.textContent = languageOptionText[lang] || lang;
       if (lang === selectedLanguage) option.selected = true;
       langSelect.appendChild(option);
     });
+
     // When the language is changed, reinitialize the app with the same XML but new language.
     langSelect.addEventListener('change', (e) => {
       init(xmlStr, { containerId, language: e.target.value });
diff --git a/src/renderers/AdminRenderer.js b/src/renderers/AdminRenderer.js
index 815fab4..ce1b88e 100644
--- a/src/renderers/AdminRenderer.js
+++ b/src/renderers/AdminRenderer.js
@@ -28,9 +28,13 @@ export function renderAdminData(adminData, language) {
   const container = document.getElementById('adminData');
   container.innerHTML = '';
 
-  // Title
+  // Localized title for Administrative Data.
+  const adminTitleMapping = {
+    de: 'Administrative Daten',
+    en: 'Administrative Data'
+  };
   const title = document.createElement('h2');
-  title.textContent = `Administrative Data`;
+  title.textContent = adminTitleMapping[language] || adminTitleMapping.en;
   container.appendChild(title);
 
   // ----- CORE DATA SECTION -----
@@ -41,14 +45,14 @@ export function renderAdminData(adminData, language) {
     coreContainer.style.border = '1px solid #ccc';
     coreContainer.style.padding = '8px';
 
-// Get the UUID from coreData.
+    // Get the UUID from coreData.
     const uuid = coreData['dcc:uniqueIdentifier'];
-// If a UUID exists, format it using renderUUID; otherwise use 'N/A'
+    // If a UUID exists, format it using renderUUID; otherwise use 'N/A'
     const formattedUUID = uuid ? renderUUID(uuid) : 'N/A';
 
     const uuidDivText = document.createElement('div');
     uuidDivText.innerHTML = `<strong>UUID:</strong> ${formattedUUID}`;
-// Increase font size and boldness
+    // Increase font size and boldness
     uuidDivText.style.marginBottom = '10px';
     uuidDivText.style.fontSize = '24px';
     uuidDivText.style.fontWeight = 'bold';
@@ -130,19 +134,53 @@ export function renderAdminData(adminData, language) {
       }
       labDiv.innerHTML += logoImg;
     }
-    // Use static label based on language
-    const labLabelMapping = {
-      de: 'Kalibrierlabor',
-      en: 'Calibration Laboratory',
-      fr: "Laboratoire d'étalonnage",
-      es: 'Laboratorio de calibración'
-    };
     labDiv.innerHTML += `<strong>${labLabelMapping[language] || 'Calibration Laboratory'}:</strong><br>`;
     labDiv.innerHTML += renderAddress(calLab);
+
+    // ----- RESPONSIBLE PERSONS SECTION -----
+    if (adminData['dcc:respPersons'] && adminData['dcc:respPersons']['dcc:respPerson']) {
+      // Separation line
+      const hr = document.createElement('hr');
+      labDiv.appendChild(hr);
+      // Header for Responsible Persons (localized)
+      const respPersonsHeader = document.createElement('h3');
+      const respPersonsMapping = {
+        de: "Verantwortliche Personen",
+        en: "Responsible Persons"
+      };
+      respPersonsHeader.textContent = respPersonsMapping[language] || respPersonsMapping.en;
+      labDiv.appendChild(respPersonsHeader);
+      let respPersons = adminData['dcc:respPersons']['dcc:respPerson'];
+      if (!Array.isArray(respPersons)) {
+        respPersons = [respPersons];
+      }
+      respPersons.forEach(respPerson => {
+        let personName = "";
+        if (respPerson['dcc:person'] &&
+            respPerson['dcc:person']['dcc:name'] &&
+            respPerson['dcc:person']['dcc:name']['dcc:content']) {
+          const content = respPerson['dcc:person']['dcc:name']['dcc:content'];
+          personName = Array.isArray(content)
+              ? (content.find(c => c.$ && c.$.lang === language) || content[0])._ || ''
+              : content._ || content;
+        }
+        const personDiv = document.createElement('div');
+        personDiv.textContent = personName;
+        // Mark main signer if applicable.
+        if (respPerson['dcc:mainSigner'] === 'true' || respPerson['dcc:mainSigner'] === true) {
+          const mainSignerMapping = {
+            de: " (Hauptunterzeichner)",
+            en: " (Main Signer)"
+          };
+          personDiv.textContent += mainSignerMapping[language] || mainSignerMapping.en;
+        }
+        labDiv.appendChild(personDiv);
+      });
+    }
     addressesContainer.appendChild(labDiv);
   }
 
-  // Customer (left side)
+  // Customer (right side)
   if (adminData['dcc:customer']) {
     const customer = adminData['dcc:customer'];
     const custDiv = document.createElement('div');
@@ -155,7 +193,44 @@ export function renderAdminData(adminData, language) {
   }
   container.appendChild(addressesContainer);
 
-// ----- ITEMS SECTION (Updated for inline image with mouseover enlargement) -----
+  // ----- PERFORMANCE FIELDS SECTION -----
+  if (adminData['dcc:coreData']) {
+    const coreData = adminData['dcc:coreData'];
+    const performanceContainer = document.createElement('div');
+    performanceContainer.style.marginBottom = '20px';
+    performanceContainer.style.border = '1px solid #ccc';
+    performanceContainer.style.padding = '8px';
+    // Mapping for performance fields.
+    const performanceLabels = {
+      'dcc:beginPerformanceDate': {
+        de: 'Begin der Kalibrierung',
+        en: 'Calibration Start'
+      },
+      'dcc:endPerformanceDate': {
+        de: 'Ende der Kalibrierung',
+        en: 'Calibration End'
+      },
+      'dcc:performanceLocation': {
+        de: 'Prüfungsort',
+        en: 'Performance Location'
+      },
+      'dcc:issueDate': {
+        de: 'Ausstellungsdatum',
+        en: 'Issue Date'
+      }
+    };
+    // Loop through each performance field and render if available.
+    Object.keys(performanceLabels).forEach(key => {
+      if (coreData[key]) {
+        const fieldDiv = document.createElement('div');
+        fieldDiv.innerHTML = `<strong>${performanceLabels[key][language] || performanceLabels[key].en}:</strong> ${coreData[key]}`;
+        performanceContainer.appendChild(fieldDiv);
+      }
+    });
+    container.appendChild(performanceContainer);
+  }
+
+  // ----- ITEMS SECTION (Updated for inline image with mouseover enlargement) -----
   if (adminData['dcc:items'] && adminData['dcc:items']['dcc:item']) {
     let items = adminData['dcc:items']['dcc:item'];
     if (!Array.isArray(items)) items = [items];
@@ -187,22 +262,19 @@ export function renderAdminData(adminData, language) {
       }
       detailsDiv.innerHTML += `<strong>Item:</strong> ${itemName}<br>`;
 
-// Manufacturer details
+      // Manufacturer details
       if (item['dcc:manufacturer']) {
         const manu = item['dcc:manufacturer'];
         let manuName = '';
         if (manu['dcc:name'] && manu['dcc:name']['dcc:content']) {
           const content = manu['dcc:name']['dcc:content'];
           if (Array.isArray(content)) {
-            // Try to find an element matching the desired language.
             let found = content.find(c => c.$ && c.$.lang === language);
-            // If not found, look for an element without a language attribute.
             if (!found) {
               found = content.find(c => !c.$ || !c.$.lang);
             }
             manuName = found ? (typeof found === 'string' ? found : found._ || '') : '';
           } else {
-            // Handle the case where content is a plain string or an object.
             manuName = typeof content === 'string' ? content : content._ || '';
           }
         }
@@ -384,7 +456,7 @@ export function renderAdminData(adminData, language) {
     container.appendChild(statementsContainer);
   }
 
-  // ----- SOFTWARE SECTION -----
+// ----- SOFTWARE SECTION (Updated) -----
   if (adminData['dcc:dccSoftware'] && adminData['dcc:dccSoftware']['dcc:software']) {
     let softwares = adminData['dcc:dccSoftware']['dcc:software'];
     if (!Array.isArray(softwares)) softwares = [softwares];
@@ -392,19 +464,34 @@ export function renderAdminData(adminData, language) {
     softwareDiv.style.border = '1px solid #ccc';
     softwareDiv.style.padding = '8px';
     softwareDiv.style.marginBottom = '20px';
-    softwareDiv.innerHTML = '<strong>Used Software:</strong><br>';
+    const softwareTitleMapping = {
+      de: 'Verwendete Software',
+      en: 'Used Software'
+    };
+    softwareDiv.innerHTML = `<strong>${softwareTitleMapping[language] || softwareTitleMapping.en}:</strong><br>`;
     softwares.forEach(sw => {
       let swName = '';
       if (sw['dcc:name'] && sw['dcc:name']['dcc:content']) {
         const content = sw['dcc:name']['dcc:content'];
-        swName = Array.isArray(content)
-            ? (content.find(c => c.$ && c.$.lang === language) || content[0])._ || ''
-            : content._ || '';
+        if (Array.isArray(content)) {
+          // Try to find an entry with a matching language.
+          const matching = content.find(c => c.$ && c.$.lang === language);
+          if (matching && matching._) {
+            swName = matching._;
+          } else {
+            // If no matching lang is found, use the first element regardless of its language attribute.
+            swName = (typeof content[0] === 'object' ? (content[0]._ || '') : content[0]);
+          }
+        } else {
+          // If content is not an array, check if it's an object with a "_" property or a plain string.
+          swName = typeof content === 'object' ? (content._ || '') : content;
+        }
       }
       softwareDiv.innerHTML += `${swName} (Release: ${sw['dcc:release'] || ''}, Type: ${sw['dcc:type'] || ''})<br>`;
     });
     container.appendChild(softwareDiv);
   }
+
 }
 
 // Helper function to render a full address from a location/contact object
diff --git a/src/styles.css b/src/styles.css
index 2525970..39c419b 100644
--- a/src/styles.css
+++ b/src/styles.css
@@ -38,3 +38,20 @@ th, td {
   padding: 10px !important;  /* Increase padding */
   font-size: 14px;  /* Adjust font size if needed */
 }
+
+.lang-select {
+  font-size: 16px;       /* Increase font size for better readability */
+  padding: 8px;          /* More padding makes it more touch-friendly */
+  width: 100%;           /* Use the available width */
+  max-width: 200px;      /* Optionally, limit the maximum width */
+  box-sizing: border-box; /* Ensure padding doesn't affect total width */
+  border: 1px solid #ccc; /* Add a light border */
+  border-radius: 4px;    /* Soften the corners */
+}
+
+@media (max-width: 600px) {
+  .lang-select {
+    font-size: 18px;     /* Slightly larger on mobile devices */
+    padding: 10px;
+  }
+}
\ No newline at end of file
-- 
GitLab