Added tabulator for all user table
This commit is contained in:
parent
9270849e12
commit
8c7336b821
|
|
@ -0,0 +1,17 @@
|
|||
## Notes
|
||||
- Certaines abbreviations sont utilisées afin de simplifier le code et d'éviter les répétitions ou noms trop longs :
|
||||
- `uo` pour `User Organization`
|
||||
- `uoId` pour `User Organization Id`
|
||||
- `oa` pour `Organization Application`
|
||||
- `at` pour `Access Token`
|
||||
- A delete command is available to delete roles
|
||||
|
||||
|
||||
### ROLES
|
||||
```bash
|
||||
php bin/console app:delete-role ROLE_NAME
|
||||
```
|
||||
|
||||
### Tabulator
|
||||
- Certaines fonctions sont déjà disponibles (snippet) mais commentées, car on ne les utilise pas
|
||||
- Exemples de sorting et filtering sont disponibles dans 'src/controller/organization.php' L.268
|
||||
10
README.MD
10
README.MD
|
|
@ -24,13 +24,3 @@
|
|||
php bin/console importmap:require choices.js
|
||||
php bin/console importmap:require choices.js/public/assets/styles/choices.min.css
|
||||
```
|
||||
### Notes
|
||||
- certaines abbreviations sont utilisées afin de simplifier le code et d'éviter les répétitions ou noms trop longs :
|
||||
- `uo` pour `User Organization`
|
||||
- `uoId` pour `User Organization Id`
|
||||
- `oa` pour `Organization Application`
|
||||
- `at` pour `Access Token`
|
||||
- A delete command is available to delete roles
|
||||
```bash
|
||||
php bin/console app:delete-role ROLE_NAME
|
||||
```
|
||||
|
|
|
|||
|
|
@ -1,16 +1,39 @@
|
|||
import { Controller } from '@hotwired/stimulus';
|
||||
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() {
|
||||
|
|
@ -29,4 +52,695 @@ export default class extends Controller {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
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 `<span class="status-dot" style="
|
||||
display:inline-block;
|
||||
width:10px;height:10px;
|
||||
border-radius:50%;
|
||||
background:${color};
|
||||
"></span>`;
|
||||
},
|
||||
// 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: "<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>Actions</b>",
|
||||
field: "showUrl",
|
||||
hozAlign: "center",
|
||||
width: 100,
|
||||
vertAlign: "middle",
|
||||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const url = cell.getValue();
|
||||
if (url) {
|
||||
return `
|
||||
<a href="${url}" class="p-3 align-middle color-primary" title="Voir">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="35px"
|
||||
height="35px"
|
||||
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>
|
||||
`;
|
||||
}
|
||||
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: 25,
|
||||
|
||||
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: "<b>Email</b>", field: "email", widthGrow: 3, vertAlign: "middle"},
|
||||
{
|
||||
title: "<b>Actions</b>",
|
||||
field: "showUrl",
|
||||
hozAlign: "center",
|
||||
width: 100,
|
||||
vertAlign: "middle",
|
||||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const url = cell.getValue();
|
||||
if (url) {
|
||||
return `
|
||||
<a href="${url}" class="p-3 align-middle color-primary" title="Voir">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="35px"
|
||||
height="35px"
|
||||
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>
|
||||
`;
|
||||
}
|
||||
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: "<b>Email</b>", field: "email", widthGrow: 3, vertAlign: "middle"},
|
||||
{
|
||||
title: "<b>Actions</b>",
|
||||
field: "showUrl",
|
||||
hozAlign: "center",
|
||||
width: 100,
|
||||
vertAlign: "middle",
|
||||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const url = cell.getValue();
|
||||
if (url) {
|
||||
return `
|
||||
<a href="${url}" class="p-3 align-middle color-primary" title="Voir">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="35px"
|
||||
height="35px"
|
||||
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>
|
||||
`;
|
||||
}
|
||||
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 `<span class="status-dot" style="
|
||||
display:inline-block;
|
||||
width:10px;height:10px;
|
||||
border-radius:50%;
|
||||
background:${color};
|
||||
"></span>`;
|
||||
},
|
||||
// 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: "<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>Actions</b>",
|
||||
field: "showUrl",
|
||||
hozAlign: "center",
|
||||
width: 100,
|
||||
vertAlign: "middle",
|
||||
headerSort: false,
|
||||
formatter: (cell) => {
|
||||
const url = cell.getValue();
|
||||
if (url) {
|
||||
return `
|
||||
<a href="${url}" class="p-3 align-middle color-primary" title="Voir">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
width="35px"
|
||||
height="35px"
|
||||
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>
|
||||
`;
|
||||
}
|
||||
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
|
||||
});
|
||||
};
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@
|
|||
border:0;
|
||||
}
|
||||
.tabulator .tabulator-header .tabulator-col .tabulator-col-title {
|
||||
font-size: 22px !important;
|
||||
font-size: 18px !important;
|
||||
}
|
||||
|
||||
/* Select hover Désactivé pour l'instant car jpp faire de jolie style */
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ use App\Entity\UsersOrganizations;
|
|||
use App\Form\UserForm;
|
||||
use App\Service\ActionService;
|
||||
use App\Service\AwsService;
|
||||
use App\Service\OrganizationsService;
|
||||
use App\Service\UserOrganizationAppService;
|
||||
use App\Service\UserOrganizationService;
|
||||
use App\Service\UserService;
|
||||
|
|
@ -18,6 +19,7 @@ use Doctrine\ORM\EntityManagerInterface;
|
|||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||
use Symfony\Component\Asset\Packages;
|
||||
use Symfony\Component\HttpFoundation\File\Exception\FileException;
|
||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\Routing\Attribute\Route;
|
||||
|
|
@ -33,7 +35,7 @@ class UserController extends AbstractController
|
|||
private readonly UserService $userService,
|
||||
private readonly ActionService $actionService,
|
||||
private readonly UserOrganizationAppService $userOrganizationAppService,
|
||||
private readonly UserOrganizationService $userOrganizationService,
|
||||
private readonly UserOrganizationService $userOrganizationService, private readonly OrganizationsService $organizationsService,
|
||||
)
|
||||
{
|
||||
}
|
||||
|
|
@ -373,10 +375,205 @@ class UserController extends AbstractController
|
|||
return $this->redirectToRoute('user_show', [
|
||||
'user' => $user,
|
||||
'id' => $user->getId(),
|
||||
'organizationId'=> $uo->getOrganization()->getId()
|
||||
'organizationId' => $uo->getOrganization()->getId()
|
||||
]);
|
||||
}
|
||||
|
||||
throw $this->createAccessDeniedException();
|
||||
}
|
||||
|
||||
/*
|
||||
* AJAX endpoint for user listing with pagination
|
||||
* Get all the users that aren´t deleted and are active
|
||||
*/
|
||||
#[Route(path: '/data', name: 'data', methods: ['GET'])]
|
||||
public function data(Request $request): JsonResponse
|
||||
{
|
||||
$this->denyAccessUnlessGranted("ROLE_ADMIN");
|
||||
|
||||
$page = max(1, (int)$request->query->get('page', 1));
|
||||
$size = max(1, (int)$request->query->get('size', 10));
|
||||
|
||||
$repo = $this->entityManager->getRepository(User::class);
|
||||
|
||||
// Base query: keep your constraints intact (isDeleted=false, isActive=true)
|
||||
$qb = $repo->createQueryBuilder('u')
|
||||
->where('u.isDeleted = :del')->setParameter('del', false)
|
||||
->andWhere('u.isActive = :active')->setParameter('active', true);
|
||||
$countQb = clone $qb;
|
||||
$total = (int)$countQb->select('COUNT(u.id)')->getQuery()->getSingleScalarResult();
|
||||
|
||||
// Pagination
|
||||
$offset = ($page - 1) * $size;
|
||||
$rows = $qb->setFirstResult($offset)->setMaxResults($size)->getQuery()->getResult();
|
||||
|
||||
// Map to array (keep isConnected)
|
||||
$data = array_map(function (User $user) {
|
||||
return [
|
||||
'pictureUrl' => $user->getPictureUrl(),
|
||||
'name' => $user->getSurname(),
|
||||
'prenom' => $user->getName(),
|
||||
'email' => $user->getEmail(),
|
||||
'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()),
|
||||
'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]),
|
||||
];
|
||||
}, $rows);
|
||||
|
||||
// Match organizations response shape
|
||||
$lastPage = (int)ceil($total / $size);
|
||||
|
||||
return $this->json([
|
||||
'data' => $data,
|
||||
'last_page' => $lastPage,
|
||||
'total' => $total, // optional but handy
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
#[Route(path: '/indexTest', name: 'indexTest', methods: ['GET'])]
|
||||
public function indexTest(): Response
|
||||
{
|
||||
$totalUsers = $this->entityManager->getRepository(User::class)->count(['isDeleted' => false, 'isActive' => true]);
|
||||
return $this->render('user/indexTest.html.twig', [
|
||||
'users' => $totalUsers
|
||||
]);
|
||||
}
|
||||
|
||||
/*
|
||||
* AJAX endpoint for new users listing
|
||||
* Get the 5 most recently created users for an organization
|
||||
*/
|
||||
#[Route(path: '/data/new', name: 'dataNew', methods: ['GET'])]
|
||||
public function dataNew(Request $request): JsonResponse
|
||||
{
|
||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
if ($this->userService->hasAccessTo($actingUser, true) && $this->isGranted("ROLE_ADMIN")) {
|
||||
$orgId = $request->query->get('orgId');
|
||||
$uos = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['organization' => $orgId], limit: 5, orderBy: ['createdAt' => 'DESC']);
|
||||
|
||||
|
||||
// Map to array (keep isConnected)
|
||||
$data = array_map(function (UsersOrganizations $uo) {
|
||||
$user = $uo->getUsers();
|
||||
$initials = $user->getName()[0] . $user->getSurname()[0];
|
||||
return [
|
||||
'pictureUrl' => $user->getPictureUrl(),
|
||||
'email' => $user->getEmail(),
|
||||
'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()),
|
||||
'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]),
|
||||
'initials' => strtoupper($initials),
|
||||
];
|
||||
}, $uos);
|
||||
return $this->json([
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* AJAX endpoint for admin users listing
|
||||
* Get all admin users for an organization
|
||||
*/
|
||||
|
||||
#[Route(path: '/data/admin', name: 'dataAdmin', methods: ['GET'])]
|
||||
public function dataAdmin(Request $request): JsonResponse
|
||||
{
|
||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
if ($this->userService->hasAccessTo($actingUser, true) && $this->isGranted("ROLE_ADMIN")) {
|
||||
$orgId = $request->query->get('orgId');
|
||||
$uos = $this->entityManager->getRepository(UsersOrganizations::class)->findBy(['organization' => $orgId]);
|
||||
$roleAdmin = $this->entityManager->getRepository(Roles::class)->findOneBy(['name' => 'ADMIN']);
|
||||
$users = [];
|
||||
foreach ($uos as $uo) {
|
||||
if ($this->entityManager->getRepository(UserOrganizatonApp::class)->findOneBy(['userOrganization' => $uo, 'role' => $roleAdmin])) {
|
||||
$users[] = $uo;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Map to array (keep isConnected)
|
||||
$data = array_map(function (UsersOrganizations $uo) {
|
||||
$user = $uo->getUsers();
|
||||
$initials = $user->getName()[0] . $user->getSurname()[0];
|
||||
return [
|
||||
'pictureUrl' => $user->getPictureUrl(),
|
||||
'email' => $user->getEmail(),
|
||||
'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()),
|
||||
'showUrl' => $this->generateUrl('user_show', ['id' => $user->getId()]),
|
||||
'initials' => strtoupper($initials),
|
||||
];
|
||||
}, $users);
|
||||
|
||||
|
||||
return $this->json([
|
||||
'data' => $data,
|
||||
]);
|
||||
}
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* AJAX endpoint for All users in an organization
|
||||
*/
|
||||
#[Route(path: '/data/organization', name: 'dataUserOrganization', methods: ['GET'])]
|
||||
public function dataUserOrganization(Request $request): JsonResponse
|
||||
{
|
||||
$actingUser = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||
|
||||
if ($this->userService->hasAccessTo($actingUser, true) && $this->isGranted("ROLE_ADMIN")) {
|
||||
$orgId = $request->query->get('orgId');
|
||||
$page = max(1, (int)$request->query->get('page', 1));
|
||||
$size = max(1, (int)$request->query->get('size', 10));
|
||||
|
||||
// Optional: read Tabulator remote sort/filter payloads
|
||||
// $sorters = $request->query->all('sorters') ?? [];
|
||||
// $filters = $request->query->all('filters') ?? [];
|
||||
|
||||
$repo = $this->entityManager->getRepository(UsersOrganizations::class);
|
||||
|
||||
// Base query
|
||||
$qb = $repo->createQueryBuilder('uo')
|
||||
->where('uo.organization = :orgId')
|
||||
->setParameter('orgId', $orgId);
|
||||
$countQb = clone $qb;
|
||||
$total = (int)$countQb->select('COUNT(uo.id)')->getQuery()->getSingleScalarResult();
|
||||
|
||||
// Pagination
|
||||
$offset = ($page - 1) * $size;
|
||||
$rows = $qb->setFirstResult($offset)->setMaxResults($size)->getQuery()->getResult();
|
||||
|
||||
// Map to array
|
||||
$data = array_map(function (UsersOrganizations $uo) {
|
||||
$user = $uo->getUsers();
|
||||
return [
|
||||
'pictureUrl' => $user->getPictureUrl(),
|
||||
'name' => $user->getSurname(),
|
||||
'prenom' => $user->getName(),
|
||||
'email' => $user->getEmail(),
|
||||
'isConnected' => $this->userService->isUserConnected($user->getUserIdentifier()),
|
||||
'showUrl' => $this->generateUrl('user_show', [
|
||||
'id' => $user->getId(),
|
||||
'organizationId' => $uo->getOrganization()->getId(),
|
||||
]),
|
||||
];
|
||||
}, $rows);
|
||||
|
||||
// Return Tabulator-compatible response
|
||||
$lastPage = (int)ceil($total / $size);
|
||||
|
||||
return $this->json([
|
||||
'data' => $data,
|
||||
'last_page' => $lastPage,
|
||||
'total' => $total,
|
||||
]);
|
||||
}
|
||||
|
||||
throw $this->createAccessDeniedException(self::ACCESS_DENIED);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ class UserService
|
|||
|
||||
/**
|
||||
* Check if the user have the rights to access the page
|
||||
* Self check can be skipped when checking access for the current user
|
||||
*
|
||||
* @param User $user
|
||||
* @param bool $skipSelfCheck
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<div class="col d-flex justify-content-between align-items-center ">
|
||||
<div class="d-flex ">
|
||||
{% if organization.logoUrl %}
|
||||
<img src="{{aws_url ~ organization.logoUrl }}" alt="Organization logo"
|
||||
<img src="{{ aws_url ~ organization.logoUrl }}" alt="Organization logo"
|
||||
class="rounded-circle" style="width:40px; height:40px;">
|
||||
{% endif %}
|
||||
<h1 class="mb-4 ms-3">{{ organization.name|title }} - Dashboard</h1>
|
||||
|
|
@ -43,31 +43,54 @@
|
|||
<div class="row">
|
||||
<div class="col-9">
|
||||
<div class="row mb-4">
|
||||
<div class="col mb-3 mb-sm-0">
|
||||
{% include 'user/userListSmall.html.twig' with {
|
||||
title: 'Nouveaux utilisateurs',
|
||||
users: newUsers,
|
||||
empty_message: 'Aucun nouveaux utilisateurs trouvé.',
|
||||
organizationId: organization.id
|
||||
} %}
|
||||
<div class="col mb-3 mb-sm-0 card me-2">
|
||||
<div class="card-header">
|
||||
<h2>
|
||||
Nouveaux utilisateurs
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="tabulator-userListSmall" data-controller="user"
|
||||
data-user-aws-value="{{ aws_url }}"
|
||||
data-user-new-value="true"
|
||||
data-user-list-small-value="true"
|
||||
data-user-org-id-value="{{ organization.id }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col mb-3 mb-sm-0 card">
|
||||
<div class="card-header">
|
||||
<h2>
|
||||
Administrateurs
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="tabulator-userListSmallAdmin" data-controller="user"
|
||||
data-user-aws-value="{{ aws_url }}"
|
||||
data-user-admin-value="true"
|
||||
data-user-list-small-value="true"
|
||||
data-user-org-id-value="{{ organization.id }}">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card mb-3 ">
|
||||
<div class="card-header">
|
||||
<h2>
|
||||
Mes utilisateurs
|
||||
</h2>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="tabulator-userListOrganization" data-controller="user"
|
||||
data-user-aws-value="{{ aws_url }}"
|
||||
data-user-statut-value="true"
|
||||
data-user-list-organization-value="true"
|
||||
data-user-org-id-value="{{ organization.id }}">
|
||||
</div>
|
||||
<div class="col mb-3 mb-sm-0">
|
||||
{% include 'user/userListSmall.html.twig' with {
|
||||
title: 'Administrateurs',
|
||||
users: adminUsers,
|
||||
empty_message: 'Aucun administrateur trouvé.'
|
||||
} %}
|
||||
</div>
|
||||
</div>
|
||||
{# <div class="m-auto"> #}
|
||||
{% include 'user/userList.html.twig' with {
|
||||
title: 'Mes utilisateurs',
|
||||
organizationId: organization.id,
|
||||
empty_message: 'Aucun utilisateurs trouvé.'
|
||||
} %}
|
||||
{# </div> #}
|
||||
{# APPLICATION ROW #}
|
||||
<div class="row ">
|
||||
<div class="row">
|
||||
{% for application in applications %}
|
||||
<div class="col-6 mb-3">
|
||||
{% include 'application/appSmall.html.twig' with {
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
<a href="{{ path('user_new') }}" class="btn btn-primary">Ajouter un utilisateur</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{#TODO: Remove/Adapt userList depending on design review #}
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') or is_granted('ROLE_ADMIN') %}
|
||||
{% if usersByOrganization|length == 0 %}
|
||||
<div class="alert alert-info">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
{% extends 'base.html.twig' %}
|
||||
|
||||
{% block title %}User Profile{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
{% if is_granted('ROLE_SUPER_ADMIN') %}
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h1>Gestion Utilisateurs</h1>
|
||||
<a href="{{ path('user_new') }}" class="btn btn-primary">Ajouter un utilisateur</a>
|
||||
</div>
|
||||
|
||||
|
||||
{% if users|length == 0 %}
|
||||
<div class="alert alert-info">
|
||||
<h4>Aucun utilisateur trouvé</h4>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div id="tabulator-userList" data-controller="user"
|
||||
data-user-aws-value="{{ aws_url }}"
|
||||
data-user-list-value="true"
|
||||
data-user-test-value="true">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="w-100 h-100 p-5 m-auto">
|
||||
<div class="alert alert-warning">
|
||||
<h4>Accès limité</h4>
|
||||
<p>Vous n'avez pas les permissions nécessaires pour voir la liste des utilisateurs.</p>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
{% block body %}
|
||||
|
||||
<div class="card border-0">
|
||||
<div class="card-title p-3 d-flex justify-content-between align-items-center ">
|
||||
<h3>{{ title }}</h3>
|
||||
{% if organizationId is defined %}
|
||||
<a href="{{ path('user_new', {'organizationId': organizationId}) }}" class="btn btn-primary">Ajouter un utilisateur</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table align-middle table-borderless">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Picture</th>
|
||||
<th>Email</th>
|
||||
<th>Visualiser</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% if users|length == 0 %}
|
||||
<tr>
|
||||
<td colspan="3" class="text-center">{{ empty_message }}</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td>
|
||||
{% if user.pictureUrl %}
|
||||
<img src="{{ aws_url ~ user.pictureUrl }}" alt="User profile pic"
|
||||
class="rounded-circle" style="width:40px; height:40px;">
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
{% if organizationId is defined %}
|
||||
<a href="{{ path('user_show', {'id': user.id, 'organizationId': organizationId}) }}"
|
||||
class="p-3 align-middle color-primary">
|
||||
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
||||
</a>
|
||||
{% else %}
|
||||
<a href="{{ path('user_show', {'id': user.id}) }}"
|
||||
class="p-3 align-middle color-primary">
|
||||
{{ ux_icon('fa6-regular:eye', {height: '30px', width: '30px'}) }}
|
||||
</a>
|
||||
{% endif %}
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
Loading…
Reference in New Issue