diff --git a/meta_creator/templates/meta_creator/showdata.html b/meta_creator/templates/meta_creator/showdata.html index a59531d..cf03150 100644 --- a/meta_creator/templates/meta_creator/showdata.html +++ b/meta_creator/templates/meta_creator/showdata.html @@ -499,7 +499,7 @@

Extra Hints!

{% endfor %} - + {% for col, value in specific_types.items %} {% if value == 'tagging' or value == 'tagging_autocomplete' %} diff --git a/static/foundation/css/foundation.css b/static/foundation/css/foundation.css index 0b346c2..a11b0be 100644 --- a/static/foundation/css/foundation.css +++ b/static/foundation/css/foundation.css @@ -656,9 +656,12 @@ tr.add-row-controls td { box-sizing: border-box; padding: 0.5em; vertical-align: middle; - background: #f9f9f9; /* Optional: subtle highlight */ + background: #dddddd; /* Optional: subtle highlight */ +} +.disabled { + cursor: not-allowed; + pointer-events: none; } - /* For the table elements in the add-row-control row*/ tr.add-row-controls td { /* Remove any display:block/flex if present */ diff --git a/static/foundation/js/vendor/table-utils.js b/static/foundation/js/vendor/table-utils.js index e5d003b..38975bf 100644 --- a/static/foundation/js/vendor/table-utils.js +++ b/static/foundation/js/vendor/table-utils.js @@ -259,9 +259,6 @@ export function setupTables() { // If neither condition is satisfied → stop row creation if (!condition1 && !condition2) { - // alert( - // "Please fill either: Identifier + Given Name + Family Name OR Given Name + Family Name + Email." - // ); showToast( "Please fill either: Identifier + Given Name + Family Name OR Given Name + Family Name + Email.", "error", @@ -276,7 +273,6 @@ export function setupTables() { (tr) => !tr.classList.contains("add-row-controls") && tr.querySelector("td"), ); - console.log(existingRows); // Define which columns determine uniqueness const columnsToCheck = ["identifier", "givenName", "familyName", "email"]; @@ -305,7 +301,6 @@ export function setupTables() { const keysToCompare = Object.keys(newRowData).filter( (k) => newRowData[k] !== "", ); - console.log(keysToCompare); if (keysToCompare.length > 0) { let isDuplicate = false; @@ -345,6 +340,52 @@ export function setupTables() { return; // Stop adding the new row } } + // Special validation for copyrightHolderTable + if (table.id === "copyrightHolderTable") { + const legalName = getInputVal("legalName"); + const identifier = getInputVal("identifier"); + + const existingRows = Array.from( + table.querySelectorAll("tbody tr"), + ).filter( + (tr) => + !tr.classList.contains("add-row-controls") && + tr.querySelector("td"), + ); + + const headers = Array.from(table.querySelectorAll("thead th")).map( + (th) => th.getAttribute("data-col"), + ); + + const legalNameIndex = headers.indexOf("legalName"); + const identifierIndex = headers.indexOf("identifier"); + + // If identifier is empty → check duplicate legalName with empty identifier + if (!identifier) { + const duplicateExists = existingRows.some((row) => { + const cells = row.querySelectorAll("td"); + + const existingLegalName = + cells[legalNameIndex]?.textContent.trim().toLowerCase() || ""; + + const existingIdentifier = + cells[identifierIndex]?.textContent.trim().toLowerCase() || ""; + + return ( + existingLegalName === legalName.toLowerCase() && + !existingIdentifier + ); + }); + + if (duplicateExists) { + showToast( + "Same Legal Name without Identifier already exists.", + "error", + ); + return; + } + } + } // Create new row const newRow = document.createElement("tr"); @@ -554,25 +595,6 @@ export function setupTables() { // functionanilties within all auto-property-tables document.querySelectorAll("table.auto-property").forEach(function (table) { table.addEventListener("click", function (e) { - // Delete rows - // if (e.target.classList.contains("delete-row-btn")) { - // const actionCell = e.target.closest(".action"); - // console.log(actionCell); - // const confirmBox = actionCell.querySelector(".delete-row"); - // confirmBox.style.display = ""; - // const row = e.target.closest("tr"); - - // if (row) { - // row.remove(); - // // Update the hidden input - // const tableId = table.id; - // if (tableId && tableId.endsWith("Table")) { - // const key = tableId.replace(/Table$/, ""); - // updateTableHiddenInput(key); - // } - // } - // } - // Update other fields // Only allow editing on that is not the last column (delete icon) const cell = e.target.closest("td"); @@ -598,7 +620,60 @@ export function setupTables() { // Save on blur or Enter function save() { - cell.textContent = input.value; + cell.textContent = input.value.trim(); + + // Only validate for copyright holder table + if (table.id === "copyrightHolderTable") { + const rows = table.querySelectorAll("tbody tr"); + + const legalName = row.children[0].textContent.trim(); + const identifier = row.children[1].textContent.trim(); + + // Both fields empty + if (legalName === "" && identifier === "") { + showToast( + "Both Identifier and Legal Name cannot be empty.", + "error", + ); + cell.textContent = oldValue; + return; + } + + let duplicate = false; + let identifierDuplicate = false; + + rows.forEach((r) => { + if (r === row) return; + + const rLegalName = r.children[0].textContent.trim(); + const rIdentifier = r.children[1].textContent.trim(); + + // Check identifier uniqueness + if (rIdentifier === identifier && identifier !== "") { + identifierDuplicate = true; + } + + // Check combination duplicate + if (rLegalName === legalName && rIdentifier === identifier) { + duplicate = true; + } + }); + + if (identifierDuplicate) { + showToast("Identifier already exists.", "error"); + cell.textContent = oldValue; + return; + } + + if (duplicate) { + showToast( + "A record with the same Legal Name and Identifier already exists.", + "error", + ); + cell.textContent = oldValue; + return; + } + } // Update the hidden input for this table const tableId = table.id; if (tableId && tableId.endsWith("Table")) { @@ -754,9 +829,11 @@ export function setupTables() { const tags = emailCell.querySelectorAll(".tag"); if (tags.length > 0) { - emails = Array.from(tags).map((t) => t.dataset.tag); + emails = Array.from(tags).map((t) => + (t.dataset.tag || "").trim().toLowerCase(), + ); } else if (emailCell.textContent.trim() !== "") { - emails = [emailCell.textContent.trim()]; + emails = [emailCell.textContent.trim().toLowerCase()]; } } @@ -800,7 +877,14 @@ export function setupTables() { Object.values(grouped).forEach((group) => { if (group.length > 1) { const mainRow = group[0].row; - const allEmails = [...new Set(group.flatMap((g) => g.emails))]; + const allEmails = [ + ...new Set( + group + .flatMap((g) => g.emails) + .filter(Boolean) + .map((e) => e.trim().toLowerCase()), + ), + ]; // 🔸 Aggregate identifier + decide if we can merge let mergedIdentifier = ""; @@ -1015,71 +1099,6 @@ export function initializeTableTaggingCells() { } const col = cell.getAttribute("data-col"); - const colType = cell.getAttribute("data-coltype"); - const dataType = cell.getAttribute("data-type"); - - // if (colType == "tagging_autocomplete") { - // // getSchema().then((schema) => { - // // autocompleteSource = - // // schema["$defs"]?.[dataType]?.properties?.[col]?.items?.enum || []; - // // if (autocompleteSource.length > 0) { - // // setupTableTagAutocomplete({ cell, autocompleteSource }); - // // } - // // }); - // } else - // if (colType === "dropdown") { - // const currentValue = - // cell.getAttribute("data-value") || cell.textContent.trim() || ""; - // cell.innerHTML = ""; - // cell.textContent = currentValue; - - // // cell.addEventListener("click", function handleDropdownCellClick(e) { - // // if (cell.querySelector("select")) return; - // // getSchema().then((schema) => { - // // const options = - // // schema["$defs"]?.[dataType]?.properties?.[col]?.enum || []; - // // const select = document.createElement("select"); - // // select.className = "table-dropdown-select"; - // // select.name = "ChangingSelect"; - // // select.innerHTML = - // // '' + - // // options - // // .map((opt) => ``) - // // .join(""); - // // select.value = currentValue; - - // // cell.innerHTML = ""; - // // cell.appendChild(select); - // // select.focus(); - - // // function finalizeSelection() { - // // const selectedValue = select.value; - // // cell.setAttribute("data-value", selectedValue); - // // setTimeout(() => { - // // cell.innerHTML = selectedValue; - // // }, 0); - - // // cell.removeEventListener("click", handleDropdownCellClick); - // // setTimeout(() => { - // // cell.addEventListener("click", handleDropdownCellClick); - // // }, 0); - - // // const table = cell.closest("table"); - // // if (table && table.id.endsWith("Table")) { - // // const key = table.id.replace(/Table$/, ""); - // // updateTableHiddenInput(key); - // // } - // // } - - // // select.addEventListener("change", finalizeSelection); - // // select.addEventListener("blur", finalizeSelection); - // // }); - - // // cell.removeEventListener("click", handleDropdownCellClick); - // // }); - - // return; - // } // Show input when cell is clicked cell.addEventListener("click", function (e) { @@ -1105,6 +1124,8 @@ export function initializeTableTaggingCells() { e.preventDefault(); e.stopPropagation(); const tag = input.value.trim(); + // Normalize email (case-insensitive) + const normalizedTag = tag.toLowerCase(); // ✅ Email Validation if (col === "email") { @@ -1115,6 +1136,17 @@ export function initializeTableTaggingCells() { //input.value = ""; return; // ❌ Stop tag creation } + + // Check if email already exists + const alreadyExists = [...tagsList.querySelectorAll(".tag")].some( + (t) => (t.dataset.tag || "").toLowerCase() === normalizedTag, + ); + + if (alreadyExists) { + showToast("This email address has already been added.", "error"); + input.value = ""; + return; // Stop tag creation + } } if ( @@ -1126,20 +1158,6 @@ export function initializeTableTaggingCells() { return; } - // let autocompleteSource = []; - // const colType = cell.getAttribute("data-coltype"); - // if (colType === "tagging_autocomplete") { - // getSchema().then((schema) => { - // autocompleteSource = - // schema["$defs"]?.[dataType]?.properties?.[col]?.items?.enum || []; - // if (!autocompleteSource.includes(tag)) { - // alert("Please select a value from the list."); - // input.value = ""; - // return; - // } - // }); - // } - const span = document.createElement("span"); span.className = "tag"; span.setAttribute("data-tag", tag);