import {Controller} from '@hotwired/stimulus'; import { Modal } from "bootstrap"; import {TabulatorFull as Tabulator} from 'tabulator-tables'; import {eyeIconLink, pencilIcon, TABULATOR_FR_LANG, trashIcon} from "../js/global.js"; export default class extends Controller { static values = { listProject : Boolean, orgId: Number, admin: Boolean } static targets = ["modal", "appList", "nameInput", "formTitle", "timestampSelect", "deletionSelect"]; connect(){ if(this.listProjectValue){ this.table(); } this.modal = new Modal(this.modalTarget); } table(){ const columns = [ {title: "ID ", field: "id", visible: false}, {title: "Nom du projet ", field: "name", headerFilter: "input", widthGrow: 2, vertAlign: "middle"}, { title: "Applications", field: "applications", headerSort: false, hozAlign: "left", formatter: (cell) => { const apps = cell.getValue(); if (!apps || apps.length === 0) { return "Aucune"; } // Wrap everything in a flex container to keep them on one line const content = apps.map(app => `
${app.name}
`).join(''); return `
${content}
`; } } ]; // 2. Add the conditional column if admin value is true if (this.adminValue) { columns.push({ title: "Base de données", field: "bddName", hozAlign: "left", }, { title: "Actions", field: "id", width: 120, hozAlign: "center", headerSort: false, formatter: (cell) => { const id = cell.getValue(); // Return a button that Stimulus can listen to return `
`; } }); } const tabulator = new Tabulator("#tabulator-projectListOrganization", { langs: TABULATOR_FR_LANG, locale: "fr", ajaxURL: `/project/organization/data`, ajaxConfig: "GET", pagination: true, paginationMode: "remote", paginationSize: 15, ajaxParams: {orgId: this.orgIdValue}, ajaxResponse: (url, params, response) => response, paginationDataSent: {page: "page", size: "size"}, paginationDataReceived: {last_page: "last_page"}, ajaxSorting: true, ajaxFiltering: true, filterMode: "remote", ajaxURLGenerator: function(url, config, params) { let queryParams = new URLSearchParams(); queryParams.append('orgId', params.orgId); queryParams.append('page', params.page || 1); queryParams.append('size', params.size || 15); // Add filters if (params.filter) { params.filter.forEach(filter => { queryParams.append(`filter[${filter.field}]`, filter.value); }); } return `${url}?${queryParams.toString()}`; }, rowHeight: 60, layout: "fitColumns", // activate French columns }) } async loadApplications() { try { const response = await fetch('/application/data/all'); const apps = await response.json(); this.appListTarget.innerHTML = apps.map(app => `
`).join(''); } catch (error) { this.appListTarget.innerHTML = '
Erreur de chargement.
'; } } async submitForm(event) { event.preventDefault(); const form = event.target; const formData = new FormData(form); // This automatically picks up the 'logo' file // 1. Validate File Format const logoFile = formData.get('logo'); if (logoFile && logoFile.size > 0) { const allowedTypes = ['image/jpeg', 'image/png', 'image/jpg']; if (!allowedTypes.includes(logoFile.type)) { alert("Format invalide. Veuillez utiliser uniquement des fichiers PNG ou JPG."); return; // Stop submission } } // 2. Prepare for Multipart sending // Since we are using FormData, we don't need JSON.stringify or 'Content-Type': 'application/json' // We add the extra fields to the formData object formData.append('organizationId', this.orgIdValue); const url = this.currentProjectId ? `/project/edit/${this.currentProjectId}/ajax` : `/project/new/ajax`; const response = await fetch(url, { method: 'POST', // IMPORTANT: Do NOT set Content-Type header when sending FormData with files // The browser will set 'multipart/form-data' and the boundary automatically body: formData }); if (response.ok) { this.modal.hide(); location.reload(); } else { const result = await response.json(); if (response.status === 409) { alert("Un projet avec ce nom existe déjà."); } else { alert(result.error || "Une erreur est survenue."); } } } async openEditModal(event) { const projectId = event.currentTarget.dataset.id; this.currentProjectId = projectId; this.modal.show(); this.nameInputTarget.disabled = true; this.formTitleTarget.textContent = "Modifier le projet"; try { // 1. Ensure checkboxes are loaded first await this.loadApplications(); // 2. Fetch the project data const response = await fetch(`/project/data/${projectId}`); const project = await response.json(); // 3. Set the name this.nameInputTarget.value = project.name; console.log(project); this.timestampSelectTarget.value = project.timestampPrecision; this.deletionSelectTarget.value = project.deletionAllowed; // 4. Check the boxes // We look for all checkboxes inside our appList target const checkboxes = this.appListTarget.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach(cb => { cb.checked = project.applications.includes(cb.value); }); } catch (error) { console.error("Error loading project data", error); alert("Erreur lors de la récupération des données du projet."); } } // Update your openCreateModal to reset the state openCreateModal() { this.currentProjectId = null; this.modal.show(); this.nameInputTarget.disabled = false; this.nameInputTarget.value = ""; this.formTitleTarget.textContent = "Nouveau Projet"; this.loadApplications(); } async deleteProject(event) { const projectId = event.currentTarget.dataset.id; if (!confirm("Êtes-vous sûr de vouloir supprimer ce projet ?")) { return; } try { const response = await fetch(`/project/delete/${projectId}/ajax`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, }); if (response.ok) { location.reload(); } }catch (error) { console.error("Error deleting project", error); alert("Erreur lors de la suppression du projet."); } } }