/** * client-assets/js/soil-import.js * * Handles the lab spreadsheet import flow on the soil test entry page. * * Flow: * 1. User picks a file → "Analyse File" button enabled * 2. File is sent to soilImportController.php?action=parse * 3. If one sample: auto-import it * If many samples: show a sample picker table * 4. User picks a sample → sent to soilImportController.php?action=import * 5. Mapped values are filled into the soil analysis form */ (function () { 'use strict'; // ── DOM refs ────────────────────────────────────────────────────────────── const fileInput = document.getElementById('lab-file-input'); const analyseBtn = document.getElementById('lab-analyse-btn'); const importStatus = document.getElementById('import-status'); const samplePanel = document.getElementById('sample-picker-panel'); const sampleTable = document.getElementById('sample-picker-table'); const sampleTbody = sampleTable ? sampleTable.querySelector('tbody') : null; const importProgress = document.getElementById('import-progress'); if (!fileInput || !analyseBtn) return; // not on the right page // ── helpers ─────────────────────────────────────────────────────────────── function showStatus(msg, type = 'info') { if (!importStatus) return; importStatus.className = `alert alert-${type} mt-2`; importStatus.textContent = msg; importStatus.hidden = false; } function hideStatus() { if (importStatus) importStatus.hidden = true; } function setProgress(visible, text = '') { if (!importProgress) return; importProgress.hidden = !visible; if (text) importProgress.querySelector('.progress-label').textContent = text; } function buildFormData(file, action, extraFields = {}) { const fd = new FormData(); fd.append('file', file); fd.append('action', action); for (const [k, v] of Object.entries(extraFields)) { fd.append(k, v); } return fd; } async function postToController(formData) { const resp = await fetch('/controllers/soilImportController.php', { method: 'POST', body: formData, }); if (!resp.ok) { throw new Error(`Server error ${resp.status}`); } return resp.json(); } // ── form population ─────────────────────────────────────────────────────── function populateForm(fields) { let filled = 0; let skipped = 0; for (const [fieldName, value] of Object.entries(fields)) { if (value === null || value === undefined || value === '') continue; const el = document.getElementById(fieldName) || document.querySelector(`[name="${fieldName}"]`); if (!el) { skipped++; continue; } if (el.tagName === 'SELECT') { // Try to find a matching option (case-insensitive) const val = String(value).toLowerCase(); for (const opt of el.options) { if (opt.value.toLowerCase() === val || opt.text.toLowerCase() === val) { el.value = opt.value; filled++; break; } } } else { el.value = value; el.dispatchEvent(new Event('input', { bubbles: true })); filled++; } } return { filled, skipped }; } // Highlight fields that were auto-filled so the user can review them function highlightFilledFields(fields) { for (const fieldName of Object.keys(fields)) { if (fields[fieldName] === null || fields[fieldName] === '') continue; const el = document.getElementById(fieldName) || document.querySelector(`[name="${fieldName}"]`); if (el) { el.classList.add('border-success', 'bg-success-subtle'); } } } // ── sample picker ───────────────────────────────────────────────────────── function renderSamplePicker(samples, file) { if (!samplePanel || !sampleTbody) return; sampleTbody.innerHTML = ''; samples.forEach((s) => { const tr = document.createElement('tr'); tr.innerHTML = `