diff --git a/html/images/attributes/display_barcode.svg b/html/images/attributes/display_barcode.svg new file mode 100644 index 0000000000000..adb69f3081d2e --- /dev/null +++ b/html/images/attributes/display_barcode.svg @@ -0,0 +1,80 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + diff --git a/html/images/attributes/edit_link.svg b/html/images/attributes/edit_link.svg new file mode 100644 index 0000000000000..1eefa8d4f42a9 --- /dev/null +++ b/html/images/attributes/edit_link.svg @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + diff --git a/html/js/product-preferences.js b/html/js/product-preferences.js index ed5fb1b893df7..6eef0ccb8b38d 100644 --- a/html/js/product-preferences.js +++ b/html/js/product-preferences.js @@ -3,7 +3,7 @@ /*global preferences_text*/ // depends on which type of page the preferences are shown on var attribute_groups; // All supported attribute groups and attributes + translated strings -var preferences; // All supported preferences + translated strings +var default_preferences; // All supported preferences + translated strings var use_user_product_preferences_for_ranking = JSON.parse(localStorage.getItem('use_user_product_preferences_for_ranking')); function get_user_product_preferences () { @@ -43,20 +43,21 @@ function display_selected_preferences (target_selected_summary, product_preferen $.each(attribute_group.attributes, function(key, attribute) { - if ((product_preferences[attribute.id]) && (product_preferences[attribute.id] != "not_important")) { + // we have a value and it is one we care of + if ((product_preferences[attribute.id]) && (product_preferences[attribute.id] in selected_preference_groups)) { + // add to html for this class of importance var attribute_html = '
  • ' + attribute.setting_name + '
  • '; selected_preference_groups[product_preferences[attribute.id]].push(attribute_html); } }); - }); + }); var selected_preferences_html = ''; $.each(selected_preference_groups, function(selected_preference, selected_preference_group) { var selected_preference_name; - - $.each(preferences, function (key, preference) { + $.each(default_preferences, function (key, preference) { if (selected_preference == preference.id) { selected_preference_name = preference.name; @@ -144,6 +145,25 @@ function display_use_preferences_switch_and_edit_preferences_button (target_sele $(document).foundation('reflow'); } +/** + * transform preferences to options list + * @param {Array} preferences - list of Objects + * @return {Array} list of Objects with label / value + */ +function options_from_preferences(preferences) { + var options = preferences.map((preference) => ({ + label: preference.name, + value: preference.id, + })); + // "not_important" is default and must be first + const default_idx = options.findIndex((option) => option.value === "not_important"); + if (default_idx > 0) { + // put in first position + options.splice(0, 0, options.splice(default_idx, 1)[0]); + } + + return options; +} // display_user_product_preferences can be called by other scripts /* exported display_user_product_preferences */ @@ -165,19 +185,21 @@ function display_user_product_preferences (target_selected, target_selection_for }); } - if (! preferences) { + if (! default_preferences) { $.getJSON( "/api/v0/preferences", function( data ) { - preferences = data; + default_preferences = data; display_user_product_preferences(target_selected, target_selection_form, change); }); } - if (attribute_groups && preferences && ! displayed_user_product_preferences) { + if (attribute_groups && default_preferences && ! displayed_user_product_preferences) { displayed_user_product_preferences = true; + + var default_options = options_from_preferences(default_preferences); var user_product_preferences = get_user_product_preferences(); @@ -204,20 +226,24 @@ function display_user_product_preferences (target_selected, target_selection_for attribute_group_html += "
  • " + "
    " - + "
    " + + "
    " + + " " + + "
    " + "" + attribute.setting_name + "
    "; - - $.each(preferences, function (key, preference) { + + const options = attribute.options || default_options; + + $.each(options, function (index, option) { var checked = ''; - - if ((! user_product_preferences[attribute.id] && preference.id == "not_important") || (user_product_preferences[attribute.id] == preference.id)) { + // if there is no value, we use the one at index 0 + if ((! user_product_preferences[attribute.id] && index === 0) || (user_product_preferences[attribute.id] == option.value)) { checked = ' checked'; } - attribute_group_html += "" - + "" + attribute_group_html += "" + + "" + ""; }); diff --git a/html/js/product-search.js b/html/js/product-search.js index f85d91f039513..0f125dd9a3183 100644 --- a/html/js/product-search.js +++ b/html/js/product-search.js @@ -140,9 +140,13 @@ function rank_products(products, product_preferences, use_user_product_preferenc } -function display_products(target, product_groups, use_user_product_preferences_for_ranking ) { +function product_edit_url(product) { + return `/cgi/product.pl?type=edit&code=${product.code}`; +} + +function display_products(target, product_groups, user_prefs) { - if (use_user_product_preferences_for_ranking) { + if (user_prefs.ranking) { $( target ).html('' + '
    '); } @@ -151,7 +155,7 @@ function display_products(target, product_groups, use_user_product_preferences_f } $.each(product_groups, function(product_group_id, product_group) { - + const user_product_preferences = user_prefs.prefs; var products_html = []; $.each( product_group, function(key, product) { @@ -159,12 +163,11 @@ function display_products(target, product_groups, use_user_product_preferences_f var product_html = ""; // Show the green / grey / colors for matching products only if we are using the user preferences - if (use_user_product_preferences_for_ranking) { - product_html += '
  • '; - } - else { - product_html += '
  • '; + let css_classes = 'list_product_a'; + if (user_prefs.ranking) { + css_classes += ' list_product_a_match_' + product.match_status; } + product_html += `
  • `; product_html += '
  • "; + // add some specific fields + if (user_product_preferences.display_barcode === "yes") { + product_html += `${product.code}`; + } + product_html += ""; + if (user_product_preferences.edit_link === "yes") { + const edit_url = product_edit_url(product); + product_html += ` + + + + `; + } + product_html += "
  • "; products_html.push(product_html); }); @@ -217,11 +232,11 @@ function display_products(target, product_groups, use_user_product_preferences_f } } else { - text_or_icon = '' + text_or_icon = '' + ' ' + product_group.length + ""; } - if (use_user_product_preferences_for_ranking) { + if (user_prefs.ranking) { $("#products_tabs_titles").append( '
  • ' + '' @@ -300,8 +315,11 @@ function rank_and_display_products (target, products) { var use_user_product_preferences_for_ranking = JSON.parse(localStorage.getItem('use_user_product_preferences_for_ranking')); var ranked_products = rank_products(products, user_product_preferences, use_user_product_preferences_for_ranking); - - display_products(target, ranked_products, use_user_product_preferences_for_ranking); + const user_prefs = { + ranking: use_user_product_preferences_for_ranking, + prefs: user_product_preferences, + }; + display_products(target, ranked_products, user_prefs); $(document).foundation('equalizer', 'reflow'); } diff --git a/lib/ProductOpener/Attributes.pm b/lib/ProductOpener/Attributes.pm index 97c6f8c988cc1..09fc8537ff7d4 100644 --- a/lib/ProductOpener/Attributes.pm +++ b/lib/ProductOpener/Attributes.pm @@ -246,6 +246,9 @@ The initialization values for the fields are not dependent on a specific product Some of them may be overridden later (e.g. the title and description) based on how the attribute matches for the specific product. +Also some attributes may contains possible values separated by '__' +otherwise the one from display_preferences_api applies. + =head3 Arguments =head4 attribute id $attribute_id @@ -268,18 +271,35 @@ A reference to the created attribute object. - Warning - Short description - Description +- Options - if there are specific options =cut sub initialize_attribute($$) { - + my $attribute_id = shift; my $target_lc = shift; - + my @options = (); + + # unpack eventual options, separated by double underscores + ($attribute_id, @options) = split /__/, $attribute_id; + my $attribute_ref = {id => $attribute_id}; - + + # Eventually initialize options + if (scalar @options) { + my $options_ref = []; + foreach my $option (@options) { + push @{$options_ref}, { + label => $option, + value => $option, + }; + } + $attribute_ref->{options} = $options_ref; + } + # Initialize icon for the attribute - + if ($attribute_id eq "nutriscore") { $attribute_ref->{icon_url} = "$static_subdomain/images/attributes/nutriscore-a.svg"; } @@ -317,6 +337,10 @@ sub initialize_attribute($$) { $attribute_ref->{icon_url} = "$static_subdomain/images/attributes/${tag}.svg"; } + # else just take attribute_id + else { + $attribute_ref->{icon_url} = "$static_subdomain/images/attributes/${attribute_id}.svg"; + } # Initialize name and setting name if a language is requested @@ -369,6 +393,15 @@ sub initialize_attribute($$) { $attribute_ref->{$field} = $value; } } + + foreach my $option_ref (@{$attribute_ref->{options}}) { + my $label = lang_in_other_lc( + $target_lc, "attribute_value_" . $option_ref->{value} . "_label" + ); + if ((defined $label) and ($label ne "")) { + $option_ref->{label} = $label; + } + } if ((not defined $attribute_ref->{setting_name}) and (defined $attribute_ref->{name})) { $attribute_ref->{setting_name} = $attribute_ref->{name}; diff --git a/lib/ProductOpener/Config_off.pm b/lib/ProductOpener/Config_off.pm index a0b94c9e7a5d9..7a6e0020e5ab0 100644 --- a/lib/ProductOpener/Config_off.pm +++ b/lib/ProductOpener/Config_off.pm @@ -778,6 +778,14 @@ $options{attribute_groups} = [ "forest_footprint", ] ], + [ + "search", + [ + # '__' separate possible values, first is default + "display_barcode__no__yes", + "edit_link__no__yes", + ] + ] ]; # default preferences for attributes @@ -787,6 +795,7 @@ $options{attribute_default_preferences} = { "ecoscore" => "important", }; + # Used to generate the sample import file for the producers platform # possible values: mandatory, recommended, optional. # when not specified, fields are considered optional diff --git a/po/common/common.pot b/po/common/common.pot index 5e7846e2fceb4..c1cee80e0b23f 100644 --- a/po/common/common.pot +++ b/po/common/common.pot @@ -5258,6 +5258,26 @@ msgctxt "attribute_forest_footprint_not_computed_description_short" msgid "Currently only for products with chicken or eggs" msgstr "" +msgctxt "attribute_group_search_name" +msgid "Search preferences" +msgstr "" + +msgctxt "attribute_display_barcode_setting_name" +msgid "Display barcode in search results" +msgstr "" + +msgctxt "attribute_edit_link_setting_name" +msgid "Add an edit link in search results" +msgstr "" + +msgctxt "attribute_value_yes_label" +msgid "Yes" +msgstr "" + +msgctxt "attribute_value_no_label" +msgid "No" +msgstr "" + msgctxt "classify_products_according_to_your_preferences" msgid "Classify products according to your preferences" msgstr "" diff --git a/po/common/en.po b/po/common/en.po index e91d878935492..65c13b4fe480f 100644 --- a/po/common/en.po +++ b/po/common/en.po @@ -5288,6 +5288,26 @@ msgctxt "attribute_forest_footprint_not_computed_description_short" msgid "Currently only for products with chicken or eggs" msgstr "Currently only for products with chicken or eggs" +msgctxt "attribute_group_search_name" +msgid "Search preferences" +msgstr "Search preferences" + +msgctxt "attribute_display_barcode_setting_name" +msgid "Display barcode in search results" +msgstr "Display barcode in search results" + +msgctxt "attribute_edit_link_setting_name" +msgid "Add an edit link in search results" +msgstr "Add an edit link in search results" + +msgctxt "attribute_value_yes_label" +msgid "Yes" +msgstr "Yes" + +msgctxt "attribute_value_no_label" +msgid "No" +msgstr "No" + msgctxt "classify_products_according_to_your_preferences" msgid "Classify products according to your preferences" msgstr "Classify products according to your preferences" diff --git a/po/common/fr.po b/po/common/fr.po index 376c543caa95f..95e8f8a4b83c4 100644 --- a/po/common/fr.po +++ b/po/common/fr.po @@ -4532,6 +4532,7 @@ msgctxt "attribute_nova_setting_name" msgid "No or little food processing (NOVA group)" msgstr "Pas ou peu de transformation des aliments (groupe NOVA)" + # keep %s, it will be replaced by the group 1, 2, 3 or 4 msgctxt "attribute_nova_group_title" msgid "NOVA %s" @@ -5127,6 +5128,26 @@ msgctxt "attribute_forest_footprint_not_computed_description_short" msgid "Currently only for products with chicken or eggs" msgstr "Pour l'instant seulement pour les produits avec du poulet ou des oeufs" +msgctxt "attribute_group_search_name" +msgid "Search preferences" +msgstr "Paramètres de recherche" + +msgctxt "attribute_display_barcode_setting_name" +msgid "Display barcode in search results" +msgstr "Afficher le code barre dans les résultats de recherche" + +msgctxt "attribute_edit_link_setting_name" +msgid "Add an edit link in search results" +msgstr "Ajouter un lien d'édition de chaque résultat" + +msgctxt "attribute_value_yes_label" +msgid "Yes" +msgstr "Oui" + +msgctxt "attribute_value_no_label" +msgid "No" +msgstr "Non" + msgctxt "classify_products_according_to_your_preferences" msgid "Classify products according to your preferences" msgstr "Classer les produits suivant vos préférences" diff --git a/scss/_off.scss b/scss/_off.scss index 11f8a77665839..f16322e6336f1 100644 --- a/scss/_off.scss +++ b/scss/_off.scss @@ -558,6 +558,9 @@ html[dir="rtl"] { // List of products with cards (e.g. search results, facets) +.search_results li { + position: relative; +} .list_product_a { border-radius:8px; @@ -587,6 +590,27 @@ html[dir="rtl"] { background-color:#faa; } +.search_results .list_display_barcode { + color: black; + font-size: .8em; +} + +.search_results .list_edit_link img { + height: 1em; +} + +.search_results .list_edit_link { + position: absolute; + top: 0; + right: 0; + padding: 0 1em; + border-radius: .3em; +} + +.search_results .list_edit_link:hover { + background-color: #aaf; +} + // Horizontally and vertically center images .list_product_img_div {