From b6f93fd21b2c9e2bf11bc9f002608d100df27a36 Mon Sep 17 00:00:00 2001
From: pfleu <fleutotp@gmail.com>
Date: Tue, 23 May 2023 23:20:45 +0200
Subject: [PATCH] =?UTF-8?q?Page=20entr=C3=A9e=20:=20tabs=20pour=20naviguer?=
 =?UTF-8?q?=20d'un=20lexique=20=C3=A0=20l'autre=20et=20copier/supprimer=20?=
 =?UTF-8?q?l'entr=C3=A9e=20vers=20un=20autre=20lexique.=20Int=C3=A9gration?=
 =?UTF-8?q?=20panneau=20de=20gestion=20des=20labels.?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 config/packages/twig.yaml                |  1 +
 public/assets/css/app.css                | 39 +++++++++-
 public/assets/js/app.js                  | 84 +++++++++++---------
 src/Controller/EntryController.php       | 97 +++++++++++++-----------
 src/Controller/HeadwordController.php    |  3 +-
 src/Controller/LexiconController.php     |  2 +-
 src/Controller/WiktionnaryController.php | 35 +++++++++
 src/Entity/Lexicon.php                   | 14 ++++
 templates/base.html.twig                 | 20 +++++
 templates/entry/_entryLabels.html.twig   | 21 +++++
 templates/entry/show.html.twig           | 79 +++++++++++++++++++
 templates/lexicon/show.html.twig         |  8 +-
 templates/nav.html.twig                  |  5 ++
 13 files changed, 321 insertions(+), 87 deletions(-)
 create mode 100644 src/Controller/WiktionnaryController.php
 create mode 100644 templates/entry/_entryLabels.html.twig
 create mode 100644 templates/entry/show.html.twig

diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml
index 32e464a..6932c92 100644
--- a/config/packages/twig.yaml
+++ b/config/packages/twig.yaml
@@ -4,6 +4,7 @@ twig:
     globals:
         languages: '@App\Manager\LanguagesManager'
         label_manager: '@App\Manager\LabelManager'
+        lexicon_manager: '@App\Manager\LexiconManager'
 
 when@test:
     twig:
diff --git a/public/assets/css/app.css b/public/assets/css/app.css
index a4b78a7..1b3b5a1 100644
--- a/public/assets/css/app.css
+++ b/public/assets/css/app.css
@@ -155,7 +155,11 @@ table.label-table > tbody > tr > td {
 .bg-definition {
     background-color: #D6EADA;
 }
-.badge.badge-milestone {line-height: 1.3}
+.badge.badge-milestone {
+    line-height: 1.3;
+    padding-top: 0;
+    padding-bottom: 0;
+}
 
 .card-body.card-definition {
     margin-top: 2px;
@@ -171,4 +175,37 @@ table.label-table > tbody > tr > td {
 
 .faded {
     opacity: 0.5;
+}
+.ajax-link {
+    cursor: pointer;
+}
+
+.nav-tabs .nav-link.tab-pink {
+    background-color: #FDF6F6;
+}
+.nav-tabs .nav-link.tab-dark-pink {
+    background-color: #FFE4E4;
+}
+.nav-tabs .nav-link.tab-grey {
+    background-color: #E4E4E4;
+    color: grey;
+}
+.nav-tabs .nav-link {
+    color: black;
+    border-bottom: 0;
+}
+.nav-tabs a.nav-link.active {
+    font-weight: bold;
+}
+
+#tabContent {
+    background-color: #FDF6F6;
+    padding: 15px;
+}
+#tabContent.tab-wiktionnary {
+    background-color: #FFE4E4;
+}
+
+#tableLabels.table > tbody > tr > td {
+    height: 52px;
 }
