```
✨ feat(Formules.php): Ajoute relation OneToOne avec FormulesRestriction. ✨ feat(Dashboard/FormulesController.php): Gère restrictions formules et formulaire. 🎨 refactor(template/formules): Améliore interface configuration restriction formule. 🐛 fix(assets/RepeatLine.js): Corrige réinitialisation TomSelect et selects "Type". ✨ feat(assets/initTomSelect.js): Gère cache options et init TomSelect. ```
This commit is contained in:
@@ -42,39 +42,41 @@ export class RepeatLine extends HTMLDivElement {
|
||||
addRow() {
|
||||
if (!this.rowHTML || this.$refs.rows.children.length >= this.$props.maxRows) return;
|
||||
|
||||
// Création de la nouvelle ligne
|
||||
let newRow = this.createFromHTML(this.rowHTML);
|
||||
newRow.removeAttribute('id');
|
||||
|
||||
// Nettoyage spécifique pour TomSelect avant insertion
|
||||
// Si on clone une ligne qui avait déjà TomSelect, on reset le select
|
||||
newRow.querySelectorAll('select').forEach(select => {
|
||||
// Supprimer les classes et éléments injectés par TomSelect si présents dans le template
|
||||
select.classList.remove('tomselect', 'ts-hidden-visually');
|
||||
select.innerHTML = '<option value="">Sélectionner...</option>';
|
||||
// Supprimer le wrapper TomSelect s'il a été cloné par erreur
|
||||
const wrapper = select.nextElementSibling;
|
||||
if (wrapper && wrapper.classList.contains('ts-wrapper')) {
|
||||
wrapper.remove();
|
||||
// 1. Si c'est un select TomSelect (sans attribut 'is')
|
||||
if (!select.hasAttribute('ds')) {
|
||||
select.classList.remove('tomselect', 'ts-hidden-visually');
|
||||
select.innerHTML = '<option value="">Sélectionner...</option>';
|
||||
|
||||
const wrapper = select.nextElementSibling;
|
||||
if (wrapper && wrapper.classList.contains('ts-wrapper')) {
|
||||
wrapper.remove();
|
||||
}
|
||||
}
|
||||
// 2. Si c'est votre select "Type" (avec l'attribut 'is')
|
||||
else {
|
||||
// On ne touche PAS au innerHTML pour garder les options (Structure, etc.)
|
||||
select.value = ""; // On remet juste à zéro la sélection
|
||||
}
|
||||
});
|
||||
|
||||
this.setUpRow(newRow);
|
||||
this.$refs.rows.appendChild(newRow);
|
||||
|
||||
// Réinitialisation des valeurs
|
||||
newRow.querySelectorAll('input,textarea,select').forEach(el => {
|
||||
// Réinitialisation des autres champs
|
||||
newRow.querySelectorAll('input,textarea').forEach(el => {
|
||||
el.value = "";
|
||||
if (el.tagName === 'SELECT') el.selectedIndex = 0;
|
||||
});
|
||||
|
||||
// --- INITIALISATION TOMSELECT SUR LA NOUVELLE LIGNE ---
|
||||
// Initialisation TomSelect uniquement sur ce qui doit l'être
|
||||
initTomSelect(newRow);
|
||||
|
||||
this.updateFieldNames();
|
||||
this.updateAddButton();
|
||||
|
||||
// Focus sur le premier élément de la nouvelle ligne
|
||||
const firstInput = newRow.querySelector('input,textarea,select');
|
||||
if (firstInput) firstInput.focus();
|
||||
}
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
// Cache pour éviter les requêtes HTTP répétitives
|
||||
import TomSelect from "tom-select";
|
||||
|
||||
// Cache séparé pour éviter les conflits entre produits et options
|
||||
let productCache = null;
|
||||
let optionsCache = null;
|
||||
|
||||
/**
|
||||
* Initialise TomSelect sur un élément ou un groupe d'éléments
|
||||
*/
|
||||
export function initTomSelect(parent = document) {
|
||||
parent.querySelectorAll('select').forEach((el) => {
|
||||
if (el.tomselect) return;
|
||||
// --- CLAUSES DE GARDE ---
|
||||
// On ignore si déjà initialisé OU si l'élément possède l'attribut "is"
|
||||
if (el.tomselect || el.hasAttribute('ds')) return;
|
||||
|
||||
// --- CONFIGURATION PRODUITS ---
|
||||
if (el.getAttribute('data-load') === "product") {
|
||||
@@ -19,34 +22,23 @@ export function initTomSelect(parent = document) {
|
||||
searchField: 'name',
|
||||
options: data,
|
||||
maxOptions: null,
|
||||
// LORSQU'ON SÉLECTIONNE UN PRODUIT
|
||||
// Dans admin.js, section onChange de TomSelect :
|
||||
onChange: (id) => {
|
||||
if (!id) return;
|
||||
|
||||
// On s'assure de trouver le produit (id peut être string ou int)
|
||||
const product = data.find(p => String(p.id) === String(id));
|
||||
|
||||
if (product) {
|
||||
// On remonte au parent le plus proche (le bloc de ligne du devis)
|
||||
const row = el.closest('.form-repeater__row') || el.closest('fieldset');
|
||||
if (!row) return;
|
||||
|
||||
// Ciblage précis des inputs
|
||||
const priceInput = row.querySelector('input[name*="[price_ht]"]');
|
||||
const priceSupInput = row.querySelector('input[name*="[price_sup_ht]"]');
|
||||
|
||||
if (priceInput) {
|
||||
priceInput.value = product.price1day;
|
||||
// Indispensable pour que d'autres scripts (calcul totaux) voient le changement
|
||||
priceInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
priceInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
if (priceSupInput) {
|
||||
priceSupInput.value = product.priceSup;
|
||||
priceSupInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
priceSupInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -59,16 +51,11 @@ export function initTomSelect(parent = document) {
|
||||
<div class="text-[10px] text-slate-400">J1: ${escape(data.price1day)}€ | Sup: ${escape(data.priceSup)}€</div>
|
||||
</div>
|
||||
</div>`,
|
||||
item: (data, escape) => `
|
||||
<div class="text-blue-400 font-bold flex items-center gap-2">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500"></span>
|
||||
${escape(data.name)}
|
||||
</div>`
|
||||
item: (data, escape) => `<div class="text-blue-400 font-bold flex items-center gap-2"><span class="w-1.5 h-1.5 rounded-full bg-blue-500"></span>${escape(data.name)}</div>`
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Utilisation du cache ou fetch
|
||||
if (productCache) {
|
||||
setupSelect(productCache);
|
||||
} else {
|
||||
@@ -80,6 +67,7 @@ export function initTomSelect(parent = document) {
|
||||
});
|
||||
}
|
||||
}
|
||||
// --- CONFIGURATION OPTIONS ---
|
||||
else if (el.getAttribute('data-load') === "options") {
|
||||
const setupSelect = (data) => {
|
||||
new TomSelect(el, {
|
||||
@@ -88,12 +76,8 @@ export function initTomSelect(parent = document) {
|
||||
searchField: 'name',
|
||||
options: data,
|
||||
maxOptions: null,
|
||||
// LORSQU'ON SÉLECTIONNE UN PRODUIT
|
||||
// Dans admin.js, section onChange de TomSelect :
|
||||
onChange: (id) => {
|
||||
if (!id) return;
|
||||
|
||||
// On s'assure de trouver le produit (id peut être string ou int)
|
||||
const product = data.find(p => String(p.id) === String(id));
|
||||
const row = el.closest('.form-repeater__row') || el.closest('fieldset');
|
||||
if (!row) return;
|
||||
@@ -101,11 +85,8 @@ export function initTomSelect(parent = document) {
|
||||
|
||||
if (priceInput) {
|
||||
priceInput.value = product.price;
|
||||
// Indispensable pour que d'autres scripts (calcul totaux) voient le changement
|
||||
priceInput.dispatchEvent(new Event('input', { bubbles: true }));
|
||||
priceInput.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
}
|
||||
|
||||
},
|
||||
render: {
|
||||
option: (data, escape) => `
|
||||
@@ -116,28 +97,23 @@ export function initTomSelect(parent = document) {
|
||||
<div class="text-[10px] text-slate-400">${escape(data.price)}€</div>
|
||||
</div>
|
||||
</div>`,
|
||||
item: (data, escape) => `
|
||||
<div class="text-blue-400 font-bold flex items-center gap-2">
|
||||
<span class="w-1.5 h-1.5 rounded-full bg-blue-500"></span>
|
||||
${escape(data.name)}
|
||||
</div>`
|
||||
item: (data, escape) => `<div class="text-blue-400 font-bold flex items-center gap-2"><span class="w-1.5 h-1.5 rounded-full bg-blue-500"></span>${escape(data.name)}</div>`
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Utilisation du cache ou fetch
|
||||
if (productCache) {
|
||||
setupSelect(productCache);
|
||||
if (optionsCache) {
|
||||
setupSelect(optionsCache);
|
||||
} else {
|
||||
fetch("/crm/options/json")
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
productCache = data;
|
||||
optionsCache = data;
|
||||
setupSelect(data);
|
||||
});
|
||||
}
|
||||
}
|
||||
// --- AUTRES SELECTS ---
|
||||
// --- AUTRES SELECTS STANDARDS ---
|
||||
else {
|
||||
new TomSelect(el, {
|
||||
controlInput: null,
|
||||
|
||||
Reference in New Issue
Block a user