`;
// Add connector and "add" button, but not after the last stage
if (index < pipelineData.length) {
pipelineContainer.innerHTML += `
+
`;
}
// Remove the last two connectors
if (index === pipelineData.length -1) {
const elements = pipelineContainer.getElementsByClassName('ap-connector');
const lastConnector = elements[elements.length-1]
const secondToLastConnector = elements[elements.length-2]
lastConnector?.parentNode?.removeChild(lastConnector);
secondToLastConnector?.parentNode?.removeChild(secondToLastConnector);
const addButtons = pipelineContainer.getElementsByClassName('ap-add-stage-btn');
const lastAddButton = addButtons[addButtons.length - 1];
lastAddButton?.parentNode?.removeChild(lastAddButton);
}
});
pipelineWrapper.innerHTML = ''; // Clear previous render
pipelineWrapper.appendChild(pipelineContainer);
}
// --- EVENT HANDLING (using event delegation) ---
pipelineWrapper.addEventListener('click', function(event) {
const target = event.target;
const action = target.dataset.action;
if (action === 'add') {
const index = parseInt(target.dataset.index);
addStage(index);
} else if (action === 'remove') {
const id = parseInt(target.dataset.id);
removeStage(id);
} else if (action === 'edit') {
makeEditable(target);
}
});
pipelineWrapper.addEventListener('change', function(event) {
const target = event.target;
const action = target.dataset.action;
if(action === 'status-change') {
const id = parseInt(target.dataset.id);
const newStatus = target.value;
updateStatus(id, newStatus);
}
});
// --- ACTION FUNCTIONS ---
function addStage(index) {
const newId = pipelineData.length > 0 ? Math.max(...pipelineData.map(s => s.id)) + 1 : 1;
const newStage = {
id: newId,
title: 'New Stage',
description: 'Click to edit description.',
status: 'Not Started'
};
pipelineData.splice(index, 0, newStage);
renderPipeline();
}
function removeStage(id) {
if (confirm('Are you sure you want to remove this stage?')) {
pipelineData = pipelineData.filter(stage => stage.id !== id);
renderPipeline();
}
}
function updateStatus(id, newStatus) {
const stage = pipelineData.find(s => s.id === id);
if (stage) {
stage.status = newStatus;
renderPipeline();
}
}
function makeEditable(element) {
element.setAttribute('contenteditable', 'true');
element.focus();
element.addEventListener('blur', saveEdit, { once: true });
element.addEventListener('keydown', (e) => {
if (e.key === 'Enter') {
e.preventDefault();
element.blur();
}
});
}
function saveEdit(event) {
const element = event.target;
element.setAttribute('contenteditable', 'false');
const id = parseInt(element.closest('.ap-stage').dataset.id);
const key = element.dataset.key;
const newValue = element.textContent;
const stage = pipelineData.find(s => s.id === id);
if (stage && stage[key] !== undefined) {
stage[key] = newValue;
}
// No re-render needed for contenteditable, avoids losing focus state
}
// --- PDF DOWNLOAD ---
downloadBtn.addEventListener('click', async function() {
const originalButtonText = downloadBtn.textContent;
downloadBtn.textContent = 'Generating...';
downloadBtn.disabled = true;
const pipelineElement = document.querySelector('.ap-pipeline');
if (!pipelineElement) {
alert('Pipeline is empty. Nothing to download.');
downloadBtn.textContent = originalButtonText;
downloadBtn.disabled = false;
return;
}
const canvas = await html2canvas(pipelineElement, {
scale: 2,
logging: false,
useCORS: true,
scrollX: 0,
scrollY: -window.scrollY
});
const imgData = canvas.toDataURL('image/png');
const { jsPDF } = window.jspdf;
const pdf = new jsPDF({
orientation: 'landscape',
unit: 'px',
format: [canvas.width, canvas.height]
});
pdf.addImage(imgData, 'PNG', 0, 0, canvas.width, canvas.height);
pdf.save('Analysis_Pipeline.pdf');
downloadBtn.textContent = originalButtonText;
downloadBtn.disabled = false;
});
// --- INITIAL RENDER ---
renderPipeline();
});
