/**
* Import-mapping modal controller.
*
* openImportMappingModal({ importId, filename, fc, onResult })
*
* Populates the modal from the parsed FeatureCollection, lets the user pick
* a target type and adjust the field map, then calls onResult with one of:
*
* { action: 'cancel' } — keep as Other / view only
* { action: 'save', targetType, mapping }
* { action: 'upload', targetType, mapping }
*
* The caller is responsible for updating external_imports + the staged
* features (this module knows nothing about the DB or the map).
*
* See LUPMIS2_Import_Upload_Design.docx §3.1.
*/
import { Modal } from 'bootstrap';
import {
TARGET_TYPES,
TARGET_FIELDS,
detectTargetType,
autoMapFields,
listSourceFields,
sampleSourceValues,
} from './import-detect.js';
const els = {}; // cached DOM lookups
let modal = null;
let state = null; // { importId, filename, fc, sourceFields, mapping, targetType, onResult }
function cacheEls() {
if (els.root) return;
els.root = document.getElementById('importMappingModal');
els.filename = document.getElementById('import-modal-filename');
els.summary = document.getElementById('import-modal-summary');
els.target = document.getElementById('import-modal-target');
els.targetHint = document.getElementById('import-modal-target-hint');
els.fieldsWrap = document.getElementById('import-modal-fields-wrap');
els.tbody = document.getElementById('import-modal-fields-tbody');
els.btnSave = document.getElementById('import-modal-save');
els.btnSaveUpload = document.getElementById('import-modal-save-upload');
els.btnCancel = document.getElementById('import-modal-cancel');
// Populate the target-type dropdown once.
if (els.target && !els.target.dataset.populated) {
els.target.innerHTML = TARGET_TYPES
.map((t) => ``)
.join('');
els.target.dataset.populated = '1';
}
// Event wiring (idempotent).
if (els.target && !els.target.dataset.wired) {
els.target.dataset.wired = '1';
els.target.addEventListener('change', onTargetChange);
}
if (els.btnSave && !els.btnSave.dataset.wired) {
els.btnSave.dataset.wired = '1';
els.btnSave.addEventListener('click', () => finish('save'));
}
if (els.btnSaveUpload && !els.btnSaveUpload.dataset.wired) {
els.btnSaveUpload.dataset.wired = '1';
els.btnSaveUpload.addEventListener('click', () => finish('upload'));
}
// Cancel uses Bootstrap's data-bs-dismiss; we hook the hidden event so
// closing via × / ESC / Cancel all behave the same: a 'cancel' result.
if (els.root && !els.root.dataset.wired) {
els.root.dataset.wired = '1';
els.root.addEventListener('hidden.bs.modal', () => {
if (state?.onResult && !state._resolved) {
state._resolved = true;
state.onResult({ action: 'cancel' });
}
state = null;
});
}
}
/** Render the field-mapping table for the current targetType. */
function renderFieldsTable() {
const targetType = state.targetType;
const columns = TARGET_FIELDS[targetType] || [];
// "Other (view only)" → no fields to map.
if (targetType === 'other' || columns.length === 0) {
els.fieldsWrap.style.display = 'none';
return;
}
els.fieldsWrap.style.display = '';
// Each dropdown option shows the source field name AND a one-feature
// sample value so the user can recognise the attribute by its content,
// not just its name. Sample is computed once in state.sourceSamples; see
// sampleSourceValues() in import-detect.js.
//
// The field name is rendered with Unicode mathematical-bold characters
// (U+1D400 / U+1D41A / U+1D7CE blocks) because HTML / CSS bold doesn't
// render inside ']
.concat(state.sourceFields.map((s) =>
``))
.join('');
els.tbody.innerHTML = columns.map((col) => {
const current = state.mapping[col] || '';
// Mark the matching option as selected. The opening `