\ No newline at end of file
diff --git a/public/assets/js/app.js b/public/assets/js/app.js
index fd5681f..2f1e070 100644
--- a/public/assets/js/app.js
+++ b/public/assets/js/app.js
@@ -326,45 +326,61 @@ function initializeFilterFormFieldsBackground() {
 function initializeAjaxLinks() {
 
     $('body').on('click', '.ajax-link', function () {
-        var data;
         var $overlay = $('#overlay').show();
-        var $target = $(this).closest('.blink-target').length ? $(this).closest('.blink-target') : $(this);
-        $target.addClass('faded');
+        var method = $(this).data('method');
 
-        // on récupère le json
-        if ($(this).data('json')) {
-            data = JSON.stringify($(this).data('json'));
-        } else {
-            data = $('#'+$(this).data('json-dynamic')).val();
-        }
+        if (!method || method.toUpperCase() === 'GET') {
 
-        $.ajax({
-            type: $(this).data('method'),
-            url: $(this).data('url'),
-            contentType: "application/json",
-            dataType: "json",
-            data: data,
-            processData: false,
-            success: function(response, textStatus, xhr) {
-// console.log(response);
-                location.reload();
-            },
-            complete: function () {
-                $overlay.hide();
-                // $target = $target.removeClass('faded');
-            },
-            error: function (jqXHR, textStatus, errorThrown) {
-                // console.log(jqXHR.responseText, textStatus, errorThrown);
-                $target = $target.removeClass('faded');
-                var message = JSON.parse(jqXHR.responseText);
-                if ($('#bootstrap-modal').is(':visible')) {
-                    $('#bootstrap-modal').find('.ajax-message').html('<div class="alert alert-danger">' + message.error + '</div>')
-                } else {
-                    $('.ajax-message').html('<div class="alert alert-danger">' + message.error + '</div>')
+            $.ajax({
+                type: 'GET',
+                url: $(this).data('url'),
+                dataType: "json",
+                success: function (response, textStatus, xhr) {
+                    location.reload();
+                },
+                error: function (jqXHR, textStatus, errorThrown) {
+                    console.log(jqXHR.responseText, textStatus, errorThrown);
+                    $overlay.hide();
                 }
-                $overlay.hide();
+            })
+
+        } else {
+
+            var data;
+            var $target = $(this).closest('.blink-target').length ? $(this).closest('.blink-target') : $(this);
+            $target.addClass('faded');
+
+            // on récupère le json
+            if ($(this).data('json')) {
+                data = JSON.stringify($(this).data('json'));
+            } else {
+                data = $('#' + $(this).data('json-dynamic')).val();
             }
-        })
+
+            $.ajax({
+                type: method,
+                url: $(this).data('url'),
+                contentType: "application/json",
+                dataType: "json",
+                data: data,
+                processData: false,
+                success: function (response, textStatus, xhr) {
+                    // console.log(response);
+                    location.reload();
+                },
+                error: function (jqXHR, textStatus, errorThrown) {
+                    // console.log(jqXHR.responseText, textStatus, errorThrown);
+                    $target = $target.removeClass('faded');
+                    var message = JSON.parse(jqXHR.responseText);
+                    if ($('#bootstrap-modal').is(':visible')) {
+                        $('#bootstrap-modal').find('.ajax-message').html('<div class="alert alert-danger">' + message.error + '</div>')
+                    } else {
+                        $('.ajax-message').html('<div class="alert alert-danger">' + message.error + '</div>')
+                    }
+                    $overlay.hide();
+                }
+            })
+        }
     })
 
 }
\ No newline at end of file
diff --git a/src/Controller/EntryController.php b/src/Controller/EntryController.php
index bba3a24..da121c1 100644
--- a/src/Controller/EntryController.php
+++ b/src/Controller/EntryController.php
@@ -4,6 +4,7 @@ namespace App\Controller;
 
 use App\Entity\Entry;
 use App\Entity\Label;
+use App\Entity\Lexicon;
 use App\Entity\Log;
 use App\Form\SearchStringType;
 use App\Manager\LabelManager;
@@ -22,55 +23,15 @@ use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
 class EntryController extends AppBaseController
 {
     /**
-     * @Route("/{id}", name="app_entry_show", methods={"GET"})
+     * @Route("/{id}/show", name="app_entry_show", requirements={"id" = "\d+"})
      */
-    public function show(ManagerRegistry $doctrine, Request $request, Entry $entry): Response
+    public function show(LabelManager $labelManager, Request $request, Entry $entry)
     {
-        $em = $doctrine->getManager();
-        $sortingColumn = null;
-        $sortingOrder = null;
-        $this->getSortingParameters($request, $sortingColumn, $sortingOrder, 'entry_show');
-
-        $form = $this->createForm(SearchStringType::class,null, array('method' => 'GET'));
-        $form->handleRequest($request);
-        $filter = $form->getData();
-
-        $filter['entry'] = $entry;
-
-        // Entrées triées par createdAt
-        // On ajoute l'ordre d'ajout de l'entrée (on se base sur createdAt) comme index
-        $entriesOrdered = $em->getRepository(Entry::class)->filter($filter, 'createdAt', 'ASC');
-        foreach ($entriesOrdered as $key => $entryOrdered) {
-            $entryOrdered->setAddingOrder($key);
-        }
-        // On ajoute l'id de l'entrée en index ( => tableau assciatif 'id'  => $entry (avec colonne addingOrder)
-        $entriesOrderedIndexedById = [];
-        foreach ($entriesOrdered as $entryOrdered) {
-            $entriesOrderedIndexedById[$entryOrdered->getId()] = $entryOrdered;
-        }
-
-        // Entrées résultat cherché (triées avec le filtre sortingColumn)
-        // On ajoute l'id de l'entrée en index ( => tableau assciatif 'id'  => $entry (avec colonne addingOrder)
-        $entries = $em->getRepository(Entry::class)->filter($filter, $sortingColumn, $sortingOrder);
-        $entriesIndexedById = [];
-        foreach ($entries as $entry) {
-            $entriesIndexedById[$entry->getId()] = $entry;
-        }
-        // On ajoute l'ordre d'ajout de l'entrée à partir du premier tableau
-        foreach ($entriesIndexedById as $id => $entryIndexedById) {
-            $entryIndexedById->setAddingOrder($entriesOrderedIndexedById[$id]->getAddingOrder());
-        }
-
-//        $entriesWithAddingOrderIndex = $entriesOrdered;
-//        usort($entriesWithAddingOrderIndex, function ($a, $b) { return $b->getCreatedAt()->getTimestamp() - $a->getCreatedAt()->getTimestamp(); });
 
-        return $this->render('entry/show.html.twig', [
-            'entries'           => $entriesIndexedById,
-            'entry'           => $entry,
-            'form'              => $form->createView(),
-            'sortingColumn'     => $sortingColumn,
-            'sortingOrder'      => $sortingOrder,
-        ]);
+        return $this->render('entry/show.html.twig', array(
+            'entry' => $entry,
+            'wiktionnaryLexicon' => false,
+        ));
     }
 
     /**
@@ -88,6 +49,50 @@ class EntryController extends AppBaseController
         ));
     }
 
+    /**
+     * @Route("/{id}/copy/{lexiconId}", name="app_entry_copy", requirements={"id" = "\d+"})
+     * @ParamConverter("entry", options={"id" = "id"})
+     * @ParamConverter("lexicon", options={"id" = "lexiconId"})
+     */
+    public function copyEntry(Request $request, Entry $entry, Lexicon $lexicon)
+    {
+        $forwardRequest = Request::create(
+            $this->generateUrl('api_copy_entries'), 'POST', [], [], [], [],
+            json_encode([
+                'entries' => [$entry->getId()],
+                'merge' => Lexicon::MERGE_OVERWRITE,
+                'target_lex' => [$lexicon->getId()],
+            ])
+        );
+
+        $response =  $this->forward('App\Controller\ApiEntryController::copyEntries', array(
+            'request' => $forwardRequest,
+//            '_route' => $request->attributes->get('_route'),
+//            '_route_params' => $request->attributes->get('_route_params'),
+        ));
+
+        if ($response->getStatusCode() == 200) {
+            $this->addFlash('success', "Copie effectuée");
+        } else {
+            $message = (array) json_decode($response->getContent());
+            $this->addFlash('danger', reset($message) ?? 'Requête terminée');
+        }
+
+        return $this->redirectToRoute('app_entry_show', ['id' => $entry->getId()]);
+    }
+
+    /**
+     * @Route("/{id}/delete", name="app_entry_delete", requirements={"id" = "\d+"})
+     */
+    public function deleteEntry(Request $request, Entry $entry)
+    {
+        $this->em->remove($entry);
+        $this->em->flush();
+        $this->addFlash('success', sprintf("L'entrée a été supprimée du lexique %s", $entry->getLexicon()));
+
+        return $this->redirect($request->get('backUrl') ? : $this->generateUrl('app_lexicon_show', ['id' => $entry->getLexicon()->getId()]));
+    }
+
     /**
      * PAS UTILISÉ
      *
diff --git a/src/Controller/HeadwordController.php b/src/Controller/HeadwordController.php
index 9f3d85a..a35ed01 100644
--- a/src/Controller/HeadwordController.php
+++ b/src/Controller/HeadwordController.php
@@ -15,6 +15,7 @@ use App\Repository\HeadwordRepository;
 use Doctrine\Persistence\ManagerRegistry;
 use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
 use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpFoundation\Response;
 use Symfony\Component\Routing\Annotation\Route;
@@ -63,7 +64,7 @@ class HeadwordController extends AppBaseController
 
         $this->em->flush();
 
-        return $this->redirectToRoute('app_lexicon_show', ['id' => $request->get('lexiconId')]);
+        return new JsonResponse();
     }
 
 }
diff --git a/src/Controller/LexiconController.php b/src/Controller/LexiconController.php
index 7f77294..c4ee8e0 100644
--- a/src/Controller/LexiconController.php
+++ b/src/Controller/LexiconController.php
@@ -133,7 +133,7 @@ class LexiconController extends AppBaseController
     /**
      * @Route("/{id}/copy-entries", name="app_lexicon_copy_entries")
      */
-    public function copySelection(LabelManager $labelManager, Request $request, Lexicon $lexicon, EntryRepository $entryRepository)
+    public function copySelection(Request $request, Lexicon $lexicon)
     {
         $formData = $request->get('form');
         $selectedIds = $formData['selected_entries'] ?? null;
diff --git a/src/Controller/WiktionnaryController.php b/src/Controller/WiktionnaryController.php
new file mode 100644
index 0000000..78a8563
--- /dev/null
+++ b/src/Controller/WiktionnaryController.php
@@ -0,0 +1,35 @@
+<?php
+
+namespace App\Controller;
+
+use App\Entity\Entry;
+use App\Entity\Label;
+use App\Entity\Log;
+use App\Form\SearchStringType;
+use App\Manager\LabelManager;
+use App\Repository\EntryRepository;
+use Doctrine\Persistence\ManagerRegistry;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\IsGranted;
+use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
+use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\HttpFoundation\Response;
+use Symfony\Component\Routing\Annotation\Route;
+use Sensio\Bundle\FrameworkExtraBundle\Configuration\ParamConverter;
+
+/**
+ * @Route("/wiktionnary")
+ */
+class WiktionnaryController extends AppBaseController
+{
+    /**
+     * @Route("/{id}/show", name="app_wiktionnary_show", requirements={"id" = "\d+"})
+     */
+    public function show(LabelManager $labelManager, Request $request, Entry $entry)
+    {
+
+        return $this->render('entry/show.html.twig', array(
+            'entry' => $entry,
+        ));
+    }
+
+}
diff --git a/src/Entity/Lexicon.php b/src/Entity/Lexicon.php
index 7e12d0d..1dac381 100644
--- a/src/Entity/Lexicon.php
+++ b/src/Entity/Lexicon.php
@@ -201,6 +201,20 @@ class Lexicon
         }
     }
 
+    /**
+     * @param Headword $headword
+     * @return Entry|mixed|null
+     */
+    public function getEntryForHeadword(Headword $headword)
+    {
+        foreach ($this->getEntries() as $entry) {
+            if ($entry->getHeadword() === $headword) {
+                return $entry;
+            }
+        }
+        return null;
+    }
+
     public function getId(): ?int
     {
         return $this->id;
diff --git a/templates/base.html.twig b/templates/base.html.twig
index fc74482..77af615 100644
--- a/templates/base.html.twig
+++ b/templates/base.html.twig
@@ -93,6 +93,26 @@
         </div>
     </div>
 
+    {# CONFIRM MODAL #}
+    <div id="confirm-dialog" class="modal fade " tabindex="-1" role="dialog" aria-hidden="true" data-keyboard="false"
+         data-backdrop="static" data-show-on-load="{{ showConfirmDialog|default(false) }}">
+        <div class="modal-dialog">
+            <div class="modal-content">
+                <div class="modal-header">
+                    <h4 class="modal-title" id="bootstrap-modal-title">{{ 'Confirmation'|trans }}</h4>
+                    <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
+                </div>
+                <div class="modal-body">
+                    <div id="confirm-dialog-message" class="pb-5">{{ confirmMessage|default('')|raw }}</div>
+                    <div class="text-center">
+                        <a class="btn btn-danger btn-ok" href="{{ confirmUrl|default('#') }}">{{ 'Confirmer'|trans }}</a>
+                        <button type="button" class="btn btn-light" data-bs-dismiss="modal" aria-label="Close">{{ "Annuler"|trans }}</button>
+                    </div>
+                </div>
+            </div>
+        </div>
+    </div>
+
 </div>
 </body>
 </html>
diff --git a/templates/entry/_entryLabels.html.twig b/templates/entry/_entryLabels.html.twig
new file mode 100644
index 0000000..05cf4c5
--- /dev/null
+++ b/templates/entry/_entryLabels.html.twig
@@ -0,0 +1,21 @@
+
+<table id="tableLabels" class="table table-bordered">
+    <tbody>
+    <tr>
+        <td class="col-md-2 modal-form" data-url="{{ path('app_entry_choose_label', {id: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_GENERAL")}) }}">
+            {% for label in entry.headword.generalLabels %}<span class="blink-target">{% include "label/_labelBadge.html.twig" %}</span>{% endfor %}
+        </td>
+    </tr>
+    <tr>
+        <td class="col-md-2 modal-form" data-url="{{ path('app_entry_choose_label', {id: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_INSTITUTIONAL")}) }}">
+            {% for label in entry.headword.institutionalLabels %}<span class="blink-target">{% include "label/_labelBadge.html.twig" %}</span>{% endfor %}
+        </td>
+    </tr>
+    <tr>
+        <td class="col-md-2 modal-form" data-url="{{ path('app_entry_choose_label', {id: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_MILESTONE")}) }}">
+            {% for label in entry.headword.milestoneLabels %}<span class="blink-target">{% include "label/_labelBadge.html.twig" %}</span>{% endfor %}
+        </td>
+    </tr>
+    </tbody>
+</table>
+
diff --git a/templates/entry/show.html.twig b/templates/entry/show.html.twig
new file mode 100644
index 0000000..a814231
--- /dev/null
+++ b/templates/entry/show.html.twig
@@ -0,0 +1,79 @@
+{% extends 'base.html.twig' %}
+{% import 'macros.html.twig' as macros %}
+{% block container %}container-fluid{% endblock %}
+
+{% block title %}{{ entry|capitalize }}{% endblock %}
+
+{% block body %}
+
+    <div class="row justify-content-center m-lg-5 m-sm-3">
+        <div class="col-md-12">
+
+            <h1 class="">
+                {% if not wiktionnaryLexicon %}
+                    <a href="{{ path('app_lexicon_show', {id: entry.lexicon.id}) }}" class="btn btn-dark"><i class="bi bi-arrow-90deg-left"></i></a>
+                {% endif %}
+                {{ entry|capitalize }}
+
+                {% set known = entry.headword.knownByUser(app.user) %}
+                <a title="{{ known ? 'Mot-vedette connu. Cliquer pour modifier'|trans : 'Mot-vedette non connu. Cliquer pour modifier'|trans }}" href="#"
+                   class="ajax-link" data-method="GET" data-url="{{ path('app_headword_toggle_known', {id: entry.headword.id, userId: app.user.id, lexiconId: entry.lexicon.id}) }}">
+                    {% if known %}<i class="fa fa-circle text-success"></i>{% else %}<i class="fa fa-circle text-warning"></i>{% endif %}
+                </a>
+            </h1>
+
+            <ul class="nav nav-tabs mt-3">
+                {% for lexicon in app.user.myLexicons %}
+                    {% set entryWithSameHeadwordInThisLexicon = lexicon.getEntryForHeadword(entry.headword) %}
+
+                    <li class="nav-item dropdown">
+                        <a class="nav-link {{ lexicon == entry.lexicon ? 'active' }} {{ entryWithSameHeadwordInThisLexicon ? 'tab-pink' : 'tab-grey' }} dropdown-toggle" data-bs-toggle="dropdown" href="#" role="button" aria-expanded="false">{{ lexicon }}</a>
+                        <ul class="dropdown-menu">
+                            {% if entryWithSameHeadwordInThisLexicon %}
+                                {% if lexicon != entry.lexicon %}
+                                    <li><a class="dropdown-item" href="{{ path('app_entry_show', {id: entryWithSameHeadwordInThisLexicon.id}) }}">{{ "Voir l'entrée dans ce lexique"|trans }}</a></li>
+                                {% endif %}
+                                {% set backUrl = (lexicon == entry.lexicon ? path('app_lexicon_show', {id: entry.lexicon.id}) : path('app_entry_show', {id: entry.id})) %}
+                                <li><a class="dropdown-item" href="#"
+                                       data-href="{{ path('app_entry_delete', {id: entryWithSameHeadwordInThisLexicon.id, backUrl: backUrl}) }}"
+                                       data-confirm="{{ "Confirmer la suppression ?"|trans }}" data-bs-toggle="modal" data-bs-target="#confirm-dialog">
+                                        {{ "Supprimer l'entrée dans ce lexique"|trans }}
+                                    </a></li>
+                            {% endif %}
+                            {% if lexicon != entry.lexicon %}
+                                {% if entryWithSameHeadwordInThisLexicon %}
+                                <li><a class="dropdown-item" href="#" data-href="{{ path('app_entry_copy', {id: entry.id, lexiconId: lexicon.id}) }}"
+                                       data-confirm="{{ "Confirmer la copie ? Une entrée similaire existe déjà dans le lexique que vous avez sélectionné. Si vous copiez celle-ci, le contenu de l'autre sera perdue"|trans }}" data-bs-toggle="modal" data-bs-target="#confirm-dialog">
+                                        {{ "Copier l'entrée dans ce lexique"|trans }}
+                                    </a></li>
+                                {% else %}
+                                    <li><a class="dropdown-item" href="{{ path('app_entry_copy', {id: entry.id, lexiconId: lexicon.id}) }}">{{ "Copier l'entrée dans ce lexique"|trans }}</a></li>
+                                {% endif %}
+                            {% endif %}
+                        </ul>
+                    </li>
+
+                {% endfor %}
+
+                {% if not entry.lexicon.isNewWords %}
+                    <li class="nav-item">
+                        <a class="nav-link tab-dark-pink" href="#">{{ "Wiktionnaire"|trans }}</a>
+                    </li>
+                {% endif %}
+            </ul>
+
+            <div id="tabContent" class="{{ wiktionnaryLexicon ? 'tab-wiktionnary' }}">
+
+                <div class="row">
+                    <div class="col-sm-6">
+                        {% include "entry/_entryLabels.html.twig" %}
+
+                    </div>
+                </div>
+
+            </div>
+
+        </div>
+    </div>
+
+{% endblock %}
diff --git a/templates/lexicon/show.html.twig b/templates/lexicon/show.html.twig
index 42ff4a7..abbc335 100644
--- a/templates/lexicon/show.html.twig
+++ b/templates/lexicon/show.html.twig
@@ -66,14 +66,14 @@
                                     <tr class="{{ known ? 'headword-known' }}">
                                         <td>{{entry.addingOrder }}</td>
                                         <td class="d-flex justify-content-between">
