let dragged = null;
const productsPlaceHolder = `
Agrupación de productos
`;
const wordsPlaceHolder = `
Lista de atributos
`;
const products = document.querySelectorAll(".draggable");
const zonesDrop = document.querySelectorAll(".dropzone");
const DATA_GRUPS = {};
products.forEach(manageDragables);
zonesDrop.forEach(manageDropZone);
/*
////
//////
//////// Add new grups
//////
////
*/
function addNewGrup() {
const container = document.getElementById("containers");
const newGrup = document.createElement("section");
const styles = "w-fit space-y-4 border rounded p-2 bg-surface-sweet";
newGrup.classList.add(...styles.split(" "));
const formWord = document.getElementById("form-word");
const formWordClone = formWord.cloneNode(true);
formWordClone.classList.remove("hidden");
newGrup.innerHTML = `
${productsPlaceHolder}
${formWordClone.outerHTML}
${wordsPlaceHolder}
`;
manageDropZone(newGrup.querySelector(".dropzone"));
container.appendChild(newGrup);
}
/*
////
//////
//////// Manage products' drags and remove drags
//////
////
*/
/**
*
* @param {HTMLElement} zone
*/
function manageDropZone(zone) {
/*
////
////// Trash Zone product
////
*/
if (zone.classList.contains("trash-products")) {
zone.addEventListener("dragover", (e) => e.preventDefault());
zone.addEventListener("drop", (event) => {
const codeDrag = dragged.dataset.code
const oldParent = dragged.parentNode;
const oldCodeZone = oldParent.getAttribute("id")
if (oldParent.classList.contains("original-products")) {
spanNotifaction("No puedes borrar este producto");
return
}
dragged.remove()
removeProduct(codeDrag, oldCodeZone)
spanNotifaction("Producto removido del grupo", false);
dragged = null
if (oldParent.classList.contains("dropzone") && oldParent.children.length === 0) {
oldParent.innerHTML = productsPlaceHolder;
}
})
return
}
/*
////
////// Identificate grups and creata dinamic data
////
*/
// Create id for grup
const idZone = generateSimpleID()
// Add products if there en the page
const products = []
const productsElements = zone.querySelectorAll(".draggable") || None;
if (productsElements) {
productsElements.forEach((proEle) => {
products.push({
id: proEle.dataset.idProduct,
code: proEle.dataset.code
})
})
}
const parentZone = zone.parentNode;
const wordsInGrup = parentZone.querySelectorAll(".item-word") || None
// Add words if there en the page
const words = []
if (wordsInGrup) {
wordsInGrup.forEach((wordElement) => {
words.push(wordElement.textContent)
})
}
DATA_GRUPS[idZone] = {
products: products,
words: words
}
/*
////
////// Drop Zone product
////
*/
zone.setAttribute("id", idZone)
zone.addEventListener("dragover", (e) => e.preventDefault());
zone.addEventListener("drop", () => {
const codeDrag = dragged.dataset.code
const zoneCode = zone.getAttribute("id")
// Check that new produck is not in currents products of the group
const obj = DATA_GRUPS[zoneCode].products.find((product) => product.code == codeDrag)
if (obj) {
spanNotifaction("Ese producto ya está en el grupo");
return
}
DATA_GRUPS[zoneCode].products.push({
id: dragged.dataset.idProduct,
code: dragged.dataset.code
})
const oldParent = dragged.parentNode;
const p = zone.querySelector("p");
if (p) {
p.remove();
}
// Move original or clone product in zone
if (oldParent.classList.contains("dropzone")) {
zone.appendChild(dragged);
} else {
const clone = dragged.cloneNode(true);
clone.classList.remove("opacity-50");
manageDragables(clone);
zone.appendChild(clone);
}
if (oldParent.classList.contains("dropzone")) {
if (oldParent.children.length === 0) oldParent.innerHTML = productsPlaceHolder;
const zoneCodeOld = oldParent.getAttribute("id")
removeProduct(codeDrag, zoneCodeOld)
}
});
// Add listeners to elements options and WordForm for the list words
addListenersButtonQuestionGrup(zone.parentNode)
manageFormWord(parentZone.querySelector("form"), parentZone.querySelector(".words-container"), idZone)
if (DATA_GRUPS[idZone].words.length) {
parentZone.querySelector(".words-container").innerHTML = "";
DATA_GRUPS[idZone].words.forEach((word) => {
const itemWord = getItemWord(word, idZone);
parentZone.querySelector(".words-container").appendChild(itemWord);
})
}
}
/*
////
//////
//////// Management Drags and FormWord
//////
////
*/
/**
*
* @param {HTMLElement} el
*/
function manageDragables(el) {
el.addEventListener("dragstart", () => {
dragged = el;
setTimeout(() => el.classList.add("opacity-50"), 0);
});
el.addEventListener("dragend", () => {
dragged = null;
el.classList.remove("opacity-50");
});
}
/**
*
* @param {HTMLFormElementt} form
* @param {HTMLElement} containerWords
*/
function manageFormWord(form, containerWords, codeGrup) {
form.addEventListener("submit", (e) => {
e.preventDefault();
if (!form.reportValidity()) {
return;
}
const input = form.querySelector('input[type="text"]');
if (!input) return;
const name = input.value.trim();
if (!name) return;
if (DATA_GRUPS[codeGrup].words.includes(name)) {
input.value = "";
input.focus();
spanNotifaction("Esa palabra ya está en la lista");
return;
}
DATA_GRUPS[codeGrup].words.push(name)
const wordItem = getItemWord(name, codeGrup)
const p = containerWords.querySelector("p");
if (p) {
p.remove();
}
containerWords.appendChild(wordItem)
input.value = "";
input.focus();
});
}
/*
////
//////
//////// Management Drags and FormWord
//////
////
*/
/**
*
* @param {HTMLElement} grup
*/
function removeGrup(grup) {
delete DATA_GRUPS[grup.querySelector(".dropzone").getAttribute("id")]
grup.remove()
}
function removeWord(code, wordName) {
const newWords = DATA_GRUPS[code].words.filter(word => word != wordName)
DATA_GRUPS[code].words = newWords
if (!newWords.length) {
const containerWords = document.getElementById(code).parentNode.querySelector(".words-container")
containerWords.innerHTML = wordsPlaceHolder;
}
}
function removeProduct(codeProduct, codeZone) {
const newProducts = DATA_GRUPS[codeZone].products.filter((product) => product.code != codeProduct)
DATA_GRUPS[codeZone].products = newProducts
}
function getItemWord(wordName, code) {
const STYLES_DIV = "cts-item-words bg-surface-ligt text-black rounded font-bold text-lg p-1 flex flex-wrap flex-row flex-1 min-w-fit justify-center items-center gap-3";
const STYLES_BTN = "cts-remove-word cts-btn-general-compress px-2 cts-btn-error"
const btn = document.createElement("button");
btn.setAttribute("data-code", code);
btn.setAttribute("data-word", wordName);
btn.classList.add(...STYLES_BTN.split(" "));
btn.textContent = "➖";
const span = document.createElement("span");
span.classList.add("item-word");
span.textContent = wordName;
const div = document.createElement("div");
div.setAttribute("id", `word-${code}-${wordName}`);
div.classList.add(...STYLES_DIV.split(" "));
div.appendChild(span);
div.appendChild(btn);
btn.addEventListener("click", (e) => {
removeWord(code, wordName)
div.remove()
})
return div;
};
/**
*
* @param {HTMLElement} grupContainer
*/
function addListenersButtonQuestionGrup(grupContainer) {
// Function hidden question
grupContainer.querySelector(".cts-question").addEventListener("click", (event) => {
hiddenQuestionRemoveGroup(grupContainer)
})
// Function cancel remove
grupContainer.querySelector(".cts-no-remove").addEventListener("click", (event) => {
hiddenQuestionRemoveGroup(grupContainer, false)
})
// Function remove grup
grupContainer.querySelector(".cts-remove").addEventListener("click", (event) => {
removeGrup(grupContainer)
})
}
/*
////
//////
//////// Management Options Save and remove groups
//////
////
*/
/**
*
* @param {HTMLDivElement} container
* @param {boolean} hiddenQuestion
*/
function hiddenQuestionRemoveGroup(container, hiddenQuestion = true) {
const question = container.querySelector(".cts-question")
const remove = container.querySelector(".cts-remove")
const noRemove = container.querySelector(".cts-no-remove")
if (hiddenQuestion) {
question.classList.add("hidden")
remove.classList.remove("hidden")
noRemove.classList.remove("hidden")
} else {
question.classList.remove("hidden")
remove.classList.add("hidden")
noRemove.classList.add("hidden")
}
}
function generateSimpleID() {
const first = Date.now().toString(35);
const second = Math.random().toString(36).slice(2);
return first + second;
}
function showOptionsSave() {
document.getElementById("question-save").classList.add("hidden");
document.getElementById("finish-session").classList.remove("hidden");
document.getElementById("cancel-save").classList.remove("hidden");
}
function showQuestionSave() {
document.getElementById("question-save").classList.remove("hidden");
document.getElementById("finish-session").classList.add("hidden");
document.getElementById("cancel-save").classList.add("hidden");
}
document
.getElementById("question-save")
.addEventListener("click", showOptionsSave);
document
.getElementById("cancel-save")
.addEventListener("click", showQuestionSave);
/*
////
//////
//////// Save data
//////
////
*/
async function saveData() {
const keysDataGrups = Object.keys(DATA_GRUPS);
const allCodesProducts = new Set()
products.forEach((productElement) => {
allCodesProducts.add(productElement.getAttribute("data-code"))
})
if (keysDataGrups.length === 0) {
spanNotifaction("No hay grupos para guardar")
return false;
}
const data = []
let thereError = false;
const codesProducts = []
keysDataGrups.forEach((key) => {
if (thereError) return
const dataGrup = DATA_GRUPS[key];
if (!dataGrup) {
spanNotifaction("No hay datos para guardar")
thereError = true;
return
}
if (dataGrup.products.length === 0) {
spanNotifaction("Los grupos deben tener por lo menos un producto para guardar")
thereError = true;
return
}
if (dataGrup.words.length === 0) {
spanNotifaction("Los grupos deben tener por lo menos una palabra para guardar")
thereError = true;
return
}
const words = dataGrup.words;
const products = dataGrup.products;
codesProducts.push(...products.map((product) => product.code))
data.push({
words,
products
})
})
if (thereError) return false;
const currentCodesProducts = new Set(codesProducts)
const difference = symmetricDifference(currentCodesProducts, allCodesProducts)
if (difference.size > 0) {
spanNotifaction("Falta un producto que debe ser ordenado")
return false
}
const URL = "/cata/testers/api/rating-sort"
const csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
try {
const response = await fetch(URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRFToken": csrfToken,
},
body: JSON.stringify(data),
})
if (!response.ok) {
spanNotifaction("Error en la respuesta del servidor")
return false;
}
const result = await response.json()
if (result.error) {
spanNotifaction(result.error)
return false
} else {
spanNotifaction(result.message, false)
return true
}
} catch (error) {
spanNotifaction("Error en proceso de guardar los datos")
return false
}
}
const buttonSaveData = document.getElementById("save-progress")
buttonSaveData.addEventListener("click", saveData)
function symmetricDifference(setA, setB) {
let _difference = new Set(setA);
for (let elem of setB) {
if (_difference.has(elem)) {
_difference.delete(elem);
} else {
_difference.add(elem);
}
}
return _difference;
}
/*
////
//////
//////// Finish session
//////
////
*/
document
.getElementById("finish-session")
.addEventListener("click", finishSession);
async function finishSession() {
const save = await saveData();
if (!save) {
spanNotifaction("Error al guardar los datos")
return
};
const FORM_ACTION = document.querySelector(".form-actions")
const inputAction = FORM_ACTION.querySelector(".action-input");
FORM_ACTION.action = "";
inputAction.value = "finish_session";
FORM_ACTION.submit();
}