refresh row on activate/deactivate user

This commit is contained in:
Charles 2025-10-27 14:56:45 +01:00
parent afac1467fa
commit 83da6d0be4
1 changed files with 93 additions and 50 deletions

View File

@ -4,7 +4,7 @@ import {TabulatorFull as Tabulator} from 'tabulator-tables';
const TABULATOR_FR_LANG = {
fr: {
ajax: { loading: "Chargement...", error: "Erreur" },
ajax: {loading: "Chargement...", error: "Erreur"},
pagination: {
page_size: "Taille de page",
page_title: "Afficher la page",
@ -17,11 +17,11 @@ const TABULATOR_FR_LANG = {
next: "Suivant",
next_title: "Page suivante",
all: "Tout",
counter: { showing: "Affiche", of: "de", rows: "lignes", pages: "pages" },
counter: {showing: "Affiche", of: "de", rows: "lignes", pages: "pages"},
},
headerFilters: { default: "Filtrer la colonne...", columns: {} },
data: { loading: "Chargement des données...", error: "Erreur de chargement des données" },
groups: { item: "élément", items: "éléments" },
headerFilters: {default: "Filtrer la colonne...", columns: {}},
data: {loading: "Chargement des données...", error: "Erreur de chargement des données"},
groups: {item: "élément", items: "éléments"},
},
};
@ -583,65 +583,77 @@ export default class extends Controller {
{title: "<b>Nom</b>", field: "name", headerFilter: "input", widthGrow: 2, vertAlign: "middle"},
{title: "<b>Prénom</b>", field: "prenom", headerFilter: "input", widthGrow: 2, vertAlign: "middle"},
{title: "<b>Email</b>", field: "email", headerFilter: "input", widthGrow: 3, vertAlign: "middle"},
{title: "<b>Statut</b>", field: "statut", vertAlign: "middle",
formatter: (cell) => {
const statut = cell.getValue();
if (statut) {
return `<span class="badge bg-success">Actif</span>`
}else{
return `<span class="badge bg-secondary">Inactif</span>`
}
}
{
title: "<b>Statut</b>", field: "statut", vertAlign: "middle",
formatter: (cell) => {
const statut = cell.getValue();
if (statut) {
return `<span class="badge bg-success">Actif</span>`
} else {
return `<span class="badge bg-secondary">Inactif</span>`
}
}
},
{
title: "<b>Actions</b>",
field: "showUrl",
width: 130,
vertAlign: "middle",
headerSort: false,
formatter: (cell) => {
const url = cell.getValue();
if (!url) return '';
// You can get other row data like ID for delete endpoint if needed
const rowData = cell.getRow().getData();
const deleteId = rowData.id;
const orgId = this.orgIdValue
return `
<div
class="d-flex gap-2 align-content-center">
<a href="${url}"
class="color-primary"
title="Voir">
<svg xmlns="http://www.w3.org/2000/svg"
width="35px"
height="35"
viewBox="0 0 576 512">
<path fill="currentColor"
d="M288 80c-65.2 0-118.8 29.6-159.9 67.7C89.6 183.5 63 226 49.4 256C63 286 89.6 328.5 128 364.3c41.2 38.1 94.8 67.7 160 67.7s118.8-29.6 159.9-67.7C486.4 328.5 513 286 526.6 256c-13.6-30-40.2-72.5-78.6-108.3C406.8 109.6 353.2 80 288 80M95.4 112.6C142.5 68.8 207.2 32 288 32s145.5 36.8 192.6 80.6c46.8 43.5 78.1 95.4 93 131.1c3.3 7.9 3.3 16.7 0 24.6c-14.9 35.7-46.2 87.7-93 131.1C433.5 443.2 368.8 480 288 480s-145.5-36.8-192.6-80.6C48.6 356 17.3 304 2.5 268.3c-3.3-7.9-3.3-16.7 0-24.6C17.3 208 48.6 156 95.4 112.6M288 336c44.2 0 80-35.8 80-80s-35.8-80-80-80h-2c1.3 5.1 2 10.5 2 16c0 35.3-28.7 64-64 64c-5.5 0-10.9-.7-16-2v2c0 44.2 35.8 80 80 80m0-208a128 128 0 1 1 0 256a128 128 0 1 1 0-256"/></svg>
</a>
const userId = rowData.id;
const statut = rowData.statut;
const orgId = this.orgIdValue;
<a href="#"
class="color-secondary delete-user"
data-id="${deleteId}"
data-org-id="${orgId}"
title="Supprimer">
<svg xmlns="http://www.w3.org/2000/svg"
width="35px"
height="35px"
viewBox="0 0 448 512">
<path fill="currentColor" d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2s-6.3 25.5 4.1 33.7l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L353.3 251.6C407.9 237 448 187.2 448 128C448 57.3 390.7 0 320 0c-69.8 0-126.5 55.8-128 125.2zm225.5 299.2C170.5 309.4 96 387.2 96 482.3c0 16.4 13.3 29.7 29.7 29.7h388.6c3.9 0 7.6-.7 11-2.1z"/></svg>
</a>
</div>
`;
// Decide which action (deactivate vs activate)
const isActive = Boolean(statut);
const actionClass = isActive ? 'deactivate-user' : 'activate-user';
const actionTitle = isActive ? 'Désactiver' : 'Réactiver';
const actionColorClass = isActive ? 'color-secondary' : 'color-primary';
// SVGs
const deactivateSvg = `
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" viewBox="0 0 640 512">
<path fill="currentColor" d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2s-6.3 25.5 4.1 33.7l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L353.3 251.6C407.9 237 448 187.2 448 128C448 57.3 390.7 0 320 0c-69.8 0-126.5 55.8-128 125.2zm225.5 299.2C170.5 309.4 96 387.2 96 482.3c0 16.4 13.3 29.7 29.7 29.7h388.6c3.9 0 7.6-.7 11-2.1z"/>
</svg>`;
const activateSvg = `
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" viewBox="0 0 640 512">
<path fill="currentColor" d="M96 128a128 128 0 1 1 256 0a128 128 0 1 1-256 0M0 482.3C0 383.8 79.8 304 178.3 304h91.4c98.5 0 178.3 79.8 178.3 178.3c0 16.4-13.3 29.7-29.7 29.7H29.7C13.3 512 0 498.7 0 482.3M625 177L497 305c-9.4 9.4-24.6 9.4-33.9 0l-64-64c-9.4-9.4-9.4-24.6 0-33.9s24.6-9.4 33.9 0l47 47L591 143c9.4-9.4 24.6-9.4 33.9 0s9.4 24.6 0 33.9z"/></svg>`;
const actionSvg = isActive ? deactivateSvg : activateSvg;
return `
<div class="d-flex gap-2 align-content-center">
<a href="${url}" class="color-primary" title="Voir">
<svg xmlns="http://www.w3.org/2000/svg" width="35" height="35" viewBox="0 0 640 512">
<path fill="currentColor"
d="M288 80c-65.2 0-118.8 29.6-159.9 67.7C89.6 183.5 63 226 49.4 256C63 286 89.6 328.5 128 364.3c41.2 38.1 94.8 67.7 160 67.7s118.8-29.6 159.9-67.7C486.4 328.5 513 286 526.6 256c-13.6-30-40.2-72.5-78.6-108.3C406.8 109.6 353.2 80 288 80M95.4 112.6C142.5 68.8 207.2 32 288 32s145.5 36.8 192.6 80.6c46.8 43.5 78.1 95.4 93 131.1c3.3 7.9 3.3 16.7 0 24.6c-14.9 35.7-46.2 87.7-93 131.1C433.5 443.2 368.8 480 288 480s-145.5-36.8-192.6-80.6C48.6 356 17.3 304 2.5 268.3c-3.3-7.9-3.3-16.7 0-24.6C17.3 208 48.6 156 95.4 112.6M288 336c44.2 0 80-35.8 80-80s-35.8-80-80-80h-2c1.3 5.1 2 10.5 2 16c0 35.3-28.7 64-64 64c-5.5 0-10.9-.7-16-2v2c0 44.2 35.8 80 80 80m0-208a128 128 0 1 1 0 256a128 128 0 1 1 0-256"/></svg>
</a>
<a href="#"
class="${actionColorClass} ${actionClass}"
data-id="${userId}"
data-org-id="${orgId}"
title="${actionTitle}">
${actionSvg}
</a>
</div>
`;
},
cellClick: function (e, cell) {
const target = e.target.closest('a');
if (target?.classList.contains('delete-user')) {
if (!target) return;
// Deactivate
if (target.classList.contains('deactivate-user')) {
e.preventDefault();
const userId = target.getAttribute('data-id');
if (confirm('Voulez-vous vraiment désactiver cet utilisateur ?')) {
const formData = new FormData();
formData.append('organizationId', target.getAttribute('data-org-id'));
@ -653,13 +665,44 @@ export default class extends Controller {
})
.then(async (response) => {
if (response.ok) {
cell.getRow().delete();
// Option 1: update row status and re-render to switch icon
const data = cell.getRow().getData();
data.statut = false;
cell.getRow().reformat();
} else {
const text = await response.text();
alert('Erreur lors de la suppression: ' + text);
alert('Erreur lors de la désactivation: ' + text);
}
})
.catch(() => alert('Erreur lors de la suppression'));
.catch(() => alert('Erreur lors de la désactivation'));
}
}
// Activate
if (target.classList.contains('activate-user')) {
e.preventDefault();
const userId = target.getAttribute('data-id');
if (confirm('Voulez-vous réactiver cet utilisateur ?')) {
const formData = new FormData();
formData.append('organizationId', target.getAttribute('data-org-id'));
fetch(`/user/organization/activate/${userId}`, {
method: 'POST',
body: formData,
headers: {'X-Requested-With': 'XMLHttpRequest'}
})
.then(async (response) => {
if (response.ok) {
// Switch status back to active and re-render row
const data = cell.getRow().getData();
data.statut = true;
cell.getRow().reformat();
} else {
const text = await response.text();
alert('Erreur lors de la réactivation: ' + text);
}
})
.catch(() => alert('Erreur lors de la réactivation'));
}
}
}