diff --git a/static/js/builder.js b/static/js/builder.js index d343c58..3822712 100644 --- a/static/js/builder.js +++ b/static/js/builder.js @@ -593,71 +593,82 @@ document.addEventListener('DOMContentLoaded', function() { updateProtocolData(); - // Use community name if available, otherwise default - const communityName = protocol.metadata.communityName || "Community"; - let markdown = `# ${communityName} Dispute Protocol\n\n`; - - // Include protocol summary if available - if (protocol.metadata.summary) { - markdown += `${protocol.metadata.summary}\n\n`; - markdown += '---\n\n'; - } - - // Include template information if a template was used - if (protocol.templateTitle) { - markdown += `**Template Used:** ${protocol.templateTitle}\n\n`; - markdown += '---\n\n'; - } - - // Loop through the stages in order - stageHeaders.forEach(header => { - const stageId = header.getAttribute('data-stage'); - const stageName = header.querySelector('h2').textContent; + try { + // Use community name if available, otherwise default + const communityName = protocol.metadata.communityName || "Community"; + let markdown = `# ${communityName}\n# Dispute Protocol\n\n`; - let stageContent = ''; - let hasContent = false; + // Include protocol summary if available + if (protocol.metadata.summary) { + markdown += `${protocol.metadata.summary}\n\n`; + markdown += '---\n\n'; + } - // Get components for this stage - const stageComponents = protocol.stages[stageId]; - if (stageComponents) { - // Loop through components - for (const componentId in stageComponents) { - const componentCard = document.getElementById(`component-${componentId}`); - if (componentCard) { - const componentName = componentCard.querySelector('h3').textContent; - - let componentContent = ''; - let componentHasContent = false; - - // Loop through fields - for (const fieldId in stageComponents[componentId]) { - const fieldValue = stageComponents[componentId][fieldId]; + // Template information removed as requested + + // Loop through the stages in order + stageHeaders.forEach(header => { + const stageId = header.getAttribute('data-stage'); + const stageName = header.querySelector('h2').textContent; + + let stageContent = ''; + let hasContent = false; + + // Get components for this stage + const stageComponents = protocol.stages[stageId]; + if (stageComponents) { + // Loop through components + for (const componentId in stageComponents) { + const componentCard = document.getElementById(`component-${componentId}`); + if (componentCard) { + const componentName = componentCard.querySelector('h3').textContent; - // Skip empty fields - if (fieldValue && fieldValue.trim()) { - const fieldLabel = document.querySelector(`label[for="${fieldId}"]`).textContent; - componentContent += `#### ${fieldLabel}\n\n${fieldValue}\n\n`; - componentHasContent = true; - hasContent = true; + let componentContent = ''; + let componentHasContent = false; + + // Loop through fields + for (const fieldId in stageComponents[componentId]) { + const fieldValue = stageComponents[componentId][fieldId]; + + // Skip empty fields + if (fieldValue && fieldValue.trim()) { + // Skip the field label, just add the content + componentContent += `${fieldValue}\n\n`; + componentHasContent = true; + hasContent = true; + } + } + + // Only add component if it has content + if (componentHasContent) { + stageContent += `### ${componentName}\n\n${componentContent}`; } - } - - // Only add component if it has content - if (componentHasContent) { - stageContent += `### ${componentName}\n\n${componentContent}`; } } } - } + + // Only add stage if it has content + if (hasContent) { + // Get the stage section ID (e.g., "Intake") + const stageSection = header.closest('.stage-section'); + const stageSectionId = stageSection ? stageSection.id.replace('stage-', '') : ''; + const stageSectionTitle = stageSectionId ? stageSectionId.charAt(0).toUpperCase() + stageSectionId.slice(1) : ''; + + // Use just the section title to avoid duplication + if (stageSectionTitle) { + markdown += `## ${stageSectionTitle}\n\n${stageContent}`; + } else { + markdown += `## ${stageName}\n\n${stageContent}`; + } + } + }); - // Only add stage if it has content - if (hasContent) { - markdown += `## ${stageName}\n\n${stageContent}`; - } - }); - - // Create and download the file - downloadFile('community_dispute_protocol.md', markdown); + // Create and download the file + downloadFile('community_dispute_protocol.md', markdown); + } catch (error) { + console.error('Error generating Markdown:', error); + alert('Failed to generate Markdown. Please try again.'); + } }); // Export to PDF @@ -666,138 +677,148 @@ document.addEventListener('DOMContentLoaded', function() { updateProtocolData(); - // Create a styled HTML version for PDF export - const { jsPDF } = window.jspdf; - const doc = new jsPDF(); - - let yPos = 20; - - // Use community name if available, otherwise default - const communityName = protocol.metadata.communityName || "Community"; - - // Add title - doc.setFontSize(18); - doc.text(`${communityName} Dispute Protocol`, 105, yPos, { align: 'center' }); - yPos += 15; - - // Add protocol summary if available - if (protocol.metadata.summary) { - doc.setFontSize(12); - const summaryLines = doc.splitTextToSize(protocol.metadata.summary, 180); - doc.text(summaryLines, 14, yPos); - yPos += summaryLines.length * 7 + 8; - } - - // Add template info if available - if (protocol.templateTitle) { - doc.setFontSize(10); - doc.text(`Template Used: ${protocol.templateTitle}`, 14, yPos); - yPos += 10; - } - - // Loop through the stages - stageHeaders.forEach(header => { - const stageId = header.getAttribute('data-stage'); - const stageName = header.querySelector('h2').textContent; - - let stageHasContent = false; - let stageStartYPos = yPos; - - // Save current position to add stage heading later if content is found - if (yPos > 250) { - doc.addPage(); - stageStartYPos = 20; - yPos = 20; + try { + // Check if jspdf is properly loaded + if (typeof window.jspdf === 'undefined') { + console.error('jsPDF library not loaded properly'); + alert('PDF export is currently unavailable. The required library failed to load.'); + return; } - // Skip stage heading for now - we'll add it if the stage has content - let currentYPos = stageStartYPos + 10; // Space for the heading + // Create a styled HTML version for PDF export + const { jsPDF } = window.jspdf; + const doc = new jsPDF(); - // Get components for this stage - const stageComponents = protocol.stages[stageId]; - if (stageComponents) { - // Loop through components - for (const componentId in stageComponents) { - const componentCard = document.getElementById(`component-${componentId}`); - if (componentCard) { - let componentHasContent = false; - let componentStartYPos = currentYPos; - - // Check for page break - if (currentYPos > 250) { - doc.addPage(); - componentStartYPos = 20; - currentYPos = 20; - } - - // Skip component heading for now - add if it has content - const componentFieldStartYPos = componentStartYPos + 8; - let fieldYPos = componentFieldStartYPos; - - const componentName = componentCard.querySelector('h3').textContent; + // Set consistent text width parameters + const margins = { + left: 14, + right: 14, + pageWidth: 210, // A4 width in mm (portrait) + width: 182 // We'll use this as the consistent text width - should be pageWidth - left - right + }; + + let yPos = 20; + + // Use community name if available, otherwise default + const communityName = protocol.metadata.communityName || "Community"; + + // Add title with line break + doc.setFontSize(18); + doc.text(`${communityName}\nDispute Protocol`, 105, yPos, { align: 'center' }); + yPos += 25; + + // Page width indicator removed + + // Add protocol summary if available + if (protocol.metadata.summary) { + doc.setFontSize(12); + const summaryLines = doc.splitTextToSize(protocol.metadata.summary, margins.width); + doc.text(summaryLines, margins.left, yPos); + yPos += summaryLines.length * 7 + 8; + } + + // Template information removed as requested + + // Set up direct rendering approach to avoid duplication + const sectionsToProcess = []; + + // Find all unique stage sections with content + document.querySelectorAll('.stage-section').forEach(section => { + const sectionId = section.id.replace('stage-', ''); + const sectionTitle = sectionId.charAt(0).toUpperCase() + sectionId.slice(1); + + const componentsWithContent = []; + + // Look for components with content in this section + section.querySelectorAll('.component-card').forEach(componentCard => { + const componentId = componentCard.id.replace('component-', ''); + + // Check if this component has content in the protocol data + const stageId = section.id.replace('stage-', ''); + if (protocol.stages[stageId] && + protocol.stages[stageId][componentId] && + Object.keys(protocol.stages[stageId][componentId]).length > 0) { + componentsWithContent.push({ + id: componentId, + element: componentCard, + name: componentCard.querySelector('h3').textContent + }); + } + }); + + // Only include sections that have components with content + if (componentsWithContent.length > 0) { + sectionsToProcess.push({ + id: sectionId, + title: sectionTitle, + components: componentsWithContent + }); + } + }); + + // Process each section directly to avoid duplication + sectionsToProcess.forEach(section => { + // Add page break if needed + if (yPos > 250) { + doc.addPage(); + yPos = 20; + } + + // Add section header only once (e.g., "Intake") + doc.setFontSize(18); + doc.text(section.title, margins.left, yPos); + yPos += 15; + + // Process components in this section + section.components.forEach(component => { + // Add page break if needed + if (yPos > 250) { + doc.addPage(); + yPos = 20; + } + + // Add component heading + doc.setFontSize(14); + doc.text(component.name, margins.left, yPos); + yPos += 8; + + // Get fields for this component + const stageId = section.id; + const componentId = component.id; + + if (protocol.stages[stageId] && protocol.stages[stageId][componentId]) { // Loop through fields - for (const fieldId in stageComponents[componentId]) { - const fieldValue = stageComponents[componentId][fieldId]; + for (const fieldId in protocol.stages[stageId][componentId]) { + const fieldValue = protocol.stages[stageId][componentId][fieldId]; // Skip empty fields if (fieldValue && fieldValue.trim()) { - const fieldLabel = document.querySelector(`label[for="${fieldId}"]`).textContent; - // Add page break if needed - if (fieldYPos > 250) { + if (yPos > 250) { doc.addPage(); - fieldYPos = 20; + yPos = 20; } - // We have content - if this is the first content in the component, - // add the component heading - if (!componentHasContent) { - componentHasContent = true; - stageHasContent = true; - - // If this is the first content in the stage, add the stage heading - if (!stageHasContent) { - doc.setFontSize(16); - doc.text(stageName, 14, stageStartYPos); - } - - // Add component heading - doc.setFontSize(14); - doc.text(componentName, 14, componentStartYPos); - } - - // Add field heading + // Format field content with larger font size (12) doc.setFontSize(12); - doc.text(fieldLabel, 14, fieldYPos); - fieldYPos += 6; - // Split the text into lines to handle wrapping - const textLines = doc.splitTextToSize(fieldValue, 180); - - // Add field content - doc.setFontSize(10); - doc.text(textLines, 14, fieldYPos); - fieldYPos += textLines.length * 5 + 8; + // Use consistent width for all text + const textLines = doc.splitTextToSize(fieldValue, margins.width); + doc.text(textLines, margins.left, yPos); + yPos += textLines.length * 6 + 8; // Slightly increase spacing for larger font } } - - // Update current Y position if component had content - if (componentHasContent) { - currentYPos = fieldYPos; - } } - } - } + }); + }); - // Update the overall Y position if stage had content - if (stageHasContent) { - yPos = currentYPos; - } - }); - - // Save the PDF - doc.save('community_dispute_protocol.pdf'); + // Save the PDF + doc.save('community_dispute_protocol.pdf'); + } catch (error) { + console.error('Error generating PDF:', error); + alert('Failed to generate PDF. Please try again or use another export format.'); + } }); // Export to JSON