import {Controller} from '@hotwired/stimulus';
import Choices from 'choices.js';
import {TabulatorFull as Tabulator} from 'tabulator-tables';
export default class extends Controller {
static values = {
rolesArray: Array,
selectedRoleIds: Array,
id: Number,
aws: String,
list: Boolean,
listOrganization: Boolean,
new: Boolean,
admin: Boolean,
listSmall: Boolean,
statut: Boolean,
orgId: Number
}
static targets = ["select"];
connect() {
this.roleSelect();
if(this.listValue){
this.table();
}
if(this.newValue){
this.tableSmall();
}
if(this.adminValue){
this.tableSmallAdmin();
}
if (this.listOrganizationValue) {
this.tableOrganization()
}
}
roleSelect() {
if (this.hasSelectTarget) {
const choicesData = this.rolesArrayValue.map(role => ({
value: role.id,
label: role.name,
selected: this.selectedRoleIdsValue.includes(role.id)
}));
new Choices(this.selectTarget, {
choices: choicesData,
removeItemButton: true,
placeholder: true,
placeholderValue: 'Ajouter un ou plusieurs rôles',
});
}
}
table() {
const columns = [
{
title: "",
field: "isConnected",
width: 40, // small column
hozAlign: "center",
vertAlign: "middle",
headerSort: false,
tooltip: false,
formatter: (cell) => {
const online = !!cell.getValue();
const color = online ? "#80F20E" : "#E42E31"; // green/red
return ``;
},
// Optional: for accessibility
formatterPrint: (cell) => (cell.getValue() ? "online" : "offline"),
formatterClipboard: (cell) => (cell.getValue() ? "online" : "offline"),
},
{
title: "Profil",
field: "pictureUrl",
width: 80,
hozAlign: "center",
headerSort: false,
formatter: (cell) => {
const data = cell.getRow().getData();
const url = cell.getValue();
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
const initials = `${first(data.name)}${first(data.prenom)}`;
const wrapper = document.createElement("div");
wrapper.className = "avatar-wrapper";
// same size for both cases
wrapper.style.width = "40px";
wrapper.style.height = "40px";
wrapper.style.display = "flex";
wrapper.style.alignItems = "center";
wrapper.style.justifyContent = "center";
wrapper.style.borderRadius = "50%";
wrapper.style.overflow = "hidden"; // ensure image clips to circle
if (!url) {
wrapper.style.background = "#6c757d"; // gray background
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "14px";
span.textContent = initials || "•";
wrapper.appendChild(span);
return wrapper;
}
// Image case: make it fill the same wrapper
const img = document.createElement("img");
img.src = `${this.awsValue || ""}${url}`;
img.alt = initials || "avatar";
img.style.width = "100%";
img.style.height = "100%";
img.style.objectFit = "cover"; // keep aspect and cover circle
wrapper.appendChild(img);
// Optional: fallback if image fails
img.addEventListener("error", () => {
wrapper.innerHTML = "";
wrapper.style.background = "#6c757d";
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "12px";
span.textContent = initials || "•";
wrapper.appendChild(span);
});
return wrapper;
},
},
{title: "Nom", field: "name", headerFilter: "input", widthGrow: 2, vertAlign: "middle"},
{title: "Prénom", field: "prenom", headerFilter: "input", widthGrow: 2, vertAlign: "middle"},
{title: "Email", field: "email", headerFilter: "input", widthGrow: 3, vertAlign: "middle"},
{
title: "Actions",
field: "showUrl",
hozAlign: "center",
width: 100,
vertAlign: "middle",
headerSort: false,
formatter: (cell) => {
const url = cell.getValue();
if (url) {
return `
`;
}
return '';
}
}];
const tabulator = new Tabulator("#tabulator-userList", {
langs: {
fr: {
ajax: {
loading: "Chargement...",
error: "Erreur",
},
pagination: {
page_size: "Taille de page",
page_title: "Afficher la page",
first: "Premier",
first_title: "Première page",
last: "Dernier",
last_title: "Dernière page",
prev: "Précédent",
prev_title: "Page précédente",
next: "Suivant",
next_title: "Page suivante",
all: "Tout",
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"},
}
},
locale: "fr", //'en' for English, 'fr' for French (en is default, no need to include it)
ajaxURL: "/user/data",
ajaxConfig: "GET",
pagination: true,
paginationMode: "remote",
paginationSize: 10,
ajaxResponse: (url, params, response) => response,
paginationDataSent: {page: "page", size: "size"},
paginationDataReceived: {last_page: "last_page"},
ajaxSorting: true,
ajaxFiltering: true,
rowHeight: 60,
layout: "fitColumns", // activate French
columns
});
};
onSelectChange(row, newValue) {
const data = row.getData();
console.log("Change select" + data);
};
tableSmall() {
const columns = [
{
title: "Profil",
field: "pictureUrl",
width: 80,
hozAlign: "center",
headerSort: false,
formatter: (cell) => {
const data = cell.getRow().getData();
const url = cell.getValue();
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
const initials = `${data.initials}`;
const wrapper = document.createElement("div");
wrapper.className = "avatar-wrapper";
// same size for both cases
wrapper.style.width = "40px";
wrapper.style.height = "40px";
wrapper.style.display = "flex";
wrapper.style.alignItems = "center";
wrapper.style.justifyContent = "center";
wrapper.style.borderRadius = "50%";
wrapper.style.overflow = "hidden"; // ensure image clips to circle
if (!url) {
wrapper.style.background = "#6c757d"; // gray background
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "14px";
span.textContent = initials || "•";
wrapper.appendChild(span);
return wrapper;
}
// Image case: make it fill the same wrapper
const img = document.createElement("img");
img.src = `${this.awsValue || ""}${url}`;
img.alt = initials || "avatar";
img.style.width = "100%";
img.style.height = "100%";
img.style.objectFit = "cover"; // keep aspect and cover circle
wrapper.appendChild(img);
// Optional: fallback if image fails
img.addEventListener("error", () => {
wrapper.innerHTML = "";
wrapper.style.background = "#6c757d";
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "12px";
span.textContent = initials || "•";
wrapper.appendChild(span);
});
return wrapper;
},
},
{title: "Email", field: "email", widthGrow: 3, vertAlign: "middle"},
{
title: "Actions",
field: "showUrl",
hozAlign: "center",
width: 100,
vertAlign: "middle",
headerSort: false,
formatter: (cell) => {
const url = cell.getValue();
if (url) {
return `
`;
}
return '';
}
}
];
const tabulator = new Tabulator("#tabulator-userListSmall", {
locale: "fr", //'en' for English, 'fr' for French (en is default, no need to include it)
ajaxURL: "/user/data/new",
ajaxConfig: "GET",
pagination: false,
paginationMode: "remote",
// paginationSize: 5,
ajaxParams: { orgId: this.orgIdValue },
langs: {
fr: {
ajax: {
loading: "Chargement...",
error: "Erreur",
},
pagination: {
page_size: "Taille de page",
page_title: "Afficher la page",
first: "Premier",
first_title: "Première page",
last: "Dernier",
last_title: "Dernière page",
prev: "Précédent",
prev_title: "Page précédente",
next: "Suivant",
next_title: "Page suivante",
all: "Tout",
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"},
}
},
ajaxResponse: (url, params, response) => response.data,
// paginationDataSent: {page: "page", size: "size"},
// paginationDataReceived: {last_page: "last_page"},
// ajaxSorting: true,
// ajaxFiltering: true,
rowHeight: 60,
layout: "fitColumns", // activate French
columns
});
}
tableSmallAdmin() {
const columns = [
{
title: "Profil",
field: "pictureUrl",
width: 80,
hozAlign: "center",
headerSort: false,
formatter: (cell) => {
const data = cell.getRow().getData();
const url = cell.getValue();
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
const initials = `${data.initials}`;
const wrapper = document.createElement("div");
wrapper.className = "avatar-wrapper";
// same size for both cases
wrapper.style.width = "40px";
wrapper.style.height = "40px";
wrapper.style.display = "flex";
wrapper.style.alignItems = "center";
wrapper.style.justifyContent = "center";
wrapper.style.borderRadius = "50%";
wrapper.style.overflow = "hidden"; // ensure image clips to circle
if (!url) {
wrapper.style.background = "#6c757d"; // gray background
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "14px";
span.textContent = initials || "•";
wrapper.appendChild(span);
return wrapper;
}
// Image case: make it fill the same wrapper
const img = document.createElement("img");
img.src = `${this.awsValue || ""}${url}`;
img.alt = initials || "avatar";
img.style.width = "100%";
img.style.height = "100%";
img.style.objectFit = "cover"; // keep aspect and cover circle
wrapper.appendChild(img);
// Optional: fallback if image fails
img.addEventListener("error", () => {
wrapper.innerHTML = "";
wrapper.style.background = "#6c757d";
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "12px";
span.textContent = initials || "•";
wrapper.appendChild(span);
});
return wrapper;
},
},
{title: "Email", field: "email", widthGrow: 3, vertAlign: "middle"},
{
title: "Actions",
field: "showUrl",
hozAlign: "center",
width: 100,
vertAlign: "middle",
headerSort: false,
formatter: (cell) => {
const url = cell.getValue();
if (url) {
return `
`;
}
return '';
}
}
];
const tabulator = new Tabulator("#tabulator-userListSmallAdmin", {
locale: "fr", //'en' for English, 'fr' for French (en is default, no need to include it)
ajaxURL: "/user/data/admin",
ajaxConfig: "GET",
pagination: false,
paginationMode: "remote",
// paginationSize: 5,
ajaxParams: { orgId: this.orgIdValue },
langs: {
fr: {
ajax: {
loading: "Chargement...",
error: "Erreur",
},
pagination: {
page_size: "Taille de page",
page_title: "Afficher la page",
first: "Premier",
first_title: "Première page",
last: "Dernier",
last_title: "Dernière page",
prev: "Précédent",
prev_title: "Page précédente",
next: "Suivant",
next_title: "Page suivante",
all: "Tout",
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"},
}
},
ajaxResponse: (url, params, response) => response.data,
// paginationDataSent: {page: "page", size: "size"},
// paginationDataReceived: {last_page: "last_page"},
// ajaxSorting: true,
// ajaxFiltering: true,
rowHeight: 60,
layout: "fitColumns", // activate French
columns
});
}
tableOrganization() {
const columns = [
{
title: "",
field: "isConnected",
width: 40, // small column
hozAlign: "center",
vertAlign: "middle",
headerSort: false,
tooltip: false,
formatter: (cell) => {
const online = !!cell.getValue();
const color = online ? "#80F20E" : "#E42E31"; // green/red
return ``;
},
// Optional: for accessibility
formatterPrint: (cell) => (cell.getValue() ? "online" : "offline"),
formatterClipboard: (cell) => (cell.getValue() ? "online" : "offline"),
},
{
title: "Profil",
field: "pictureUrl",
width: 80,
hozAlign: "center",
headerSort: false,
formatter: (cell) => {
const data = cell.getRow().getData();
const url = cell.getValue();
const first = (s) => (typeof s === "string" && s.length ? s.trim()[0].toUpperCase() : "");
const initials = `${first(data.name)}${first(data.prenom)}`;
const wrapper = document.createElement("div");
wrapper.className = "avatar-wrapper";
// same size for both cases
wrapper.style.width = "40px";
wrapper.style.height = "40px";
wrapper.style.display = "flex";
wrapper.style.alignItems = "center";
wrapper.style.justifyContent = "center";
wrapper.style.borderRadius = "50%";
wrapper.style.overflow = "hidden"; // ensure image clips to circle
if (!url) {
wrapper.style.background = "#6c757d"; // gray background
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "14px";
span.textContent = initials || "•";
wrapper.appendChild(span);
return wrapper;
}
// Image case: make it fill the same wrapper
const img = document.createElement("img");
img.src = `${this.awsValue || ""}${url}`;
img.alt = initials || "avatar";
img.style.width = "100%";
img.style.height = "100%";
img.style.objectFit = "cover"; // keep aspect and cover circle
wrapper.appendChild(img);
// Optional: fallback if image fails
img.addEventListener("error", () => {
wrapper.innerHTML = "";
wrapper.style.background = "#6c757d";
const span = document.createElement("span");
span.className = "avatar-initials";
span.style.color = "#fff";
span.style.fontWeight = "600";
span.style.fontSize = "12px";
span.textContent = initials || "•";
wrapper.appendChild(span);
});
return wrapper;
},
},
{title: "Nom", field: "name", headerFilter: "input", widthGrow: 2, vertAlign: "middle"},
{title: "Prénom", field: "prenom", headerFilter: "input", widthGrow: 2, vertAlign: "middle"},
{title: "Email", field: "email", headerFilter: "input", widthGrow: 3, vertAlign: "middle"},
{
title: "Actions",
field: "showUrl",
hozAlign: "center",
width: 100,
vertAlign: "middle",
headerSort: false,
formatter: (cell) => {
const url = cell.getValue();
if (url) {
return `
`;
}
return '';
}
}];
// if (this.statutValue) {
// columns.push(
// {
// title: "Statut", field: "role", // or any field you want
// headerSort: false,
// hozAlign: "center",
// vertAlign: "middle",
// formatter: (cell) => {
// const row = cell.getRow();
// const current = cell.getValue() ?? "";
//
// const select = document.createElement("select");
// select.className = "table-select-action";
// // Options
// [
// {value: "", label: "Choisir..."},
// {value: "viewer", label: "Viewer"},
// {value: "editor", label: "Editor"},
// {value: "admin", label: "Admin"},
// ].forEach(opt => {
// const o = document.createElement("option");
// o.value = opt.value;
// o.textContent = opt.label;
// if (opt.value === current) o.selected = true;
// select.appendChild(o);
// });
//
// // Hook change
// select.addEventListener("change", (e) => {
// this.onSelectChange(row, e.target.value);
// });
//
// // Return a DOM node from a formatter → Tabulator will mount it
// return select;
// },
// // Optional: provide text for clipboard/print
// formatterClipboard: cell => cell.getValue(),
// formatterPrint: cell => cell.getValue(),
// },
// )
// }
const tabulator = new Tabulator("#tabulator-userListOrganization", {
langs: {
fr: {
ajax: {
loading: "Chargement...",
error: "Erreur",
},
pagination: {
page_size: "Taille de page",
page_title: "Afficher la page",
first: "Premier",
first_title: "Première page",
last: "Dernier",
last_title: "Dernière page",
prev: "Précédent",
prev_title: "Page précédente",
next: "Suivant",
next_title: "Page suivante",
all: "Tout",
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"},
}
},
locale: "fr",
ajaxURL: "/user/data/organization",
ajaxConfig: "GET",
ajaxParams: { orgId: this.orgIdValue },
pagination: true,
paginationMode: "remote",
paginationSize: 10,
ajaxResponse: (url, params, response) => response,
paginationDataSent: { page: "page", size: "size" },
paginationDataReceived: { last_page: "last_page" },
ajaxSorting: true,
ajaxFiltering: true,
rowHeight: 60,
layout: "fitColumns", // activate French
columns
});
};
}