Files
ludikevent_crm/assets/libs/initTomSelect.js
Serreau Jovann 7dc2978094 ```
 feat(Devis): Ajoute options, dates début/fin et améliore affichage PDF

Ajoute les champs date de début et fin au devis. Permet l'ajout d'options au devis. Améliore l'affichage du PDF.
```
2026-01-22 10:36:26 +01:00

151 lines
6.9 KiB
JavaScript

// Cache pour éviter les requêtes HTTP répétitives
import TomSelect from "tom-select";
let productCache = 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;
// --- CONFIGURATION PRODUITS ---
if (el.getAttribute('data-load') === "product") {
const setupSelect = (data) => {
new TomSelect(el, {
valueField: 'id',
labelField: 'name',
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 }));
}
}
},
render: {
option: (data, escape) => `
<div class="flex items-center gap-3 py-2 px-3 border-b border-slate-800/50">
<img src="${escape(data.image)}" class="w-8 h-8 object-cover rounded shadow-sm">
<div class="flex flex-col">
<div class="text-[13px] font-bold text-white">${escape(data.name)}</div>
<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>`
}
});
};
// Utilisation du cache ou fetch
if (productCache) {
setupSelect(productCache);
} else {
fetch("/crm/products/json")
.then(r => r.json())
.then(data => {
productCache = data;
setupSelect(data);
});
}
}
else if (el.getAttribute('data-load') === "options") {
const setupSelect = (data) => {
new TomSelect(el, {
valueField: 'id',
labelField: 'name',
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;
const priceInput = row.querySelector('input[name*="[price_ht]"]');
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) => `
<div class="flex items-center gap-3 py-2 px-3 border-b border-slate-800/50">
<img src="${escape(data.image)}" class="w-8 h-8 object-cover rounded shadow-sm">
<div class="flex flex-col">
<div class="text-[13px] font-bold text-white">${escape(data.name)}</div>
<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>`
}
});
};
// Utilisation du cache ou fetch
if (productCache) {
setupSelect(productCache);
} else {
fetch("/crm/options/json")
.then(r => r.json())
.then(data => {
productCache = data;
setupSelect(data);
});
}
}
// --- AUTRES SELECTS ---
else {
new TomSelect(el, {
controlInput: null,
allowEmptyOption: true,
highlight: true,
plugins: ['dropdown_input'],
});
}
});
}