import { CATALOG } from './data.mjs'; import { evaluatePlantFit } from './model.mjs'; export function mountPlantFit(root = document) { const form = root.querySelector('#plant-fit-form'); const errorBox = root.querySelector('#plant-fit-errors'); const summary = root.querySelector('#plant-fit-summary'); const heading = root.querySelector('#plant-fit-heading'); const results = root.querySelector('#plant-fit-results'); const fallback = root.querySelector('#plant-fit-fallback'); if (!form || !errorBox || !summary || !heading || !results || !fallback || form.dataset.mounted) return; const fields = ['sunHours', 'containerGallons', 'season', 'support', 'care']; const controls = Object.fromEntries(fields.map(name => [name, form.elements.namedItem(name)])); if (fields.some(name => !controls[name])) return; const clear = message => { results.replaceChildren(); errorBox.textContent = ''; errorBox.hidden = true; fields.forEach(name => { controls[name].removeAttribute('aria-invalid'); controls[name].removeAttribute('aria-describedby'); }); summary.textContent = message; }; const element = (tag, text) => { const node = document.createElement(tag); node.textContent = text; return node; }; form.addEventListener('submit', event => { event.preventDefault(); clear(''); const result = evaluatePlantFit(Object.fromEntries(fields.map(name => [name, controls[name].value]))); if (!result.ok) { errorBox.textContent = result.errors.map(e => e.message).join(' '); errorBox.hidden = false; result.errors.forEach(e => { controls[e.field].setAttribute('aria-invalid', 'true'); controls[e.field].setAttribute('aria-describedby', 'plant-fit-errors'); }); summary.textContent = 'Correct the highlighted fields before checking plant fit.'; controls[result.errors[0].field].focus(); return; } const count = result.results.filter(r => r.status === 'meets-screen').length; summary.textContent = count ? `${count} of 4 crops meet this screen. Review the limits before choosing a plant.` : 'No crops meet every check yet. Review the changes below.'; result.results.forEach(row => { const crop = CATALOG.find(c => c.id === row.id); const article = document.createElement('article'); article.dataset.resultId = row.id; article.dataset.status = row.status; article.append(element('h3', crop.name), element('p', row.status === 'meets-screen' ? 'Meets this screen' : 'Changes or checks needed')); article.append(element('p', `Screen: ${crop.minSun}+ direct-sun hours; listed minimum ${crop.minGallons} US gallons; ${crop.season} conditions.`)); if (row.reasons.length) { const list = document.createElement('ul'); row.reasons.forEach(reason => { const li = element('li', reason.text); li.dataset.reason = reason.code; list.append(li); }); article.append(list); } else article.append(element('p', 'Selected checks passed. Confirm cultivar, root depth, drainage and local conditions before planting.')); results.append(article); }); heading.focus(); }); const invalidate = () => clear('Inputs changed. Check plant fit again for updated results.'); form.addEventListener('input', invalidate); form.addEventListener('change', invalidate); form.addEventListener('reset', () => clear('Enter your conditions, then check plant fit.')); form.dataset.mounted = 'true'; form.hidden = false; fallback.hidden = true; } mountPlantFit();