Field Notes Template Generator

Field Notes Template Generator

Live Template Preview

No fields added yet. Go to the 'Template Configuration' tab to build your notes template.

Add New Field

Current Template Fields

No fields added yet.

"; return; } templateFields.forEach((field, index) => { const item = document.createElement("div"); item.className = "fntg-field-item"; const typeMap = { 'header': 'Header', 'text_single': 'Single-Line Text', 'text_multi': 'Multi-Line Text', 'checkbox': 'Checkbox Group' }; item.innerHTML = `

${escapeHTML(field.label)}

${typeMap[field.type]}
`; fieldList.appendChild(item); }); // Add listeners to new buttons fieldList.querySelectorAll('.fntg-edit-btn').forEach(b => b.addEventListener('click', () => handleEditClick(b.dataset.id))); fieldList.querySelectorAll('.fntg-delete-btn').forEach(b => b.addEventListener('click', () => handleDeleteClick(b.dataset.id))); fieldList.querySelectorAll('.fntg-move-up-btn').forEach(b => b.addEventListener('click', () => handleMoveClick(b.dataset.id, 'up'))); fieldList.querySelectorAll('.fntg-move-down-btn').forEach(b => b.addEventListener('click', () => handleMoveClick(b.dataset.id, 'down'))); } /** * Handles config form submission for add/edit. */ function handleFieldFormSubmit(e) { e.preventDefault(); const id = fieldIdInput.value; const field = { type: fieldTypeSelect.value, label: fieldLabelInput.value.trim(), options: fieldOptionsInput.value.split('\n').filter(opt => opt.trim() !== "") }; if (!field.label) { alert("Please enter a field label."); return; } if (id) { // Update const index = templateFields.findIndex(f => f.id == id); if (index !== -1) { templateFields[index] = { ...templateFields[index], ...field }; } } else { // Add new field.id = Date.now(); templateFields.push(field); } renderAll(); resetForm(); } /** * Resets the config form. */ function resetForm() { if (!formTitle || !fieldIdInput || !fieldForm || !saveBtn) return; formTitle.textContent = "Add New Field"; fieldIdInput.value = ""; fieldForm.reset(); saveBtn.textContent = "Add Field"; toggleOptionsInput(); // Hides options group } /** * Populates form for editing a field. */ function handleEditClick(id) { const field = templateFields.find(f => f.id == id); if (!field) return; formTitle.textContent = "Edit Field"; fieldIdInput.value = field.id; fieldTypeSelect.value = field.type; fieldLabelInput.value = field.label; fieldOptionsInput.value = field.options.join('\n'); saveBtn.textContent = "Update Field"; toggleOptionsInput(); fieldLabelInput.focus(); } /** * Deletes a field. */ function handleDeleteClick(id) { if (confirm("Are you sure you want to delete this field?")) { templateFields = templateFields.filter(f => f.id != id); renderAll(); } } /** * Moves a field up or down in the list. */ function handleMoveClick(id, direction) { const index = templateFields.findIndex(f => f.id == id); if (index === -1) return; if (direction === 'up' && index > 0) { [templateFields[index-1], templateFields[index]] = [templateFields[index], templateFields[index-1]]; } else if (direction === 'down' && index < templateFields.length - 1) { [templateFields[index], templateFields[index+1]] = [templateFields[index+1], templateFields[index]]; } renderAll(); } // --- Tab 1: Dashboard Logic --- /** * Renders the live preview form. */ function renderPreview() { if (!previewArea || !placeholderMsg) return; previewArea.innerHTML = ""; // Clear if (templateFields.length === 0) { placeholderMsg.style.display = "block"; downloadPdfBtn.style.display = "none"; } else { placeholderMsg.style.display = "none"; downloadPdfBtn.style.display = "inline-block"; templateFields.forEach(field => { switch (field.type) { case 'header': const header = document.createElement("h3"); header.className = "fntg-preview-header"; header.textContent = field.label; previewArea.appendChild(header); break; case 'text_single': const singleWrapper = document.createElement("div"); singleWrapper.className = "fntg-preview-field"; singleWrapper.innerHTML = ` `; previewArea.appendChild(singleWrapper); break; case 'text_multi': const multiWrapper = document.createElement("div"); multiWrapper.className = "fntg-preview-field"; multiWrapper.innerHTML = ` `; previewArea.appendChild(multiWrapper); break; case 'checkbox': const checkWrapper = document.createElement("div"); checkWrapper.className = "fntg-preview-field"; const checkLabel = document.createElement("label"); checkLabel.textContent = escapeHTML(field.label); checkWrapper.appendChild(checkLabel); const checkGroup = document.createElement("div"); checkGroup.className = "fntg-checkbox-group"; field.options.forEach((opt, index) => { const item = document.createElement("div"); item.className = "fntg-checkbox-item"; item.innerHTML = ` `; checkGroup.appendChild(item); }); checkWrapper.appendChild(checkGroup); previewArea.appendChild(checkWrapper); break; } }); } } /** * Generates and downloads a printable PDF template. */ function downloadPDF() { if (typeof jspdf === "undefined") { alert("Error: PDF generation library failed to load."); return; } try { const doc = new jsPDF(); const pageMargin = 20; const pageWidth = doc.internal.pageSize.getWidth() - pageMargin * 2; const lineSpacing = 7; let yPos = 25; const title = "Field Notes"; doc.setFont("helvetica", "bold"); doc.setFontSize(20); doc.setTextColor("#004d40"); doc.text(title, pageMargin, yPos); yPos += 15; const checkPageBreak = () => { if (yPos > 270) { doc.addPage(); yPos = 20; } }; templateFields.forEach(field => { checkPageBreak(); switch (field.type) { case 'header': doc.setFont("helvetica", "bold"); doc.setFontSize(14); doc.setTextColor("#004d40"); yPos += lineSpacing * 1.5; // Extra space before header doc.text(field.label, pageMargin, yPos); doc.setDrawColor(200); doc.line(pageMargin, yPos + 2, pageMargin + pageWidth, yPos + 2); yPos += lineSpacing * 1.5; break; case 'text_single': doc.setFont("helvetica", "normal"); doc.setFontSize(11); doc.setTextColor("#333"); const labelWidth = doc.getTextWidth(field.label) + 2; doc.text(field.label, pageMargin, yPos); // Draw line doc.setDrawColor(150); doc.line(pageMargin + labelWidth, yPos, pageMargin + pageWidth, yPos); yPos += lineSpacing * 1.5; break; case 'text_multi': doc.setFont("helvetica", "normal"); doc.setFontSize(11); doc.setTextColor("#333"); doc.text(field.label, pageMargin, yPos); yPos += lineSpacing; // Draw box doc.setDrawColor(150); doc.rect(pageMargin, yPos, pageWidth, 40); // 40px height for box yPos += 40 + lineSpacing; break; case 'checkbox': doc.setFont("helvetica", "normal"); doc.setFontSize(11); doc.setTextColor("#333"); doc.text(field.label, pageMargin, yPos); yPos += lineSpacing; let xPos = pageMargin + 5; doc.setDrawColor(100); field.options.forEach(opt => { const optWidth = doc.getTextWidth(opt) + 10; if (xPos + optWidth > pageMargin + pageWidth) { xPos = pageMargin + 5; yPos += lineSpacing; } checkPageBreak(); doc.rect(xPos, yPos - 4, 4, 4); // Checkbox doc.text(opt, xPos + 6, yPos); xPos += optWidth; }); yPos += lineSpacing * 1.5; break; } }); doc.save("Field-Notes-Template.pdf"); } catch (error) { console.error("Failed to generate PDF:", error); alert("An error occurred while generating the PDF."); } } /** * Utility to escape HTML. */ function escapeHTML(str) { if (!str) return ""; return str .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // --- Event Listeners --- if (tabButtons[1]) tabButtons[1].onclick = () => showTab(1); if (tabButtons[2]) tabButtons[2].onclick = () => showTab(2); if (prevBtn) prevBtn.onclick = () => showTab(currentTab - 1); if (nextBtn) nextBtn.onclick = () => showTab(currentTab + 1); // Tab 1 if (downloadPdfBtn) downloadPdfBtn.addEventListener("click", downloadPDF); // Tab 2 if (fieldForm) fieldForm.addEventListener("submit", handleFieldFormSubmit); if (clearBtn) clearBtn.addEventListener("click", resetForm); if (fieldTypeSelect) fieldTypeSelect.addEventListener("change", toggleOptionsInput); // --- Initialization --- function init() { loadSampleData(); renderAll(); showTab(1); // Set initial tab state } init(); });
Scroll to Top