Electron_generator/renderer.js

341 lines
13 KiB
JavaScript

// renderer.js
document.addEventListener('DOMContentLoaded', () => {
console.log('DOM fully loaded. renderer.js is now running.');
// --- 0. INITIALIZE FORM DEFAULTS ---
const publicationDateInput = document.getElementById('publicationDate');
if (publicationDateInput) {
publicationDateInput.value = new Date().toISOString().split('T')[0];
}
// --- 1. TAB SWITCHING LOGIC ---
const tabButtons = document.querySelectorAll('.tab-btn');
const tabContents = document.querySelectorAll('.tab-content');
tabButtons.forEach(button => {
button.addEventListener('click', () => {
tabButtons.forEach(btn => btn.classList.remove('active'));
tabContents.forEach(content => content.classList.remove('active'));
button.classList.add('active');
document.getElementById(button.dataset.tab + '-tab').classList.add('active');
});
});
document.getElementById('machine-tab').classList.add('active');
// --- 2. GENERIC DYNAMIC TABLE LOGIC ---
function setupDynamicTable({
tableBodyId,
templateId,
bulkInputId,
bulkBtnId,
fields, // Array of field names in order (e.g. ['alarm_id', 'alarm_name', 'alarm_message'])
prefix, // e.g. 'ALARM'
nameFormat, // e.g. (id, paddedId) => `ALARM_${paddedId}`
initialRow = true, // Number of initial rows to add
}) {
const tableBody = document.getElementById(tableBodyId);
const rowTemplate = document.getElementById(templateId);
const bulkInput = bulkInputId ? document.getElementById(bulkInputId) : null;
const bulkBtn = bulkBtnId ? document.getElementById(bulkBtnId) : null;
// Add initial row
if (initialRow) {
addRow(tableBody, rowTemplate, 1, fields, prefix, nameFormat);
}
// Bulk add
if (bulkBtn && bulkInput) {
bulkBtn.addEventListener('click', () => {
let count = parseInt(bulkInput.value, 10);
if (isNaN(count) || count < 1) count = 1;
const currentIdInputs = tableBody.querySelectorAll(`[name="${fields[0]}"]`);
let maxId = 0;
currentIdInputs.forEach(input => {
const currentId = parseInt(input.value, 10);
if (!isNaN(currentId) && currentId > maxId) maxId = currentId;
});
for (let i = 1; i <= count; i++) {
addRow(tableBody, rowTemplate, maxId + i, fields, prefix, nameFormat);
}
});
} else if (bulkBtn){
bulkBtn.addEventListener('click', () => {
const currentIdInputs = tableBody.querySelectorAll(`[name="${fields[0]}"]`);
let maxId = 0;
currentIdInputs.forEach(input => {
const currentId = parseInt(input.value, 10);
if (!isNaN(currentId) && currentId > maxId) maxId = currentId;
});
addRow(tableBody, rowTemplate, maxId + 1, fields, prefix, nameFormat);
});
}
// Paste from Excel
tableBody.addEventListener('paste', (event) => {
const targetCell = event.target.closest('td');
const targetRow = event.target.closest('tr');
const allRows = Array.from(tableBody.querySelectorAll('tr'));
let startRowIdx = allRows.indexOf(targetRow);
if (startRowIdx === -1) startRowIdx = allRows.length;
// Find the cell index in the row
let startCellIdx = 0;
if (targetCell && targetRow) {
const cells = Array.from(targetRow.children);
startCellIdx = cells.indexOf(targetCell);
if (startCellIdx === -1) startCellIdx = 0;
}
// Get clipboard data
const clipboardData = event.clipboardData || window.clipboardData;
const pastedData = clipboardData.getData('Text');
if (!pastedData) return;
// Find current max ID for auto-increment
const currentIdInputs = tableBody.querySelectorAll(`[name="${fields[0]}"]`);
let maxId = 0;
currentIdInputs.forEach(input => {
const currentId = parseInt(input.value, 10);
if (!isNaN(currentId) && currentId > maxId) maxId = currentId;
});
// Split rows by newline, columns by tab
const rows = pastedData.trim().split(/\r?\n/);
rows.forEach((row, rowOffset) => {
const cols = row.split('\t');
if (cols.every(col => !col.trim())) return; // Ignore empty rows
let rowToFill = allRows[startRowIdx + rowOffset];
if (!rowToFill) {
// Add new row if needed
maxId += 1;
rowToFill = addRow(tableBody, rowTemplate, maxId, fields, prefix, nameFormat);
}
// Get all editable fields in the row
const editableFields = fields.map(f => rowToFill.querySelector(`[name="${f}"]`));
// If only last field (description/message) is pasted, auto-fill ID and name
if (cols.length === 1 || (cols.length > 0 && cols.slice(0, -1).every(col => !col.trim()))) {
maxId += (!rowToFill.querySelector(`[name="${fields[0]}"]`).value ? 1 : 0);
const paddedId = maxId.toString().padStart(3, '0');
if (editableFields[0]) editableFields[0].value = maxId;
if (editableFields[1] && nameFormat) editableFields[1].value = nameFormat(maxId, paddedId);
if (editableFields[editableFields.length - 1]) editableFields[editableFields.length - 1].value = cols[cols.length - 1].trim();
} else {
// Paste values to the right, only filling available cells
for (let colOffset = 0; colOffset < cols.length; colOffset++) {
const fieldIdx = startCellIdx + colOffset;
if (fieldIdx < editableFields.length && editableFields[fieldIdx]) {
editableFields[fieldIdx].value = cols[colOffset].trim();
}
}
}
});
event.preventDefault(); // Prevent default paste
});
}
function addRow(tableBody, rowTemplate, id, fields, prefix, nameFormat) {
const paddedId = id.toString().padStart(3, '0');
const clone = rowTemplate.content.cloneNode(true);
if (fields[0]) clone.querySelector(`[name="${fields[0]}"]`).value = id;
if (fields[1] && nameFormat) clone.querySelector(`[name="${fields[1]}"]`).value = nameFormat(id, paddedId);
const removeBtn = clone.querySelector('.remove-row-btn');
if (removeBtn) {
removeBtn.addEventListener('click', (e) => {
e.target.closest('tr').remove();
});
}
tableBody.appendChild(clone);
return tableBody.querySelectorAll('tr')[tableBody.querySelectorAll('tr').length - 1];
}
// --- ALARMS ---
setupDynamicTable({
tableBodyId: 'alarms-table-body',
templateId: 'alarm-row-template',
bulkInputId: 'bulk-alarm-count',
bulkBtnId: 'add-multiple-alarms-btn',
fields: ['alarm_id', 'alarm_name', 'alarm_message'],
prefix: 'ALARM',
nameFormat: (id, paddedId) => `ALARM_${paddedId}`,
initialRow: true
});
// --- WARNINGS ---
setupDynamicTable({
tableBodyId: 'warnings-table-body',
templateId: 'warning-row-template',
bulkInputId: 'bulk-warning-count',
bulkBtnId: 'add-multiple-warnings-btn',
fields: ['warning_id', 'warning_name', 'warning_message'],
prefix: 'WARNING',
nameFormat: (id, paddedId) => `WARNING_${paddedId}`,
initialRow: true
});
// --- COUNTERS ---
setupDynamicTable({
tableBodyId: 'counters-table-body',
templateId: 'counter-row-template',
bulkInputId: null, // Add bulk if you want
bulkBtnId: 'add-counter-btn', // Add bulk if you want
fields: ['counter_id', 'counter_name', 'counter_type', 'counter_description'],
prefix: 'COUNTER',
nameFormat: (id, paddedId) => `COUNTER_${paddedId}`,
initialRow: true
});
// --- SETPOINTS ---
setupDynamicTable({
tableBodyId: 'setpoints-table-body',
templateId: 'setpoint-row-template',
bulkInputId: null, // Add bulk if you want
bulkBtnId: 'add-setpoint-btn', // Add bulk if you want
fields: ['setpoint_id', 'setpoint_name', 'setpoint_message'],
prefix: 'SETPOINT',
nameFormat: (id, paddedId) => `SETPOINT_${paddedId}`,
initialRow: false
});
// --- REALTIME VARIABLES ---
setupDynamicTable({
tableBodyId: 'realtime-table-body',
templateId: 'realtime-row-template',
bulkInputId: null, // Add bulk if you want
bulkBtnId: 'add-realtime-btn', // Add bulk if you want
fields: ['realtime_id', 'realtime_name', 'realtime_message'],
prefix: 'REALTIME',
nameFormat: (id, paddedId) => `REALTIME_${paddedId}`,
initialRow: false
});
// --- STATES ---
setupDynamicTable({
tableBodyId: 'states-table-body',
templateId: 'state-row-template',
bulkInputId: null,
bulkBtnId: 'add-state-btn',
fields: ['state_id', 'state_name', 'state_description'],
prefix: 'STATE',
nameFormat: (id, paddedId) => `STATE_${paddedId}`,
initialRow: false
});
// 3. FORM SUBMISSION LOGIC (MODULARIZED)
/**
* Collects data from a dynamic table.
* @param {string} tableBodyId - The ID of the table's tbody element.
* @param {string[]} fields - An array of field names (name attributes of inputs).
* @param {string} keyField - A field name that must have a value for the row to be included.
* @returns {Object[]} An array of objects, where each object represents a row.
*/
const collectTableData = (tableBodyId, fields, keyField) => {
const tableBody = document.getElementById(tableBodyId);
if (!tableBody) return [];
const rows = tableBody.querySelectorAll('tr');
const data = [];
rows.forEach(row => {
const rowData = {};
let hasKeyField = false;
fields.forEach(field => {
const input = row.querySelector(`[name="${field}"]`);
if (input) {
rowData[field.split('_').pop()] = input.value;
if (field === keyField && input.value) {
hasKeyField = true;
}
}
});
// Only add the row if the key field is present and has a value
if (hasKeyField) {
data.push(rowData);
}
});
return data;
};
const form = document.getElementById('machine-form');
if (form) {
form.addEventListener('submit', async (event) => {
event.preventDefault();
// A. Collect data from the main "Machine" tab using FormData
const formData = new FormData(form);
const mainData = {};
for (const [key, value] of formData.entries()) {
// This check prevents multi-value fields (like addins) from being overwritten
if (!mainData[key]) {
mainData[key] = value;
}
}
mainData.addins = formData.getAll('addins');
// B. Collect data from all dynamic tables using the modular function
const alarmsData = collectTableData('alarms-table-body', ['alarm_id', 'alarm_name', 'alarm_message'], 'alarm_message');
const warningsData = collectTableData('warnings-table-body', ['warning_id', 'warning_name', 'warning_message'], 'warning_message');
const statesData = collectTableData('states-table-body', ['state_id', 'state_name', 'state_description'], 'state_description');
const countersData = collectTableData('counters-table-body', ['counter_id', 'counter_name', 'counter_type', 'counter_description'], 'counter_type');
const setpointsData = collectTableData('setpoints-table-body', ['setpoint_id', 'setpoint_name', 'setpoint_message'], 'setpoint_message');
const realtimeData = collectTableData('realtime-table-body', ['realtime_id', 'realtime_name', 'realtime_message'], 'realtime_message');
// C. Combine all data into a single object
const finalData = {
...mainData,
alarms: alarmsData,
warnings: warningsData,
states: statesData,
counters: countersData,
setpoints: setpointsData,
realtime: realtimeData,
states: statesData
};
console.log('Sending all collected data to main process:', finalData);
try {
const result = await window.electronAPI.submitForm(finalData);
if (result.success) {
console.log('Form submitted successfully!');
// Optionally reset the form or give user feedback
// form.reset();
// document.getElementById('publicationDate').value = new Date().toISOString().split('T')[0];
}
} catch (error) {
console.error('Error during form submission process:', error);
}
});
}
// --- 4. TAB NAVIGATION BUTTONS ---
const tabOrder = [
'machine-tab',
'alarms-tab',
'warnings-tab',
'counters-tab',
'setpoints-tab',
'realtime-tab',
'states-tab'
];
// Add event listeners to all "Next" buttons
document.querySelectorAll('.next-tab-btn').forEach((btn, idx) => {
btn.addEventListener('click', () => {
// Find the currently active tab
const currentTab = document.querySelector('.tab-content.active');
if (!currentTab) return;
const currentTabId = currentTab.id;
const currentIdx = tabOrder.indexOf(currentTabId);
const nextTabId = tabOrder[currentIdx + 1];
if (nextTabId) {
// Switch tab button active state
document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
document.querySelector(`[data-tab="${nextTabId.replace('-tab','')}"]`).classList.add('active');
// Switch tab content active state
document.querySelectorAll('.tab-content').forEach(tc => tc.classList.remove('active'));
document.getElementById(nextTabId).classList.add('active');
}
});
});
});