/** * UI Orchestration logic with Config support, 4 converter modes, * page navigation, and Watcher integration. */ window.EDIBridge = window.EDIBridge || {}; class App { constructor() { this.mode = 'outbound-bosch'; this.currentPage = 'converter'; this.currentData = null; this.rawContent = null; this.masterData = {}; this.config = null; this.configFileName = 'config.txt'; this.watcher = null; this.werksWatcher = null; this.initElements(); this.bindEvents(); this.loadConfigFromStorage(); this.initWatcher(); this.initOutboundWatcher(); this.initWerksWatcher(); // Initialize Sub-modules if (window.EDIBridge.Viewer) { this.viewer = new window.EDIBridge.Viewer(this); } if (window.EDIBridge.Editor) { this.editor = new window.EDIBridge.Editor(this); } if (window.EDIBridge.HistoryManager) { this.historyManager = new window.EDIBridge.HistoryManager(this); } } // ─── Elements ──────────────────────────────────────────────────── initElements() { this.dropZone = document.getElementById('dropZone'); this.fileInput = document.getElementById('fileInput'); this.configInput = document.getElementById('configInput'); this.enrichmentSection = document.getElementById('enrichmentSection'); this.resultSection = document.getElementById('resultSection'); this.previewArea = document.getElementById('previewArea'); this.dropText = document.getElementById('dropText'); this.configStatus = document.getElementById('configStatus'); this.statsGrid = document.getElementById('stats-grid'); this.ownCustomerNumber = document.getElementById('ownCustomerNumber'); } // ─── Events ────────────────────────────────────────────────────── bindEvents() { if (this.dropZone) { this.dropZone.onclick = (e) => { e.stopPropagation(); if (this.fileInput) this.fileInput.click(); }; this.dropZone.ondragover = (e) => { e.preventDefault(); this.dropZone.classList.add('dragover'); }; this.dropZone.ondragleave = () => this.dropZone.classList.remove('dragover'); this.dropZone.ondrop = (e) => { e.preventDefault(); this.dropZone.classList.remove('dragover'); this.handleFile(e.dataTransfer.files[0]); }; } if (this.fileInput) { this.fileInput.onchange = (e) => this.handleFile(e.target.files[0]); } if (this.configInput) { this.configInput.onchange = (e) => this.loadConfig(e.target.files[0]); } } // ─── Watcher Init ──────────────────────────────────────────────── initWatcher() { const WatcherBridge = window.EDIBridge.WatcherBridge; if (!WatcherBridge) return; this.watcher = new WatcherBridge(); this.watcher.onStatusChange = (status) => this.updateWatcherUI(status); this.watcher.onLogEntry = (entry) => this.appendLogEntry(entry); // Check if running in Electron if (!this.watcher.isElectron()) { const notice = document.getElementById('electronNotice'); if (notice) notice.style.display = 'flex'; // Disable folder buttons const btnIn = document.getElementById('btnSelectInput'); const btnOut = document.getElementById('btnSelectOutput'); const btnInvrptOut = document.getElementById('btnSelectInvrptOutput'); const btnStart = document.getElementById('btnStartWatcher'); if (btnIn) btnIn.disabled = true; if (btnOut) btnOut.disabled = true; if (btnInvrptOut) btnInvrptOut.disabled = true; if (btnStart) btnStart.disabled = true; } else { // Load saved settings this.watcher.loadSettings().then(settings => { if (settings.inputDir) { document.getElementById('inputDirPath').value = settings.inputDir; } if (settings.outputDir) { document.getElementById('outputDirPath').value = settings.outputDir; } if (settings.invrptOutputDir) { document.getElementById('invrptOutputPath').value = settings.invrptOutputDir; } }); } } initOutboundWatcher() { const OutboundWatcherBridge = window.EDIBridge.OutboundWatcherBridge; if (!OutboundWatcherBridge) return; this.outboundWatcher = new OutboundWatcherBridge(); this.outboundWatcher.onStatusChange = (status) => this.updateOutboundWatcherUI(status); this.outboundWatcher.onLogEntry = (entry) => this.appendLogEntry(entry); if (!this.outboundWatcher.isElectron()) return; // Load saved settings window.electronAPI.loadSettings().then(settings => { if (settings.outboundInputDir) { document.getElementById('outboundInputPath').value = settings.outboundInputDir; } if (settings.outboundOutputDir) { document.getElementById('outboundOutputPath').value = settings.outboundOutputDir; } }); } initWerksWatcher() { const WerksWatcherBridge = window.EDIBridge.WerksWatcherBridge; if (!WerksWatcherBridge) return; this.werksWatcher = new WerksWatcherBridge(); this.werksWatcher.onStatusChange = (status) => this.updateWerksWatcherUI(status); this.werksWatcher.onLogEntry = (entry) => this.appendLogEntry(entry); if (!this.werksWatcher.isElectron()) return; // Load saved settings window.electronAPI.loadSettings().then(settings => { if (settings.werksInputDir) { document.getElementById('werksInputPath').value = settings.werksInputDir; } if (settings.werksOutputDir) { document.getElementById('werksOutputPath').value = settings.werksOutputDir; } }); } showPage(page) { this.currentPage = page; document.querySelectorAll('.page-nav-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.page === page); }); document.getElementById('pageConverter').style.display = page === 'converter' ? '' : 'none'; document.getElementById('pageSettings').style.display = page === 'settings' ? '' : 'none'; if (page === 'settings') { this.switchSettingsSection('inbound'); } const pageViewer = document.getElementById('pageViewer'); if (pageViewer) pageViewer.style.display = page === 'viewer' ? '' : 'none'; const pageEditor = document.getElementById('pageEditor'); if (pageEditor) pageEditor.style.display = page === 'editor' ? '' : 'none'; const pageHistory = document.getElementById('pageHistory'); if (pageHistory) pageHistory.style.display = page === 'history' ? '' : 'none'; // Activate history manager when page is shown if (page === 'history' && this.historyManager) { this.historyManager.activate(); } if (window.lucide) try { lucide.createIcons(); } catch (e) { } } // ─── Settings Sidebar Navigation ───────────────────────────── switchSettingsSection(section) { document.querySelectorAll('.settings-nav-btn').forEach(btn => { btn.classList.toggle('active', btn.dataset.section === section); }); document.querySelectorAll('.settings-section').forEach(el => { el.style.display = el.id === `settingsSection-${section}` ? '' : 'none'; }); if (window.lucide) try { lucide.createIcons(); } catch (e) { } } // ─── Config ────────────────────────────────────────────────────── async loadConfigFromStorage() { let stored = null; // Try getting config from Database first if (window.electronAPI && window.electronAPI.dbGetConfig) { try { stored = await window.electronAPI.dbGetConfig(); if (stored) console.log("Config loaded from Database"); } catch (e) { console.error("Failed to load config from DB:", e); } } // Fallback to localStorage if DB doesn't have it if (!stored) { stored = localStorage.getItem('ediConfig'); if (stored) console.log("Config loaded from LocalStorage fallback"); } if (stored) { const ConfigParser = window.EDIBridge.ConfigParser; this.config = ConfigParser.parse(stored); if (this.watcher) this.watcher.config = this.config; this.updateConfigUI('Gespeichert (DB/Local)'); } } loadConfig(file) { if (!file) return; this.configFileName = file.name; const reader = new FileReader(); reader.onload = (e) => { const text = e.target.result; const ConfigParser = window.EDIBridge.ConfigParser; this.config = ConfigParser.parse(text); if (this.watcher) this.watcher.config = this.config; localStorage.setItem('ediConfig', text); this.updateConfigUI(file.name); if (this.currentData) { this.autoPopulateNADs(); this.renderEnrichmentTable(); } }; reader.readAsText(file); } updateConfigUI(name) { this.configStatus.innerHTML = 'Konfig: ' + name + ''; this.configStatus.classList.add('loaded'); const badge = document.getElementById('nadSourceBadge'); if (badge) { badge.textContent = 'Aus Konfig'; badge.className = 'badge badge-config'; } document.getElementById('btnSaveConfig').style.display = 'inline-flex'; this.renderMappingTable(); this.renderIdMapping711Table(); // Populate Own Customer Number if (this.ownCustomerNumber && window.EDIBridge.ConfigParser) { this.ownCustomerNumber.value = window.EDIBridge.ConfigParser.lookupGeneral(this.config, 'OWN_CUSTOMER_NUMBER', ''); } if (window.lucide) lucide.createIcons(); } async saveConfig() { if (!this.config) return; this.mergeUIToConfig(); const ConfigParser = window.EDIBridge.ConfigParser; const text = ConfigParser.serialize(this.config); // Save to LocalStorage array as fallback localStorage.setItem('ediConfig', text); // Save to Database (the main requirement) if (window.electronAPI && window.electronAPI.dbInsertConfig) { try { await window.electronAPI.dbInsertConfig(text); console.log("Config successfully saved to Database"); } catch (e) { console.error("Failed to save config to DB:", e); } } // Sync with watcher if (this.watcher) this.watcher.config = this.config; const blob = new Blob([text], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = this.configFileName || 'config.txt'; document.body.appendChild(a); a.click(); document.body.removeChild(a); // Revoke after a short delay to ensure browser handled the click setTimeout(() => URL.revokeObjectURL(url), 100); } saveToStorage() { if (!this.config) { // If no config object yet, create an empty one const ConfigParser = window.EDIBridge.ConfigParser; this.config = { __sectionMap__: {} }; this.updateConfigUI('Gespeichert (Local)'); } this.mergeUIToConfig(); const ConfigParser = window.EDIBridge.ConfigParser; const text = ConfigParser.serialize(this.config); localStorage.setItem('ediConfig', text); console.log("Config auto-saved to LocalStorage"); if (this.watcher) this.watcher.config = this.config; } mergeUIToConfig() { const ConfigParser = window.EDIBridge.ConfigParser; const updateSection = (sectionName, selector, dataKey) => { const section = ConfigParser.getSection(this.config, sectionName) || ConfigParser.ensureSection(this.config, sectionName); document.querySelectorAll(selector).forEach(input => { const val = input.value.trim(); const key = input.dataset[dataKey]; if (val) { section[key] = val; } else { delete section[key]; } }); }; // Sync General Settings if (this.ownCustomerNumber) { const genSection = ConfigParser.ensureSection(this.config, 'GENERAL'); const ownCust = this.ownCustomerNumber.value.trim(); if (ownCust) genSection['OWN_CUSTOMER_NUMBER'] = ownCust; else delete genSection['OWN_CUSTOMER_NUMBER']; } updateSection('PAC_WEIGHT', '.pac-weight-input', 'pacmat'); updateSection('PAC_LENGTH', '.pac-ln-input', 'pacmat'); updateSection('PAC_WIDTH', '.pac-wd-input', 'pacmat'); updateSection('PAC_HEIGHT', '.pac-ht-input', 'pacmat'); updateSection('LIN_WEIGHT', '.lin-weight-input', 'suppmat'); // Sync NAD fields back to config if (this.activeSourceId || this.activeTargetId) { const quals = ['by', 'se', 'st', 'sf']; const idMap = { 'by': this.activeTargetId, 'st': this.activeTargetId, 'se': this.activeSourceId, 'sf': this.activeSourceId }; const fields = { 'id': 'PARTY', 'qual': 'QUAL', 'name': 'NAME', 'name2': 'NAME2', 'street': 'STREET', 'city': 'CITY', 'zip': 'POSTCODE', 'country': 'COUNTRY' }; quals.forEach(q => { const plantId = idMap[q]; if (!plantId) return; Object.entries(fields).forEach(([f, sectionSuffix]) => { const el = document.getElementById(`nad-${q}-${f}`); if (el) { const val = el.value.trim(); const sectionName = `NAD_${q.toUpperCase()}_${sectionSuffix}`; const section = ConfigParser.ensureSection(this.config, sectionName); if (val) section[plantId] = val; else delete section[plantId]; } }); }); } } autoPopulateNADs() { if (!this.config || !this.currentData) return; const ConfigParser = window.EDIBridge.ConfigParser; const h = this.currentData.interchanges[0]?.header; if (!h) return; this.activeTargetId = h.targetId; this.activeSourceId = h.sourceId; const targetId = h.targetId; const sourceId = h.sourceId; const by = ConfigParser.lookupNAD(this.config, 'BY', targetId); if (by.name) this.fillNAD('by', by); const se = ConfigParser.lookupNAD(this.config, 'SE', sourceId); if (se.name) this.fillNAD('se', se); const st = ConfigParser.lookupNAD(this.config, 'ST', targetId); if (st.name) this.fillNAD('st', st); const sf = ConfigParser.lookupNAD(this.config, 'SF', sourceId); if (sf.name) this.fillNAD('sf', sf); } fillNAD(prefix, data) { const set = (field, val) => { const el = document.getElementById('nad-' + prefix + '-' + field); if (el) el.value = val || ''; }; set('id', data.id); set('qual', data.qual); set('name', data.name); set('name2', data.name2); set('street', data.street); set('city', data.city); set('zip', data.zip); set('country', data.country); } // ─── Mode Toggle ───────────────────────────────────────────────── setMode(mode) { this.mode = mode; const modes = ['outbound-bosch', 'outbound-zf', 'outbound-ifm', 'inbound-bosch', 'inbound-ifm', 'inbound-invrpt', 'validate-edifact']; const btnIds = ['btnOutboundBosch', 'btnOutboundZf', 'btnOutboundIfm', 'btnInboundBosch', 'btnInboundIfm', 'btnInboundInvrpt', 'btnValidateEdifact']; modes.forEach((m, i) => { const btn = document.getElementById(btnIds[i]); if (btn) btn.classList.toggle('active', m === mode); }); // Update drop zone text const labelMap = { 'outbound-bosch': 'VDA 4913 Datei auswählen → Bosch DESADV', 'outbound-zf': 'VDA 4913 Datei auswählen → ZF DESADV', 'outbound-ifm': 'VDA 4913 Datei auswählen → IFM DELVRY03', 'inbound-bosch': 'EDIFACT DELFOR Datei auswählen → VDA 4905', 'inbound-ifm': 'IFM DELFOR D04A Datei auswählen → VDA 4905', 'inbound-invrpt': 'INVRPT Datei auswählen → VDA 4913 EDL36', 'validate-edifact': 'EDIFACT Datei auswählen zur reinen Validierung/Prüfung', }; if (this.dropText) this.dropText.textContent = labelMap[mode] || ''; // Hide enrichment/result when switching modes if (this.enrichmentSection) this.enrichmentSection.style.display = 'none'; if (this.resultSection) this.resultSection.style.display = 'none'; } // ─── File Handling ─────────────────────────────────────────────── handleFile(file) { if (!file) return; this.currentFilename = file.name; const reader = new FileReader(); reader.onload = (e) => { this.rawContent = e.target.result; this._processContent(this.rawContent); }; reader.readAsText(file); } _processContent(content) { const trimmed = content.trim(); // Check if we should auto-detect based on customer ID const VDAParser = window.EDIBridge.VDAParser; let detectedMode = null; if (VDAParser) { let customerId = VDAParser.getCustomerId(trimmed); // If not VDA, try EDIFACT/DELFOR if (!customerId && window.EDIBridge.DelforParser) { customerId = window.EDIBridge.DelforParser.getCustomerId(trimmed); } console.log("Detected Customer ID:", customerId); if (customerId) { const ConfigParser = window.EDIBridge.ConfigParser; detectedMode = ConfigParser.lookupCustomerMode(this.config, customerId); console.log("Mapped mode for ID", customerId, ":", detectedMode); if (detectedMode) { this.setMode(detectedMode); } } } switch (this.mode) { case 'outbound-bosch': this.processOutboundBosch(trimmed); break; case 'outbound-zf': this.processOutboundZf(trimmed); break; case 'outbound-ifm': this.processOutboundIfm(trimmed); break; case 'inbound-bosch': this.processInboundBosch(trimmed); break; case 'inbound-ifm': this.processInboundIfm(trimmed); break; case 'inbound-invrpt': this.processInboundInvrpt(trimmed); break; case 'validate-edifact': this.processValidateEdifact(trimmed); break; default: // Auto-detect format fallback const upper = trimmed.toUpperCase(); if (upper.includes('INVRPT')) { this.setMode('inbound-invrpt'); this.processInboundInvrpt(trimmed); } else if (upper.startsWith('UNA') || upper.startsWith('UNB')) { this.setMode('inbound-bosch'); this.processInboundBosch(trimmed); } else { this.setMode('outbound-bosch'); this.processOutboundBosch(trimmed); } } } // ─── Inbound: Bosch DELFOR → VDA 4905 ──────────────────────────── processInboundBosch(content) { this.statsGrid.innerHTML = ''; this.enrichmentSection.style.display = 'none'; this.currentData = null; try { const parser = window.EDIBridge.DelforParser; const generator = window.EDIBridge.VDA4905Generator; if (!parser || !generator) throw new Error('Inbound-Module (DelforParser/VDA4905Generator) nicht geladen.'); const parsed = parser.parse(content); const vda4905 = generator.generate(parsed); this.previewArea.innerText = vda4905; this.resultSection.style.display = 'block'; document.getElementById('resultTitle').innerText = 'VDA 4905 Ergebnis (DELFOR → VDA 4905)'; const statsHtml = `
${parsed.segments.length}
EDIFACT Segments
${vda4905.split('\n').filter(l => l.trim()).length}
VDA 4905 Records
`; this.statsGrid.innerHTML = statsHtml; this.resultSection.scrollIntoView({ behavior: 'smooth' }); } catch (e) { alert('Inbound Error (Bosch): ' + e.message); console.error(e); } } // ─── Inbound: IFM DELFOR D04A → VDA 4905 ──────────────────────── processInboundIfm(content) { this.statsGrid.innerHTML = ''; this.enrichmentSection.style.display = 'none'; this.currentData = null; try { const Converter = window.EDIBridge.DelforToVDA4905Converter; if (!Converter) throw new Error('IFM DELFOR Converter (DelforToVDA4905Converter) nicht geladen.'); const result = Converter.convertWithValidation(content); if (result.errors && result.errors.length > 0) { console.warn('IFM DELFOR Validation Warnings:', result.errors); } const vda4905 = result.output || result; const outputText = typeof vda4905 === 'string' ? vda4905 : JSON.stringify(vda4905, null, 2); this.previewArea.innerText = outputText; this.resultSection.style.display = 'block'; document.getElementById('resultTitle').innerText = 'VDA 4905 Ergebnis (IFM DELFOR D04A → VDA 4905)'; const lines = outputText.split('\n').filter(l => l.trim()).length; const statsHtml = `
${lines}
VDA 4905 Records
`; this.statsGrid.innerHTML = statsHtml; if (result.warnings && result.warnings.length > 0) { this.statsGrid.innerHTML += `
${result.warnings.length}
Warnungen
`; } this.resultSection.scrollIntoView({ behavior: 'smooth' }); } catch (e) { alert('Inbound Error (IFM): ' + e.message); console.error(e); } } // ─── Inbound: INVRPT D13A → VDA 4913 ──────────────────────── processInboundInvrpt(content) { this.statsGrid.innerHTML = ''; this.enrichmentSection.style.display = 'none'; this.currentData = null; try { const Converter = window.EDIBridge.InvrptToVDA4913; if (!Converter) throw new Error('INVRPT-Module (InvrptToVDA4913) nicht geladen.'); const vda4913 = Converter.convert(content, this.config); this.previewArea.innerText = vda4913; this.resultSection.style.display = 'block'; document.getElementById('resultTitle').innerText = 'VDA 4913 Ergebnis (INVRPT → VDA 4913)'; const statsHtml = `
${vda4913.split('\\n').filter(l => l.trim()).length}
VDA 4913 Records
`; this.statsGrid.innerHTML = statsHtml; this.resultSection.scrollIntoView({ behavior: 'smooth' }); } catch (e) { alert('Inbound Error (INVRPT): ' + e.message); console.error(e); } } // ─── Utility: Validate EDIFACT (ts-edifact) ───────────────────── async processValidateEdifact(content) { this.statsGrid.innerHTML = ''; this.enrichmentSection.style.display = 'none'; this.currentData = null; if (!window.electronAPI || !window.electronAPI.validateEdifact) { alert('Validierung (ts-edifact) ist nur in der Electron-App verfügbar.'); return; } try { const result = await window.electronAPI.validateEdifact(content); const parsed = await window.electronAPI.parseEdifactLib(content); document.getElementById('resultTitle').innerText = 'EDIFACT Validierungsprozess'; let html = `

${result.valid ? ' Gültiges EDIFACT' : ' Ungültiges EDIFACT'}

`; if (result.stats.messageType) { html += `

Nachrichtentyp: ${result.stats.messageType} (Version: ${result.stats.messageVersion})

`; } if (result.stats.sender || result.stats.receiver) { html += `

Sender: ${result.stats.sender || '-'}    Empfänger: ${result.stats.receiver || '-'}

`; } if (result.errors && result.errors.length > 0) { html += `

Fehler

`; } if (result.warnings && result.warnings.length > 0) { html += `

Warnungen

`; } html += `

Extrahierte Segmente (Rohdaten)

${JSON.stringify(parsed.segments, null, 2)}
`; // Since previewArea usually escapes HTML via innerText, we set innerHTML and handle styling: this.previewArea.innerHTML = html; this.previewArea.style.whiteSpace = 'normal'; this.previewArea.style.backgroundColor = 'transparent'; this.previewArea.style.padding = '0'; this.resultSection.style.display = 'block'; const statsHtml = `
${result.valid ? 'OK' : 'ERR'}
Syntax-Status
${result.stats.segmentCount || 0}
Segmente
`; this.statsGrid.innerHTML = statsHtml; this.resultSection.scrollIntoView({ behavior: 'smooth' }); if (window.lucide) lucide.createIcons(); } catch (e) { alert('Validierungsfehler: ' + e.message); console.error(e); } } // ─── Outbound: VDA 4913 → Bosch DESADV ────────────────────────── processOutboundBosch(content) { const VDAParser = window.EDIBridge.VDAParser; const vda = VDAParser.parse(content); if (vda.interchanges.length === 0) { alert('Keine gültigen VDA 4913 Daten gefunden.'); return; } this.currentData = vda; this.updateStats(vda); this.autoPopulateNADs(); this.renderEnrichmentTable(); } // ─── Outbound: VDA 4913 → ZF DESADV ────────────────────────── processOutboundZf(content) { const VDAParser = window.EDIBridge.VDAParser; const vda = VDAParser.parse(content); if (vda.interchanges.length === 0) { alert('Keine gültigen VDA 4913 Daten gefunden.'); return; } this.currentData = vda; this.updateStats(vda); this.autoPopulateNADs(); this.renderEnrichmentTable(); } // ─── Outbound: VDA 4913 → IFM DELVRY03 ────────────────────────── processOutboundIfm(content) { this.statsGrid.innerHTML = ''; this.enrichmentSection.style.display = 'none'; try { const VDAParser = window.EDIBridge.VDAParser; const Converter = window.EDIBridge.VDA4913ToDELVRY03; if (!VDAParser) throw new Error('VDA Parser nicht geladen.'); if (!Converter) throw new Error('VDA4913ToDELVRY03 Converter nicht geladen.'); const parsed = VDAParser.parse(content); if (!parsed.interchanges || parsed.interchanges.length === 0) { alert('Keine gültigen VDA 4913 Daten gefunden.'); return; } const idocs = Converter.convert(parsed, this.config || {}); const xml = Converter.toXML(idocs); this.previewArea.innerText = xml; this.resultSection.style.display = 'block'; document.getElementById('resultTitle').innerText = 'IFM DELVRY03 Ergebnis (VDA 4913 → DELVRY03)'; const statsHtml = `
${parsed.interchanges.length}
Interchanges
${idocs.length}
IDocs erzeugt
`; this.statsGrid.innerHTML = statsHtml; this.resultSection.scrollIntoView({ behavior: 'smooth' }); } catch (e) { alert('Outbound Error (IFM): ' + e.message); console.error(e); } } // ─── Stats ─────────────────────────────────────────────────────── updateStats(vda) { if (!vda || !vda.interchanges) return; const count = vda.interchanges.length; const dnCount = vda.interchanges.reduce((acc, i) => acc + i.transports.reduce((tAcc, t) => tAcc + t.deliveryNotes.length, 0), 0); this.statsGrid.innerHTML = `
${count}
Interchanges
${dnCount}
Delivery Notes
`; } // ─── Enrichment Table ──────────────────────────────────────────── renderEnrichmentTable() { const tbody = document.querySelector('#enrichmentTable tbody'); if (!tbody || !this.currentData) return; tbody.innerHTML = ''; const ConfigParser = window.EDIBridge.ConfigParser; const positions = []; this.currentData.interchanges.forEach(i => { i.transports.forEach(t => { t.deliveryNotes.forEach(dn => { dn.positions.forEach(pos => positions.push(pos)); }); }); }); positions.forEach(pos => { const row = document.createElement('tr'); const suppMat = pos.data.suppMat; let weight = 0; let status = 'Aus VDA'; if (this.config) { weight = ConfigParser.lookupLinWeight(this.config, suppMat); if (weight > 0) { status = 'Aus Konfig'; } } row.innerHTML = ` ${pos.data.custMat} ${suppMat} ${pos.data.qty} ${pos.data.unit} ${status} `; tbody.appendChild(row); }); this.enrichmentSection.style.display = 'block'; this.renderPackagingTable(); } renderPackagingTable() { const tbody = document.querySelector('#packagingTable tbody'); if (!tbody || !this.currentData) return; tbody.innerHTML = ''; const ConfigParser = window.EDIBridge.ConfigParser; const seen = new Set(); const packages = []; this.currentData.interchanges.forEach(i => { i.transports.forEach(t => { t.deliveryNotes.forEach(dn => { dn.positions.forEach(pos => { pos.packaging.forEach(p => { const key = p.packMatSupp; if (!seen.has(key)) { seen.add(key); packages.push(p); } }); }); }); }); }); packages.forEach(p => { const row = document.createElement('tr'); const packMatSupp = p.packMatSupp; let weight = null, ln = null, wd = null, ht = null; let status = 'Default'; if (this.config) { const w = ConfigParser.lookupPacWeight(this.config, packMatSupp); if (w > 0) weight = w; const l = ConfigParser.lookupPacDimension(this.config, packMatSupp, 'LENGTH'); if (l > 0) ln = l; const w_d = ConfigParser.lookupPacDimension(this.config, packMatSupp, 'WIDTH'); if (w_d > 0) wd = w_d; const h = ConfigParser.lookupPacDimension(this.config, packMatSupp, 'HEIGHT'); if (h > 0) ht = h; if (weight || ln || wd || ht) { status = 'Aus Konfig'; } } row.innerHTML = ` ${p.packMatCust} ${packMatSupp} ${p.qty} ${status} `; tbody.appendChild(row); }); } // ─── Customer Mapping Editor ───────────────────────────────────── renderMappingTable() { if (!this.config) return; const ConfigParser = window.EDIBridge.ConfigParser; const section = ConfigParser.getSection(this.config, 'CUSTOMER_MAPPING') || {}; const tbody = document.querySelector('#mappingTable tbody'); if (!tbody) return; tbody.innerHTML = ''; const formatMap = { 'outbound-bosch': 'VDA 4913 → Bosch DESADV', 'outbound-zf': 'VDA 4913 → ZF DESADV', 'outbound-ifm': 'VDA 4913 → IFM DELVRY03', 'inbound-bosch': 'DELFOR → VDA 4905', 'inbound-ifm': 'IFM DELFOR → VDA 4905', 'inbound-invrpt': 'INVRPT → VDA 4913', 'validate-edifact': 'EDIFACT Validieren' }; for (const [id, mode] of Object.entries(section)) { const row = document.createElement('tr'); row.innerHTML = ` ${id} ${formatMap[mode] || mode} `; tbody.appendChild(row); } if (window.lucide) lucide.createIcons(); } addMapping() { if (!this.config) { alert('Bitte zuerst eine Konfiguration laden!'); return; } const idInput = document.getElementById('newMappingId'); const modeInput = document.getElementById('newMappingMode'); const id = idInput.value.trim(); const mode = modeInput.value; if (!id) { alert('Bitte eine Kundennummer eingeben.'); return; } const ConfigParser = window.EDIBridge.ConfigParser; const section = ConfigParser.ensureSection(this.config, 'CUSTOMER_MAPPING'); section[id] = mode; idInput.value = ''; this.renderMappingTable(); this.saveToStorage(); // Update UI status to show it's active but maybe needs file export if (this.configStatus.classList.contains('loaded')) { // Keep "loaded" but maybe change icon temporarily or just keep it } else { this.updateConfigUI('Gespeichert (Local)'); } if (window.lucide) lucide.createIcons(); } removeMapping(id) { if (!this.config) return; const ConfigParser = window.EDIBridge.ConfigParser; const section = ConfigParser.getSection(this.config, 'CUSTOMER_MAPPING'); if (section && section[id]) { delete section[id]; this.renderMappingTable(); this.saveToStorage(); if (window.lucide) lucide.createIcons(); } } // ─── VDA 711 ID Mapping Editor ─────────────────────────────────── renderIdMapping711Table() { if (!this.config) return; const ConfigParser = window.EDIBridge.ConfigParser; const section = ConfigParser.getSection(this.config, 'VDA711_ID_MAPPING') || {}; const tbody = document.querySelector('#idMapping711Table tbody'); if (!tbody) return; tbody.innerHTML = ''; for (const [oldId, newId] of Object.entries(section)) { const row = document.createElement('tr'); row.innerHTML = ` ${oldId} ${newId} `; tbody.appendChild(row); } if (window.lucide) lucide.createIcons(); } addIdMapping711() { if (!this.config) { alert('Bitte zuerst eine Konfiguration laden!'); return; } const oldInput = document.getElementById('newIdMapOriginal'); const newInput = document.getElementById('newIdMapTarget'); const oldId = oldInput.value.trim(); const newId = newInput.value.trim(); if (!oldId || !newId) { alert('Bitte sowohl die alte als auch die neue ID eingeben.'); return; } const ConfigParser = window.EDIBridge.ConfigParser; const section = ConfigParser.ensureSection(this.config, 'VDA711_ID_MAPPING'); section[oldId] = newId; oldInput.value = ''; newInput.value = ''; this.renderIdMapping711Table(); this.saveToStorage(); } removeIdMapping711(id) { if (!this.config) return; const ConfigParser = window.EDIBridge.ConfigParser; const section = ConfigParser.getSection(this.config, 'VDA711_ID_MAPPING'); if (section && section[id]) { delete section[id]; this.renderIdMapping711Table(); this.saveToStorage(); } } // ─── Collect Data ──────────────────────────────────────────────── collectNADData() { const readNAD = (prefix) => ({ id: document.getElementById(`nad-${prefix}-id`).value.trim(), qual: document.getElementById(`nad-${prefix}-qual`).value.trim(), name: document.getElementById(`nad-${prefix}-name`).value.trim(), name2: document.getElementById(`nad-${prefix}-name2`).value.trim(), street: document.getElementById(`nad-${prefix}-street`).value.trim(), city: document.getElementById(`nad-${prefix}-city`).value.trim(), zip: document.getElementById(`nad-${prefix}-zip`).value.trim(), country: document.getElementById(`nad-${prefix}-country`).value.trim() }); return { BY: readNAD('by'), SE: readNAD('se'), ST: readNAD('st'), SF: readNAD('sf') }; } collectWeights() { const linWeights = {}; document.querySelectorAll('.lin-weight-input').forEach(input => { const val = parseFloat(input.value); if (val) linWeights[input.dataset.suppmat] = val; }); const pacWeights = {}; document.querySelectorAll('.pac-weight-input').forEach(input => { const val = parseFloat(input.value); if (val) pacWeights[input.dataset.pacmat] = val; }); const pacDims = {}; document.querySelectorAll('.pac-ln-input').forEach(input => { const key = input.dataset.pacmat; const dims = pacDims[key] || { ln: 0, wd: 0, ht: 0 }; dims.ln = parseFloat(input.value) || dims.ln; pacDims[key] = dims; }); document.querySelectorAll('.pac-wd-input').forEach(input => { const key = input.dataset.pacmat; const dims = pacDims[key] || { ln: 0, wd: 0, ht: 0 }; dims.wd = parseFloat(input.value) || dims.wd; pacDims[key] = dims; }); document.querySelectorAll('.pac-ht-input').forEach(input => { const key = input.dataset.pacmat; const dims = pacDims[key] || { ln: 0, wd: 0, ht: 0 }; dims.ht = parseFloat(input.value) || dims.ht; pacDims[key] = dims; }); return { linWeights, pacWeights, pacDims }; } // ─── Generate & Download ───────────────────────────────────────── generateOutput() { if (this.mode === 'outbound-bosch') { const VDA4913ToDESADV = window.EDIBridge.VDA4913ToDESADV; if (!VDA4913ToDESADV) return; const nadData = this.collectNADData(); const weights = this.collectWeights(); const edifact = VDA4913ToDESADV.convert(this.rawContent, nadData, weights, this.config); this.previewArea.innerText = edifact; this.resultSection.style.display = 'block'; document.getElementById('resultTitle').innerText = 'Bosch DESADV Ergebnis'; this.resultSection.scrollIntoView({ behavior: 'smooth' }); } else if (this.mode === 'outbound-zf') { const VDA4913ToDESADVZF = window.EDIBridge.VDA4913ToDESADVZF; if (!VDA4913ToDESADVZF) { console.error("VDA4913ToDESADVZF is not loaded"); return; } const nadData = this.collectNADData(); const weights = this.collectWeights(); const edifact = VDA4913ToDESADVZF.convert(this.rawContent, nadData, weights, this.config); this.previewArea.innerText = edifact; this.resultSection.style.display = 'block'; document.getElementById('resultTitle').innerText = 'ZF DESADV Ergebnis'; this.resultSection.scrollIntoView({ behavior: 'smooth' }); } else if (this.mode === 'outbound-ifm') { // Already processed in processOutboundIfm this.processOutboundIfm(this.rawContent); } } downloadResult() { const text = this.previewArea.innerText; if (!text) return; const blob = new Blob([text], { type: 'text/plain' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; let base = this.currentFilename || 'output'; const lastDot = base.lastIndexOf('.'); if (lastDot !== -1) base = base.substring(0, lastDot); const extMap = { 'outbound-bosch': '.edi', 'outbound-zf': '.edi', 'outbound-ifm': '.delvry', 'inbound-bosch': '.vda', 'inbound-ifm': '.vda', 'inbound-invrpt': '.vda', }; a.download = base + (extMap[this.mode] || '.edi'); a.click(); URL.revokeObjectURL(url); } viewResult() { const text = this.previewArea.innerText; if (!text) return; this.showPage('viewer'); if (this.viewer) { this.viewer.processContent(text); } } editResult() { const text = this.previewArea.innerText; if (!text) return; this.showPage('editor'); if (this.editor) { this.editor.fileName = this.currentFilename || 'output.edi'; this.editor.processContent(text); } } /** * Log a conversion to the SQLite history database. * @param {Object} opts */ async _logConversion(opts) { if (!window.electronAPI || !window.electronAPI.dbInsert) return; try { await window.electronAPI.dbInsert({ dateiname: opts.dateiname || this.currentFilename || 'unbekannt', ausgangsdateiname: opts.ausgangsdateiname || null, quellformat: opts.quellformat || null, zielformat: opts.zielformat || null, konvertierungsmodus: opts.konvertierungsmodus || this.mode, kunden_id: opts.kunden_id || null, eingang_daten: opts.eingang_daten || null, ausgang_daten: opts.ausgang_daten || null, status: opts.status || 'ERFOLGREICH', fehlermeldung: opts.fehlermeldung || null, quelle: opts.quelle || 'MANUELL' }); } catch (e) { console.warn('[DB] Log error:', e); } } // ─── Settings: Folder Selection ────────────────────────────────── async selectInputFolder() { if (!this.watcher) return; const folder = await this.watcher.selectFolder(); if (folder) { document.getElementById('inputDirPath').value = folder; this.watcher.saveSettings({ inputDir: folder, outputDir: document.getElementById('outputDirPath').value }); } } async selectOutputFolder() { if (!this.watcher) return; const folder = await this.watcher.selectFolder(); if (folder) { document.getElementById('outputDirPath').value = folder; this.watcher.saveSettings({ inputDir: document.getElementById('inputDirPath').value, outputDir: folder }); } } async selectInvrptOutputFolder() { if (!this.watcher) return; const folder = await this.watcher.selectFolder(); if (folder) { document.getElementById('invrptOutputPath').value = folder; this.watcher.saveSettings({ invrptOutputDir: folder }); } } // ─── Settings: Watcher Controls ────────────────────────────────── async startWatcher() { if (!this.watcher) return; const inputDir = document.getElementById('inputDirPath').value; const outputDir = document.getElementById('outputDirPath').value; const invrptOutputDir = document.getElementById('invrptOutputPath').value; if (!inputDir || !outputDir) { alert('Bitte beide Ordner (Input & Output) angeben.'); return; } const result = await this.watcher.start(inputDir, outputDir, invrptOutputDir); if (result.error) { alert('Watcher-Fehler: ' + result.error); } } async stopWatcher() { if (this.watcher) await this.watcher.stop(); } async pauseWatcher() { if (this.watcher) await this.watcher.pause(); } async resumeWatcher() { if (this.watcher) await this.watcher.resume(); } // ─── Settings: Outbound Watcher Controls ────────────────────────── async selectOutboundInput() { if (!this.outboundWatcher) return; const folder = await window.electronAPI.selectFolder(); if (folder) { document.getElementById('outboundInputPath').value = folder; window.electronAPI.saveSettings({ outboundInputDir: folder }); } } async selectOutboundOutput() { if (!this.outboundWatcher) return; const folder = await window.electronAPI.selectFolder(); if (folder) { document.getElementById('outboundOutputPath').value = folder; window.electronAPI.saveSettings({ outboundOutputDir: folder }); } } async startOutboundWatcher() { if (!this.outboundWatcher) return; const inPath = document.getElementById('outboundInputPath').value; const outPath = document.getElementById('outboundOutputPath').value; if (!inPath || !outPath) { alert('Bitte beide Pfade für ausgehende Dateien angeben.'); return; } await this.outboundWatcher.start(inPath, outPath); } async stopOutboundWatcher() { if (this.outboundWatcher) await this.outboundWatcher.stop(); } async pauseOutboundWatcher() { if (this.outboundWatcher) await this.outboundWatcher.pause(); } async resumeOutboundWatcher() { if (this.outboundWatcher) await this.outboundWatcher.resume(); } updateOutboundWatcherUI(status) { const dot = document.getElementById('outboundWatcherDot'); const text = document.getElementById('outboundWatcherStatusText'); const btnStart = document.getElementById('btnStartOutboundWatcher'); const btnPause = document.getElementById('btnPauseOutboundWatcher'); const btnResume = document.getElementById('btnResumeOutboundWatcher'); const btnStop = document.getElementById('btnStopOutboundWatcher'); dot.className = 'watcher-status-dot'; switch (status) { case 'running': dot.classList.add('running'); text.textContent = 'Aktiv – Läuft'; btnStart.disabled = true; btnPause.disabled = false; btnPause.style.display = ''; btnResume.style.display = 'none'; btnStop.disabled = false; break; case 'paused': dot.classList.add('paused'); text.textContent = 'Pausiert'; btnStart.disabled = true; btnPause.style.display = 'none'; btnResume.style.display = ''; btnStop.disabled = false; break; case 'stopped': default: dot.classList.add('stopped'); text.textContent = 'Gestoppt'; btnStart.disabled = false; btnPause.disabled = true; btnPause.style.display = ''; btnResume.style.display = 'none'; btnStop.disabled = true; break; } if (window.lucide) try { lucide.createIcons(); } catch (e) { } } // ─── Settings: Werksnummer Watcher Controls ────────────────────── async selectWerksInput() { if (!this.werksWatcher) return; const folder = await window.electronAPI.selectFolder(); if (folder) { document.getElementById('werksInputPath').value = folder; window.electronAPI.saveSettings({ werksInputDir: folder }); } } async selectWerksOutput() { if (!this.werksWatcher) return; const folder = await window.electronAPI.selectFolder(); if (folder) { document.getElementById('werksOutputPath').value = folder; window.electronAPI.saveSettings({ werksOutputDir: folder }); } } async startWerksWatcher() { if (!this.werksWatcher) return; const inPath = document.getElementById('werksInputPath').value; const outPath = document.getElementById('werksOutputPath').value; if (!inPath || !outPath) { alert('Bitte beide Werks-Pfade angeben.'); return; } await this.werksWatcher.start(inPath, outPath); } async stopWerksWatcher() { if (this.werksWatcher) await this.werksWatcher.stop(); } async pauseWerksWatcher() { if (this.werksWatcher) await this.werksWatcher.pause(); } async resumeWerksWatcher() { if (this.werksWatcher) await this.werksWatcher.resume(); } updateWerksWatcherUI(status) { const dot = document.getElementById('werksWatcherDot'); const text = document.getElementById('werksWatcherStatusText'); const btnStart = document.getElementById('btnStartWerksWatcher'); const btnPause = document.getElementById('btnPauseWerksWatcher'); const btnResume = document.getElementById('btnResumeWerksWatcher'); const btnStop = document.getElementById('btnStopWerksWatcher'); dot.className = 'watcher-status-dot'; switch (status) { case 'running': dot.classList.add('running'); text.textContent = 'Aktiv – Läuft'; btnStart.disabled = true; btnPause.disabled = false; btnPause.style.display = ''; btnResume.style.display = 'none'; btnStop.disabled = false; break; case 'paused': dot.classList.add('paused'); text.textContent = 'Pausiert'; btnStart.disabled = true; btnPause.style.display = 'none'; btnResume.style.display = ''; btnStop.disabled = false; break; case 'stopped': default: dot.classList.add('stopped'); text.textContent = 'Gestoppt'; btnStart.disabled = false; btnPause.disabled = true; btnPause.style.display = ''; btnResume.style.display = 'none'; btnStop.disabled = true; break; } if (window.lucide) try { lucide.createIcons(); } catch (e) { } } updateWatcherUI(status) { const dot = document.getElementById('watcherDot'); const text = document.getElementById('watcherStatusText'); const btnStart = document.getElementById('btnStartWatcher'); const btnPause = document.getElementById('btnPauseWatcher'); const btnResume = document.getElementById('btnResumeWatcher'); const btnStop = document.getElementById('btnStopWatcher'); // Reset dot.className = 'watcher-status-dot'; switch (status) { case 'running': dot.classList.add('running'); text.textContent = 'Aktiv – Überwachung läuft'; btnStart.disabled = true; btnPause.disabled = false; btnPause.style.display = ''; btnResume.style.display = 'none'; btnStop.disabled = false; break; case 'paused': dot.classList.add('paused'); text.textContent = 'Pausiert'; btnStart.disabled = true; btnPause.style.display = 'none'; btnResume.style.display = ''; btnStop.disabled = false; break; case 'stopped': default: dot.classList.add('stopped'); text.textContent = 'Gestoppt'; btnStart.disabled = false; btnPause.disabled = true; btnPause.style.display = ''; btnResume.style.display = 'none'; btnStop.disabled = true; break; } if (window.lucide) try { lucide.createIcons(); } catch (e) { } } // ─── Log ───────────────────────────────────────────────────────── appendLogEntry(entry) { const container = document.getElementById('logContainer'); if (!container) return; // Remove empty message if present const empty = container.querySelector('.log-empty'); if (empty) empty.remove(); const el = document.createElement('div'); el.className = 'log-entry log-' + entry.level; el.innerHTML = ` ${entry.time} ${entry.level.toUpperCase()} ${entry.fileName ? `${entry.fileName}` : ''} ${entry.message} `; container.prepend(el); } clearLog() { const container = document.getElementById('logContainer'); if (container) { container.innerHTML = '
Noch keine Aktivitäten.
'; } if (this.watcher) this.watcher.log = []; } } window.EDIBridge.App = App;