diff --git a/public/assets/css/app.css b/public/assets/css/app.css index c85ae9efcc95b6433c529f5d63a14629d8d2d491..3c13ac09fbb206d475963c719d5cdcf1753e0763 100755 --- a/public/assets/css/app.css +++ b/public/assets/css/app.css @@ -623,4 +623,10 @@ audio::-webkit-media-controls-mute-button { padding-left: 40px; padding-right: 40px; box-shadow: 2px 2px 6px 0px rgba(0,0,0,0.63); +} + +.disabled-ajax-reload { + /*background-color: lightgrey;*/ + pointer-events: none; + opacity: 0.7; } \ No newline at end of file diff --git a/public/assets/js/app.js b/public/assets/js/app.js index c4ed772d7763abd607d08496c79dced4a92c2938..0a0d41251703b964f4f85daea9e7e08b37c5aa58 100755 --- a/public/assets/js/app.js +++ b/public/assets/js/app.js @@ -15,6 +15,13 @@ $(function() { }); }); + + // Initialize Tooltips Bootstrap 5 + var tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]')) + var tooltipList = tooltipTriggerList.map(function (tooltipTriggerEl) { + return new bootstrap.Tooltip(tooltipTriggerEl) + }) + // NAV : Notifications var down = false; $('#bell').click(function(e){ @@ -45,7 +52,6 @@ $(function() { initializeFormToggles(); initializeConfirmDialog(); // Confirmation des suppressions dans une modal Boostrap initializeAllCollections(); - initializePopover(); initializeFormAutomaticReload(); // initializeFieldsSelect2(); // initializeSubmitRedirect(); @@ -225,11 +231,6 @@ function initializeConfirmDialog() { } } -function initializePopover() { - $('[data-toggle="popover"]').popover({'html':true}); - $('[data-toggle="tooltip"]').tooltip({html: true}); -} - // // Initialise datepicker et datetimepicker // function initializeDatepicker() { @@ -499,7 +500,7 @@ function initializeAjaxReload() { $('body').on('click', '.ajax-reload', function (e) { // Si on a cliqué sur un lien qui demande confirmation, on ne fait rien, l'action aura lieu lors du click sur le bouton de confirmation - if ($(e.currentTarget).attr('data-confirm')) {console.log('canceled reload'); + if ($(e.currentTarget).attr('data-confirm')) { return; } diff --git a/src/Controller/LexiconController.php b/src/Controller/LexiconController.php index 5ffc276fe1ee2d2305980bd9a88bc2cf1f69cb65..87583f35fba5ba4479ad533dac8088d572269d00 100644 --- a/src/Controller/LexiconController.php +++ b/src/Controller/LexiconController.php @@ -53,6 +53,9 @@ class LexiconController extends AppBaseController $filter = $form->getData(); $filter['lexicon'] = $lexicon; + $labelFilterIds = $request->get('labelFilterIds'); + $filter['labels'] = $labelFilterIds; + $labelsFiltered = $labelFilterIds ? $this->getLabels($labelFilterIds) : []; // Entrées triées par createdAt // On ajoute l'ordre d'ajout de l'entrée (on se base sur createdAt) comme index @@ -107,6 +110,7 @@ class LexiconController extends AppBaseController return $this->render('lexicon/show.html.twig', [ 'entries' => $entriesIndexedById, 'lexicon' => $lexicon, + 'labelsFiltered' => $labelsFiltered, 'form' => $form->createView(), 'sortingColumn' => $sortingColumn, 'sortingOrder' => $sortingOrder, diff --git a/src/Repository/EntryRepository.php b/src/Repository/EntryRepository.php index a0de5b087543559c7c9b12eb7893ee74b74c240e..66eae1e7f5e55e0f2581ff3e8fa27c81a338c0e1 100644 --- a/src/Repository/EntryRepository.php +++ b/src/Repository/EntryRepository.php @@ -150,6 +150,12 @@ class EntryRepository extends ServiceEntityRepository ->setParameter('lexicon', $filter['lexicon']); } + if (!empty($filter['labels'])) { + $qb ->leftJoin('h.labels', 'l') + ->andWhere('l.id IN (:labels)') + ->setParameter('labels', $filter['labels']); + } + // if (!empty($filter['headword'])) { // $qb ->andWhere('e.headword = :headword') // ->setParameter('headword', $filter['headword']); diff --git a/templates/lexicon/show.html.twig b/templates/lexicon/show.html.twig index 8063eaaf62e21894b7976bf6d2e2e2c33ed8c15b..f9ab819abe28b18c6f0f88b79f3b021d5d0d56c5 100644 --- a/templates/lexicon/show.html.twig +++ b/templates/lexicon/show.html.twig @@ -32,6 +32,8 @@ </div> {% endif %} + + {########################## SEARCHBAR ############################} <div class="row mt-4"> <div class="col-lg-4 col-md-6 mb-1"> @@ -59,10 +61,16 @@ </a> </div> {% endif %} + + {% if is_granted('LEXICON_EDIT', lexicon) %} + <a href="#" data-url="{{ path('app_lexicon_add_words_list', {id: lexicon.id}) }}" class="modal-form btn btn-dark ms-5"> {{ "Ajouter une liste de mot"|trans }}</a> + {% endif %} </div> </div> + + {########################## LISTE ENTRÉES ############################} <div class="row mt-4"> <div class="col"> @@ -70,6 +78,8 @@ <form name="entries_selection" id="entriesSelection" action="{{ path('app_lexicon_process_selected_entries', {id: lexicon.id}) }}" method="post" novalidate="novalidate" autocomplete="off"> <table class="table table-bordered rounded-3 overflow-hidden"> + + {###### TABLE HEADER ######} <thead> <tr> <th colspan="2" class="text-center"> @@ -79,7 +89,7 @@ <a title="{{ "Ajouter un mot au lexique"|trans }}" href="#" data-url="{{ path('app_lexicon_add_headword', {id: lexicon.id}) }}" class="ms-2 modal-form"><i class="fa fa-plus-circle text-success"></i></a> {% endif %} </div> - {% if searchString %} + {% if searchString %} {# FILTRE HEADWORD supprimable #} <span class="badge bg-danger"> {{ "Filtre: " }}{{ searchString }} <a href="{{ path('app_lexicon_show', {id: lexicon.id}) }}" class="hover-grey"><i title="{{ "Supprimer le filtre"|trans }}" class="fa fa-times-circle"></i></a> @@ -87,7 +97,19 @@ {% endif %} </th> {# <th colspan="3" class="text-center">{{ "Labels"|trans }}</th>#} - <th colspan="2" class="text-center">{{ "Labels"|trans }}</th> + <th colspan="2" class="text-center"> + {{ "Labels"|trans }} + {% if labelsFiltered %} {# FILTRES par LABEL supprimables #} + <div> + {% for label in labelsFiltered %} + <span class="badge bg-danger"> + {{ "Filtre: " }}{{ label }} + <a href="{{ path('app_lexicon_show', { id: lexicon.id, labelFilterIds: app.request.query.get('labelFilterIds')|filter((v, k) => v != label.id) } ) }}" class="hover-grey"><i title="{{ "Supprimer le filtre"|trans }}" class="fa fa-times-circle"></i></a> + </span> + {% endfor %} + </div> + {% endif %} + </th> <th></th> {% if not lexicon.user %} <th></th> @@ -96,6 +118,7 @@ <tr> <th class="text-nowrap"> <span>{{ macros.sorting_column_with_filter("Ordre"|trans, 'createdAt', _context, {id: lexicon.id}) }}</span> + {# On ajoute le param shuffle dans la query et on supprime les params sortingOrder et sortingColumn #} <a href="{{ path('app_lexicon_show', {id: lexicon.id}|merge({shuffle: 'SHUFFLE'})|merge(app.request.query.all|filter((value, key) => (key != 'sortingColumn' and key != 'sortingOrder')))) }}"> <i class="ms-3 fa fa-random"></i> </a> @@ -116,6 +139,8 @@ {% endif %} </tr> </thead> + + {###### TABLE BODY ######} <tbody> {% for entry in entries %} {# @var entry \App\Entity\Entry #} @@ -139,16 +164,38 @@ </div> {# {% endif %}#} </td> + + {############ LABELS ############} {# <td>{% for label in entry.headword.morphologicalLabels %}<span class="badge bg-primary">{{ label }}</span> {% endfor %}</td>#} - <td class="col-md-2 {{ is_granted('LEXICON_EDIT', lexicon) ? 'modal-form' }}" data-url="{{ path('app_label_choose', {lexiconId: lexicon.id, entryId: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_GENERAL")}) }}"> - {% for label in label_manager.entryVisibleLabels(entry, app.user, constant("App\\Entity\\Label::LABEL_CATEGORY_GENERAL")) %}<span class="blink-target">{% include "label/_labelBadge.html.twig" %}</span>{% endfor %} + <td class="col-md-2"> + <div class="d-flex justify-content-between"> + <span> + {% for label in label_manager.entryVisibleLabels(entry, app.user, constant("App\\Entity\\Label::LABEL_CATEGORY_GENERAL")) %} + <a href="{{ path('app_lexicon_show', {id: lexicon.id, labelFilterIds: [label.id]|merge(app.request.query.get('labelFilterIds') ?: {}) }) }}" data-bs-placement="left" data-bs-toggle="tooltip" title="{{ 'Afficher uniquement les mots possédant ce label'|trans }}">{% include "label/_labelBadge.html.twig" %}</a> + {% endfor %} + </span> + {% if is_granted('LEXICON_EDIT', lexicon) %} + <a class="modal-form links-container" data-url="{{ path('app_label_choose', {lexiconId: lexicon.id, entryId: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_GENERAL")}) }}"><i class="fa fa-pencil-square text-grey"></i></a> + {% endif %} + </div> + </td> + <td class="col-md-2"> + <div class="d-flex justify-content-between"> + <span> + {% for label in label_manager.entryVisibleLabels(entry, app.user, constant("App\\Entity\\Label::LABEL_CATEGORY_MILESTONE")) %} + <a href="{{ path('app_lexicon_show', {id: lexicon.id, labelFilterIds: [label.id]|merge(app.request.query.get('labelFilterIds') ?: {}) }) }}" data-bs-placement="left" data-bs-toggle="tooltip" title="{{ 'Afficher uniquement les mots possédant ce label'|trans }}">{% include "label/_labelBadge.html.twig" %}</a> + {% endfor %} + </span> + {% if is_granted('LEXICON_EDIT', lexicon) %} + <a class="modal-form links-container" data-url="{{ path('app_label_choose', {lexiconId: lexicon.id, entryId: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_MILESTONE")}) }}"><i class="fa fa-pencil-square text-grey"></i></a> + {% endif %} + </div> </td> {# <td class="col-md-2 {{ is_granted('LEXICON_EDIT', lexicon) ? 'modal-form' }}" data-url="{{ path('app_label_choose', {lexiconId: lexicon.id, entryId: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_INSTITUTIONAL")}) }}">#} {# {% for label in label_manager.entryVisibleLabels(entry, app.user, constant("App\\Entity\\Label::LABEL_CATEGORY_INSTITUTIONAL")) %}<span class="blink-target">{% include "label/_labelBadge.html.twig" %}</span>{% endfor %}#} {# </td>#} - <td class="col-md-2 {{ is_granted('LEXICON_EDIT', lexicon) ? 'modal-form' }}" data-url="{{ path('app_label_choose', {lexiconId: lexicon.id, entryId: entry.id, category: constant("App\\Entity\\Label::LABEL_CATEGORY_MILESTONE")}) }}"> - {% for label in label_manager.entryVisibleLabels(entry, app.user, constant("App\\Entity\\Label::LABEL_CATEGORY_MILESTONE")) %}<span class="blink-target">{% include "label/_labelBadge.html.twig" %}</span>{% endfor %} - </td> + + {############ DEFINITIONS ############} <td> {% for pos, definitions in entry.formattedDefinitions %} <a class="text-decoration-none text-toggle" data-bs-toggle="collapse" href="#def-{{ entry.id }}-{{ loop.index }}" role="button" aria-expanded="false" aria-controls="collapseExample"> @@ -177,13 +224,6 @@ </form> - <div class="row"> - <div class="col-md-12"> - {% if is_granted('LEXICON_EDIT', lexicon) %} - <a href="#" data-url="{{ path('app_lexicon_add_words_list', {id: lexicon.id}) }}" class="modal-form btn btn-dark btn-xs"> {{ "Ajouter une liste de mot"|trans }}</a> - {% endif %} - </div> - </div> </div> diff --git a/translations/messages+intl-icu.en.yaml b/translations/messages+intl-icu.en.yaml index a0e0cd661278350b3cf137e65ed7ed503a2111f9..1caa84cb5142e2bf0fe0e5dbe6a57f8ac75ae63f 100644 --- a/translations/messages+intl-icu.en.yaml +++ b/translations/messages+intl-icu.en.yaml @@ -338,3 +338,4 @@ copy_entry: label: 'Target lexicon' copy_entry.merge.submit: 'Submit' copy_entry.merge.label: 'Merging preference' +"Afficher uniquement les mots possédant ce label": "Display only words with this label" \ No newline at end of file diff --git a/translations/messages+intl-icu.fr.yaml b/translations/messages+intl-icu.fr.yaml index dbb992ec1537f3673bff2d9c20a813374027b194..3f932399354a86d21965b0fdf89ca65595f2ff3f 100644 --- a/translations/messages+intl-icu.fr.yaml +++ b/translations/messages+intl-icu.fr.yaml @@ -352,4 +352,5 @@ copy_entry.merge.submit: 'Enregistrer' copy_entry.merge.label: 'Préférence de fusion' copy_entry: lexicon: - label: 'Lexique cible' \ No newline at end of file + label: 'Lexique cible' +"Afficher uniquement les mots possédant ce label": "Afficher uniquement les mots possédant ce label" \ No newline at end of file