Skip to content

Commit

Permalink
feat: 5204 - preliminary step for multi-product price addition (openf…
Browse files Browse the repository at this point in the history
…oodfacts#5367)

* feat: 5204 - preliminary conceptual step

New files:
* `price_amount_model.dart`: Model for the price of a single product.
* `price_meta_product.dart`: Meta version of a product, coming from OFF or from Prices.
* `price_product_search_page.dart`: Product Search Page, for Prices.
* `price_product_list_tile.dart`: Displays a meta product with an action button, as a ListTile.

Impacted files:
* `app_en.arb`: added 2 labels for the new "price product search" page
* `app_fr.arb`: added 2 labels for the new "price product search" page
* `edit_product_page.dart`: minor refactoring
* `get_prices_model.dart`: minor refactoring
* `price_amount_card.dart`: minor refactoring
* `price_amount_field.dart`: minor refactoring
* `price_count_widget.dart`: minor refactoring
* `price_model.dart`: moved product amount code to new class `PriceAmountModel`
* `price_product_widget.dart`: unrelated UI refactoring
* `prices_card.dart`: minor refactoring
* `product_price_add_page.dart`: minor refactoring

* No "edit" button for product
  • Loading branch information
monsieurtanuki authored Jun 14, 2024
1 parent f5973da commit 7bf53e8
Show file tree
Hide file tree
Showing 15 changed files with 499 additions and 243 deletions.
10 changes: 10 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,16 @@
"prices_generic_title": "Prices",
"prices_add_a_price": "Add a price",
"prices_send_the_price": "Send the price",
"prices_barcode_search_title": "Product search",
"prices_barcode_search_running": "Looking for {barcode}",
"@prices_barcode_search_running": {
"description": "Dialog title about barcode look-up",
"placeholders": {
"barcode": {
"type": "String"
}
}
},
"prices_view_prices": "View the prices",
"prices_list_length_one_page": "{count,plural, =0{No price yet} =1{Only one price} other{All {count} prices}}",
"@prices_list_length_one_page": {
Expand Down
10 changes: 10 additions & 0 deletions packages/smooth_app/lib/l10n/app_fr.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,16 @@
"prices_generic_title": "Prix",
"prices_add_a_price": "Ajouter un prix",
"prices_send_the_price": "Envoyer le prix",
"prices_barcode_search_title": "Recherche de produit",
"prices_barcode_search_running": "À la recherche de {barcode}",
"@prices_barcode_search_running": {
"description": "Dialog title about barcode look-up",
"placeholders": {
"barcode": {
"type": "String"
}
}
},
"prices_view_prices": "Voir les prix",
"prices_list_length_one_page": "{count,plural, =0{Aucun prix} =1{Un seul prix} other{Tous les {count} prix}}",
"@prices_list_length_one_page": {
Expand Down
34 changes: 5 additions & 29 deletions packages/smooth_app/lib/pages/prices/get_prices_model.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:smooth_app/helpers/product_cards_helper.dart';
import 'package:smooth_app/pages/prices/price_meta_product.dart';
import 'package:smooth_app/pages/prices/product_price_add_page.dart';
import 'package:smooth_app/query/product_query.dart';

Expand All @@ -20,18 +20,15 @@ class GetPricesModel {

/// Gets latest prices for a product.
factory GetPricesModel.product({
required final Product product,
required final PriceMetaProduct product,
required final BuildContext context,
}) =>
GetPricesModel(
parameters: _getProductPricesParameters(product.barcode!),
parameters: _getProductPricesParameters(product.barcode),
displayOwner: true,
displayProduct: false,
uri: _getProductPricesUri(product.barcode!),
title: getProductNameAndBrands(
product,
AppLocalizations.of(context),
),
uri: _getProductPricesUri(product.barcode),
title: product.getName(AppLocalizations.of(context)),
subtitle: product.barcode,
addButton: () async => ProductPriceAddPage.showProductPage(
context: context,
Expand All @@ -40,27 +37,6 @@ class GetPricesModel {
enableCountButton: false,
);

/// Gets latest prices for a barcode.
factory GetPricesModel.barcode({
required final String barcode,
required final String name,
required final BuildContext context,
}) =>
GetPricesModel(
parameters: _getProductPricesParameters(barcode),
displayOwner: true,
displayProduct: false,
uri: _getProductPricesUri(barcode),
title: name,
subtitle: barcode,
addButton: () async => ProductPriceAddPage.showBarcodePage(
context: context,
barcode: barcode,
title: name,
),
enableCountButton: false,
);

static GetPricesParameters _getProductPricesParameters(
final String barcode,
) =>
Expand Down
26 changes: 17 additions & 9 deletions packages/smooth_app/lib/pages/prices/price_amount_card.dart
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';
import 'package:smooth_app/generic_lib/buttons/smooth_large_button_with_icon.dart';
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_model.dart';
import 'package:smooth_app/pages/prices/price_amount_model.dart';
import 'package:smooth_app/pages/prices/price_product_list_tile.dart';

/// Card that displays the amounts (discounted or not) for price adding.
class PriceAmountCard extends StatefulWidget {
const PriceAmountCard();
const PriceAmountCard(this.model);

final PriceAmountModel model;

@override
State<PriceAmountCard> createState() => _PriceAmountCardState();
Expand All @@ -22,16 +24,22 @@ class _PriceAmountCardState extends State<PriceAmountCard> {

@override
Widget build(BuildContext context) {
final PriceModel model = context.watch<PriceModel>();
final AppLocalizations appLocalizations = AppLocalizations.of(context);
return SmoothCard(
child: Column(
children: <Widget>[
Text(appLocalizations.prices_amount_subtitle),
PriceProductListTile(
product: widget.model.product,
),
SmoothLargeButtonWithIcon(
icon: model.promo ? Icons.check_box : Icons.check_box_outline_blank,
icon: widget.model.promo
? Icons.check_box
: Icons.check_box_outline_blank,
text: appLocalizations.prices_amount_is_discounted,
onPressed: () => model.promo = !model.promo,
onPressed: () => setState(
() => widget.model.promo = !widget.model.promo,
),
),
const SizedBox(height: SMALL_SPACE),
LayoutBuilder(
Expand All @@ -46,10 +54,10 @@ class _PriceAmountCardState extends State<PriceAmountCard> {
child: PriceAmountField(
controller: _controllerPaid,
isPaidPrice: true,
model: model,
model: widget.model,
),
);
if (!model.promo) {
if (!widget.model.promo) {
return Align(
alignment: Alignment.centerLeft,
child: columnPaid,
Expand All @@ -60,7 +68,7 @@ class _PriceAmountCardState extends State<PriceAmountCard> {
child: PriceAmountField(
controller: _controllerWithoutDiscount,
isPaidPrice: false,
model: model,
model: widget.model,
),
);
return Row(
Expand Down
8 changes: 4 additions & 4 deletions packages/smooth_app/lib/pages/prices/price_amount_field.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:smooth_app/generic_lib/widgets/smooth_text_form_field.dart';
import 'package:smooth_app/pages/prices/price_model.dart';
import 'package:smooth_app/pages/prices/price_amount_model.dart';

/// Text field that displays a single amount for price adding.
class PriceAmountField extends StatelessWidget {
Expand All @@ -11,7 +11,7 @@ class PriceAmountField extends StatelessWidget {
required this.controller,
});

final PriceModel model;
final PriceAmountModel model;
final bool isPaidPrice;
final TextEditingController controller;

Expand Down Expand Up @@ -46,7 +46,7 @@ class PriceAmountField extends StatelessWidget {
if (value == null || value.isEmpty) {
return appLocalizations.prices_amount_price_mandatory;
}
final double? doubleValue = model.validateDouble(value);
final double? doubleValue = PriceAmountModel.validateDouble(value);
if (doubleValue == null) {
return appLocalizations.prices_amount_price_incorrect;
}
Expand All @@ -57,7 +57,7 @@ class PriceAmountField extends StatelessWidget {
if (value == null || value.isEmpty) {
return null;
}
final double? doubleValue = model.validateDouble(value);
final double? doubleValue = PriceAmountModel.validateDouble(value);
if (doubleValue == null) {
return appLocalizations.prices_amount_price_incorrect;
}
Expand Down
48 changes: 48 additions & 0 deletions packages/smooth_app/lib/pages/prices/price_amount_model.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:smooth_app/pages/prices/price_meta_product.dart';

/// Model for the price of a single product.
class PriceAmountModel {
PriceAmountModel({
required this.product,
});

PriceMetaProduct product;

String _paidPrice = '';
String _priceWithoutDiscount = '';

set paidPrice(final String value) => _paidPrice = value;

set priceWithoutDiscount(final String value) => _priceWithoutDiscount = value;

late double _checkedPaidPrice;
double? _checkedPriceWithoutDiscount;

double get checkedPaidPrice => _checkedPaidPrice;

double? get checkedPriceWithoutDiscount => _checkedPriceWithoutDiscount;

bool promo = false;

static double? validateDouble(final String value) =>
double.tryParse(value) ??
double.tryParse(
value.replaceAll(',', '.'),
);

String? checkParameters(final BuildContext context) {
_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 null;
}
}
30 changes: 11 additions & 19 deletions packages/smooth_app/lib/pages/prices/price_count_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,19 @@ import 'package:smooth_app/database/dao_product.dart';
import 'package:smooth_app/database/local_database.dart';
import 'package:smooth_app/pages/prices/get_prices_model.dart';
import 'package:smooth_app/pages/prices/price_button.dart';
import 'package:smooth_app/pages/prices/price_meta_product.dart';
import 'package:smooth_app/pages/prices/prices_page.dart';

/// Price Count display.
class PriceCountWidget extends StatelessWidget {
const PriceCountWidget(
this.count, {
required this.barcode,
required this.name,
required this.priceProduct,
required this.enableCountButton,
});

final int count;
final String barcode;

/// Product name to display in case we have only the barcode, not the product.
final String name;

final PriceProduct priceProduct;
final bool enableCountButton;

@override
Expand All @@ -31,24 +27,20 @@ class PriceCountWidget extends StatelessWidget {
: () async {
final LocalDatabase localDatabase =
context.read<LocalDatabase>();
final Product? product =
await DaoProduct(localDatabase).get(barcode);
final Product? newProduct =
await DaoProduct(localDatabase).get(priceProduct.code);
if (!context.mounted) {
return;
}
return Navigator.of(context).push<void>(
MaterialPageRoute<void>(
builder: (BuildContext context) => PricesPage(
product != null
? GetPricesModel.product(
product: product,
context: context,
)
: GetPricesModel.barcode(
barcode: barcode,
name: name,
context: context,
),
GetPricesModel.product(
product: newProduct != null
? PriceMetaProduct.product(newProduct)
: PriceMetaProduct.priceProduct(priceProduct),
context: context,
),
),
),
);
Expand Down
25 changes: 25 additions & 0 deletions packages/smooth_app/lib/pages/prices/price_meta_product.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:openfoodfacts/openfoodfacts.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;

final Product? product;
final PriceProduct? priceProduct;

String get barcode =>
product != null ? product!.barcode! : priceProduct!.code;

String getName(final AppLocalizations appLocalizations) => product != null
? getProductNameAndBrands(
product!,
appLocalizations,
)
: priceProduct!.name ?? priceProduct!.code;

String? get imageUrl => product != null ? null : priceProduct!.imageURL;
}
Loading

0 comments on commit 7bf53e8

Please sign in to comment.