Public Hearing Notice Generator

Public Hearing Notice Generator

The specific title of the proposed action or topic.

The specific legal mandate or file number authorizing this hearing.

Clearly describe what will be discussed, amended, or approved.

How can the public submit written comments in advance or during the hearing?

Deadline for requesting accessibility accommodations.

Review your generated Public Hearing Notice below.

Click "Next" or "Previous" to refresh this preview.

Impacted Area: ${escapeHTML(data.jurisdiction)}

Legal Authority: ${escapeHTML(data.authority)}

Public Participation

Comment Submission:

${formatText(data.commentDetails)}

ADA Accommodation Deadline: ${escapeHTML(data.adaDeadline)}

Contact Information

Name: ${escapeHTML(data.contactName)}

Phone: ${escapeHTML(data.contactPhone)}

Email: ${escapeHTML(data.contactEmail)}

`; }; // --- Download Functions --- const getTxtContent = () => { const data = getNoticeData(); let content = `NOTICE OF PUBLIC HEARING\n`; content += `========================================\n\n`; content += `SPONSORING ORGANIZATION: ${data.organization}\n`; content += `HEARING SUBJECT: ${data.hearingTitle}\n\n`; content += `--- LOGISTICS ---\n`; content += `DATE: ${data.date}\n`; content += `TIME: ${data.time}\n`; content += `LOCATION/LINK: ${data.location}\n\n`; content += `--- PURPOSE AND AUTHORITY ---\n`; content += `PURPOSE/PROPOSAL:\n${formatTextPlain(data.purpose)}\n\n`; content += `IMPACTED AREA: ${data.jurisdiction}\n`; content += `LEGAL AUTHORITY: ${data.authority}\n\n`; content += `--- PUBLIC PARTICIPATION ---\n`; content += `COMMENT SUBMISSION DETAILS:\n${formatTextPlain(data.commentDetails)}\n\n`; content += `ADA ACCOMMODATION DEADLINE: ${data.adaDeadline}\n\n`; content += `--- CONTACT INFORMATION ---\n`; content += `NAME: ${data.contactName}\n`; content += `PHONE: ${data.contactPhone}\n`; content += `EMAIL: ${data.contactEmail}\n`; return content; }; const downloadTxt = () => { const txtContent = getTxtContent(); const blob = new Blob([txtContent], { type: 'text/plain;charset=utf-8' }); const a = document.createElement('a'); a.href = URL.createObjectURL(blob); a.download = `notice_${inputs.hearingTitle.value.replace(/ /g, '_') || 'hearing'}.txt`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(a.href); }; const downloadPDF = () => { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF === 'undefined') { alert('Error: jsPDF library not loaded.'); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF('p', 'mm', 'a4'); const data = getNoticeData(); const margin = 20; const pageWidth = doc.internal.pageSize.getWidth(); const usableWidth = pageWidth - (margin * 2); let yPos = 25; const addSectionTitle = (title) => { if (yPos > 260) { doc.addPage(); yPos = 20; } yPos += 5; doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.setTextColor(0, 123, 255); // Blue doc.text(title, margin, yPos); yPos += 6; doc.setDrawColor(0, 123, 255); doc.setLineWidth(0.5); doc.line(margin, yPos, pageWidth - margin, yPos); yPos += 5; }; const addText = (label, text, isBold = false) => { if (yPos > 270) { doc.addPage(); yPos = 20; } doc.setFontSize(11); doc.setFont(undefined, isBold ? 'bold' : 'normal'); doc.setTextColor(52, 73, 94); // Dark Gray let combinedText = isBold ? `${label}` : `${label} ${text}`; if (!isBold) { doc.setFont(undefined, 'bold'); combinedText = label; doc.text(combinedText, margin, yPos); doc.setFont(undefined, 'normal'); const valueText = text; const splitText = doc.splitTextToSize(valueText, usableWidth - doc.getTextWidth(combinedText) - 5); // Try to place the value immediately after the label on the same line if (splitText.length === 1 && doc.getTextWidth(combinedText) + doc.getTextWidth(splitText[0]) + 5 < usableWidth) { doc.text(splitText[0], margin + doc.getTextWidth(combinedText) + 2, yPos); yPos += 5; } else { // If it wraps, start on the next line (full width) yPos += 5; doc.setFont(undefined, 'normal'); const fullSplitText = doc.splitTextToSize(text, usableWidth); doc.text(fullSplitText, margin, yPos); yPos += fullSplitText.length * 5 + 2; } } else { const splitText = doc.splitTextToSize(combinedText, usableWidth); doc.text(splitText, margin, yPos); yPos += splitText.length * 5 + 2; } }; // 1. HEADER doc.setFontSize(18); doc.setFont(undefined, 'bold'); doc.setTextColor(44, 62, 80); doc.text(`NOTICE OF PUBLIC HEARING`, pageWidth / 2, yPos, { align: 'center' }); yPos += 10; doc.setFontSize(14); doc.text(data.hearingTitle, pageWidth / 2, yPos, { align: 'center' }); yPos += 10; // 2. LOGISTICS addSectionTitle('I. BASIC LOGISTICS'); addText('Organization:', data.organization); addText('Date:', data.date); addText('Time:', data.time); addText('Location:', data.location); // 3. PURPOSE & LEGAL addSectionTitle('II. PURPOSE AND AUTHORITY'); addText('Legal Authority/File No.:', data.authority); addText('Impacted Area:', data.jurisdiction); yPos = addText('Purpose/Proposal:', '', false); doc.setFont(undefined, 'normal'); const purposeSplit = doc.splitTextToSize(data.purpose, usableWidth); doc.text(purposeSplit, margin, yPos); yPos += purposeSplit.length * 5 + 2; // 4. PARTICIPATION addSectionTitle('III. PUBLIC PARTICIPATION'); addText('ADA Accommodation Deadline:', data.adaDeadline); yPos = addText('Comment Submission Details:', '', false); doc.setFont(undefined, 'normal'); const commentSplit = doc.splitTextToSize(data.commentDetails, usableWidth); doc.text(commentSplit, margin, yPos); yPos += commentSplit.length * 5 + 2; // 5. CONTACT addSectionTitle('IV. CONTACT INFORMATION'); addText('Contact Name:', data.contactName); addText('Phone:', data.contactPhone); addText('Email:', data.contactEmail); doc.save(`notice_${data.hearingTitle.replace(/ /g, '_') || 'hearing'}.pdf`); }; // --- Event Listeners --- // Tab Buttons tabButtons.forEach((btn, index) => { btn.addEventListener('click', () => showTab(index + 1)); }); // Next/Prev Navigation nextBtn.addEventListener('click', () => showTab(currentTab + 1)); prevBtn.addEventListener('click', () => showTab(currentTab - 1)); // Tab 4 Actions downloadPdfBtn.addEventListener('click', downloadPDF); downloadTxtBtn.addEventListener('click', downloadTxt); // --- Initialization --- // Pre-populate with sample data inputs.hearingTitle.value = "Proposed Zoning Change for Central Business District"; inputs.organization.value = "City Council of Metropolis"; inputs.date.value = "Tuesday, November 15, 2025"; inputs.time.value = "7:00 PM EST"; inputs.location.value = "City Hall Council Chambers, 123 Main St, Metropolis, USA"; inputs.authority.value = "Pursuant to State Law Section 102.B / File No. Z-2025-04"; inputs.purpose.value = "The purpose is to receive public comment on a proposal to rezone parcels 10A and 10B from 'Residential R-2' to 'Commercial C-3' to permit the development of a mixed-use residential and retail complex to encourage local economic growth."; inputs.jurisdiction.value = "Central Business District, Metropolis, USA"; inputs.commentDetails.value = "Written comments must be emailed to cityclerk@metropolis.gov or mailed to the City Clerk's Office by 5:00 PM on the day prior to the hearing. Oral comments will be limited to 3 minutes per speaker in the interest of time."; inputs.adaDeadline.value = "November 8, 2025"; inputs.contactName.value = "John Smith, City Clerk"; inputs.contactPhone.value = "(555) 123-4567"; inputs.contactEmail.value = "info@metropolis.gov"; showTab(1); // Set initial state });
Scroll to Top