${clause.text.substring(0, 150)}...
`;
clauseList.appendChild(clauseEl);
});
}
};
// Renders the document builder with selected clauses
const renderDocumentBuilder = () => {
documentBuilder.innerHTML = ''; // Clear existing content
if (documentClauses.length === 0) {
documentBuilder.appendChild(emptyState);
downloadPdfBtn.disabled = true;
} else {
documentClauses.forEach((clause, index) => {
const clauseEl = document.createElement('div');
clauseEl.className = 'p-4 border border-slate-300 rounded-lg bg-white fade-in';
clauseEl.setAttribute('data-index', index);
clauseEl.innerHTML = `
`;
documentBuilder.appendChild(clauseEl);
});
downloadPdfBtn.disabled = false;
}
};
// --- EVENT HANDLERS ---
// Handles adding a clause to the builder
const handleAddClause = (clauseId) => {
if (documentClauses.some(c => c.id === clauseId)) return; // Already added
const clauseToAdd = initialClauses.find(c => c.id === clauseId);
if (clauseToAdd) {
// Create a deep copy to allow for independent editing
documentClauses.push(JSON.parse(JSON.stringify(clauseToAdd)));
renderAll();
}
};
// Handles removing a clause from the builder
const handleRemoveClause = (index) => {
documentClauses.splice(index, 1);
renderAll();
};
// Handles moving a clause up or down
const handleMoveClause = (index, direction) => {
if (direction === 'up' && index > 0) {
[documentClauses[index], documentClauses[index - 1]] = [documentClauses[index - 1], documentClauses[index]];
} else if (direction === 'down' && index < documentClauses.length - 1) {
[documentClauses[index], documentClauses[index + 1]] = [documentClauses[index + 1], documentClauses[index]];
}
renderAll();
};
// Handles editing a clause inline
const handleEditClause = (index, wrapperEl) => {
const currentText = documentClauses[index].text;
// Adhering to specification: all data is customizable via manual input
wrapperEl.innerHTML = `
`;
// Focus on the textarea
const textarea = wrapperEl.querySelector('.edit-textarea');
if(textarea) textarea.focus();
};
// Handles saving an edited clause
const handleSaveClause = (index, newText) => {
documentClauses[index].text = newText;
renderDocumentBuilder(); // Just re-render the builder
};
// Main event delegation for dynamic elements
document.body.addEventListener('click', (e) => {
const target = e.target;
const parentClauseEl = target.closest('[data-index]');
const index = parentClauseEl ? parseInt(parentClauseEl.dataset.index, 10) : -1;
if (target.closest('.add-clause-btn')) {
const clauseId = parseInt(target.closest('.add-clause-btn').dataset.clauseId, 10);
handleAddClause(clauseId);
}
if (target.closest('.remove-btn') && index !== -1) {
handleRemoveClause(index);
}
if (target.closest('.move-up-btn') && index !== -1) {
handleMoveClause(index, 'up');
}
if (target.closest('.move-down-btn') && index !== -1) {
handleMoveClause(index, 'down');
}
if (target.closest('.edit-btn') && index !== -1) {
const contentWrapper = parentClauseEl.querySelector('.clause-content-wrapper');
if (contentWrapper) handleEditClause(index, contentWrapper);
}
if (target.closest('.save-edit-btn') && index !== -1) {
const textarea = parentClauseEl.querySelector('.edit-textarea');
if(textarea) handleSaveClause(index, textarea.value);
}
if (target.closest('.cancel-edit-btn')) {
renderDocumentBuilder(); // Re-render to cancel edit
}
});
// PDF Download functionality
const handleDownloadPdf = () => {
// Check for jsPDF library
if (typeof window.jspdf === 'undefined') {
alert('PDF generation library is not available. Please check your internet connection.');
return;
}
const { jsPDF } = window.jspdf;
const doc = new jsPDF({
orientation: 'p',
unit: 'pt',
format: 'a4'
});
// Adhering to specification: professional formatting, consistent color scheme
const margin = 40;
const pageWidth = doc.internal.pageSize.getWidth();
const usableWidth = pageWidth - (2 * margin);
let y = margin;
// Title
doc.setFont('helvetica', 'bold');
doc.setFontSize(24);
doc.setTextColor('#1e293b'); // slate-800
doc.text('Contract Document', pageWidth / 2, y, { align: 'center' });
y += 50;
documentClauses.forEach((clause, index) => {
if (y > doc.internal.pageSize.getHeight() - 80) { // Check for new page
doc.addPage();
y = margin;
}
// Clause Title
doc.setFont('helvetica', 'bold');
doc.setFontSize(14);
doc.setTextColor('#334155'); // slate-700
const titleText = `${index + 1}. ${clause.title}`;
doc.text(titleText, margin, y);
y += 20;
// Clause Text
doc.setFont('helvetica', 'normal');
doc.setFontSize(11);
doc.setTextColor('#475569'); // slate-600
// The splitTextToSize function handles text wrapping
const clauseTextLines = doc.splitTextToSize(clause.text, usableWidth);
doc.text(clauseTextLines, margin, y);
y += (clauseTextLines.length * 12) + 25; // Line height * number of lines + spacing
});
// Save the PDF
doc.save('Contract-Document.pdf');
};
// --- INITIALIZATION ---
// Populates the category filter dropdown from the initial data
const initializeCategoryFilter = () => {
const categories = [...new Set(initialClauses.map(c => c.category))];
categories.sort().forEach(category => {
const option = document.createElement('option');
option.value = category;
option.textContent = category;
categoryFilter.appendChild(option);
});
};
// Combined render function to update the whole UI
const renderAll = () => {
renderClauseLibrary();
renderDocumentBuilder();
};
// Attach event listeners for filters
searchInput.addEventListener('input', renderClauseLibrary);
categoryFilter.addEventListener('change', renderClauseLibrary);
downloadPdfBtn.addEventListener('click', handleDownloadPdf);
// Initial setup call
initializeCategoryFilter();
renderAll();
});