]> git.agnieray.net Git - galette.git/commitdiff
Core fields configuration screen overhaul
authorGuillaume AGNIERAY <dev@agnieray.net>
Sun, 25 Feb 2024 10:54:00 +0000 (11:54 +0100)
committerJohan Cwiklinski <johan@x-tnd.be>
Tue, 27 Feb 2024 07:59:55 +0000 (08:59 +0100)
Use tabs and table display like on Dynamic fields configuration

refs #1775

galette/templates/default/elements/edit_core_fields.html.twig [new file with mode: 0644]
galette/templates/default/pages/configuration_core_fields.html.twig
ui/semantic/galette/collections/menu.overrides

diff --git a/galette/templates/default/elements/edit_core_fields.html.twig b/galette/templates/default/elements/edit_core_fields.html.twig
new file mode 100644 (file)
index 0000000..11c5500
--- /dev/null
@@ -0,0 +1,87 @@
+{#
+/**
+ * Copyright © 2003-2024 The Galette Team
+ *
+ * This file is part of Galette (https://galette.eu).
+ *
+ * Galette is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Galette is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Galette. If not, see <http://www.gnu.org/licenses/>.
+ */
+#}
+    <div class="ui basic fitted segment loader_selector">
+        <table class="listing ui celled striped table">
+            <thead>
+                <tr>
+                    <th class="id_row">#</th>
+                    <th>{{ _T("Name") }}</th>
+                    <th>{{ _T("Permissions") }}</th>
+                    <th class="date_row">{{ _T("Required") }}</th>
+{% if preferences.pref_member_form_grid != 'one' %}
+                    <th class="date_row">{{ _T("Width in forms") }}</th>
+{% endif %}
+                </tr>
+            </thead>
+            <tbody id="sortable_fields_{{ fs }}" class="sortable-items" data-category="{{ fs }}">
+{% for col, field in categorized_fields[fs] %}
+    {% if (preferences.pref_show_id or field.field_id != 'id_adh') and field.field_id != 'parent_id' %}
+        {% set fid = field.field_id %}
+                <tr>
+                    <td data-scope="id" class="collapsing">
+                        <i class="jsonly displaynone arrows alternate icon" aria-hidden="true"></i>
+                        {{ loop.index }}
+                    </td>
+                    <td class="fieldname" data-scope="row">
+                        <input type="hidden" name="fields[]" value="{{ fid }}"/>
+                        <input type="hidden" name="{{ fid }}_category" value="{{ category.id_field_category }}"/>
+                        <input type="hidden" name="{{ fid }}_label" value="{{ field.label }}"/>
+                        {{ field.label }}
+                    </td>
+                    <td class="visibility_cell" data-col-label="{{ _T("Permissions") }}">
+                        <span data-prop-label="{{ _T("Permissions") }}" class="visible" title="{{ _T("Change '%field' permissions")|replace({'%field': field.label}) }}">
+                            <select name="{{ fid }}_visible" id="{{ fid }}_visible" class="ui dropdown">
+                                <option value="{{ constant('Galette\\Entity\\FieldsConfig::NOBODY') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::NOBODY') %} selected="selected"{% endif %}>{{ _T("Inaccessible") }}</option>
+                                <option value="{{ constant('Galette\\Entity\\FieldsConfig::ADMIN') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::ADMIN') %} selected="selected"{% endif %}>{{ _T("Administrator") }}</option>
+                                <option value="{{ constant('Galette\\Entity\\FieldsConfig::STAFF') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::STAFF') %} selected="selected"{% endif %}>{{ _T("Staff member") }}</option>
+                                <option value="{{ constant('Galette\\Entity\\FieldsConfig::MANAGER') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::MANAGER') %} selected="selected"{% endif %}>{{ _T("Group manager") }}</option>
+                                <option value="{{ constant('Galette\\Entity\\FieldsConfig::USER_READ') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::USER_READ') %} selected="selected"{% endif %}>{{ _T("Read only") }}</option>
+                                <option value="{{ constant('Galette\\Entity\\FieldsConfig::USER_WRITE') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::USER_WRITE') %} selected="selected"{% endif %}>{{ _T("Read/Write") }}</option>
+                            </select>
+                        </span>
+                    </td>
+                    <td class="required_cell" data-col-label="{{ _T("Required") }}">
+                        <span data-prop-label="{{ _T("Required") }}" class="required" title="{% if fid in non_required %}{{ _T("Field '%field' cannot be set as required.")|replace({'%field': field.label}) }}{% else %}{{ _T("Mark '%field' as (not) required")|replace({'%field': field.label}) }}{% endif %}">
+                            <label for="{{ fid }}_required_yes">{{ _T("Yes") }}</label>
+                            <input type="radio" name="{{ fid }}_required" id="{{ fid }}_required_yes" value="1"{% if field.required %} checked="checked"{% endif %}{% if fid in non_required %} disabled="disabled"{% endif %}/>
+                            <label for="{{ fid }}_required_no">{{ _T("No") }}</label>
+                            <input type="radio" name="{{ fid }}_required" id="{{ fid }}_required_no" value="0"{% if not field.required %} checked="checked"{% endif %}{% if fid in non_required %} disabled="disabled"{% endif %}/>
+                        </span>
+                    </td>
+        {% if preferences.pref_member_form_grid != 'one' %}
+                    <td class="left" data-col-label="{{ _T("Width in forms") }}">
+                        <span data-prop-label="{{ _T("Width in forms") }}" title="{{ _T("Change field's width with in forms") }}">
+                            <select name="{{ fid }}_width_in_forms" id="{{ fid }}_width_in_forms" class="ui dropdown">
+                                <option value="1"{% if field.width_in_forms == 1 or (preferences.pref_member_form_grid == 'two' and field.width_in_forms == 3) %} selected="selected"{% endif %}>{{ _T("Default") }}</option>
+                                <option value="2"{% if field.width_in_forms == 2 %} selected="selected"{% endif %}>{{ _T("Full") }}</option>
+            {% if preferences.pref_member_form_grid == 'three' %}
+                                <option value="3"{% if field.width_in_forms == 3 %} selected="selected"{% endif %}>{{ _T("Half") }}</option>
+            {% endif %}
+                            </select>
+                        </span>
+                    </td>
+        {% endif %}
+                </tr>
+    {% endif %}
+{% endfor %}
+            </tbody>
+        </table>
+    </div>
index a69f3b9ae1e64687c065d75ffe0a44373c879406..de3df63c6719fdda98a083d748ba493913c93737 100644 (file)
 {% extends 'page.html.twig' %}
 
 {% block content %}
-    <div class="jsonly displaynone ui basic fitted segment">
-        <a class="ui labeled icon button collapse">
-            <i class="angle double down icon" aria-hidden="true"></i>
-            {{ _T("Expand all") }}
-        </a>
-    </div>
     <form action="{{ url_for('configureCoreFields') }}" method="post" id="config_fields_form" class="ui form">
-    <div id="members_tab">
-{% for category in categories %}
-    {% set catname = category.category %}
-        <div class="galetteform ui styled fluid accordion field">
-            <div class="ui title">
-                <i class="jsonly displaynone dropdown icon" aria-hidden="true"></i>
-                {{ _T(catname) }}
+        <div id="sortable_categories" class="ui stackable pointing inverted menu">
+    {% for category in categories %}
+            {% set catname = _T(category.category) %}
+            {% set fs = category.id_field_category %}
+            <a href="#{{ fs }}" class="item" data-tab="{{ fs }}">
+                <input type="hidden" name="categories[]" id="category{{ loop.index }}" value="{{ category.id_field_category }}"/>
                 <i class="jsonly displaynone arrows alternate icon" aria-hidden="true"></i>
-            </div>
-            <div class="content">
-                <div class="ui basic fitted segment">
-                    <div class="ui {% if preferences.pref_member_form_grid != 'one' %}four{% else %}three{% endif %} column stackable grid core-fields-properties">
-                        <div class="middle aligned row">
-                            <div class="column">
-                                <span class="ui fluid label">{{ _T("Field name") }}</span>
-                            </div>
-                            <div class="column">
-                                <span class="ui fluid label">{{ _T("Required") }}</span>
-                            </div>
-                            <div class="column">
-                                <span class="ui fluid label">{{ _T("Permissions") }}</span>
-                            </div>
-    {% if preferences.pref_member_form_grid != 'one' %}
-                            <div class="column">
-                                <span class="ui fluid label">{{ _T("Width in forms") }}</span>
-                            </div>
-    {% endif %}
-                        </div>
-                    </div>
-                </div>
-                <ul id="sortable_{{ loop.index }}" class="sortable-items">
-    {% set fs = category.id_field_category %}
-    {% for col, field in categorized_fields[fs] %}
-        {% if (preferences.pref_show_id or field.field_id != 'id_adh') and field.field_id != 'parent_id' %}
-            {% set fid = field.field_id %}
-                    <li class="ui segment">
-                        <div class="ui {% if preferences.pref_member_form_grid != 'one' %}four{% else %}three{% endif %} column stackable grid core-fields-listing">
-                            <div class="middle aligned row">
-                                <div class="column">
-                                    <i class="jsonly displaynone arrows alternate icon" aria-hidden="true"></i>
-                                    <span data-prop-label="{{ _T("Field name") }}" class="fieldname">
-                                        <input type="hidden" name="fields[]" value="{{ fid }}"/>
-                                        <input type="hidden" name="{{ fid }}_category" value="{{ category.id_field_category }}"/>
-                                        <input type="hidden" name="{{ fid }}_label" value="{{ field.label }}"/>
-                                        {{ field.label }}
-                                    </span>
-                                </div>
-                                <div class="column">
-                                    <span data-prop-label="{{ _T("Required") }}" class="yesno" title="{% if fid in non_required %}{{ _T("Field '%field' cannot be set as required.")|replace({'%field': field.label}) }}{% else %}{{ _T("Mark '%field' as (not) required")|replace({'%field': field.label}) }}{% endif %}">
-                                        <label for="{{ fid }}_required_yes">{{ _T("Yes") }}</label>
-                                        <input type="radio" name="{{ fid }}_required" id="{{ fid }}_required_yes" value="1"{% if field.required %} checked="checked"{% endif %}{% if fid in non_required %} disabled="disabled"{% endif %}/>
-                                        <label for="{{ fid }}_required_no">{{ _T("No") }}</label>
-                                        <input type="radio" name="{{ fid }}_required" id="{{ fid }}_required_no" value="0"{% if not field.required %} checked="checked"{% endif %}{% if fid in non_required %} disabled="disabled"{% endif %}/>
-                                    </span>
-                                </div>
-                                <div class="column">
-                                    <span data-prop-label="{{ _T("Permissions") }}" class="access" title="{{ _T("Change '%field' permissions")|replace({'%field': field.label}) }}">
-                                        <select name="{{ fid }}_visible" id="{{ fid }}_visible" class="ui dropdown">
-                                            <option value="{{ constant('Galette\\Entity\\FieldsConfig::NOBODY') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::NOBODY') %} selected="selected"{% endif %}>{{ _T("Inaccessible") }}</option>
-                                            <option value="{{ constant('Galette\\Entity\\FieldsConfig::ADMIN') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::ADMIN') %} selected="selected"{% endif %}>{{ _T("Administrator") }}</option>
-                                            <option value="{{ constant('Galette\\Entity\\FieldsConfig::STAFF') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::STAFF') %} selected="selected"{% endif %}>{{ _T("Staff member") }}</option>
-                                            <option value="{{ constant('Galette\\Entity\\FieldsConfig::MANAGER') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::MANAGER') %} selected="selected"{% endif %}>{{ _T("Group manager") }}</option>
-                                            <option value="{{ constant('Galette\\Entity\\FieldsConfig::USER_READ') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::USER_READ') %} selected="selected"{% endif %}>{{ _T("Read only") }}</option>
-                                            <option value="{{ constant('Galette\\Entity\\FieldsConfig::USER_WRITE') }}"{% if field.visible == constant('Galette\\Entity\\FieldsConfig::USER_WRITE') %} selected="selected"{% endif %}>{{ _T("Read/Write") }}</option>
-                                        </select>
-                                    </span>
-                                </div>
-            {% if preferences.pref_member_form_grid != 'one' %}
-                                <div class="column">
-                                    <span data-prop-label="{{ _T("Width in forms") }}" class="access" title="{{ _T("Change field's width with in forms") }}">
-                                        <select name="{{ fid }}_width_in_forms" id="{{ fid }}_width_in_forms" class="ui dropdown">
-                                            <option value="1"{% if field.width_in_forms == 1 %} selected="selected"{% endif %}>{{ _T("Default") }}</option>
-                                            <option value="2"{% if field.width_in_forms == 2 %} selected="selected"{% endif %}>{{ _T("Full") }}</option>
-                {% if preferences.pref_member_form_grid == 'three' %}
-                                            <option value="3"{% if field.width_in_forms == 3 %} selected="selected"{% endif %}>{{ _T("Half") }}</option>
-                {% endif %}
-                                        </select>
-                                    </span>
-                                </div>
-            {% endif %}
-                            </div>
-                        </div>
-                    </li>
-        {% endif %}
+                {{ catname }}
+            </a>
     {% endfor %}
-                </ul>
-            </div>
-            <input type="hidden" name="categories[]" id="category{{ loop.index }}" value="{{ category.id_field_category }}"/>
         </div>
-{% endfor %}
-    </div>
-    <div class="jsonly hidden ui basic fitted segment">
-        <a class="ui labeled icon button collapse">
-            <i class="angle double down icon" aria-hidden="true"></i>
-            {{ _T("Expand all") }}
-        </a>
-    </div>
+    {% for category in categories %}
+        {% set fs = category.id_field_category %}
+        <div class="ui tab segment" data-tab="{{ fs }}">
+            {% include 'elements/edit_core_fields.html.twig' %}
+        </div>
+    {% endfor %}
         <div class="ui basic center aligned segment">
             <button type="submit" class="ui labeled icon primary button action">
                 <i class="save icon" aria-hidden="true"></i> {{ _T("Save") }}
 
 {% block javascripts %}
     <script type="module">
+        $(function() {
+            $('.pointing.menu .item').tab();
+        });
 
         var _initSortable = function(){
-            var _categories = document.getElementById('members_tab');
+            var _categories = document.getElementById('sortable_categories');
 
             var _nestedSortables = [].slice.call(document.querySelectorAll('.sortable-items'));
+            var _drag_on_tabs = null;
+
+            function changeTab() {
+                $('.pointing.menu .item').tab('change tab', this.dataset.tab);
+            }
+
             for (var i = 0; i < _nestedSortables.length; i++) {
                 new Sortable(_nestedSortables[i], {
                     group: 'nested',
                     ghostClass: 'yellow',
                     fallbackOnBody: true,
                     swapThreshold: 0.65,
+                    onStart: function (evt) {
+                        const targets = document.querySelectorAll('#sortable_categories .item');
+                        for (let i = 0; i < targets.length; i++) {
+                            targets[i].addEventListener('dragenter', changeTab, false);
+                        }
+                    },
+                    onEnd: function (evt) {
+                        const targets = document.querySelectorAll('#sortable_categories .item');
+                        for (let i = 0; i < targets.length; i++) {
+                            targets[i].removeEventListener('dragenter', changeTab, false);
+                        }
+                    },
                     onAdd: function (evt) {
                         var _item = evt.item;
-                        var _category = _item.parentElement.parentElement.parentElement.querySelectorAll('input[name^=categories]')[0].attributes.value.nodeValue;
+                        var _category = _item.parentElement.dataset.category;
                         _item.classList.add('yellow');
                         _item.querySelectorAll('input[name$=category]')[0].setAttribute('value', _category);
                     },
 
             new Sortable(_categories, {
                 animation: 150,
-                ghostClass: 'yellow',
                 onUpdate: function (evt) {
                     var _item = evt.item;
-                    _item.classList.add('yellow');
-                }
-            });
-        }
-
-
-        var _bindCollapse = function() {
-            $('.collapse').click(function(){
-                var _this = $(this);
-                var _buttons = $('.collapse');
-                var _expandTxt = '<i class="angle double down icon" aria-hidden="true"></i> {{ _T("Expand all") }}';
-                var _collapseTxt = '<i class="angle double up icon" aria-hidden="true"></i> {{ _T("Collapse all") }}';
-                var _isExpand = false;
-                var _icon = _this.children('.icon');
-                if( _icon.is('.down') ) {
-                    _buttons.html(_collapseTxt);
-                } else {
-                    _isExpand = true;
-                    _buttons.html(_expandTxt);
-                }
-                if (_isExpand == true) {
-                    $('.ui.accordion.galetteform').accordion('close', 0);
-                } else {
-                    $('.ui.accordion.galetteform').accordion('open', 0);
+                    _item.classList.add('moved');
                 }
             });
         }
         var _warnings = [];
         var _checkCoherence = function(index, elt){
             var _elt = $(elt);
-            var _disabled = _elt.find('.yesno input:disabled, .access input:disabled');
+            var _disabled = _elt.find('.required input:disabled, .visible input:disabled');
             if ( _disabled.length == 0 ) {
-                var _required = parseInt(_elt.find('.yesno input:checked').val());
-                var _accessible = parseInt(_elt.find('.access option:selected').val());
-
+                var _required = parseInt(_elt.find('.required input:checked').val());
+                var _visible = parseInt(_elt.find('.visible option:selected').val());
 
-                if ( _required === 1 && _accessible === 0 ) {
-                    _elt.addClass('red');
+                if ( _required === 1 && _visible === 0 ) {
+                    _elt.addClass('red colored');
+                    _elt.find('.required_cell, .visibility_cell').addClass('left red marked');
                     _warnings[_warnings.length] = _elt;
                 }
             }
             $('#config_fields_form').submit(function(){
 
                 _warnings = [];
-                $('#members_tab .segment').removeClass('red');
-                $('.fields_list li').each(_checkCoherence);
+                $('.sortable-items tr').removeClass('red colored');
+                $('.required_cell, .visibility_cell').removeClass('left red marked');
+                $('.sortable-items tr').each(_checkCoherence);
 
                 if ( _warnings.length > 0 ) {
-                    var _w = $('#warnings');
-
-                    _w.find('li').remove();
+                    document.getElementById('galette_body').scrollTo({top: 0});
+                    var _message = '{{ _T("Some warnings has been thrown:")|e("js") }}<ul>';
                     $.each(_warnings, function(i,w){
                         var _val = w[0].getElementsByClassName('fieldname')[0].textContent.trim();
-                        _w.find('ul').append('<li>' + _val + '</li>');
+                        _message += '<li>' + _val + '</li>';
                     });
-
-                    {% include "elements/js/modal.js.twig" with {
-                        modal_title: "_w.find('.header')",
-                        modal_content: "_w.find('.content')",
-                        modal_class: "tiny",
-                        modal_deny_only: true,
-                        modal_cancel_text: _T("Close")|e("js"),
-                        modal_classname: "redalert",
-                    } %}
+                    _message += '</ul>{{ _T("Please correct above warnings to continue.") }}';
+                    $('body')
+                        .toast({
+                            title: '{{ _T("Warning")|e("js") }}',
+                            displayTime: 0,
+                            closeIcon: true,
+                            position: 'top attached',
+                            message: _message,
+                            showIcon: 'exclamation triangle',
+                            class: 'warning'
+                        })
+                    ;
                     return false;
                 } else {
                     return true;
         }
 
         $(function() {
-            $('body').append($('<div id="warnings" title="{{ _T("Warning")|e("js") }}"><div class="header">{{ _T("Some warnings has been thrown:")|e("js") }}</div><div class="content"><ul></ul><p class="center aligned">{{ _T("Please correct above warnings to continue.") }}</p></div></div>').hide());
-
             _bindForm();
-
-            _bindCollapse();
-
             _initSortable();
 
-            $('#add_category').click(function() {
+            /*$('#add_category').click(function() {
                 var _fieldsets = $('fieldset[id^=cat_]');
                 var _cat_iter = _fieldsets.length + 1;
 
                 document.location = _url;
                 _legend.children(':input').focus();
                 return false;
-            });
+            });*/
         });
     </script>
 {% endblock %}
index e368d4e722956b34c5ada4d9756bc109bbaba92f..17092632a4cc77b59debd3c96ae06c45f465b7dc 100644 (file)
     color: @hoveredTextColor;
 }
 
+/* -------------------------------------------------------------------
+      Sortable pointing inverted menu on Core fields Configuration
+-------------------------------------------------------------------- */
+
+#sortable_categories.ui.pointing.inverted.menu {
+  .item.sortable-chosen,
+  .item.moved {
+    background-color: @invertedActiveBackground;
+    color: @red !important;
+  }
+  .item.active.moved {
+    background-color: @yellow;
+  }
+}
+
 /*------------------
      Pagination
 -------------------*/