-
+
+
-
-
-
-
+
+ ${idx > 0 ? `
` : '
'}
+
`;
container.appendChild(div);
diff --git a/api/app/static/js/shared.js b/api/app/static/js/shared.js
index 075cc32..5486eb2 100644
--- a/api/app/static/js/shared.js
+++ b/api/app/static/js/shared.js
@@ -204,6 +204,154 @@ function renderMobileSegmented(containerId, pills, onSelect) {
});
}
+// ── Shared Quick Map Modal ────────────────────────
+let _qmOnSave = null;
+let _qmAcTimeout = null;
+
+/**
+ * Open the shared quick-map modal.
+ * @param {object} opts
+ * @param {string} opts.sku
+ * @param {string} opts.productName
+ * @param {Array} [opts.prefill] - [{codmat, cantitate, denumire}]
+ * @param {boolean}[opts.isDirect] - true if SKU=CODMAT direct
+ * @param {object} [opts.directInfo] - {codmat, denumire} for direct SKU info
+ * @param {function} opts.onSave - callback(sku, mappings) after successful save
+ */
+function openQuickMap(opts) {
+ _qmOnSave = opts.onSave || null;
+ document.getElementById('qmSku').textContent = opts.sku;
+ document.getElementById('qmProductName').textContent = opts.productName || '-';
+ document.getElementById('qmPctWarning').style.display = 'none';
+
+ const container = document.getElementById('qmCodmatLines');
+ container.innerHTML = '';
+
+ const directInfo = document.getElementById('qmDirectInfo');
+ const saveBtn = document.getElementById('qmSaveBtn');
+
+ if (opts.isDirect && opts.directInfo) {
+ if (directInfo) {
+ directInfo.innerHTML = `
SKU = CODMAT direct in nomenclator (
${esc(opts.directInfo.codmat)} — ${esc(opts.directInfo.denumire || '')}).
Poti suprascrie cu un alt CODMAT daca e necesar (ex: reambalare).`;
+ directInfo.style.display = '';
+ }
+ if (saveBtn) saveBtn.textContent = 'Suprascrie mapare';
+ addQmCodmatLine();
+ } else {
+ if (directInfo) directInfo.style.display = 'none';
+ if (saveBtn) saveBtn.textContent = 'Salveaza';
+
+ if (opts.prefill && opts.prefill.length > 0) {
+ opts.prefill.forEach(d => addQmCodmatLine({ codmat: d.codmat, cantitate: d.cantitate, denumire: d.denumire }));
+ } else {
+ addQmCodmatLine();
+ }
+ }
+
+ new bootstrap.Modal(document.getElementById('quickMapModal')).show();
+}
+
+function addQmCodmatLine(prefill) {
+ const container = document.getElementById('qmCodmatLines');
+ const idx = container.children.length;
+ const codmatVal = prefill?.codmat || '';
+ const cantVal = prefill?.cantitate || 1;
+ const denumireVal = prefill?.denumire || '';
+ const div = document.createElement('div');
+ div.className = 'qm-line';
+ div.innerHTML = `
+
+
+
+ ${idx > 0 ? `
` : '
'}
+
+
${esc(denumireVal)}
+ `;
+ container.appendChild(div);
+
+ const input = div.querySelector('.qm-codmat');
+ const dropdown = div.querySelector('.qm-ac-dropdown');
+ const selected = div.querySelector('.qm-selected');
+
+ input.addEventListener('input', () => {
+ clearTimeout(_qmAcTimeout);
+ _qmAcTimeout = setTimeout(() => _qmAutocomplete(input, dropdown, selected), 250);
+ });
+ input.addEventListener('blur', () => {
+ setTimeout(() => dropdown.classList.add('d-none'), 200);
+ });
+}
+
+async function _qmAutocomplete(input, dropdown, selectedEl) {
+ const q = input.value;
+ if (q.length < 2) { dropdown.classList.add('d-none'); return; }
+
+ try {
+ const res = await fetch(`/api/articles/search?q=${encodeURIComponent(q)}`);
+ const data = await res.json();
+ if (!data.results || data.results.length === 0) { dropdown.classList.add('d-none'); return; }
+
+ dropdown.innerHTML = data.results.map(r =>
+ `
+ ${esc(r.codmat)} — ${esc(r.denumire)}${r.um ? ` (${esc(r.um)})` : ''}
+
`
+ ).join('');
+ dropdown.classList.remove('d-none');
+ } catch { dropdown.classList.add('d-none'); }
+}
+
+function _qmSelectArticle(el, codmat, label) {
+ const line = el.closest('.qm-line');
+ line.querySelector('.qm-codmat').value = codmat;
+ line.querySelector('.qm-selected').textContent = label;
+ line.querySelector('.qm-ac-dropdown').classList.add('d-none');
+}
+
+async function saveQuickMapping() {
+ const lines = document.querySelectorAll('#qmCodmatLines .qm-line');
+ const mappings = [];
+
+ for (const line of lines) {
+ const codmat = line.querySelector('.qm-codmat').value.trim();
+ const cantitate = parseFloat(line.querySelector('.qm-cantitate').value) || 1;
+ if (!codmat) continue;
+ mappings.push({ codmat, cantitate_roa: cantitate });
+ }
+
+ if (mappings.length === 0) { alert('Selecteaza cel putin un CODMAT'); return; }
+
+ const sku = document.getElementById('qmSku').textContent;
+
+ try {
+ let res;
+ if (mappings.length === 1) {
+ res = await fetch('/api/mappings', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ sku, codmat: mappings[0].codmat, cantitate_roa: mappings[0].cantitate_roa })
+ });
+ } else {
+ res = await fetch('/api/mappings/batch', {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({ sku, mappings })
+ });
+ }
+ const data = await res.json();
+ if (data.success) {
+ bootstrap.Modal.getInstance(document.getElementById('quickMapModal')).hide();
+ if (_qmOnSave) _qmOnSave(sku, mappings);
+ } else {
+ alert('Eroare: ' + (data.error || 'Unknown'));
+ }
+ } catch (err) {
+ alert('Eroare: ' + err.message);
+ }
+}
+
// ── Dot helper ────────────────────────────────────
function statusDot(status) {
switch ((status || '').toUpperCase()) {
diff --git a/api/app/templates/base.html b/api/app/templates/base.html
index b4f5ea4..d6918a0 100644
--- a/api/app/templates/base.html
+++ b/api/app/templates/base.html
@@ -7,7 +7,7 @@
{% set rp = request.scope.get('root_path', '') %}
-
+
@@ -27,9 +27,41 @@
{% block content %}{% endblock %}
+
+
+
+
+
+
+
+ Produs:
+
+
+ CODMAT
+ Cant.
+
+
+
+
+
+
+
+
+
+
+
+
-
+
{% block scripts %}{% endblock %}