Export Administration Regulations (EAR) Classification Worksheet

EAR Classification Worksheet

Item Classification Summary (Editable)

Define your item's details and technical parameters using the "Item Configuration" tab.

Item Identification

This is your initial finding based on the Commerce Control List (CCL).
(e.g., Anti-Terrorism, Export Control Classification)

Technical Parameters & Justification

Current Parameters

No technical parameters added yet.

"; return; } parameters.forEach(p => { const itemEl = document.createElement("div"); itemEl.className = "ear-config-list-item"; itemEl.dataset.id = p.id; itemEl.innerHTML = ` ${escapeHTML(p.parameter)} (${escapeHTML(p.value)}) - Ref: ${escapeHTML(p.ref)} `; configParameterList.appendChild(itemEl); }); } // --- Dashboard Management --- function renderDashboard(isInitial = true) { // Get config values const itemName = configItemName.value; const description = configDescription.value; const eccnInitial = configEccnInitial.value; const reason = configReason.value; // --- Build Dashboard HTML --- dashboardOutput.innerHTML = `
# Parameter / Specification Measured Value / Finding CCL Reference / ECCN Paragraph Action
`; // Populate Table Body const tbody = dashboardOutput.querySelector('#ear-dash-tbody'); if (parameters.length === 0) { tbody.innerHTML = ` No technical parameters logged yet. `; } else { parameters.forEach((p, index) => { const tr = document.createElement('tr'); tr.dataset.id = p.id; tr.innerHTML = ` ${index + 1} `; tbody.appendChild(tr); }); } setupDashboardListeners(); if (!isInitial) showTab('ear-tab-dashboard'); } /** * Attaches listeners to the dashboard table elements */ function setupDashboardListeners() { const output = dashboardOutput; if (!output) return; // Event delegation for removal output.addEventListener('click', (e) => { if (e.target.dataset.action === 'remove-dash-item') { const tr = e.target.closest('tr'); parameters.splice(parameters.findIndex(p => p.id === tr.dataset.id), 1); // Rerender entirely to renumber and update state/config renderDashboard(false); updateConfigListDisplay(); } }); // Event delegation for input/change updates output.addEventListener('input', handleDashboardUpdate); output.addEventListener('change', handleDashboardUpdate); } /** * Handles updates made directly to the dashboard inputs/textareas */ function handleDashboardUpdate(e) { const target = e.target; const value = target.value; // 1. Update Config header fields from Dashboard if (target.id === 'ear-dash-name') configItemName.value = value; else if (target.id === 'ear-dash-description') configDescription.value = value; else if (target.id === 'ear-dash-eccn') configEccnInitial.value = value; else if (target.id === 'ear-dash-reason') configReason.value = value; // 2. Update Parameter State const tr = target.closest('tr'); if (tr) { const pId = tr.dataset.id; const param = parameters.find(p => p.id === pId); if (param) { const field = target.dataset.field; if (field) { param[field] = value; // Rerender config list immediately updateConfigListDisplay(); } } } } /** * Generates a PDF report from the dashboard data */ function downloadPDF() { if (typeof window.jspdf === 'undefined' || typeof window.jspdf.jsPDF.autoTable === 'undefined') { alert("Error: PDF libraries could not be loaded. Please try again."); return; } // Get final data from dashboard inputs const itemName = configItemName.value || "Untitled Item"; const description = configDescription.value || "N/A"; const finalEccn = configEccnInitial.value || "EAR99"; const reason = configReason.value || "N/A"; const date = new Date().toLocaleDateString('en-US'); if (!itemName.trim()) { alert("Please generate the worksheet first."); return; } const { jsPDF } = window.jspdf; const doc = new jsPDF("p", "pt", "a4"); const margin = 40; let yPos = margin; const lineHeight = 16; const usableWidth = doc.internal.pageSize.getWidth() - margin * 2; // Function to add a structured text block function addBlock(title, text, isMainTitle = false) { const titleHeight = isMainTitle ? 25 : 20; const textLines = doc.splitTextToSize(text, usableWidth); const textHeight = textLines.length * lineHeight; if (yPos + titleHeight + textHeight > doc.internal.pageSize.getHeight() - margin) { doc.addPage(); yPos = margin; } doc.setFontSize(isMainTitle ? 16 : 12); doc.setFont(undefined, 'bold'); doc.text(title, margin, yPos); yPos += lineHeight * 1.5; if (!isMainTitle) { doc.setFontSize(10); doc.setFont(undefined, 'normal'); doc.text(textLines, margin, yPos); yPos += textHeight + lineHeight * 0.5; } else { yPos += 5; // Extra space after main title } } // --- Document Title & Item Header --- doc.setFontSize(20); doc.setFont(undefined, 'bold'); doc.text("EAR CLASSIFICATION WORKSHEET", doc.internal.pageSize.getWidth() / 2, yPos, { align: 'center' }); yPos += lineHeight * 2; addBlock(`Item Name: ${itemName}`, `Date: ${date}`, true); // --- 1. Item Description --- addBlock("1. General Description", description); // --- 2. Final ECCN Determination --- const eccnText = `FINAL ECCN: ${finalEccn}\nREASON(S) FOR CONTROL: ${reason}`; const eccnLines = doc.splitTextToSize(eccnText, usableWidth); if (yPos > doc.internal.pageSize.getHeight() - 150) { doc.addPage(); yPos = margin; } doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.text("2. Classification Determination", margin, yPos); yPos += 20; doc.setFontSize(11); doc.setFont(undefined, 'bold'); doc.text(eccnLines, margin, yPos); yPos += eccnLines.length * 14 + 15; // --- 3. Technical Parameters Table --- if (yPos > doc.internal.pageSize.getHeight() - 100) { doc.addPage(); yPos = margin; } doc.setFontSize(14); doc.setFont(undefined, 'bold'); doc.text("3. Technical Parameters and Justification", margin, yPos); yPos += 20; const tableHead = [["#", "Parameter / Specification", "Measured Value", "CCL Reference"]]; const tableBody = parameters.map((p, index) => [ index + 1, p.parameter, p.value, p.ref ]); doc.autoTable({ startY: yPos, head: tableHead, body: tableBody, theme: 'grid', headStyles: { fillColor: [0, 115, 230], textColor: [255, 255, 255], fontSize: 9 }, styles: { fontSize: 8, cellPadding: 4, overflow: 'linebreak' }, columnStyles: { 0: { cellWidth: 20, halign: 'center' }, 1: { cellWidth: 150 }, 2: { cellWidth: 120 }, 3: { cellWidth: 'auto' } }, margin: { left: margin, right: margin } }); doc.save(`${itemName.replace(/ /g,"_")}_EAR_Worksheet.pdf`); } /** * Helper to escape HTML */ function escapeHTML(str) { if (!str) return ""; return str .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // --- 4. INITIALIZATION & EVENT LISTENERS --- // Tab Listeners tabButtons.forEach((btn) => { btn.addEventListener("click", () => showTab(btn.dataset.target)); }); navButtons.forEach((btn) => { btn.addEventListener("click", () => showTab(btn.dataset.target)); }); // Config Tab Listeners if (addParameterForm) { addParameterForm.addEventListener("submit", handleAddParameter); } if (configParameterList) { configParameterList.addEventListener("click", handleRemoveConfigItem); } if (generateBtn) { generateBtn.addEventListener("click", () => renderDashboard(false)); } // PDF Button if (pdfBtn) { pdfBtn.addEventListener("click", downloadPDF); } // Dashboard Listeners if (dashboardOutput) { dashboardOutput.addEventListener('input', handleDashboardUpdate); dashboardOutput.addEventListener('change', handleDashboardUpdate); dashboardOutput.addEventListener('click', (e) => { if (e.target.dataset.action === 'remove-dash-item') { const tr = e.target.closest('tr'); parameters.splice(parameters.findIndex(p => p.id === tr.dataset.id), 1); renderDashboard(false); updateConfigListDisplay(); } }); } // Initial config list display updateConfigListDisplay(); // Initial State: Generate dashboard with samples renderDashboard(); showTab("ear-tab-dashboard"); });
Scroll to Top