From 702cd3a2430c7fe026b020a12af6f8a125c88e37 Mon Sep 17 00:00:00 2001 From: Edouard Marquez Date: Sun, 2 Feb 2025 18:37:56 +0100 Subject: [PATCH] Traceability codes --- packages/smooth_app/lib/l10n/app_en.arb | 34 +++- .../product/simple_input_page_helpers.dart | 63 +++++++- .../widgets/smooth_explanation_banner.dart | 149 +++++++++++------- 3 files changed, 185 insertions(+), 61 deletions(-) diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index 8b462d68c21..e1d52b453cd 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -1638,9 +1638,37 @@ "@edit_product_form_item_emb_codes_type": { "description": "Product edition - Traceability Codes - input textfield type" }, - "edit_product_form_item_emb_codes_explanations": "In Europe, code in an ellipse with the 2 country initials followed by a number and CE.\nExamples: EMB 53062, FR 62.448.034 CE, 84 R 20, 33 RECOLTANT 522", - "@edit_product_form_item_emb_codes_examples": { - "description": "Product edition - EMB Codes - explanations" + "edit_product_form_item_emb_help_title": "Good practices: traceability codes", + "@edit_product_form_item_emb_help_title": { + "description": "Title for the help section about traceability codes" + }, + "edit_product_form_item_emb_help_info1": "In this section, you can input codes related to **packaging marks**, **identification marks** or **health marks**.", + "@edit_product_form_item_emb_help_info1": { + "description": "Text explaining how to write a traceability code" + }, + "edit_product_form_item_emb_help_info2_title": "Examples of traceability codes", + "@edit_product_form_item_emb_help_info2_title": { + "description": "Explanation about EMB/EC… codes" + }, + "edit_product_form_item_emb_help_info2_item1_text": "**EC codes** used in the European Community to identify food producers or packagers:", + "@edit_product_form_item_emb_help_info2_item1_text": { + "description": "Explanation about EC codes" + }, + "edit_product_form_item_emb_help_info2_item1_example": "FR\n72.264.002\nCE", + "@edit_product_form_item_emb_help_info2_item1_example": { + "description": "Example of an EC code (you can change with a valid code in another european country)" + }, + "edit_product_form_item_emb_help_info2_item1_explanation": "**FR**: country code of **France**\n**72.264.002**: geographic data\n**CE**: European Community", + "@edit_product_form_item_emb_help_info2_item1_explanation": { + "description": "Example of an EC code (shouldn't be translated)" + }, + "edit_product_form_item_emb_help_info2_item2_text": "**EMB codes** used in France:", + "@edit_product_form_item_emb_help_info2_item2_text": { + "description": "Explanation about EMB codes" + }, + "edit_product_form_item_emb_help_info2_item2_explanation": "EMB 72264", + "@edit_product_form_item_emb_help_info2_item2_explanation": { + "description": "Example of an EMB code" }, "edit_product_form_item_categories_title": "Categories", "@edit_product_form_item_categories_title": { diff --git a/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart b/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart index b83f1433a0d..adfdd6c9f08 100644 --- a/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart +++ b/packages/smooth_app/lib/pages/product/simple_input_page_helpers.dart @@ -9,6 +9,7 @@ import 'package:smooth_app/pages/image_crop_page.dart'; import 'package:smooth_app/pages/product/multilingual_helper.dart'; import 'package:smooth_app/query/product_query.dart'; import 'package:smooth_app/resources/app_icons.dart' as icons; +import 'package:smooth_app/themes/theme_provider.dart'; import 'package:smooth_app/widgets/smooth_explanation_banner.dart'; /// Abstract helper for Simple Input Page. @@ -527,12 +528,68 @@ class SimpleInputPageEmbCodeHelper extends AbstractSimpleInputPageHelper { String getTypeLabel(AppLocalizations appLocalizations) => appLocalizations.edit_product_form_item_emb_codes_type; + @override + String? getAddExplanationsTitle(AppLocalizations appLocalizations) => + appLocalizations.edit_product_form_item_emb_help_title; + @override WidgetBuilder? getAddExplanationsContent() => (BuildContext context) { final AppLocalizations appLocalizations = AppLocalizations.of(context); - return ExplanationBodyInfo( - text: - appLocalizations.edit_product_form_item_emb_codes_explanations); + + return Column( + children: [ + ExplanationBodyInfo( + text: appLocalizations.edit_product_form_item_emb_help_info1, + icon: false, + ), + ExplanationTextContainer( + title: + appLocalizations.edit_product_form_item_emb_help_info2_title, + items: [ + ExplanationTextContainerContentItem( + text: appLocalizations + .edit_product_form_item_emb_help_info2_item1_text, + example: appLocalizations + .edit_product_form_item_emb_help_info2_item1_explanation, + visualExamplePosition: + ExplanationVisualExamplePosition.afterTitle, + visualExample: Container( + width: 104.0, + decoration: BoxDecoration( + border: Border.all( + color: + context.lightTheme() ? Colors.black : Colors.white, + width: 1.0, + ), + borderRadius: const BorderRadius.all( + Radius.elliptical(100, 50), + ), + ), + padding: const EdgeInsetsDirectional.symmetric( + vertical: VERY_SMALL_SPACE, + ), + child: Text( + appLocalizations + .edit_product_form_item_emb_help_info2_item1_example, + textAlign: TextAlign.center, + textScaler: TextScaler.noScaling, + style: const TextStyle( + fontSize: 12.0, + height: 1.2, + ), + ), + ), + ), + ExplanationTextContainerContentItem( + text: appLocalizations + .edit_product_form_item_emb_help_info2_item2_text, + example: appLocalizations + .edit_product_form_item_emb_help_info2_item2_explanation, + ), + ], + ), + ], + ); }; @override diff --git a/packages/smooth_app/lib/widgets/smooth_explanation_banner.dart b/packages/smooth_app/lib/widgets/smooth_explanation_banner.dart index e193558a9d2..da87849ad2d 100644 --- a/packages/smooth_app/lib/widgets/smooth_explanation_banner.dart +++ b/packages/smooth_app/lib/widgets/smooth_explanation_banner.dart @@ -230,41 +230,49 @@ class ExplanationTextContainer extends StatelessWidget { children: [ _ExplanationContainerTitle( label: title, - foregroundColor: lightTheme ? Colors.black : Colors.white, + foregroundColor: Colors.white, backgroundColor: - lightTheme ? extension.primaryMedium : extension.primaryDark, + lightTheme ? extension.primarySemiDark : extension.primaryDark, ), - ...items - .mapIndexed((int position, ExplanationTextContainerContent item) { - return switch (item) { - ExplanationTextContainerContentText() => Padding( - padding: const EdgeInsetsDirectional.only( - start: LARGE_SPACE, - end: LARGE_SPACE, - top: MEDIUM_SPACE, - bottom: VERY_SMALL_SPACE, - ), - child: TextWithBoldParts( - text: item.text, - textStyle: TextStyle( - color: lightTheme ? extension.primaryDark : Colors.white, + ...items.mapIndexed( + (int position, ExplanationTextContainerContent item) { + return switch (item) { + ExplanationTextContainerContentText() => Padding( + padding: const EdgeInsetsDirectional.only( + start: LARGE_SPACE, + end: LARGE_SPACE, + top: MEDIUM_SPACE, + bottom: VERY_SMALL_SPACE, + ), + child: TextWithBoldParts( + text: item.text, + textStyle: TextStyle( + color: lightTheme ? extension.primaryDark : Colors.white, + ), ), ), - ), - ExplanationTextContainerContentItem() => Padding( - padding: const EdgeInsetsDirectional.only( - top: SMALL_SPACE, - ), - child: _ExplanationBodyListItem( - icon: const icons.Arrow.right(size: 11.0), - iconBackgroundColor: extension.greyNormal, - iconPadding: EdgeInsets.zero, - text: item.example, - explanation: item.text, + ExplanationTextContainerContentItem() => Padding( + padding: const EdgeInsetsDirectional.only( + top: SMALL_SPACE, + ), + child: _ExplanationBodyListItem( + icon: icons.Arrow.right( + size: 11.0, + color: lightTheme ? null : extension.primarySemiDark, + ), + iconBackgroundColor: lightTheme + ? extension.primarySemiDark + : extension.primaryLight, + iconPadding: EdgeInsets.zero, + title: item.text, + text: item.example, + visualExample: item.visualExample, + visualExamplePosition: item.visualExamplePosition, + ), ), - ), - }; - }), + }; + }, + ), ], ); } @@ -283,11 +291,15 @@ class ExplanationTextContainerContentItem extends ExplanationTextContainerContent { ExplanationTextContainerContentItem({ required this.text, - required this.example, + this.example, + this.visualExample, + this.visualExamplePosition, }); final String text; - final String example; + final String? example; + final Widget? visualExample; + final ExplanationVisualExamplePosition? visualExamplePosition; } class ExplanationGoodExamplesContainer extends StatelessWidget { @@ -350,7 +362,7 @@ class ExplanationBadExamplesContainer extends StatelessWidget { iconBackgroundColor: extension.error, iconPadding: EdgeInsetsDirectional.zero, text: item, - explanation: explanations[position], + title: explanations[position], ), ), ], @@ -415,15 +427,19 @@ class _ExplanationBodyListItem extends StatelessWidget { required this.icon, required this.iconBackgroundColor, required this.iconPadding, - required this.text, - this.explanation, + this.title, + this.text, + this.visualExample, + this.visualExamplePosition = ExplanationVisualExamplePosition.afterExample, }); final Widget icon; final Color iconBackgroundColor; final EdgeInsetsGeometry iconPadding; - final String text; - final String? explanation; + final String? text; + final String? title; + final Widget? visualExample; + final ExplanationVisualExamplePosition? visualExamplePosition; @override Widget build(BuildContext context) { @@ -438,7 +454,7 @@ class _ExplanationBodyListItem extends StatelessWidget { top: 10.0, ), child: Row( - crossAxisAlignment: explanation == null + crossAxisAlignment: title == null ? CrossAxisAlignment.center : CrossAxisAlignment.start, children: [ @@ -455,16 +471,16 @@ class _ExplanationBodyListItem extends StatelessWidget { ), ), ), - SizedBox(width: explanation != null ? 11.0 : 13.0), + SizedBox(width: title != null ? 11.0 : 13.0), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (explanation != null) ...[ + if (title != null) ...[ Padding( padding: const EdgeInsetsDirectional.only(start: 2.0), child: TextWithBoldParts( - text: explanation!, + text: title!, textStyle: TextStyle( color: lightTheme ? extension.primaryDark @@ -483,24 +499,42 @@ class _ExplanationBodyListItem extends StatelessWidget { ), const SizedBox(height: VERY_SMALL_SPACE), ], - DecoratedBox( - decoration: BoxDecoration( - color: lightTheme - ? extension.primaryLight - : extension.primaryMedium, - borderRadius: ROUNDED_BORDER_RADIUS, + if (visualExample != null && + visualExamplePosition == + ExplanationVisualExamplePosition.afterTitle) + Padding( + padding: const EdgeInsetsDirectional.only( + top: VERY_SMALL_SPACE, + bottom: BALANCED_SPACE, + ), + child: visualExample, ), - child: Padding( - padding: const EdgeInsetsDirectional.symmetric( - horizontal: MEDIUM_SPACE, - vertical: BALANCED_SPACE, + if (text != null) + DecoratedBox( + decoration: BoxDecoration( + color: lightTheme + ? extension.primaryLight + : extension.primaryMedium, + borderRadius: ROUNDED_BORDER_RADIUS, ), - child: TextWithBoldParts( - text: text, - textStyle: const TextStyle(color: Colors.black), + child: Padding( + padding: const EdgeInsetsDirectional.symmetric( + horizontal: MEDIUM_SPACE, + vertical: BALANCED_SPACE, + ), + child: TextWithBoldParts( + text: text!, + textStyle: const TextStyle(color: Colors.black), + ), ), ), - ) + if (visualExample != null && + visualExamplePosition == + ExplanationVisualExamplePosition + .afterExample) ...[ + const SizedBox(height: VERY_SMALL_SPACE), + visualExample!, + ], ], ), ) @@ -509,3 +543,8 @@ class _ExplanationBodyListItem extends StatelessWidget { ); } } + +enum ExplanationVisualExamplePosition { + afterTitle, + afterExample, +}