Added tabulator to organization index
This commit is contained in:
parent
e818a17371
commit
00c58b55d1
|
|
@ -1,25 +1,89 @@
|
||||||
import {Controller} from '@hotwired/stimulus'
|
import {Controller} from '@hotwired/stimulus'
|
||||||
import {TabulatorFull as Tabulator} from "tabulator-tables";
|
// Important: include a build with Ajax + pagination (TabulatorFull is simplest)
|
||||||
|
import {TabulatorFull as Tabulator} from 'tabulator-tables';
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static values = {data: String, aws: String};
|
static values = {aws: String};
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
|
const table = new Tabulator("#tabulator-org", {
|
||||||
this.table = new Tabulator("#tabulator-org", {
|
// Register locales here
|
||||||
data: JSON.parse(this.dataValue), layout: "fitColumns", columns: [
|
langs: {
|
||||||
{
|
fr: {
|
||||||
title: "Logo", field: "logoUrl", formatter: "image",
|
ajax: {
|
||||||
width: 100,
|
loading: "Chargement...",
|
||||||
formatterParams: {height: "50px", width: "50px", urlPrefix: this.awsValue, urlSuffix: ""},
|
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" },
|
||||||
},
|
},
|
||||||
{title: "Nom", field: "name", headerFilter: "input"},
|
},
|
||||||
{title: "Email", field: "email", headerFilter: "input"},
|
|
||||||
|
locale: "fr", //'en' for English, 'fr' for French (en is default, no need to include it)
|
||||||
|
|
||||||
|
ajaxURL: "/organization/data",
|
||||||
|
ajaxConfig: "GET",
|
||||||
|
pagination: true,
|
||||||
|
paginationMode: "remote",
|
||||||
|
paginationSize: 10,
|
||||||
|
//paginationSizeSelector: [5, 10, 20, 50], // Désactivé pour l'instant car jpp faire de jolie style
|
||||||
|
|
||||||
|
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: [
|
||||||
|
{
|
||||||
|
title: "Logo",
|
||||||
|
field: "logoUrl",
|
||||||
|
formatter: "image",
|
||||||
|
formatterParams: {
|
||||||
|
height: "50px",
|
||||||
|
width: "50px",
|
||||||
|
urlPrefix: this.awsValue,
|
||||||
|
urlSuffix: "",
|
||||||
|
},
|
||||||
|
width: 100,
|
||||||
|
},
|
||||||
|
// TODO: regarder quel style est mieux
|
||||||
|
{title: "Nom", field: "name", headerFilter: "input", widthGrow: 2, vertAlign: "middle", headerHozAlign: "left"},
|
||||||
|
{title: "Email", field: "email", headerFilter: "input", widthGrow: 2, vertAlign: "middle", hozAlign: "center"},
|
||||||
{
|
{
|
||||||
title: "Actions",
|
title: "Actions",
|
||||||
field: "showUrl",
|
field: "showUrl",
|
||||||
hozAlign: "center",
|
hozAlign: "center",
|
||||||
width: 80,
|
width: 100,
|
||||||
|
vertAlign: "middle",
|
||||||
headerSort: false,
|
headerSort: false,
|
||||||
formatter: (cell) => {
|
formatter: (cell) => {
|
||||||
const url = cell.getValue();
|
const url = cell.getValue();
|
||||||
|
|
@ -27,8 +91,8 @@ export default class extends Controller {
|
||||||
return `
|
return `
|
||||||
<a href="${url}" class="p-3 align-middle color-primary" title="Voir">
|
<a href="${url}" class="p-3 align-middle color-primary" title="Voir">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg"
|
<svg xmlns="http://www.w3.org/2000/svg"
|
||||||
width="30px"
|
width="35px"
|
||||||
height="30px"
|
height="35px"
|
||||||
viewBox="0 0 576 512">
|
viewBox="0 0 576 512">
|
||||||
<path fill="currentColor"
|
<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>
|
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>
|
||||||
|
|
@ -37,13 +101,7 @@ export default class extends Controller {
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
}]
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
disconnect() {
|
|
||||||
if (this.table) {
|
|
||||||
this.table.destroy();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
/* Remove outer table border */
|
/* Remove outer table border */
|
||||||
.tabulator {
|
.tabulator {
|
||||||
border: none !important;
|
border: none !important;
|
||||||
|
font-size: 18px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Remove header and row cell borders */
|
/* Remove header and row cell borders */
|
||||||
|
|
@ -50,3 +51,64 @@
|
||||||
.tabulator-header .tabulator-col{
|
.tabulator-header .tabulator-col{
|
||||||
background: none !important;
|
background: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tabulator-footer {border-top: none !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabulator-footer .tabulator-page.active{
|
||||||
|
background-color: var(--primary-blue-light) !important;
|
||||||
|
border: 1px solid var(--primary-blue-light) !important;
|
||||||
|
color: #FFFFFF/* text color */ !important
|
||||||
|
}
|
||||||
|
.tabulator-footer .tabulator-page {
|
||||||
|
background-color: transparent !important;
|
||||||
|
border: 1px solid var(--primary-blue-light) !important;
|
||||||
|
color: var(--black-font)/* text color */ !important;
|
||||||
|
}
|
||||||
|
.tabulator-footer .tabulator-page:hover,
|
||||||
|
.tabulator-footer .tabulator-page.active:hover{
|
||||||
|
background-color: var(--primary-blue-dark) !important;
|
||||||
|
border: 1px solid var(--primary-blue-dark) !important;
|
||||||
|
color: #FFFFFF/* text color */ !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabulator-footer select{
|
||||||
|
border: 1px solid var(--primary-blue-light) !important;
|
||||||
|
background-color: transparent !important;
|
||||||
|
color: var(--black-font)/* text color */ !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabulator-header input{
|
||||||
|
border: 0;
|
||||||
|
border-radius: 10px;
|
||||||
|
height: 40px;
|
||||||
|
background-color: lightgray !important;
|
||||||
|
padding-left: 15px !important;
|
||||||
|
}
|
||||||
|
.tabulator-header input::placeholder{
|
||||||
|
color: var(--black-font) !important;
|
||||||
|
font-size: 14px !important;
|
||||||
|
opacity: 1 !important; /* Firefox */
|
||||||
|
}
|
||||||
|
|
||||||
|
.tabulator-header input:focus {
|
||||||
|
border:0;
|
||||||
|
}
|
||||||
|
.tabulator .tabulator-header .tabulator-col .tabulator-col-title {
|
||||||
|
font-size: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Select hover Désactivé pour l'instant car jpp faire de jolie style */
|
||||||
|
/*#tabulator-org .tabulator-footer select:hover {*/
|
||||||
|
/* border: 1px solid var(--primary-blue-dark) !important;*/
|
||||||
|
/* background-color: var(--primary-blue-dark) !important;*/
|
||||||
|
/* color: #fff !important;*/
|
||||||
|
/*}*/
|
||||||
|
|
||||||
|
/*.tabulator-footer select:focus {*/
|
||||||
|
/* border: 1px solid var(--primary-blue-dark) !important;*/
|
||||||
|
/* outline: none !important;*/
|
||||||
|
/* background-color: var(--primary-blue-dark) !important;*/
|
||||||
|
/* color: #fff !important;*/
|
||||||
|
/*}*/
|
||||||
|
|
@ -16,6 +16,7 @@ use App\Service\UserService;
|
||||||
use Doctrine\ORM\EntityManagerInterface;
|
use Doctrine\ORM\EntityManagerInterface;
|
||||||
use Exception;
|
use Exception;
|
||||||
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
|
||||||
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\Routing\Attribute\Route;
|
use Symfony\Component\Routing\Attribute\Route;
|
||||||
use App\Entity\Organizations;
|
use App\Entity\Organizations;
|
||||||
|
|
@ -254,5 +255,74 @@ class OrganizationController extends AbstractController
|
||||||
return $this->redirectToRoute('organization_index');
|
return $this->redirectToRoute('organization_index');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// API endpoint to fetch organization data for Tabulator
|
||||||
|
#[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));
|
||||||
|
|
||||||
|
$sorters = $request->query->all('sorters');
|
||||||
|
$filters = $request->query->all('filters');
|
||||||
|
|
||||||
|
|
||||||
|
$user = $this->userService->getUserByIdentifier($this->getUser()->getUserIdentifier());
|
||||||
|
|
||||||
|
$qb = $this->entityManager->getRepository(Organizations::class)->createQueryBuilder('o')
|
||||||
|
->where('o.isDeleted = :del')->setParameter('del', false);
|
||||||
|
|
||||||
|
// Example: apply filters (basic equals/like)
|
||||||
|
foreach ($filters as $f) {
|
||||||
|
if (!isset($f['field'], $f['type'])) { continue; }
|
||||||
|
$param = 'p_' . $f['field'];
|
||||||
|
if ($f['type'] === 'like' || $f['type'] === 'contains') {
|
||||||
|
$qb->andWhere("LOWER(o.{$f['field']}) LIKE :$param")
|
||||||
|
->setParameter($param, '%' . mb_strtolower((string)$f['value']) . '%');
|
||||||
|
} elseif ($f['type'] === '=') {
|
||||||
|
$qb->andWhere("o.{$f['field']} = :$param")
|
||||||
|
->setParameter($param, $f['value']);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example: apply sorters
|
||||||
|
foreach ($sorters as $s) {
|
||||||
|
if (!isset($s['field'], $s['dir'])) { continue; }
|
||||||
|
$dir = strtolower($s['dir']) === 'desc' ? 'DESC' : 'ASC';
|
||||||
|
$qb->addOrderBy('o.' . $s['field'], $dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count total
|
||||||
|
$countQb = clone $qb;
|
||||||
|
$total = (int)$countQb->select('COUNT(o.id)')->getQuery()->getSingleScalarResult();
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
$offset = ($page - 1) * $size;
|
||||||
|
$rows = $qb->setFirstResult($offset)->setMaxResults($size)->getQuery()->getResult();
|
||||||
|
|
||||||
|
// Map to array
|
||||||
|
$data = array_map(function(Organizations $org) {
|
||||||
|
return [
|
||||||
|
'id' => $org->getId(),
|
||||||
|
'name' => $org->getName(),
|
||||||
|
'email' => $org->getEmail(),
|
||||||
|
'logoUrl' => $org->getLogoUrl() ?: null,
|
||||||
|
'active' => $org->isActive(),
|
||||||
|
'showUrl' => $this->generateUrl('organization_show', ['id' => $org->getId()]),
|
||||||
|
];
|
||||||
|
}, $rows);
|
||||||
|
|
||||||
|
// Tabulator expects: data, last_page (total pages), or total row count depending on config
|
||||||
|
$lastPage = (int)ceil($total / $size);
|
||||||
|
|
||||||
|
return $this->json([
|
||||||
|
'data' => $data,
|
||||||
|
'last_page' => $lastPage,
|
||||||
|
'total' => $total, // optional, useful for debugging
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue