Skip to content

Commit

Permalink
feat: 5203 - "add receipt" and "add price tags", even offline or not …
Browse files Browse the repository at this point in the history
…found (#5392)

Impacted files:
* `app_en.arb`: added 2 "add" and 4 "price product" labels
* `app_fr.arb`: added 2 "add" and 4 "price product" labels
* `edit_product_page.dart`: minor refactoring
* `get_prices_model.dart`: minor refactoring
* `price_amount_card.dart`: added the case of "no product yet"
* `price_amount_model.dart`: added "no product yet" check
* `price_meta_product.dart`: 2 more cases - "no product yet" and "not found product"
* `price_product_list_tile.dart`: minor refactoring
* `price_product_search_page.dart`: now we accept "not found" products and we don't force the server lookup
* `prices_card.dart`: minor refactoring
* `product_price_add_page.dart`: minor refactoring
* `user_preferences_account.dart`: added "Add receipt" and "Add price tags"
  • Loading branch information
monsieurtanuki authored Jun 18, 2024
1 parent 4b4f387 commit b9f83c4
Show file tree
Hide file tree
Showing 12 changed files with 174 additions and 41 deletions.
6 changes: 6 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1710,6 +1710,11 @@
"prices_send_n_prices": "{count,plural, =1{Send the price} other{Send {count} prices}}",
"prices_add_an_item": "Add an item",
"prices_add_a_price": "Add a price",
"prices_add_a_receipt": "Add a receipt",
"prices_add_price_tags": "Add price tags",
"prices_barcode_search_not_found": "Product not found",
"prices_barcode_search_none_yet": "No product yet",
"prices_barcode_search_question": "Do you want to look for this product?",
"prices_barcode_search_title": "Product search",
"prices_barcode_search_running": "Looking for {barcode}",
"@prices_barcode_search_running": {
Expand Down Expand Up @@ -1781,6 +1786,7 @@
"prices_amount_price_normal": "Price",
"prices_amount_price_discounted": "Discounted price",
"prices_amount_price_not_discounted": "Original price",
"prices_amount_no_product": "One product is missing!",
"prices_amount_price_incorrect": "Incorrect value",
"prices_amount_price_mandatory": "Mandatory value",
"prices_currency_subtitle": "Currency",
Expand Down
6 changes: 6 additions & 0 deletions packages/smooth_app/lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1678,6 +1678,11 @@
"prices_send_n_prices": "{count,plural, =1{Envoyer le prix} other{Envoyer {count} prix}}",
"prices_add_an_item": "Ajouter un article",
"prices_add_a_price": "Ajouter un prix",
"prices_add_a_receipt": "Ajouter un ticket de caisse",
"prices_add_price_tags": "Ajouter des étiquettes de prix",
"prices_barcode_search_not_found": "Produit non trouvé",
"prices_barcode_search_none_yet": "Pas encore de produit",
"prices_barcode_search_question": "Voulez-vous chercher ce produit ?",
"prices_barcode_search_title": "Recherche de produit",
"prices_barcode_search_running": "À la recherche de {barcode}",
"@prices_barcode_search_running": {
Expand Down Expand Up @@ -1749,6 +1754,7 @@
"prices_amount_price_normal": "Prix",
"prices_amount_price_discounted": "Prix en promo",
"prices_amount_price_not_discounted": "Prix d'origine",
"prices_amount_no_product": "Il manque un produit !",
"prices_amount_price_incorrect": "Valeur incorrecte",
"prices_amount_price_mandatory": "Valeur obligatoire",
"prices_currency_subtitle": "Devise",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ import 'package:smooth_app/pages/preferences/user_preferences_item.dart';
import 'package:smooth_app/pages/preferences/user_preferences_list_tile.dart';
import 'package:smooth_app/pages/preferences/user_preferences_page.dart';
import 'package:smooth_app/pages/prices/get_prices_model.dart';
import 'package:smooth_app/pages/prices/price_meta_product.dart';
import 'package:smooth_app/pages/prices/price_user_button.dart';
import 'package:smooth_app/pages/prices/prices_page.dart';
import 'package:smooth_app/pages/prices/prices_proofs_page.dart';
import 'package:smooth_app/pages/prices/prices_users_page.dart';
import 'package:smooth_app/pages/prices/product_price_add_page.dart';
import 'package:smooth_app/pages/product/common/product_query_page_helper.dart';
import 'package:smooth_app/pages/user_management/login_page.dart';
import 'package:smooth_app/query/paged_product_query.dart';
Expand Down Expand Up @@ -237,6 +239,24 @@ class UserPreferencesAccount extends AbstractUserPreferences {
),
Icons.receipt,
),
_getListTile(
appLocalizations.prices_add_a_receipt,
() async => ProductPriceAddPage.showProductPage(
context: context,
product: PriceMetaProduct.empty(),
proofType: ProofType.receipt,
),
Icons.add_shopping_cart,
),
_getListTile(
appLocalizations.prices_add_price_tags,
() async => ProductPriceAddPage.showProductPage(
context: context,
product: PriceMetaProduct.empty(),
proofType: ProofType.priceTag,
),
Icons.add_shopping_cart,
),
_getListTile(
appLocalizations.all_search_prices_latest_title,
() async => Navigator.of(context).push(
Expand Down
1 change: 1 addition & 0 deletions packages/smooth_app/lib/pages/prices/get_prices_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class GetPricesModel {
addButton: () async => ProductPriceAddPage.showProductPage(
context: context,
product: product,
proofType: ProofType.priceTag,
),
enableCountButton: false,
);
Expand Down
36 changes: 29 additions & 7 deletions packages/smooth_app/lib/pages/prices/price_amount_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_card.dart';
import 'package:smooth_app/pages/prices/price_amount_field.dart';
import 'package:smooth_app/pages/prices/price_amount_model.dart';
import 'package:smooth_app/pages/prices/price_meta_product.dart';
import 'package:smooth_app/pages/prices/price_model.dart';
import 'package:smooth_app/pages/prices/price_product_list_tile.dart';
import 'package:smooth_app/pages/prices/price_product_search_page.dart';

/// Card that displays the amounts (discounted or not) for price adding.
class PriceAmountCard extends StatefulWidget {
Expand Down Expand Up @@ -36,6 +38,7 @@ class _PriceAmountCardState extends State<PriceAmountCard> {
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final bool isEmpty = widget.model.product.barcode.isEmpty;
return SmoothCard(
child: Column(
children: <Widget>[
Expand All @@ -45,13 +48,32 @@ class _PriceAmountCardState extends State<PriceAmountCard> {
),
PriceProductListTile(
product: widget.model.product,
trailingIconData: widget.total == 1 ? null : Icons.clear,
onPressed: widget.total == 1
? null
: () {
widget.priceModel.priceAmountModels.removeAt(widget.index);
widget.refresh.call();
},
trailingIconData: isEmpty
? Icons.edit
: widget.total == 1
? null
: Icons.clear,
onPressed: isEmpty
? () async {
final PriceMetaProduct? product =
await Navigator.of(context).push<PriceMetaProduct>(
MaterialPageRoute<PriceMetaProduct>(
builder: (BuildContext context) =>
const PriceProductSearchPage(),
),
);
if (product == null) {
return;
}
setState(() => widget.model.product = product);
}
: widget.total == 1
? null
: () {
widget.priceModel.priceAmountModels
.removeAt(widget.index);
widget.refresh.call();
},
),
SmoothLargeButtonWithIcon(
icon: widget.model.promo
Expand Down
6 changes: 5 additions & 1 deletion packages/smooth_app/lib/pages/prices/price_amount_model.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,17 @@ class PriceAmountModel {
);

String? checkParameters(final BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
if (product.barcode.isEmpty) {
return appLocalizations.prices_amount_no_product;
}
_checkedPaidPrice = validateDouble(_paidPrice)!;
_checkedPriceWithoutDiscount = null;
if (promo) {
if (_priceWithoutDiscount.isNotEmpty) {
_checkedPriceWithoutDiscount = validateDouble(_priceWithoutDiscount);
if (_checkedPriceWithoutDiscount == null) {
return AppLocalizations.of(context).prices_amount_price_incorrect;
return appLocalizations.prices_amount_price_incorrect;
}
}
}
Expand Down
84 changes: 70 additions & 14 deletions packages/smooth_app/lib/pages/prices/price_meta_product.dart
Original file line number Diff line number Diff line change
@@ -1,25 +1,81 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_product_image.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';

/// Meta version of a product, coming from OFF or from Prices.
class PriceMetaProduct {
PriceMetaProduct.product(Product this.product) : priceProduct = null;
PriceMetaProduct.priceProduct(PriceProduct this.priceProduct)
: product = null;
PriceMetaProduct.product(final Product product)
: _product = product,
_priceProduct = null,
_barcode = null;
PriceMetaProduct.priceProduct(final PriceProduct priceProduct)
: _product = null,
_priceProduct = priceProduct,
_barcode = null;
PriceMetaProduct.empty()
: _product = null,
_priceProduct = null,
_barcode = null;
PriceMetaProduct.unknown(final String barcode)
: _product = null,
_priceProduct = null,
_barcode = barcode;

final Product? product;
final PriceProduct? priceProduct;
final Product? _product;
final PriceProduct? _priceProduct;
final String? _barcode;

String get barcode =>
product != null ? product!.barcode! : priceProduct!.code;
// TODO(monsieurtanuki): refine this test
bool get isValid => barcode.length >= 8;

String getName(final AppLocalizations appLocalizations) => product != null
? getProductNameAndBrands(
product!,
appLocalizations,
)
: priceProduct!.name ?? priceProduct!.code;
String get barcode {
if (_product != null) {
return _product.barcode!;
}
if (_priceProduct != null) {
return _priceProduct.code;
}
return _barcode ?? '';
}

String? get imageUrl => product != null ? null : priceProduct!.imageURL;
String getName(final AppLocalizations appLocalizations) {
if (_product != null) {
return getProductNameAndBrands(
_product,
appLocalizations,
);
}
if (_priceProduct != null) {
return _priceProduct.name ?? _priceProduct.code;
}
if (barcode.isEmpty) {
return appLocalizations.prices_barcode_search_none_yet;
}
return appLocalizations.prices_barcode_search_not_found;
}

Widget getImageWidget(final double size) {
if (_product != null) {
return SmoothMainProductImage(
product: _product,
width: size,
height: size,
);
}
if (_priceProduct != null) {
final String? imageURL = _priceProduct.imageURL;
return SmoothImage(
width: size,
height: size,
imageProvider: imageURL == null ? null : NetworkImage(imageURL),
);
}
return SmoothImage(
width: size,
height: size,
);
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/widgets/images/smooth_image.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_product_image.dart';
import 'package:smooth_app/pages/prices/price_meta_product.dart';

/// Displays a meta product with an action button, as a ListTile.
Expand All @@ -28,19 +26,7 @@ class PriceProductListTile extends StatelessWidget {
children: <Widget>[
SizedBox(
width: size,
child: product.product != null
? SmoothMainProductImage(
product: product.product!,
width: size,
height: size,
)
: SmoothImage(
width: size,
height: size,
imageProvider: product.imageUrl == null
? null
: NetworkImage(product.imageUrl!),
),
child: product.getImageWidget(size),
),
const SizedBox(width: SMALL_SPACE),
Expanded(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:smooth_app/data_models/fetched_product.dart';
import 'package:smooth_app/database/dao_product.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/generic_lib/design_constants.dart';
import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart';
import 'package:smooth_app/generic_lib/loading_dialog.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_back_button.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_text_form_field.dart';
Expand Down Expand Up @@ -50,6 +51,8 @@ class _PriceProductSearchPageState extends State<PriceProductSearchPage> {
@override
Widget build(BuildContext context) {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final PriceMetaProduct priceMetaProduct =
_product ?? PriceMetaProduct.unknown(_controller.text);
// TODO(monsieurtanuki): add WillPopScope2
return SmoothScaffold(
appBar: SmoothAppBar(
Expand Down Expand Up @@ -80,13 +83,13 @@ class _PriceProductSearchPageState extends State<PriceProductSearchPage> {
prefixIcon: const Icon(CupertinoIcons.barcode),
textInputAction: TextInputAction.search,
),
if (_product != null)
if (priceMetaProduct.isValid)
Padding(
padding: const EdgeInsets.symmetric(vertical: LARGE_SPACE),
child: PriceProductListTile(
product: _product!,
product: priceMetaProduct,
trailingIconData: Icons.check_circle,
onPressed: () => Navigator.of(context).pop(_product),
onPressed: () => Navigator.of(context).pop(priceMetaProduct),
),
),
],
Expand Down Expand Up @@ -167,6 +170,7 @@ class _PriceProductSearchPageState extends State<PriceProductSearchPage> {
setState(() => _product = PriceMetaProduct.product(product));
return;
}
setState(() {});
}

Future<void> _onFieldSubmitted(final BuildContext context) async {
Expand All @@ -187,6 +191,7 @@ class _PriceProductSearchPageState extends State<PriceProductSearchPage> {
}

Future<void> _scan(final BuildContext context) async {
final AppLocalizations appLocalizations = AppLocalizations.of(context);
final String? barcode = await Navigator.of(context).push<String>(
MaterialPageRoute<String>(
builder: (BuildContext context) => const PriceScanPage(),
Expand All @@ -206,6 +211,26 @@ class _PriceProductSearchPageState extends State<PriceProductSearchPage> {
if (!context.mounted) {
return;
}
final bool? accepts = await showDialog(
context: context,
builder: (final BuildContext context) => SmoothAlertDialog(
body: Text(appLocalizations.prices_barcode_search_question),
neutralAction: SmoothActionButton(
text: appLocalizations.cancel,
onPressed: () => Navigator.of(context).pop(false),
),
positiveAction: SmoothActionButton(
text: appLocalizations.yes,
onPressed: () => Navigator.of(context).pop(true),
),
),
);
if (!context.mounted) {
return;
}
if (accepts != true) {
return;
}
await _onFieldSubmitted(context);
}
}
1 change: 1 addition & 0 deletions packages/smooth_app/lib/pages/prices/prices_card.dart
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ class PricesCard extends StatelessWidget {
onPressed: () async => ProductPriceAddPage.showProductPage(
context: context,
product: PriceMetaProduct.product(product),
proofType: ProofType.priceTag,
),
),
),
Expand Down
Loading

0 comments on commit b9f83c4

Please sign in to comment.