`;
return;
}
let tableHtml = `
`;
dashComponentList.innerHTML = tableHtml;
// Attach blur listener for component fields
dashboardOutput.querySelectorAll('.up-editable-field[data-section="components"]').forEach(el => {
el.removeEventListener('blur', updateDashboardData);
el.addEventListener('blur', updateDashboardData);
});
}
function renderConfigTable() {
configTableBody.innerHTML = '';
if (projectData.components.length === 0) {
configTableBody.innerHTML = ` `;
return;
}
projectData.components.forEach(comp => {
const row = document.createElement('tr');
row.innerHTML = `
`;
configTableBody.appendChild(row);
});
// Attach listeners for config edits
configTableBody.querySelectorAll('.up-config-edit').forEach(el => {
el.removeEventListener('change', updateConfigData);
el.removeEventListener('blur', updateConfigData);
if (el.tagName === 'SELECT') {
el.addEventListener('change', updateConfigData);
} else {
el.addEventListener('blur', updateConfigData);
}
});
// Attach delete listeners
configTableBody.querySelectorAll('.up-delete-btn').forEach(btn => {
btn.removeEventListener('click', deleteComponent);
btn.addEventListener('click', deleteComponent);
});
}
// --- Data Manipulation ---
function updateDashboardData(e) {
const el = e.target;
const section = el.dataset.section;
const field = el.dataset.field;
const value = el.innerText || el.value;
if (section === 'details') {
if (projectData.projectDetails && projectData.projectDetails.hasOwnProperty(field)) {
projectData.projectDetails[field] = value;
loadInitialData(); // Sync back to input fields
}
} else if (section === 'components') {
const id = el.dataset.id;
const compIndex = projectData.components.findIndex(c => c.id === id);
if (compIndex > -1 && projectData.components[compIndex].hasOwnProperty(field)) {
projectData.components[compIndex][field] = value;
}
}
}
function updateConfigData(e) {
const el = e.target;
const id = el.dataset.id;
const field = el.dataset.field;
const value = el.value;
const compIndex = projectData.components.findIndex(c => c.id === id);
if (compIndex > -1 && projectData.components[compIndex].hasOwnProperty(field)) {
projectData.components[compIndex][field] = value;
}
}
function deleteComponent(e) {
const id = e.target.dataset.id;
if(confirm(`Are you sure you want to delete component ID ${id}?`)) {
projectData.components = projectData.components.filter(c => c.id !== id);
renderConfigTable();
}
}
// --- Event Handlers ---
// Tab 2: Save Project Details
document.getElementById('up-save-setup-btn').addEventListener('click', () => {
const name = inputName.value.trim();
const audience = inputAudience.value.trim();
const goals = inputGoals.value.trim();
if (!name || !audience) {
alert("Project Name and Target Audience are mandatory fields.");
return;
}
projectData.projectDetails = { name, audience, goals };
renderDashboard();
alert("Project details saved! Proceed to Component Planning.");
});
// Tab 3: Add Component
document.getElementById('up-add-component-btn').addEventListener('click', () => {
const name = compName.value.trim();
const purpose = compPurpose.value.trim();
const type = compType.value;
const status = compStatus.value;
if (!name || !purpose) {
alert("Component Name and Purpose are required.");
return;
}
const newComp = {
id: generateId(),
type: type,
name: name,
purpose: purpose,
status: status
};
projectData.components.push(newComp);
// Clear form
document.getElementById('up-component-form').reset();
alert("Component added to plan.");
});
// Tab 3: Clear Component Form
document.getElementById('up-clear-component-btn').addEventListener('click', () => {
document.getElementById('up-component-form').reset();
});
// Tab 4: Reset Defaults
document.getElementById('up-reset-defaults-btn').addEventListener('click', () => {
if(confirm("This will overwrite ALL current project data and components with the default sample data. Are you sure?")) {
projectData = JSON.parse(JSON.stringify(defaultData));
loadInitialData();
renderConfigTable();
renderDashboard();
alert("Project plan reset to defaults.");
}
});
// --- PDF Generation ---
document.getElementById('up-download-pdf-btn').addEventListener('click', async () => {
const { jsPDF } = window.jspdf;
const elementToPrint = document.getElementById('up-pdf-content');
const originalBtnText = document.getElementById('up-download-pdf-btn').innerText;
document.getElementById('up-download-pdf-btn').innerText = "Generating PDF...";
// Clone the content to apply print-friendly styles and ensure it's fully rendered
const clone = elementToPrint.cloneNode(true);
// Remove contenteditable attributes from the clone so they don't show up in PDF
clone.querySelectorAll('[contenteditable="true"]').forEach(el => {
el.removeAttribute('contenteditable');
el.style.border = 'none';
});
// Add PDF-specific title
const pdfTitle = document.createElement('h1');
pdfTitle.className = 'up-pdf-title';
pdfTitle.innerText = `UI Design Project Plan: ${projectData.projectDetails.name}`;
clone.prepend(pdfTitle);
// Append clone to a hidden container to render for canvas
const container = document.createElement('div');
container.style.position = 'absolute';
container.style.top = '0';
container.style.left = '0';
container.style.width = '800px';
container.style.backgroundColor = '#ffffff';
container.style.padding = '30px';
container.appendChild(clone);
document.body.appendChild(container);
try {
const canvas = await html2canvas(clone, {
scale: 2,
useCORS: true,
backgroundColor: '#ffffff'
});
const imgData = canvas.toDataURL('image/jpeg', 1.0);
const pdf = new jsPDF('p', 'mm', 'a4');
const pdfWidth = pdf.internal.pageSize.getWidth();
const pdfHeight = (canvas.height * pdfWidth) / canvas.width;
let heightUsed = 0;
let pageHeight = pdf.internal.pageSize.getHeight();
let y = 0;
// Loop to handle content spanning multiple pages
while(heightUsed < pdfHeight) {
let pageTop = y;
let heightToPrint = Math.min(pageHeight, pdfHeight - heightUsed);
if (y > 0) {
pdf.addPage();
}
// Calculate the source Y position for the image cropping
let sourceY = heightUsed / (pdfHeight / canvas.height);
pdf.addImage(imgData, 'JPEG', 0, 0, pdfWidth, pdfHeight, null, 'NONE', 0, sourceY, pdfWidth, heightToPrint);
heightUsed += heightToPrint;
y = heightToPrint;
}
pdf.save(`UI_Plan_${projectData.projectDetails.name.replace(/[^a-z0-9]/gi, '_')}.pdf`);
} catch (error) {
console.error("PDF Generation Error:", error);
// Non-intrusive message box instead of alert()
const messageBox = document.createElement('div');
messageBox.style.cssText = 'position:fixed;top:10%;left:50%;transform:translateX(-50%);padding:20px;background:#ef4444;color:white;border-radius:8px;z-index:1000;box-shadow:0 4px 12px rgba(0,0,0,0.2);';
messageBox.innerHTML = 'PDF generation failed. Please check the console for details.';
document.body.appendChild(messageBox);
setTimeout(() => document.body.removeChild(messageBox), 3000);
} finally {
document.body.removeChild(container);
document.getElementById('up-download-pdf-btn').innerText = originalBtnText;
}
});
// Add font-awesome for icons (needed for the empty state)
const faScript = document.createElement('script');
faScript.src = 'https://kit.fontawesome.com/a076d05399.js'; // Replace with a common, functional FA kit URL if available
faScript.crossOrigin = 'anonymous';
document.head.appendChild(faScript);
});
| Type | Name | Purpose | Status |
|---|---|---|---|
| ${comp.type} | ${comp.name} |
${comp.purpose} |
${comp.status} |
No components defined.