-                                            <label>
+                                            <a href="{{ path('app_entry_show', {id: entry.id}) }}">
                                                 {# "value" est utilisé par Symfo quand on soumet le form. "data-headword-id" est utilisé par la vue chooseLabel pour injecter du json dans le sliens ajax #}
                                                 <input class="me-2" type="checkbox" name="form[selected_entries][]" data-headword-id="{{ entry.headword.id }}"
                                                        value="{{ entry.id }}" id="form_selected_entries_{{ entry.id }}"/>
                                                 {{ entry }}
-                                            </label>
-                                            <a title="{{ known ? 'Mot-vedette connu. Cliquer pour modifier'|trans : 'Mot-vedette connu. Cliquer pour modifier'|trans }}"
-                                               href="{{ path('app_headword_toggle_known', {id: entry.headword.id, userId: app.user.id, lexiconId: lexicon.id}) }}">
+                                            </a>
+                                            <a title="{{ known ? 'Mot-vedette connu. Cliquer pour modifier'|trans : 'Mot-vedette non connu. Cliquer pour modifier'|trans }}"
+                                               href="#" class="ajax-link" data-method="GET" data-url="{{ path('app_headword_toggle_known', {id: entry.headword.id, userId: app.user.id, lexiconId: lexicon.id}) }}">
                                                 {% if known %}<i class="fa fa-circle text-success"></i>{% else %}<i class="fa fa-circle text-warning"></i>{% endif %}
                                             </a>
                                         </td>
diff --git a/templates/nav.html.twig b/templates/nav.html.twig
index 06cb0d8..47c362a 100644
--- a/templates/nav.html.twig
+++ b/templates/nav.html.twig
@@ -6,6 +6,11 @@
         </button>
         <div class="collapse navbar-collapse" id="navbarSupportedContent">
 
+            <form class="d-flex" role="search">
+                <input class="form-control me-2" type="search" placeholder="{{ "Rechercher"|trans }}" aria-label="Search">
+                <button class="btn btn-light me-3" type="submit"><i class="fa fa-search"></i> </button>
+            </form>
+
             {% if app.user %}
 
                 <ul class="navbar-nav me-auto mb-2 mb-lg-0">
-- 
GitLab