Improved PDF and MD export

This commit is contained in:
Nathan Schneider 2025-04-09 16:52:45 -06:00
parent 521c782c42
commit 57204e9a67

View File

@ -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