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 @@
- |
+ |
{% 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);
|