|
@@ -42,7 +42,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
|
|
renderProducts();
|
|
renderProducts();
|
|
|
getAtributeList();
|
|
getAtributeList();
|
|
|
document.getElementById('btnSubmit').addEventListener('click', submitAttributes);
|
|
document.getElementById('btnSubmit').addEventListener('click', submitAttributes);
|
|
|
- document.getElementById('btnReset').addEventListener('click', resetAll);
|
|
|
|
|
|
|
+ // document.getElementById('btnReset').addEventListener('click', resetAll);
|
|
|
// document.getElementById('btnSelectAll').addEventListener('click', () => {
|
|
// document.getElementById('btnSelectAll').addEventListener('click', () => {
|
|
|
// if (selectedIds.size === PRODUCT_BASE.length) { selectedIds.clear(); } else { selectedIds = new Set(PRODUCT_BASE.map(p => p.id)); }
|
|
// if (selectedIds.size === PRODUCT_BASE.length) { selectedIds.clear(); } else { selectedIds = new Set(PRODUCT_BASE.map(p => p.id)); }
|
|
|
// // renderProducts();
|
|
// // renderProducts();
|
|
@@ -304,17 +304,17 @@ if(p.product_type_details.length > 0){
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
// --- Render Mandatory Attributes ---
|
|
// --- Render Mandatory Attributes ---
|
|
|
- if (mandatoryAttributes.length > 0) {
|
|
|
|
|
- const manTitle = el('p', "pSelectRight mandatory-title");
|
|
|
|
|
- manTitle.innerHTML = "Mandatory Attributes:";
|
|
|
|
|
- attrContainer.appendChild(manTitle);
|
|
|
|
|
-
|
|
|
|
|
- mandatoryAttributes.forEach(attr => {
|
|
|
|
|
- const initialSelected = getSelectedAttributes(p.item_id)[attr.attribute_name] || attr.possible_values;
|
|
|
|
|
- const chipGroup = createAttributeChips(p, attr, initialSelected, true, updateProductState);
|
|
|
|
|
- attrContainer.appendChild(chipGroup);
|
|
|
|
|
- });
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ // if (mandatoryAttributes.length > 0) {
|
|
|
|
|
+ // const manTitle = el('p', "pSelectRight mandatory-title");
|
|
|
|
|
+ // manTitle.innerHTML = "Mandatory Attributes:";
|
|
|
|
|
+ // attrContainer.appendChild(manTitle);
|
|
|
|
|
+
|
|
|
|
|
+ // mandatoryAttributes.forEach(attr => {
|
|
|
|
|
+ // const initialSelected = getSelectedAttributes(p.item_id)[attr.attribute_name] || attr.possible_values;
|
|
|
|
|
+ // const chipGroup = createAttributeChips(p, attr, initialSelected, true, updateProductState);
|
|
|
|
|
+ // attrContainer.appendChild(chipGroup);
|
|
|
|
|
+ // });
|
|
|
|
|
+ // }
|
|
|
|
|
|
|
|
// --- Render Optional Attributes ---
|
|
// --- Render Optional Attributes ---
|
|
|
if (optionalAttributes.length > 0) {
|
|
if (optionalAttributes.length > 0) {
|
|
@@ -348,7 +348,7 @@ if(p.product_type_details.length > 0){
|
|
|
row.appendChild(container); row.appendChild(mid);
|
|
row.appendChild(container); row.appendChild(mid);
|
|
|
if(p.product_type_details.length > 0){
|
|
if(p.product_type_details.length > 0){
|
|
|
console.log("IN ");
|
|
console.log("IN ");
|
|
|
- row.appendChild(attrContainer); // Append the new attribute selectors container
|
|
|
|
|
|
|
+ // row.appendChild(attrContainer); // Append the new attribute selectors container
|
|
|
}
|
|
}
|
|
|
row.appendChild(right);
|
|
row.appendChild(right);
|
|
|
// if (p.mandatoryAttributes && p.mandatoryAttributes.length > 0) {
|
|
// if (p.mandatoryAttributes && p.mandatoryAttributes.length > 0) {
|
|
@@ -724,7 +724,7 @@ function renderProductsTable(items = getCurrentSlice()) {
|
|
|
const trh = document.createElement('tr');
|
|
const trh = document.createElement('tr');
|
|
|
|
|
|
|
|
// Table Headers
|
|
// Table Headers
|
|
|
- ['Select', 'Image', 'Product', 'SKU', 'Type', 'Short Description', 'Attributes'].forEach(h => {
|
|
|
|
|
|
|
+ ['Select', 'Image', 'Product', 'SKU', 'Type', 'Short Description'].forEach(h => {
|
|
|
const th = document.createElement('th'); th.textContent = h; trh.appendChild(th);
|
|
const th = document.createElement('th'); th.textContent = h; trh.appendChild(th);
|
|
|
});
|
|
});
|
|
|
thead.appendChild(trh); table.appendChild(thead);
|
|
thead.appendChild(trh); table.appendChild(thead);
|
|
@@ -757,7 +757,7 @@ function renderProductsTable(items = getCurrentSlice()) {
|
|
|
const tdName = document.createElement('td'); tdName.textContent = p.product_name || '—'; tr.appendChild(tdName);
|
|
const tdName = document.createElement('td'); tdName.textContent = p.product_name || '—'; tr.appendChild(tdName);
|
|
|
const tdSku = document.createElement('td'); tdSku.textContent = p.item_id || '—'; tr.appendChild(tdSku);
|
|
const tdSku = document.createElement('td'); tdSku.textContent = p.item_id || '—'; tr.appendChild(tdSku);
|
|
|
const tdType = document.createElement('td'); const b = document.createElement('span'); b.className = 'badge'; b.textContent = p.product_type || '—'; tdType.appendChild(b); tr.appendChild(tdType);
|
|
const tdType = document.createElement('td'); const b = document.createElement('span'); b.className = 'badge'; b.textContent = p.product_type || '—'; tdType.appendChild(b); tr.appendChild(tdType);
|
|
|
- const tdDesc = document.createElement('td'); tdDesc.textContent = p.product_short_description || ''; tr.appendChild(tdDesc);
|
|
|
|
|
|
|
+ const tdDesc = document.createElement('td'); tdDesc.innerHTML = p.product_short_description || ''; tr.appendChild(tdDesc);
|
|
|
|
|
|
|
|
// ---------------------------------------------
|
|
// ---------------------------------------------
|
|
|
// --- ATTRIBUTE SELECTION IMPLEMENTATION ---
|
|
// --- ATTRIBUTE SELECTION IMPLEMENTATION ---
|
|
@@ -770,7 +770,7 @@ function renderProductsTable(items = getCurrentSlice()) {
|
|
|
detailRow.id = `detail-row-${p.id}`;
|
|
detailRow.id = `detail-row-${p.id}`;
|
|
|
|
|
|
|
|
const detailCell = document.createElement('td');
|
|
const detailCell = document.createElement('td');
|
|
|
- detailCell.colSpan = 7; // Must span all columns
|
|
|
|
|
|
|
+ detailCell.colSpan = 6; // Must span all columns
|
|
|
|
|
|
|
|
const attrContainer = document.createElement('div');
|
|
const attrContainer = document.createElement('div');
|
|
|
attrContainer.id = `attr-container-${p.item_id}`; // Unique ID for targeting by updateProductState
|
|
attrContainer.id = `attr-container-${p.item_id}`; // Unique ID for targeting by updateProductState
|
|
@@ -792,7 +792,7 @@ function renderProductsTable(items = getCurrentSlice()) {
|
|
|
toggleButton.textContent = 'Configure';
|
|
toggleButton.textContent = 'Configure';
|
|
|
toggleButton.classList.add('btn', 'btn-sm', 'btn-info', 'attribute-toggle-btn');
|
|
toggleButton.classList.add('btn', 'btn-sm', 'btn-info', 'attribute-toggle-btn');
|
|
|
tdAttr.appendChild(toggleButton);
|
|
tdAttr.appendChild(toggleButton);
|
|
|
- tr.appendChild(tdAttr);
|
|
|
|
|
|
|
+ // tr.appendChild(tdAttr);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@@ -850,7 +850,7 @@ function renderProductsTable(items = getCurrentSlice()) {
|
|
|
} else {
|
|
} else {
|
|
|
const tr = el('tr');
|
|
const tr = el('tr');
|
|
|
const tdName = el('td');
|
|
const tdName = el('td');
|
|
|
- tdName.colSpan = 7;
|
|
|
|
|
|
|
+ tdName.colSpan = 6;
|
|
|
tdName.innerHTML = "No Products Found.";
|
|
tdName.innerHTML = "No Products Found.";
|
|
|
tr.appendChild(tdName);
|
|
tr.appendChild(tdName);
|
|
|
tbody.appendChild(tr);
|
|
tbody.appendChild(tr);
|
|
@@ -1416,10 +1416,23 @@ function renderMandatoryComparisonTable(attributes, title) {
|
|
|
* @param {string} title - The title for the table section.
|
|
* @param {string} title - The title for the table section.
|
|
|
* @returns {HTMLElement} A div containing the table.
|
|
* @returns {HTMLElement} A div containing the table.
|
|
|
*/
|
|
*/
|
|
|
-function renderAttributesAsTable(attributes, title) {
|
|
|
|
|
|
|
+function renderAttributesAsTable(attributes, title, mandatoryData = null) {
|
|
|
const section = el('div', 'attribute-section');
|
|
const section = el('div', 'attribute-section');
|
|
|
let attributeEntries = [];
|
|
let attributeEntries = [];
|
|
|
|
|
|
|
|
|
|
+ // --- STEP 1: Create a Set of all Mandatory Original Values (Normalized) ---
|
|
|
|
|
+ // A Set is used for fast lookups. Values are normalized (trimmed, lowercase)
|
|
|
|
|
+ const mandatoryOriginalValuesSet = new Set();
|
|
|
|
|
+ if (mandatoryData) {
|
|
|
|
|
+ Object.values(mandatoryData).forEach(attrArray => {
|
|
|
|
|
+ const originalValue = attrArray[0]?.original_value;
|
|
|
|
|
+ if (originalValue) {
|
|
|
|
|
+ mandatoryOriginalValuesSet.add(String(originalValue).trim().toLowerCase());
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Helper to extract attribute entries consistently
|
|
|
const processAttribute = (key, values) => {
|
|
const processAttribute = (key, values) => {
|
|
|
const valuesArray = Array.isArray(values) ? values : [values];
|
|
const valuesArray = Array.isArray(values) ? values : [values];
|
|
|
valuesArray.forEach(v => {
|
|
valuesArray.forEach(v => {
|
|
@@ -1431,19 +1444,29 @@ function renderAttributesAsTable(attributes, title) {
|
|
|
});
|
|
});
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // Iterate through attributes (OCR/Visual/Additional) and flatten them
|
|
|
Object.keys(attributes).forEach(key => {
|
|
Object.keys(attributes).forEach(key => {
|
|
|
const attribute = attributes[key];
|
|
const attribute = attributes[key];
|
|
|
|
|
|
|
|
if (Array.isArray(attribute)) {
|
|
if (Array.isArray(attribute)) {
|
|
|
processAttribute(key, attribute);
|
|
processAttribute(key, attribute);
|
|
|
} else if (typeof attribute === 'object' && attribute !== null) {
|
|
} else if (typeof attribute === 'object' && attribute !== null) {
|
|
|
- Object.keys(attribute).forEach(subKey => {
|
|
|
|
|
- const subAttribute = attribute[subKey];
|
|
|
|
|
- if (Array.isArray(subAttribute)) {
|
|
|
|
|
- // Combines parent key (e.g., 'size') and sub-key (e.g., 'waist_size')
|
|
|
|
|
- processAttribute(`${key} (${subKey.replace(/_/g, ' ')})`, subAttribute);
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
|
|
+ // Handle simple { "key": { "value": "X", "source": "Y" } } structure
|
|
|
|
|
+ if (attribute.value !== undefined) {
|
|
|
|
|
+ attributeEntries.push({
|
|
|
|
|
+ name: key,
|
|
|
|
|
+ value: attribute.value,
|
|
|
|
|
+ source: attribute.source || 'N/A'
|
|
|
|
|
+ });
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // Handle nested objects
|
|
|
|
|
+ Object.keys(attribute).forEach(subKey => {
|
|
|
|
|
+ const subAttribute = attribute[subKey];
|
|
|
|
|
+ if (Array.isArray(subAttribute)) {
|
|
|
|
|
+ processAttribute(`${key} (${subKey.replace(/_/g, ' ')})`, subAttribute);
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
|
|
|
|
|
@@ -1458,7 +1481,6 @@ function renderAttributesAsTable(attributes, title) {
|
|
|
return section;
|
|
return section;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // --- SCROLL WRAPPER ADDITION ---
|
|
|
|
|
const scrollWrapper = el('div', 'attribute-scroll-wrapper');
|
|
const scrollWrapper = el('div', 'attribute-scroll-wrapper');
|
|
|
const table = el('table', 'attribute-detail-table');
|
|
const table = el('table', 'attribute-detail-table');
|
|
|
|
|
|
|
@@ -1478,34 +1500,236 @@ function renderAttributesAsTable(attributes, title) {
|
|
|
attributeEntries.forEach(attr => {
|
|
attributeEntries.forEach(attr => {
|
|
|
const row = el('tr');
|
|
const row = el('tr');
|
|
|
|
|
|
|
|
|
|
+ // --- CORE COMPARISON LOGIC (Global Check) ---
|
|
|
|
|
+ let colorClass = '';
|
|
|
|
|
+ const hasMandatoryBaseline = mandatoryOriginalValuesSet.size > 0;
|
|
|
|
|
+
|
|
|
|
|
+ if (hasMandatoryBaseline) {
|
|
|
|
|
+ // Normalize the current attribute's value
|
|
|
|
|
+ const currentValueNormalized = String(attr.value).trim().toLowerCase();
|
|
|
|
|
+
|
|
|
|
|
+ // Check if the current value exists ANYWHERE in the mandatory original values set
|
|
|
|
|
+ if (mandatoryOriginalValuesSet.has(currentValueNormalized)) {
|
|
|
|
|
+ colorClass = 'green-text'; // Found a match in the global mandatory set
|
|
|
|
|
+ } else {
|
|
|
|
|
+ colorClass = 'red-text'; // Did NOT find a match
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // --- END CORE COMPARISON LOGIC ---
|
|
|
|
|
+
|
|
|
const nameTd = el('td', 'attribute-name');
|
|
const nameTd = el('td', 'attribute-name');
|
|
|
nameTd.textContent = formatString(attr.name).replace(/_/g, ' ');
|
|
nameTd.textContent = formatString(attr.name).replace(/_/g, ' ');
|
|
|
row.appendChild(nameTd);
|
|
row.appendChild(nameTd);
|
|
|
|
|
|
|
|
const valueTd = el('td', 'attribute-value');
|
|
const valueTd = el('td', 'attribute-value');
|
|
|
- const displayValue = Array.isArray(attr.value)
|
|
|
|
|
- ? (attr.value.map(v => formatString(v.value))).join(', ') +'('+ formatString(attr.source)+ ')'
|
|
|
|
|
- : formatString(attr.value) +'('+ formatString(attr.source)+ ')';
|
|
|
|
|
|
|
+ const displayValue = formatString(attr.value) + ' (' + formatString(attr.source) + ')';
|
|
|
|
|
+
|
|
|
valueTd.textContent = displayValue || 'N/A';
|
|
valueTd.textContent = displayValue || 'N/A';
|
|
|
- row.appendChild(valueTd);
|
|
|
|
|
-
|
|
|
|
|
- // const sourceTd = el('td', 'attribute-source');
|
|
|
|
|
- // sourceTd.textContent = attr.source || 'Unknown';
|
|
|
|
|
- // row.appendChild(sourceTd);
|
|
|
|
|
|
|
+
|
|
|
|
|
+ // Apply the determined color class to the value cell
|
|
|
|
|
+ if (colorClass) {
|
|
|
|
|
+ valueTd.classList.add(colorClass);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ row.appendChild(valueTd);
|
|
|
tbody.appendChild(row);
|
|
tbody.appendChild(row);
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
table.appendChild(tbody);
|
|
table.appendChild(tbody);
|
|
|
- scrollWrapper.appendChild(table); // Append table to wrapper
|
|
|
|
|
- section.appendChild(scrollWrapper); // Append wrapper to section
|
|
|
|
|
|
|
+ scrollWrapper.appendChild(table);
|
|
|
|
|
+ section.appendChild(scrollWrapper);
|
|
|
return section;
|
|
return section;
|
|
|
}
|
|
}
|
|
|
|
|
+// function renderAttributesAsTable(attributes, title, mandatoryData = null) {
|
|
|
|
|
+// const section = el('div', 'attribute-section');
|
|
|
|
|
+// let attributeEntries = [];
|
|
|
|
|
+
|
|
|
|
|
+// const processAttribute = (key, values) => {
|
|
|
|
|
+// const valuesArray = Array.isArray(values) ? values : [values];
|
|
|
|
|
+// valuesArray.forEach(v => {
|
|
|
|
|
+// attributeEntries.push({
|
|
|
|
|
+// name: key,
|
|
|
|
|
+// value: v.value,
|
|
|
|
|
+// source: v.source || 'N/A'
|
|
|
|
|
+// });
|
|
|
|
|
+// });
|
|
|
|
|
+// };
|
|
|
|
|
+
|
|
|
|
|
+// Object.keys(attributes).forEach(key => {
|
|
|
|
|
+// const attribute = attributes[key];
|
|
|
|
|
+
|
|
|
|
|
+// if (Array.isArray(attribute)) {
|
|
|
|
|
+// processAttribute(key, attribute);
|
|
|
|
|
+// } else if (typeof attribute === 'object' && attribute !== null) {
|
|
|
|
|
+// Object.keys(attribute).forEach(subKey => {
|
|
|
|
|
+// const subAttribute = attribute[subKey];
|
|
|
|
|
+// if (Array.isArray(subAttribute)) {
|
|
|
|
|
+// // Combines parent key (e.g., 'size') and sub-key (e.g., 'waist_size')
|
|
|
|
|
+// processAttribute(`${key} (${subKey.replace(/_/g, ' ')})`, subAttribute);
|
|
|
|
|
+// }
|
|
|
|
|
+// });
|
|
|
|
|
+// }
|
|
|
|
|
+// });
|
|
|
|
|
+
|
|
|
|
|
+// const titleEl = el('div', 'section-title');
|
|
|
|
|
+// titleEl.innerHTML = `<h3>${title} (${attributeEntries.length})</h3>`;
|
|
|
|
|
+// section.appendChild(titleEl);
|
|
|
|
|
+
|
|
|
|
|
+// if (attributeEntries.length === 0) {
|
|
|
|
|
+// const msg = el('p', 'no-attributes-message');
|
|
|
|
|
+// msg.textContent = `No ${title.toLowerCase()} found.`;
|
|
|
|
|
+// section.appendChild(msg);
|
|
|
|
|
+// return section;
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
|
|
+// // --- SCROLL WRAPPER ADDITION ---
|
|
|
|
|
+// const scrollWrapper = el('div', 'attribute-scroll-wrapper');
|
|
|
|
|
+// const table = el('table', 'attribute-detail-table');
|
|
|
|
|
+
|
|
|
|
|
+// const thead = el('thead');
|
|
|
|
|
+// const headerRow = el('tr');
|
|
|
|
|
+
|
|
|
|
|
+// ['Attribute Name', 'Value'].forEach(text => {
|
|
|
|
|
+// const th = el('th');
|
|
|
|
|
+// th.textContent = text;
|
|
|
|
|
+// headerRow.appendChild(th);
|
|
|
|
|
+// });
|
|
|
|
|
+// thead.appendChild(headerRow);
|
|
|
|
|
+// table.appendChild(thead);
|
|
|
|
|
+
|
|
|
|
|
+// const tbody = el('tbody');
|
|
|
|
|
+
|
|
|
|
|
+// attributeEntries.forEach(attr => {
|
|
|
|
|
+// const row = el('tr');
|
|
|
|
|
+
|
|
|
|
|
+// const nameTd = el('td', 'attribute-name');
|
|
|
|
|
+// nameTd.textContent = formatString(attr.name).replace(/_/g, ' ');
|
|
|
|
|
+// row.appendChild(nameTd);
|
|
|
|
|
+
|
|
|
|
|
+// const valueTd = el('td', 'attribute-value');
|
|
|
|
|
+// const displayValue = Array.isArray(attr.value)
|
|
|
|
|
+// ? (attr.value.map(v => formatString(v.value))).join(', ') +'('+ formatString(attr.source)+ ')'
|
|
|
|
|
+// : formatString(attr.value) +'('+ formatString(attr.source)+ ')';
|
|
|
|
|
+// valueTd.textContent = displayValue || 'N/A';
|
|
|
|
|
+// row.appendChild(valueTd);
|
|
|
|
|
+
|
|
|
|
|
+// // const sourceTd = el('td', 'attribute-source');
|
|
|
|
|
+// // sourceTd.textContent = attr.source || 'Unknown';
|
|
|
|
|
+// // row.appendChild(sourceTd);
|
|
|
|
|
+
|
|
|
|
|
+// tbody.appendChild(row);
|
|
|
|
|
+// });
|
|
|
|
|
+
|
|
|
|
|
+// table.appendChild(tbody);
|
|
|
|
|
+// scrollWrapper.appendChild(table); // Append table to wrapper
|
|
|
|
|
+// section.appendChild(scrollWrapper); // Append wrapper to section
|
|
|
|
|
+// return section;
|
|
|
|
|
+// }
|
|
|
|
|
|
|
|
// ------------------------------------------------------------------
|
|
// ------------------------------------------------------------------
|
|
|
// --- 3. MAIN RENDER FUNCTION (REPLACEMENT) ---
|
|
// --- 3. MAIN RENDER FUNCTION (REPLACEMENT) ---
|
|
|
// ------------------------------------------------------------------
|
|
// ------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
+// function renderInlineForTable() {
|
|
|
|
|
+// const api = API_RESPONSE_AI;
|
|
|
|
|
+// const table = $('#tableContainer');
|
|
|
|
|
+// if (!table) return;
|
|
|
|
|
+
|
|
|
|
|
+// // Remove existing detail rows
|
|
|
|
|
+// table.querySelectorAll('tr.detail-row').forEach(r => r.remove());
|
|
|
|
|
+
|
|
|
|
|
+// PRODUCT_BASE.forEach((p, idx) => {
|
|
|
|
|
+// if (!isProductSelected(p.item_id)) return;
|
|
|
|
|
+
|
|
|
|
|
+// const res = findApiResultForProduct(p, idx, api);
|
|
|
|
|
+
|
|
|
|
|
+// const tbody = table.querySelector('tbody');
|
|
|
|
|
+// const baseRow = tbody ? tbody.querySelector(`#row-${p.id}`) : null;
|
|
|
|
|
+// if (!baseRow) return;
|
|
|
|
|
+
|
|
|
|
|
+// // --- Detail Row Construction ---
|
|
|
|
|
+// const detail = el('tr', 'detail-row');
|
|
|
|
|
+// // td.colSpan must match the number of columns in your main table
|
|
|
|
|
+// const td = el('td'); td.colSpan = 7;
|
|
|
|
|
+// const content = el('div', 'detail-content-tables');
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+// // // 1. MANDATORY Attributes Table (NOW USES CARD COMPARISON)
|
|
|
|
|
+// // const mandatorySection = renderMandatoryComparisonCards( // <-- NEW FUNCTION NAME
|
|
|
|
|
+// // res?.mandatory || {},
|
|
|
|
|
+// // 'Mandatory Attributes Comparison'
|
|
|
|
|
+// // );
|
|
|
|
|
+// // content.appendChild(mandatorySection);
|
|
|
|
|
+
|
|
|
|
|
+// // // 2. COMBINED Attributes Section (Additional, OCR, Visuals) - REMAINS THE SAME
|
|
|
|
|
+// // content.appendChild(el('hr', 'section-separator'));
|
|
|
|
|
+
|
|
|
|
|
+// // 1. MANDATORY Attributes Table (USES COMPARISON FUNCTION)
|
|
|
|
|
+// const mandatoryTable = renderMandatoryComparisonTable(
|
|
|
|
|
+// res?.mandatory || {},
|
|
|
|
|
+// 'Mandatory Attributes Comparison'
|
|
|
|
|
+// );
|
|
|
|
|
+// content.appendChild(mandatoryTable);
|
|
|
|
|
+
|
|
|
|
|
+// // 2. COMBINED Attributes Section (Additional, OCR, Visuals)
|
|
|
|
|
+// content.appendChild(el('hr', 'section-separator'));
|
|
|
|
|
+
|
|
|
|
|
+// const combinedTitle = el('div', 'section-title');
|
|
|
|
|
+// combinedTitle.innerHTML = '<h2>Additional & AI-Driven Attributes</h2>';
|
|
|
|
|
+// content.appendChild(combinedTitle);
|
|
|
|
|
+
|
|
|
|
|
+// const combinedAttributesContainer = el('div', 'combined-attributes-container');
|
|
|
|
|
+
|
|
|
|
|
+// // Use the general renderer for these sections
|
|
|
|
|
+// const additionalTable = renderAttributesAsTable(
|
|
|
|
|
+// res?.additional || {},
|
|
|
|
|
+// 'Additional Attributes'
|
|
|
|
|
+// );
|
|
|
|
|
+
|
|
|
|
|
+// const ocrTable = renderAttributesAsTable(
|
|
|
|
|
+// res?.ocr_results?.extracted_attributes || {},
|
|
|
|
|
+// 'OCR Results'
|
|
|
|
|
+// );
|
|
|
|
|
+
|
|
|
|
|
+// const visualsTable = renderAttributesAsTable(
|
|
|
|
|
+// res?.visual_results?.visual_attributes || {},
|
|
|
|
|
+// 'Visual Results'
|
|
|
|
|
+// );
|
|
|
|
|
+
|
|
|
|
|
+// // Append all sections to the combined container
|
|
|
|
|
+// combinedAttributesContainer.appendChild(additionalTable);
|
|
|
|
|
+// combinedAttributesContainer.appendChild(ocrTable);
|
|
|
|
|
+// combinedAttributesContainer.appendChild(visualsTable);
|
|
|
|
|
+// content.appendChild(combinedAttributesContainer);
|
|
|
|
|
+
|
|
|
|
|
+// // --- Summary Counts ---
|
|
|
|
|
+// const mandCount = Object.keys(res?.mandatory || {}).length;
|
|
|
|
|
+// const addCount = Object.keys(res?.additional || {}).length;
|
|
|
|
|
+// const ocrExtractedCount = Object.keys(res?.ocr_results?.extracted_attributes || {}).length;
|
|
|
|
|
+// const visualExtractedCount = Object.keys(res?.visual_results?.visual_attributes || {}).length;
|
|
|
|
|
+
|
|
|
|
|
+// const counts = el('div', 'attribute-summary-pills');
|
|
|
|
|
+// const c1 = el('span', 'pill primary'); c1.textContent = `Mandatory: ${mandCount}`;
|
|
|
|
|
+// const c2 = el('span', 'pill secondary'); c2.textContent = `Additional: ${addCount}`;
|
|
|
|
|
+// const c3 = el('span', 'pill secondary'); c3.textContent = `OCR Keys: ${ocrExtractedCount}`;
|
|
|
|
|
+// const c4 = el('span', 'pill secondary'); c4.textContent = `Visual Keys: ${visualExtractedCount}`;
|
|
|
|
|
+// counts.appendChild(c1); counts.appendChild(c2); counts.appendChild(c3); counts.appendChild(c4);
|
|
|
|
|
+// content.appendChild(counts);
|
|
|
|
|
+
|
|
|
|
|
+// // Final assembly and insertion
|
|
|
|
|
+// td.appendChild(content);
|
|
|
|
|
+// detail.appendChild(td);
|
|
|
|
|
+// baseRow.insertAdjacentElement('afterend', detail);
|
|
|
|
|
+// });
|
|
|
|
|
+
|
|
|
|
|
+// // Update summary statistics
|
|
|
|
|
+// $('#statTotal').textContent = api.total_products ?? 0;
|
|
|
|
|
+// $('#statOk').textContent = api.successful ?? 0;
|
|
|
|
|
+// $('#statKo').textContent = api.failed ?? 0;
|
|
|
|
|
+// const apiSummary = $('#api-summary');
|
|
|
|
|
+// if (apiSummary) apiSummary.style.display = 'block';
|
|
|
|
|
+// }
|
|
|
|
|
+
|
|
|
function renderInlineForTable() {
|
|
function renderInlineForTable() {
|
|
|
const api = API_RESPONSE_AI;
|
|
const api = API_RESPONSE_AI;
|
|
|
const table = $('#tableContainer');
|
|
const table = $('#tableContainer');
|
|
@@ -1526,63 +1750,76 @@ function renderInlineForTable() {
|
|
|
// --- Detail Row Construction ---
|
|
// --- Detail Row Construction ---
|
|
|
const detail = el('tr', 'detail-row');
|
|
const detail = el('tr', 'detail-row');
|
|
|
// td.colSpan must match the number of columns in your main table
|
|
// td.colSpan must match the number of columns in your main table
|
|
|
- const td = el('td'); td.colSpan = 7;
|
|
|
|
|
|
|
+ const td = el('td'); td.colSpan = 6;
|
|
|
const content = el('div', 'detail-content-tables');
|
|
const content = el('div', 'detail-content-tables');
|
|
|
|
|
|
|
|
-
|
|
|
|
|
- // // 1. MANDATORY Attributes Table (NOW USES CARD COMPARISON)
|
|
|
|
|
- // const mandatorySection = renderMandatoryComparisonCards( // <-- NEW FUNCTION NAME
|
|
|
|
|
- // res?.mandatory || {},
|
|
|
|
|
- // 'Mandatory Attributes Comparison'
|
|
|
|
|
- // );
|
|
|
|
|
- // content.appendChild(mandatorySection);
|
|
|
|
|
-
|
|
|
|
|
- // // 2. COMBINED Attributes Section (Additional, OCR, Visuals) - REMAINS THE SAME
|
|
|
|
|
- // content.appendChild(el('hr', 'section-separator'));
|
|
|
|
|
-
|
|
|
|
|
- // 1. MANDATORY Attributes Table (USES COMPARISON FUNCTION)
|
|
|
|
|
- const mandatoryTable = renderMandatoryComparisonTable(
|
|
|
|
|
- res?.mandatory || {},
|
|
|
|
|
- 'Mandatory Attributes Comparison'
|
|
|
|
|
- );
|
|
|
|
|
- content.appendChild(mandatoryTable);
|
|
|
|
|
|
|
+ // 1. MANDATORY Attributes Table
|
|
|
|
|
+ // 🚨 Note: The color check (Requirement #2) must be implemented inside renderMandatoryComparisonTable.
|
|
|
|
|
+ const mandatoryData = res?.mandatory || {};
|
|
|
|
|
+ if (Object.keys(mandatoryData).length > 0) {
|
|
|
|
|
+ const mandatoryTable = renderMandatoryComparisonTable(
|
|
|
|
|
+ mandatoryData,
|
|
|
|
|
+ 'Mandatory Attributes Comparison'
|
|
|
|
|
+ );
|
|
|
|
|
+ content.appendChild(mandatoryTable);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// 2. COMBINED Attributes Section (Additional, OCR, Visuals)
|
|
// 2. COMBINED Attributes Section (Additional, OCR, Visuals)
|
|
|
- content.appendChild(el('hr', 'section-separator'));
|
|
|
|
|
-
|
|
|
|
|
- const combinedTitle = el('div', 'section-title');
|
|
|
|
|
- combinedTitle.innerHTML = '<h2>Additional & AI-Driven Attributes</h2>';
|
|
|
|
|
- content.appendChild(combinedTitle);
|
|
|
|
|
|
|
+ const additionalData = res?.additional || {};
|
|
|
|
|
+ const ocrData = res?.ocr_results?.extracted_attributes || {};
|
|
|
|
|
+ const visualsData = res?.visual_results?.visual_attributes || {};
|
|
|
|
|
|
|
|
- const combinedAttributesContainer = el('div', 'combined-attributes-container');
|
|
|
|
|
-
|
|
|
|
|
- // Use the general renderer for these sections
|
|
|
|
|
- const additionalTable = renderAttributesAsTable(
|
|
|
|
|
- res?.additional || {},
|
|
|
|
|
- 'Additional Attributes'
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- const ocrTable = renderAttributesAsTable(
|
|
|
|
|
- res?.ocr_results?.extracted_attributes || {},
|
|
|
|
|
- 'OCR Results'
|
|
|
|
|
- );
|
|
|
|
|
-
|
|
|
|
|
- const visualsTable = renderAttributesAsTable(
|
|
|
|
|
- res?.visual_results?.visual_attributes || {},
|
|
|
|
|
- 'Visual Results'
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ const hasCombinedData = Object.keys(additionalData).length > 0 ||
|
|
|
|
|
+ Object.keys(ocrData).length > 0 ||
|
|
|
|
|
+ Object.keys(visualsData).length > 0;
|
|
|
|
|
|
|
|
- // Append all sections to the combined container
|
|
|
|
|
- combinedAttributesContainer.appendChild(additionalTable);
|
|
|
|
|
- combinedAttributesContainer.appendChild(ocrTable);
|
|
|
|
|
- combinedAttributesContainer.appendChild(visualsTable);
|
|
|
|
|
- content.appendChild(combinedAttributesContainer);
|
|
|
|
|
|
|
+ if (hasCombinedData) {
|
|
|
|
|
+ content.appendChild(el('hr', 'section-separator'));
|
|
|
|
|
+
|
|
|
|
|
+ const combinedTitle = el('div', 'section-title');
|
|
|
|
|
+ combinedTitle.innerHTML = '<h2>Additional & AI-Driven Attributes</h2>';
|
|
|
|
|
+ content.appendChild(combinedTitle);
|
|
|
|
|
+
|
|
|
|
|
+ const combinedAttributesContainer = el('div', 'combined-attributes-container');
|
|
|
|
|
+
|
|
|
|
|
+ // Render Additional Table (Conditional based on data existence)
|
|
|
|
|
+ if (Object.keys(additionalData).length > 0) {
|
|
|
|
|
+ const additionalTable = renderAttributesAsTable(
|
|
|
|
|
+ additionalData,
|
|
|
|
|
+ 'Additional Attributes',
|
|
|
|
|
+ mandatoryData
|
|
|
|
|
+ );
|
|
|
|
|
+ combinedAttributesContainer.appendChild(additionalTable);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Render OCR Table (Conditional based on data existence)
|
|
|
|
|
+ if (Object.keys(ocrData).length > 0) {
|
|
|
|
|
+ const ocrTable = renderAttributesAsTable(
|
|
|
|
|
+ ocrData,
|
|
|
|
|
+ 'OCR Results',
|
|
|
|
|
+ mandatoryData
|
|
|
|
|
+ );
|
|
|
|
|
+ combinedAttributesContainer.appendChild(ocrTable);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Render Visuals Table (Conditional based on data existence)
|
|
|
|
|
+ if (Object.keys(visualsData).length > 0) {
|
|
|
|
|
+ const visualsTable = renderAttributesAsTable(
|
|
|
|
|
+ visualsData,
|
|
|
|
|
+ 'Visual Results',
|
|
|
|
|
+ mandatoryData
|
|
|
|
|
+ );
|
|
|
|
|
+ combinedAttributesContainer.appendChild(visualsTable);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ content.appendChild(combinedAttributesContainer);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
// --- Summary Counts ---
|
|
// --- Summary Counts ---
|
|
|
- const mandCount = Object.keys(res?.mandatory || {}).length;
|
|
|
|
|
- const addCount = Object.keys(res?.additional || {}).length;
|
|
|
|
|
- const ocrExtractedCount = Object.keys(res?.ocr_results?.extracted_attributes || {}).length;
|
|
|
|
|
- const visualExtractedCount = Object.keys(res?.visual_results?.visual_attributes || {}).length;
|
|
|
|
|
|
|
+ const mandCount = Object.keys(mandatoryData).length;
|
|
|
|
|
+ const addCount = Object.keys(additionalData).length;
|
|
|
|
|
+ const ocrExtractedCount = Object.keys(ocrData).length;
|
|
|
|
|
+ const visualExtractedCount = Object.keys(visualsData).length;
|
|
|
|
|
|
|
|
const counts = el('div', 'attribute-summary-pills');
|
|
const counts = el('div', 'attribute-summary-pills');
|
|
|
const c1 = el('span', 'pill primary'); c1.textContent = `Mandatory: ${mandCount}`;
|
|
const c1 = el('span', 'pill primary'); c1.textContent = `Mandatory: ${mandCount}`;
|
|
@@ -1685,7 +1922,7 @@ function submitAttributes() {
|
|
|
"extract_additional": extractAdditional,
|
|
"extract_additional": extractAdditional,
|
|
|
"process_image": processImage,
|
|
"process_image": processImage,
|
|
|
"multiple": selectedValues,
|
|
"multiple": selectedValues,
|
|
|
- "threshold_abs": threshold, // Lower threshold to be more permissive
|
|
|
|
|
|
|
+ "threshold_abs": 0.6, // Lower threshold to be more permissive
|
|
|
// "margin": 0.3, // Larger margin to include more candidates
|
|
// "margin": 0.3, // Larger margin to include more candidates
|
|
|
// "use_adaptive_margin": true,
|
|
// "use_adaptive_margin": true,
|
|
|
// "use_semantic_clustering": true
|
|
// "use_semantic_clustering": true
|
|
@@ -1726,9 +1963,9 @@ function resetAll() {
|
|
|
const thresholdInput = document.getElementById('thresholdRange');
|
|
const thresholdInput = document.getElementById('thresholdRange');
|
|
|
const thresholdDisplay = document.getElementById('thresholdValue');
|
|
const thresholdDisplay = document.getElementById('thresholdValue');
|
|
|
|
|
|
|
|
- thresholdInput.value = '0.5'; // or any default value you prefer
|
|
|
|
|
|
|
+ thresholdInput.value = '0.65'; // or any default value you prefer
|
|
|
if (thresholdDisplay) {
|
|
if (thresholdDisplay) {
|
|
|
- thresholdDisplay.textContent = '0.5';
|
|
|
|
|
|
|
+ thresholdDisplay.textContent = '0.65';
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|