308 lines
12 KiB
JavaScript
308 lines
12 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}`
|
|
}) {
|
|
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
|
|
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);
|
|
}
|
|
});
|
|
}
|
|
|
|
// 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}`
|
|
});
|
|
|
|
// --- 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}`
|
|
});
|
|
|
|
// --- COUNTERS ---
|
|
setupDynamicTable({
|
|
tableBodyId: 'counters-table-body',
|
|
templateId: 'counter-row-template',
|
|
bulkInputId: null, // Add bulk if you want
|
|
bulkBtnId: null, // Add bulk if you want
|
|
fields: ['counter_id', 'counter_name', 'counter_type', 'counter_description'],
|
|
prefix: 'COUNTER',
|
|
nameFormat: (id, paddedId) => `COUNTER_${paddedId}`
|
|
});
|
|
|
|
// --- SETPOINTS ---
|
|
setupDynamicTable({
|
|
tableBodyId: 'setpoints-table-body',
|
|
templateId: 'setpoint-row-template',
|
|
bulkInputId: null, // Add bulk if you want
|
|
bulkBtnId: null, // Add bulk if you want
|
|
fields: ['setpoint_id', 'setpoint_name', 'setpoint_message'],
|
|
prefix: 'SETPOINT',
|
|
nameFormat: (id, paddedId) => `SETPOINT_${paddedId}`
|
|
});
|
|
|
|
// --- REALTIME VARIABLES ---
|
|
setupDynamicTable({
|
|
tableBodyId: 'realtime-table-body',
|
|
templateId: 'realtime-row-template',
|
|
bulkInputId: null, // Add bulk if you want
|
|
bulkBtnId: null, // Add bulk if you want
|
|
fields: ['realtime_id', 'realtime_name', 'realtime_message'],
|
|
prefix: 'REALTIME',
|
|
nameFormat: (id, paddedId) => `REALTIME_${paddedId}`
|
|
});
|
|
|
|
// --- 3. FORM SUBMISSION LOGIC ---
|
|
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);
|
|
// Convert FormData to a plain object
|
|
const mainData = {};
|
|
for (const [key, value] of formData.entries()) {
|
|
// This check prevents multi-value fields from overwriting themselves
|
|
if (!mainData[key]) {
|
|
mainData[key] = value;
|
|
}
|
|
}
|
|
mainData.addins = formData.getAll('addins');
|
|
|
|
// B. Collect all alarm rows from the table
|
|
const alarmRows = alarmsTableBody.querySelectorAll('tr');
|
|
const alarmsData = [];
|
|
alarmRows.forEach(row => {
|
|
const alarm = {
|
|
id: row.querySelector('[name="alarm_id"]').value,
|
|
name: row.querySelector('[name="alarm_name"]').value,
|
|
message: row.querySelector('[name="alarm_message"]').value,
|
|
};
|
|
if (alarm.message) {
|
|
alarmsData.push(alarm);
|
|
}
|
|
});
|
|
|
|
// D. Collect all warning rows from the table
|
|
const warningRows = warningsTableBody.querySelectorAll('tr');
|
|
const warningsData = [];
|
|
warningRows.forEach(row => {
|
|
const warning = {
|
|
id: row.querySelector('[name="warning_id"]').value,
|
|
name: row.querySelector('[name="warning_name"]').value,
|
|
message: row.querySelector('[name="warning_message"]').value,
|
|
};
|
|
if (warning.message) {
|
|
warningsData.push(warning);
|
|
}
|
|
});
|
|
|
|
// E. Collect all counter rows from the table
|
|
const counterRows = countersTableBody.querySelectorAll('tr');
|
|
const countersData = [];
|
|
counterRows.forEach(row => {
|
|
const counter = {
|
|
id: row.querySelector('[name="counter_id"]').value,
|
|
name: row.querySelector('[name="counter_name"]').value,
|
|
type: row.querySelector('[name="counter_type"]').value,
|
|
description: row.querySelector('[name="counter_description"]').value,
|
|
};
|
|
if (counter.type) {
|
|
countersData.push(counter);
|
|
}
|
|
});
|
|
|
|
// C. Combine all data into a single object
|
|
const finalData = { ...mainData, alarms: alarmsData, warnings: warningsData, counters: countersData };
|
|
|
|
console.log('Sending all collected data to main process:', finalData);
|
|
|
|
try {
|
|
const result = await window.electronAPI.submitForm(finalData);
|
|
if (result.success) {
|
|
// // Reset the entire form to its initial state
|
|
// form.reset();
|
|
// // Re-apply the defaults that are not part of the standard reset
|
|
// document.getElementById('publicationDate').value = new Date().toISOString().split('T')[0];
|
|
// document.querySelector('#stopReasonYes').checked = true; // Set StopReason back to Yes
|
|
// updateFilename(); // Clear the filename preview
|
|
}
|
|
} 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'
|
|
];
|
|
|
|
// 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');
|
|
}
|
|
});
|
|
});
|
|
}); |