1506 lines
61 KiB
JavaScript
1506 lines
61 KiB
JavaScript
/**
|
||
* 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 = '<i data-lucide="check-circle"></i><span>Konfig: ' + name + '</span>';
|
||
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 = `
|
||
<div class="stat-card">
|
||
<div class="stat-value">${parsed.segments.length}</div>
|
||
<div class="stat-label">EDIFACT Segments</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value">${vda4905.split('\n').filter(l => l.trim()).length}</div>
|
||
<div class="stat-label">VDA 4905 Records</div>
|
||
</div>
|
||
`;
|
||
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 = `
|
||
<div class="stat-card">
|
||
<div class="stat-value">${lines}</div>
|
||
<div class="stat-label">VDA 4905 Records</div>
|
||
</div>
|
||
`;
|
||
this.statsGrid.innerHTML = statsHtml;
|
||
|
||
if (result.warnings && result.warnings.length > 0) {
|
||
this.statsGrid.innerHTML += `
|
||
<div class="stat-card stat-warning">
|
||
<div class="stat-value">${result.warnings.length}</div>
|
||
<div class="stat-label">Warnungen</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
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 = `
|
||
<div class="stat-card">
|
||
<div class="stat-value">${vda4913.split('\\n').filter(l => l.trim()).length}</div>
|
||
<div class="stat-label">VDA 4913 Records</div>
|
||
</div>
|
||
`;
|
||
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 = `<div style="font-family: inherit;">
|
||
<h3 style="margin-top:0; color: ${result.valid ? '#10b981' : '#ef4444'}">
|
||
${result.valid ? '<i data-lucide="check-circle" style="vertical-align:middle"></i> Gültiges EDIFACT' : '<i data-lucide="x-circle" style="vertical-align:middle"></i> Ungültiges EDIFACT'}
|
||
</h3>`;
|
||
|
||
if (result.stats.messageType) {
|
||
html += `<p><strong>Nachrichtentyp:</strong> ${result.stats.messageType} (Version: ${result.stats.messageVersion})</p>`;
|
||
}
|
||
if (result.stats.sender || result.stats.receiver) {
|
||
html += `<p><strong>Sender:</strong> ${result.stats.sender || '-'} <strong>Empfänger:</strong> ${result.stats.receiver || '-'}</p>`;
|
||
}
|
||
|
||
if (result.errors && result.errors.length > 0) {
|
||
html += `<div style="background:#fee2e2; color:#b91c1c; padding: 15px; border-radius: 6px; margin-top: 15px;">
|
||
<h4 style="margin-top:0"><i data-lucide="alert-circle" style="vertical-align:middle; width:16px;"></i> Fehler</h4>
|
||
<ul style="margin-bottom:0">${result.errors.map(e => `<li>${e}</li>`).join('')}</ul>
|
||
</div>`;
|
||
}
|
||
|
||
if (result.warnings && result.warnings.length > 0) {
|
||
html += `<div style="background:#fef3c7; color:#b45309; padding: 15px; border-radius: 6px; margin-top: 15px;">
|
||
<h4 style="margin-top:0"><i data-lucide="alert-triangle" style="vertical-align:middle; width:16px;"></i> Warnungen</h4>
|
||
<ul style="margin-bottom:0">${result.warnings.map(e => `<li>${e}</li>`).join('')}</ul>
|
||
</div>`;
|
||
}
|
||
|
||
html += `<h4 style="margin-top: 25px;">Extrahierte Segmente (Rohdaten)</h4>
|
||
<div style="background:#1e1e1e; color:#d4d4d4; padding:15px; border-radius:6px; overflow-x:auto; font-family:'Fira Code', monospace; font-size:13px; max-height: 400px; overflow-y:auto;">
|
||
<pre style="margin:0;">${JSON.stringify(parsed.segments, null, 2)}</pre>
|
||
</div>
|
||
</div>`;
|
||
|
||
// 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 = `
|
||
<div class="stat-card" style="border-left: 4px solid ${result.valid ? '#10b981' : '#ef4444'}">
|
||
<div class="stat-value">${result.valid ? 'OK' : 'ERR'}</div>
|
||
<div class="stat-label">Syntax-Status</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value">${result.stats.segmentCount || 0}</div>
|
||
<div class="stat-label">Segmente</div>
|
||
</div>
|
||
`;
|
||
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 = `
|
||
<div class="stat-card">
|
||
<div class="stat-value">${parsed.interchanges.length}</div>
|
||
<div class="stat-label">Interchanges</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value">${idocs.length}</div>
|
||
<div class="stat-label">IDocs erzeugt</div>
|
||
</div>
|
||
`;
|
||
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 = `
|
||
<div class="stat-card">
|
||
<div class="stat-value">${count}</div>
|
||
<div class="stat-label">Interchanges</div>
|
||
</div>
|
||
<div class="stat-card">
|
||
<div class="stat-value">${dnCount}</div>
|
||
<div class="stat-label">Delivery Notes</div>
|
||
</div>
|
||
`;
|
||
}
|
||
|
||
// ─── 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 = '<span class="badge badge-vda">Aus VDA</span>';
|
||
|
||
if (this.config) {
|
||
weight = ConfigParser.lookupLinWeight(this.config, suppMat);
|
||
if (weight > 0) {
|
||
status = '<span class="badge badge-config">Aus Konfig</span>';
|
||
}
|
||
}
|
||
|
||
row.innerHTML = `
|
||
<td>${pos.data.custMat}</td>
|
||
<td>${suppMat}</td>
|
||
<td>${pos.data.qty}</td>
|
||
<td>${pos.data.unit}</td>
|
||
<td><input type="number" step="0.001" class="nad-input lin-weight-input" data-suppmat="${suppMat}" value="${weight || ''}" placeholder="0.000" onchange="app.saveToStorage()"></td>
|
||
<td>${status}</td>
|
||
`;
|
||
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 = '<span class="badge badge-vda">Default</span>';
|
||
|
||
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 = '<span class="badge badge-config">Aus Konfig</span>';
|
||
}
|
||
}
|
||
|
||
row.innerHTML = `
|
||
<td>${p.packMatCust}</td>
|
||
<td>${packMatSupp}</td>
|
||
<td>${p.qty}</td>
|
||
<td><input type="number" step="0.1" class="nad-input pac-weight-input" data-pacmat="${packMatSupp}" value="${weight || ''}" placeholder="1.0" onchange="app.saveToStorage()"></td>
|
||
<td><input type="number" class="nad-input pac-ln-input" data-pacmat="${packMatSupp}" value="${ln || ''}" placeholder="40" onchange="app.saveToStorage()"></td>
|
||
<td><input type="number" class="nad-input pac-wd-input" data-pacmat="${packMatSupp}" value="${wd || ''}" placeholder="30" onchange="app.saveToStorage()"></td>
|
||
<td><input type="number" class="nad-input pac-ht-input" data-pacmat="${packMatSupp}" value="${ht || ''}" placeholder="15" onchange="app.saveToStorage()"></td>
|
||
<td>${status}</td>
|
||
`;
|
||
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 = `
|
||
<td><strong>${id}</strong></td>
|
||
<td>${formatMap[mode] || mode}</td>
|
||
<td>
|
||
<button class="btn-secondary" onclick="app.removeMapping('${id}')" title="Löschen">
|
||
<i data-lucide="trash-2" style="width: 16px; height: 16px; color: #ef4444;"></i>
|
||
</button>
|
||
</td>
|
||
`;
|
||
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 = `
|
||
<td><strong>${oldId}</strong></td>
|
||
<td><strong>${newId}</strong></td>
|
||
<td>
|
||
<button class="btn-secondary" onclick="app.removeIdMapping711('${oldId}')" title="Löschen">
|
||
<i data-lucide="trash-2" style="width: 16px; height: 16px; color: #ef4444;"></i>
|
||
</button>
|
||
</td>
|
||
`;
|
||
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 = `
|
||
<span class="log-time">${entry.time}</span>
|
||
<span class="log-level">${entry.level.toUpperCase()}</span>
|
||
${entry.fileName ? `<span class="log-file">${entry.fileName}</span>` : ''}
|
||
<span class="log-msg">${entry.message}</span>
|
||
`;
|
||
container.prepend(el);
|
||
}
|
||
|
||
clearLog() {
|
||
const container = document.getElementById('logContainer');
|
||
if (container) {
|
||
container.innerHTML = '<div class="log-empty">Noch keine Aktivitäten.</div>';
|
||
}
|
||
if (this.watcher) this.watcher.log = [];
|
||
}
|
||
}
|
||
|
||
window.EDIBridge.App = App;
|