feat(Product.php): Ajoute relation DevisLine et méthodes associées en français.
 feat(DevisLine.php): Ajoute propriétés et relations pour ligne de devis en français.
 feat(DevisController.php): Intègre génération PDF et ajout de lignes de devis en français.
🎨 style: Améliore la mise en page et l'esthétique de l'interface admin en français.
 feat: Initialise TomSelect et gère les adresses client dans DevisManager en français.
🐛 fix: Corrige l'initialisation de TomSelect et la gestion des lignes répétées en français.
 test: Ajoute génération du bon pour accord et signature en français.
```
This commit is contained in:
Serreau Jovann
2026-01-19 17:56:57 +01:00
parent 44d619d659
commit 5d6c0fdde7
13 changed files with 716 additions and 401 deletions

View File

@@ -1,11 +1,11 @@
import './admin.scss'
import './admin.scss';
import * as Sentry from "@sentry/browser";
import * as Turbo from "@hotwired/turbo";
import TomSelect from "tom-select";
import {RepeatLine} from "./libs/RepeatLine.js";
import {DevisManager} from "./libs/DevisManager.js";
// --- INITIALISATION SENTRY (En premier !) ---
import { RepeatLine } from "./libs/RepeatLine.js";
import { DevisManager } from "./libs/DevisManager.js";
import { initTomSelect } from "./libs/initTomSelect.js";
// --- INITIALISATION SENTRY ---
Sentry.init({
dsn: "https://803814be6540031b1c37bf92ba9c0f79@sentry.esy-web.dev/24",
tunnel: "/sentry-tunnel",
@@ -15,171 +15,73 @@ Sentry.init({
Sentry.replayIntegration()
],
tracesSampleRate: 1.0,
tracePropagationTargets: ["localhost", "esy-web.dev"], // Remplace par ton domaine réel
tracePropagationTargets: ["localhost", "esy-web.dev"],
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0
});
// Cache global pour éviter de fetch les produits à chaque nouvelle ligne
let productCache = null;
/**
* Initialise les composants de l'interface d'administration.
*/
function initAdminLayout() {
if (!customElements.get('copy-text')) {
customElements.define('repeat-line', RepeatLine, {extends: 'div'})
customElements.define('devis-manager', DevisManager, {extends: 'div'})
// Enregistrement des Custom Elements
if (!customElements.get('repeat-line')) {
customElements.define('repeat-line', RepeatLine, { extends: 'div' });
}
document.querySelectorAll('select').forEach((el) => {
if (!el.tomselect) { // Éviter la double initialisation avec Turbo
if(el.getAttribute('data-load') == "product") {
fetch("/crm/product/json")
.then(r=>r.json())
.then(products=>{
})
} else {
new TomSelect(el, {
controlInput: null,
allowEmptyOption: true,
highlight: true,
plugins: ['dropdown_input'], // Permet d'avoir la recherche dans le dropdown
render: {
option: function (data, escape) {
return `<div class="py-2 px-3">
<div class="text-[13px] font-bold text-white">${escape(data.text)}</div>
</div>`;
},
item: function (data, escape) {
return `<div class="text-blue-400 font-bold">${escape(data.text)}</div>`;
}
}
});
}
}
});
const imageInput = document.getElementById('product_image_input');
const previewImage = document.getElementById('product-image-preview');
const placeholderIcon = document.getElementById('product-image-placeholder');
if (imageInput) {
imageInput.addEventListener('change', (event) => {
const file = event.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => {
previewImage.src = e.target.result;
previewImage.classList.remove('hidden');
if (placeholderIcon) {
placeholderIcon.classList.add('hidden');
}
};
reader.readAsDataURL(file);
}
});
if (!customElements.get('devis-manager')) {
customElements.define('devis-manager', DevisManager, { extends: 'div' });
}
// Sidebar & UI
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebar-overlay');
const toggleBtn = document.getElementById('sidebar-toggle');
const settingsToggle = document.getElementById('settings-toggle');
const settingsSubmenu = document.getElementById('settings-submenu');
// --- 1. GESTION DE LA SIDEBAR (MOBILE) ---
if (toggleBtn && sidebar && overlay) {
toggleBtn.onclick = () => {
sidebar.classList.toggle('-translate-x-full');
overlay.classList.toggle('hidden');
};
overlay.onclick = () => {
sidebar.classList.add('-translate-x-full');
overlay.classList.add('hidden');
};
}
// --- 2. GESTION DU DROPDOWN (PARAMÈTRES) ---
// Dropdown Paramètres
const settingsToggle = document.getElementById('settings-toggle');
const settingsSubmenu = document.getElementById('settings-submenu');
if (settingsToggle && settingsSubmenu) {
const settingsChevron = settingsToggle.querySelector('svg:last-child');
const toggleDropdown = (show, animate = true) => {
if (!animate) settingsSubmenu.style.transition = 'none';
if (show) {
settingsSubmenu.classList.remove('hidden');
settingsSubmenu.style.maxHeight = settingsSubmenu.scrollHeight + "px";
settingsChevron?.classList.add('rotate-180');
localStorage.setItem('admin_settings_open', 'true');
} else {
settingsSubmenu.style.maxHeight = "0px";
settingsChevron?.classList.remove('rotate-180');
localStorage.setItem('admin_settings_open', 'false');
if (animate) {
setTimeout(() => {
if (settingsSubmenu.style.maxHeight === "0px") {
settingsSubmenu.classList.add('hidden');
}
}, 300);
} else {
settingsSubmenu.classList.add('hidden');
}
}
if (!animate) {
settingsSubmenu.offsetHeight; // force redraw
settingsSubmenu.style.transition = '';
}
};
settingsToggle.onclick = (e) => {
e.preventDefault();
const isClosed = settingsSubmenu.style.maxHeight === "0px" || settingsSubmenu.classList.contains('hidden');
toggleDropdown(isClosed);
settingsSubmenu.classList.toggle('hidden');
const isOpen = !settingsSubmenu.classList.contains('hidden');
localStorage.setItem('admin_settings_open', isOpen);
};
// PERSISTANCE
const isSettingsRoute = window.location.pathname.includes('/crm/administrateur') ||
window.location.pathname.includes('/crm/logs');
const wasOpen = localStorage.getItem('admin_settings_open') === 'true';
if (isSettingsRoute || wasOpen) {
toggleDropdown(true, false);
}
// HIGHLIGHT
settingsSubmenu.querySelectorAll('a').forEach(link => {
if (window.location.pathname === link.getAttribute('href')) {
link.classList.add('text-blue-600', 'dark:text-blue-400', 'font-semibold');
link.classList.remove('text-slate-500');
}
});
}
// --- 3. GESTION DES MESSAGES FLASH ---
// Flash messages
document.querySelectorAll('.flash-message').forEach((flash) => {
setTimeout(() => {
flash.classList.add('opacity-0', 'translate-x-10');
setTimeout(() => flash.remove(), 500);
}, 8000);
}, 5000);
});
}
// --- CORRECTIF DATA-TURBO-CONFIRM ---
// Turbo Hooks
document.addEventListener('turbo:load', () => {
initAdminLayout();
initTomSelect(); // Init au chargement de la page
});
document.addEventListener("turbo:click", (event) => {
const message = event.target.closest("[data-turbo-confirm]")?.getAttribute("data-turbo-confirm");
if (message && !confirm(message)) {
event.preventDefault();
}
});
document.addEventListener('turbo:load', initAdminLayout);
document.addEventListener('turbo:before-cache', () => {
const sidebar = document.getElementById('sidebar');
const overlay = document.getElementById('sidebar-overlay');
if (sidebar) sidebar.classList.add('-translate-x-full');
if (overlay) overlay.classList.add('hidden');
});