UI Design Planner Generator

UI Design Project Planner

UI Design Project Planner

Project Overview: This dashboard provides a complete summary. You can edit text directly here or download the final plan below.
Project Name
Marketing Dashboard 2.0
Target Audience
Internal Marketing Managers (Ages 25-45, high digital literacy)
Core Goals
Increase data readability by 30%; Reduce time-to-insight by 50%; Ensure mobile responsiveness.

Component Breakdown

Project Definition

Define the high-level purpose and context for your UI project.

Define UI Components & Screens

Break the project down into manageable, reusable blocks.

Component Data Configuration

Review, edit, or delete the components added to your project plan.

Type Name Purpose Status Actions

No components defined yet. Go to Component Planning to add items.

`; return; } let tableHtml = `
`; projectData.components.forEach(comp => { tableHtml += ` `; }); tableHtml += `
Type Name Purpose Status
${comp.type}
${comp.name}
${comp.purpose}
${comp.status}
`; 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 = `
No components defined.
`; 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); });
Scroll to Top