diff --git a/assets/controllers/organization_controller.js b/assets/controllers/organization_controller.js index fae9cf4..dba278f 100644 --- a/assets/controllers/organization_controller.js +++ b/assets/controllers/organization_controller.js @@ -1,25 +1,89 @@ 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 { - static values = {data: String, aws: String}; + static values = {aws: String}; connect() { - - this.table = new Tabulator("#tabulator-org", { - data: JSON.parse(this.dataValue), layout: "fitColumns", columns: [ - { - title: "Logo", field: "logoUrl", formatter: "image", - width: 100, - formatterParams: {height: "50px", width: "50px", urlPrefix: this.awsValue, urlSuffix: ""}, + const table = new Tabulator("#tabulator-org", { + // Register locales here + 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" }, }, - {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", field: "showUrl", hozAlign: "center", - width: 80, + width: 100, + vertAlign: "middle", headerSort: false, formatter: (cell) => { const url = cell.getValue(); @@ -27,8 +91,8 @@ export default class extends Controller { return ` @@ -37,13 +101,7 @@ export default class extends Controller { } return ''; } - }] + }], }); } - - disconnect() { - if (this.table) { - this.table.destroy(); - } - } } \ No newline at end of file diff --git a/assets/styles/tabulator.css b/assets/styles/tabulator.css index 48a29c1..327cd61 100644 --- a/assets/styles/tabulator.css +++ b/assets/styles/tabulator.css @@ -2,6 +2,7 @@ /* Remove outer table border */ .tabulator { border: none !important; + font-size: 18px !important; } /* Remove header and row cell borders */ @@ -50,3 +51,64 @@ .tabulator-header .tabulator-col{ 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;*/ +/*}*/ \ No newline at end of file diff --git a/src/Controller/OrganizationController.php b/src/Controller/OrganizationController.php index fdc2469..f62675c 100644 --- a/src/Controller/OrganizationController.php +++ b/src/Controller/OrganizationController.php @@ -16,6 +16,7 @@ use App\Service\UserService; use Doctrine\ORM\EntityManagerInterface; use Exception; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\Routing\Attribute\Route; use App\Entity\Organizations; @@ -254,5 +255,74 @@ class OrganizationController extends AbstractController 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 + ]); + } + }