✨ feat(ReserverController): Ordonne les formules par position 🎨 style(formule/show.twig): Simplifie le texte de tarification ♻️ refactor(FormulesController): Permet de réordonner les formules 🐛 fix(SortableReorder.js): Corrige l'attribut URL de tbody ✨ feat(formules/view.twig): Ajoute un sélecteur de type de formule 🐛 fix(formules.twig): Correction de l'ordre d'affichage 🐛 fix(revervation.twig): Correction de la description SEO ```
85 lines
2.5 KiB
JavaScript
85 lines
2.5 KiB
JavaScript
import Sortable from 'sortablejs';
|
|
|
|
/**
|
|
* Composant <sortable-reorder url="/api/update-order">
|
|
* Encapsule un tableau pour rendre ses lignes triables.
|
|
*/
|
|
export class SortableReorder extends HTMLTableElement {
|
|
constructor() {
|
|
super();
|
|
this.sortable = null;
|
|
}
|
|
|
|
connectedCallback() {
|
|
// On attend que le DOM enfant soit parsé
|
|
setTimeout(() => this.init(), 0);
|
|
}
|
|
|
|
init() {
|
|
const tbody = this.querySelector('tbody');
|
|
const url = tbody.getAttribute('url');
|
|
if (!tbody || !url) {
|
|
console.warn('SortableReorder: <tbody> ou attribut "url" manquant.');
|
|
return;
|
|
}
|
|
|
|
this.sortable = new Sortable(tbody, {
|
|
animation: 150,
|
|
handle: '.cursor-move', // On attrape par l'icône uniquement
|
|
ghostClass: 'sortable-ghost', // Classe ajoutée à l'élément en mouvement (fond bleu)
|
|
onEnd: () => this.saveOrder(tbody, url)
|
|
});
|
|
}
|
|
|
|
async saveOrder(tbody, url) {
|
|
// Récupération des IDs dans le nouvel ordre
|
|
const items = Array.from(tbody.querySelectorAll('tr[data-id]'))
|
|
.map(tr => tr.dataset.id);
|
|
|
|
try {
|
|
const response = await fetch(url, {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'X-Requested-With': 'XMLHttpRequest'
|
|
},
|
|
body: JSON.stringify({ items })
|
|
});
|
|
|
|
if (!response.ok) {
|
|
throw new Error('Erreur réseau lors de la sauvegarde.');
|
|
}
|
|
|
|
// Petit feedback visuel (optionnel)
|
|
this.showFeedback('success');
|
|
|
|
} catch (error) {
|
|
console.error('SortableReorder:', error);
|
|
this.showFeedback('error');
|
|
}
|
|
}
|
|
|
|
showFeedback(type) {
|
|
// Simple flash visuel sur le conteneur
|
|
const color = type === 'success' ? 'rgba(16, 185, 129, 0.2)' : 'rgba(239, 68, 68, 0.2)';
|
|
const originalTransition = this.style.transition;
|
|
|
|
this.style.transition = 'background-color 0.3s';
|
|
this.style.backgroundColor = color;
|
|
|
|
setTimeout(() => {
|
|
this.style.backgroundColor = 'transparent';
|
|
setTimeout(() => {
|
|
this.style.transition = originalTransition;
|
|
}, 300);
|
|
}, 500);
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
if (this.sortable) {
|
|
this.sortable.destroy();
|
|
}
|
|
}
|
|
}
|
|
|