diff --git a/mobile/lib/pages/all_products_page.dart b/mobile/lib/pages/all_products_page.dart index 2c5ff47..5cc28f2 100644 --- a/mobile/lib/pages/all_products_page.dart +++ b/mobile/lib/pages/all_products_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mobile/repos/product.dart'; +import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/sized_card.dart'; import 'package:provider/provider.dart'; import 'product_page.dart'; @@ -77,7 +78,7 @@ class ProductListItem extends StatelessWidget { crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(name, style: Theme.of(context).textTheme.bodyLarge), - Text("${price.toString()} kr", + Text(formatDkkCents(price), style: Theme.of(context).textTheme.bodyMedium), ], )), @@ -127,7 +128,7 @@ class AllProductsPage extends StatelessWidget { itemBuilder: (_, idx) => ProductListItem( productId: products[idx].id, name: products[idx].name, - price: products[idx].price, + price: products[idx].priceInDkkCent, productPage: ProductPage(product: products[idx]), product: products[idx], ), diff --git a/mobile/lib/pages/all_receipts_page.dart b/mobile/lib/pages/all_receipts_page.dart index 3394fcd..d9a67a4 100644 --- a/mobile/lib/pages/all_receipts_page.dart +++ b/mobile/lib/pages/all_receipts_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:mobile/pages/receipt_page.dart'; import 'package:mobile/repos/receipt.dart'; +import 'package:mobile/utils/price.dart'; import 'package:provider/provider.dart'; class ReceiptsListItem extends StatelessWidget { @@ -27,7 +28,7 @@ class ReceiptsListItem extends StatelessWidget { padding: const EdgeInsets.all(20), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [Text(dateFormatted), Text("$totalPrice kr")], + children: [Text(dateFormatted), Text(formatDkkCents(totalPrice))], ), ), ), diff --git a/mobile/lib/pages/cart_page.dart b/mobile/lib/pages/cart_page.dart index 0cca791..5f20636 100644 --- a/mobile/lib/pages/cart_page.dart +++ b/mobile/lib/pages/cart_page.dart @@ -7,6 +7,7 @@ import 'package:mobile/repos/cart.dart'; import 'package:mobile/repos/product.dart'; import 'package:mobile/repos/user.dart'; import 'package:mobile/results.dart'; +import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/primary_button.dart'; import 'package:mobile/widgets/sized_card.dart'; import 'package:provider/provider.dart'; @@ -44,7 +45,7 @@ class CartItemView extends StatelessWidget { child: Text(name, style: Theme.of(context).textTheme.bodyLarge), ), - Text("$price kr", + Text(formatDkkCents(price), style: Theme.of(context).textTheme.bodyMedium), ], ), @@ -160,7 +161,7 @@ class CartPage extends StatelessWidget { cartRepo: cartRepo, productId: cart[idx].product.id, name: cart[idx].product.name, - price: cart[idx].product.price, + price: cart[idx].product.priceInDkkCent, amount: cart[idx].amount), itemCount: cart.length, ); diff --git a/mobile/lib/pages/finish_shopping_page.dart b/mobile/lib/pages/finish_shopping_page.dart index c7132a4..ee5c9bc 100644 --- a/mobile/lib/pages/finish_shopping_page.dart +++ b/mobile/lib/pages/finish_shopping_page.dart @@ -4,6 +4,7 @@ import 'package:mobile/repos/paying_state.dart'; import 'package:mobile/repos/receipt.dart'; import 'package:mobile/repos/user.dart'; import 'package:mobile/results.dart'; +import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/primary_button.dart'; import 'package:mobile/widgets/receipt_item.dart'; import 'package:provider/provider.dart'; @@ -21,122 +22,125 @@ class FinishShoppingPage extends StatelessWidget { final cart = cartRepo.allCartItems(); return Scaffold( - body: Stack( - children: [ - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const BackButton(), - Container( - margin: const EdgeInsets.all(20), - child: Expanded( - child: ListView.builder( - shrinkWrap: true, - itemBuilder: (_, idx) => ReceiptItemView( - pricePerAmount: cart[idx].product.price, - name: cart[idx].product.name, - amount: cart[idx].amount), - itemCount: cart.length)), - ), - Container( - margin: const EdgeInsets.all(20), - child: Expanded( - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text( - "Total:", - style: TextStyle(fontWeight: FontWeight.bold), - ), - Text("${cartRepo.totalPrice()} kr"), - ], - )), - ), - Expanded( - child: Center( - child: PrimaryButton( - onPressed: () async { - payingStateRepo.next(); - await Future.delayed(const Duration(seconds: 1)); - if (user.pay(cartRepo.totalPrice()) is Err) { - if (context.mounted) { - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - content: const Text( - 'Du har desværre ikke råd til at købe dette'), - actions: [ - TextButton( - onPressed: () => - Navigator.pop(context, 'OK'), - child: const Text('OK'), - ), - ], - ), - ); - } - payingStateRepo.reset(); - return; - } - receiptRepo.createReceipt(cart); - payingStateRepo.next(); - await Future.delayed(const Duration(seconds: 1)); - cartRepo.clearCart(); - payingStateRepo.reset(); - if (context.mounted) Navigator.pop(context); - }, - child: const Text("Betal"))), - ), - ], - ), - if (payingStateRepo.state != PayingState.unset) ...[ - Container( - color: Colors.black.withValues(alpha: 0.5), - ), - ], - Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, + body: SafeArea( + child: Stack( + children: [ + Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - ...switch (payingStateRepo.state) { - PayingState.unset => [], - PayingState.loading => [ - Container( - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Colors.white, - ), - padding: const EdgeInsets.all(20), - child: SizedBox( - width: 50, - height: 50, - child: CircularProgressIndicator( - valueColor: AlwaysStoppedAnimation( - Theme.of(context).primaryColor), - strokeWidth: 6.0, - ), - ), + const BackButton(), + Container( + margin: const EdgeInsets.all(20), + child: Expanded( + child: ListView.builder( + shrinkWrap: true, + itemBuilder: (_, idx) => ReceiptItemView( + pricePerAmount: cart[idx].product.priceInDkkCent, + name: cart[idx].product.name, + amount: cart[idx].amount), + itemCount: cart.length)), + ), + Container( + margin: const EdgeInsets.all(20), + child: Expanded( + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + const Text( + "Total:", + style: TextStyle(fontWeight: FontWeight.bold), ), + Text(formatDkkCents(cartRepo.totalPrice())), ], - PayingState.done => [ - Container( - padding: const EdgeInsets.all(10), - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(10)), - color: Colors.white, - ), - child: Icon( - Icons.check_rounded, - color: Theme.of(context).primaryColor, - size: 70, - ), - ) - ] - }, + )), + ), + Expanded( + child: Center( + child: PrimaryButton( + onPressed: () async { + payingStateRepo.next(); + await Future.delayed(const Duration(seconds: 1)); + if (user.pay(cartRepo.totalPrice()) is Err) { + if (context.mounted) { + showDialog( + context: context, + builder: (BuildContext context) => + AlertDialog( + content: const Text( + 'Du har desværre ikke råd til at købe dette'), + actions: [ + TextButton( + onPressed: () => + Navigator.pop(context, 'OK'), + child: const Text('OK'), + ), + ], + ), + ); + } + payingStateRepo.reset(); + return; + } + receiptRepo.createReceipt(cart); + payingStateRepo.next(); + await Future.delayed(const Duration(seconds: 1)); + cartRepo.clearCart(); + payingStateRepo.reset(); + if (context.mounted) Navigator.pop(context); + }, + child: const Text("Betal"))), + ), ], ), - ) - ], + if (payingStateRepo.state != PayingState.unset) ...[ + Container( + color: Colors.black.withValues(alpha: 0.5), + ), + ], + Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + ...switch (payingStateRepo.state) { + PayingState.unset => [], + PayingState.loading => [ + Container( + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Colors.white, + ), + padding: const EdgeInsets.all(20), + child: SizedBox( + width: 50, + height: 50, + child: CircularProgressIndicator( + valueColor: AlwaysStoppedAnimation( + Theme.of(context).primaryColor), + strokeWidth: 6.0, + ), + ), + ), + ], + PayingState.done => [ + Container( + padding: const EdgeInsets.all(10), + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Colors.white, + ), + child: Icon( + Icons.check_rounded, + color: Theme.of(context).primaryColor, + size: 70, + ), + ) + ] + }, + ], + ), + ) + ], + ), ), ); } diff --git a/mobile/lib/pages/home_page.dart b/mobile/lib/pages/home_page.dart index 62c86f0..fba00a1 100644 --- a/mobile/lib/pages/home_page.dart +++ b/mobile/lib/pages/home_page.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:mobile/pages/log_in_page.dart'; import 'package:mobile/repos/user.dart'; +import 'package:mobile/utils/price.dart'; class HomePage extends StatelessWidget { final User user; @@ -19,6 +20,17 @@ class HomePage extends StatelessWidget { child: const SettingsMenu()), ], ), + Card( + child: Container( + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(10)), + color: Color(0xFFFFFFFF), + ), + padding: const EdgeInsets.all(10), + child: Text("Saldo: ${formatDkkCents(user.balanceInDkkCents)}", + style: Theme.of(context).textTheme.headlineSmall), + ), + ), Expanded( child: Column( mainAxisAlignment: MainAxisAlignment.center, diff --git a/mobile/lib/pages/product_page.dart b/mobile/lib/pages/product_page.dart index 8d7fea2..060c2df 100644 --- a/mobile/lib/pages/product_page.dart +++ b/mobile/lib/pages/product_page.dart @@ -3,6 +3,7 @@ import 'package:mobile/pages/product_location_page.dart'; import 'package:mobile/repos/add_to_cart_state.dart'; import 'package:mobile/repos/cart.dart'; import 'package:mobile/repos/product.dart'; +import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/primary_button.dart'; import 'package:provider/provider.dart'; @@ -38,7 +39,7 @@ class ProductPage extends StatelessWidget { ), ), Text( - "${product.price} kr", + formatDkkCents(product.priceInDkkCent), style: const TextStyle( fontSize: 16, ), @@ -66,7 +67,7 @@ class ProductPage extends StatelessWidget { style: Theme.of(context).textTheme.bodyLarge, ), Text( - "${product.price} kr", + formatDkkCents(product.priceInDkkCent), style: Theme.of(context).textTheme.bodyLarge, ), Padding( diff --git a/mobile/lib/pages/receipt_page.dart b/mobile/lib/pages/receipt_page.dart index f5de1e1..5f4a66b 100644 --- a/mobile/lib/pages/receipt_page.dart +++ b/mobile/lib/pages/receipt_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:mobile/repos/receipt.dart'; +import 'package:mobile/utils/price.dart'; import 'package:mobile/widgets/receipt_item.dart'; class ReceiptView extends StatelessWidget { @@ -27,7 +28,8 @@ class ReceiptView extends StatelessWidget { ListView.builder( shrinkWrap: true, itemBuilder: (_, idx) => ReceiptItemView( - pricePerAmount: receiptItems[idx].product.price, + pricePerAmount: + receiptItems[idx].product.priceInDkkCent, name: receiptItems[idx].product.name, amount: receiptItems[idx].amount), itemCount: receiptItems.length), @@ -38,7 +40,7 @@ class ReceiptView extends StatelessWidget { "Total:", style: TextStyle(fontWeight: FontWeight.bold), ), - Text("${receipt.totalPrice()} kr"), + Text(formatDkkCents(receipt.totalPrice())), ], ), ], diff --git a/mobile/lib/repos/cart.dart b/mobile/lib/repos/cart.dart index 1138c8d..6c9420e 100644 --- a/mobile/lib/repos/cart.dart +++ b/mobile/lib/repos/cart.dart @@ -9,14 +9,14 @@ class CartRepo extends ChangeNotifier { product: Product( id: 1, name: "Letmælk", - price: 13, + priceInDkkCent: 1295, description: "Konventionel letmælk med fedtprocent på 1,5%"), amount: 1), CartItem( product: Product( id: 2, name: "Frilands Øko Supermælk", - price: 20, + priceInDkkCent: 1995, description: "Økologisk mælk af frilandskøer med fedtprocent på 3,5%. Ikke homogeniseret eller pasteuriseret. Skaber store muskler og styrker knoglerne 💪"), amount: 6), @@ -24,7 +24,7 @@ class CartRepo extends ChangeNotifier { product: Product( id: 3, name: "Minimælk", - price: 12, + priceInDkkCent: 1195, description: "Konventionel minimælk med fedtprocent på 0,4%"), amount: 1), ]; @@ -96,7 +96,9 @@ class CartRepo extends ChangeNotifier { int totalPrice() { return cart.fold( - 0, (prev, cartItem) => prev + cartItem.amount * cartItem.product.price); + 0, + (prev, cartItem) => + prev + cartItem.amount * cartItem.product.priceInDkkCent); } void clearCart() { diff --git a/mobile/lib/repos/product.dart b/mobile/lib/repos/product.dart index b69ec51..058a379 100644 --- a/mobile/lib/repos/product.dart +++ b/mobile/lib/repos/product.dart @@ -48,48 +48,77 @@ class ProductRepo extends ChangeNotifier { Product( id: _nextId++, name: "Minimælk", - price: 12, + priceInDkkCent: 1200, description: "Konventionel minimælk med fedtprocent på 0,4%"), Product( id: _nextId++, name: "Letmælk", - price: 13, + priceInDkkCent: 1300, description: "Konventionel letmælk med fedtprocent på 1,5%", location: Coordinate(x: 1800, y: 100)), Product( id: _nextId++, name: "Frilands Øko Supermælk", - price: 20, + priceInDkkCent: 2000, description: "Økologisk mælk af frilandskøer med fedtprocent på 3,5%. Ikke homogeniseret eller pasteuriseret. Skaber store muskler og styrker knoglerne 💪"), Product( id: _nextId++, name: "Øko Gulerødder 1 kg", - price: 10, + priceInDkkCent: 1000, + description: ""), + Product( + id: _nextId++, + name: "Øko Agurk", + priceInDkkCent: 1000, + description: ""), + Product( + id: _nextId++, + name: "Æbler 1 kg", + priceInDkkCent: 1000, + description: ""), + Product( + id: _nextId++, + name: "Basmati Ris", + priceInDkkCent: 2000, + description: ""), + Product( + id: _nextId++, + name: "Haribo Mix", + priceInDkkCent: 3000, + description: ""), + Product( + id: _nextId++, name: "Smør", priceInDkkCent: 3000, description: ""), + Product( + id: _nextId++, + name: "Harboe Cola", + priceInDkkCent: 500, description: ""), - Product(id: _nextId++, name: "Øko Agurk", price: 10, description: ""), - Product(id: _nextId++, name: "Æbler 1 kg", price: 10, description: ""), - Product(id: _nextId++, name: "Basmati Ris", price: 20, description: ""), - Product(id: _nextId++, name: "Haribo Mix", price: 30, description: ""), - Product(id: _nextId++, name: "Smør", price: 30, description: ""), - Product(id: _nextId++, name: "Harboe Cola", price: 5, description: ""), Product( id: _nextId++, name: "Monster Energi Drik", - price: 20, + priceInDkkCent: 2000, + description: ""), + Product( + id: _nextId++, + name: "Spaghetti", + priceInDkkCent: 1000, + description: ""), + Product( + id: _nextId++, + name: "Rød Cecil", + priceInDkkCent: 6000, description: ""), - Product(id: _nextId++, name: "Spaghetti", price: 10, description: ""), - Product(id: _nextId++, name: "Rød Cecil", price: 60, description: ""), Product( id: _nextId++, name: "Jägermeister 750 ml", - price: 120, + priceInDkkCent: 12000, description: ""), Product( id: _nextId++, barcode: "5711953068881", name: "Protein Chokoladedrik", - price: 15, + priceInDkkCent: 1500, description: "Arla's protein chokolade drik der giver store muskler"), ]; } @@ -106,14 +135,14 @@ class Product { final int id; final String name; final String description; - final int price; + final int priceInDkkCent; final Coordinate? location; final String? barcode; Product({ required this.id, required this.name, - required this.price, + required this.priceInDkkCent, required this.description, this.location, this.barcode, diff --git a/mobile/lib/repos/receipt.dart b/mobile/lib/repos/receipt.dart index 3825573..b72e22d 100644 --- a/mobile/lib/repos/receipt.dart +++ b/mobile/lib/repos/receipt.dart @@ -13,14 +13,14 @@ class ReceiptRepo extends ChangeNotifier { product: Product( id: 1243, name: "Letmælk", - price: 13, + priceInDkkCent: 13, description: "Konventionel minimælk med fedtprocent på 0,4%"), amount: 1), ReceiptItem( product: Product( id: 340, name: "Minimælk", - price: 12, + priceInDkkCent: 12, description: "Konventionel minimælk med fedtprocent på 0,4%"), amount: 3), ]), @@ -32,14 +32,14 @@ class ReceiptRepo extends ChangeNotifier { product: Product( id: 12341, name: "Letmælk", - price: 13, + priceInDkkCent: 13, description: "Konventionel minimælk med fedtprocent på 0,4%"), amount: 3), ReceiptItem( product: Product( id: 1234443, name: "Minimælk", - price: 12, + priceInDkkCent: 12, description: "Konventionel minimælk med fedtprocent på 0,4%"), amount: 1), ]) @@ -119,6 +119,6 @@ class ReceiptItem { ReceiptItem({required this.product, required this.amount}); int totalPrice() { - return product.price * amount; + return product.priceInDkkCent * amount; } } diff --git a/mobile/lib/repos/user.dart b/mobile/lib/repos/user.dart index 40d1fc2..00b87ee 100644 --- a/mobile/lib/repos/user.dart +++ b/mobile/lib/repos/user.dart @@ -33,7 +33,11 @@ class UsersRepo extends ChangeNotifier { } final user = User( - id: nextId++, name: name, mail: mail, password: password, balance: 0); + id: nextId++, + name: name, + mail: mail, + password: password, + balanceInDkkCents: 0); users.add(user); return Ok(user); @@ -67,9 +71,13 @@ class UsersRepo extends ChangeNotifier { mail: "test@test.com", name: "test", password: "test", - balance: 1000)) - ..add( - User(id: nextId++, mail: "", name: "", password: "", balance: 10000)); + balanceInDkkCents: 10000)) + ..add(User( + id: nextId++, + mail: "", + name: "", + password: "", + balanceInDkkCents: 100000)); } } @@ -78,20 +86,22 @@ class User { final String mail; final String name; final String password; - int balance; + + // balance is in øre + int balanceInDkkCents; User( {required this.id, required this.mail, required this.name, required this.password, - required this.balance}); + required this.balanceInDkkCents}); Result pay(int amount) { - if (balance < amount) { + if (balanceInDkkCents < amount) { return Err("User can not afford paying amount $amount"); } - balance -= amount; - return Ok(balance); + balanceInDkkCents -= amount; + return Ok(balanceInDkkCents); } } diff --git a/mobile/lib/utils/price.dart b/mobile/lib/utils/price.dart new file mode 100644 index 0000000..b606beb --- /dev/null +++ b/mobile/lib/utils/price.dart @@ -0,0 +1,6 @@ +import 'package:intl/intl.dart'; + +String formatDkkCents(int priceInDkkCents) { + final formatter = NumberFormat("###,##0.00", "da_DK"); + return "${formatter.format(priceInDkkCents / 100.0)} kr"; +} diff --git a/mobile/lib/widgets/receipt_item.dart b/mobile/lib/widgets/receipt_item.dart index 8b7ad78..0f79594 100644 --- a/mobile/lib/widgets/receipt_item.dart +++ b/mobile/lib/widgets/receipt_item.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:mobile/utils/price.dart'; class ReceiptItemView extends StatelessWidget { final int pricePerAmount; @@ -20,16 +21,16 @@ class ReceiptItemView extends StatelessWidget { Row( children: [ SizedBox( - width: 60, + width: 80, child: Text( "$amount stk", textAlign: TextAlign.end, overflow: TextOverflow.ellipsis, )), SizedBox( - width: 60, + width: 80, child: Text( - "${pricePerAmount * amount} kr", + formatDkkCents(pricePerAmount * amount), textAlign: TextAlign.end, overflow: TextOverflow.ellipsis, )) diff --git a/mobile/pubspec.lock b/mobile/pubspec.lock index 894da4a..7b7bee1 100644 --- a/mobile/pubspec.lock +++ b/mobile/pubspec.lock @@ -131,6 +131,14 @@ packages: url: "https://pub.dev" source: hosted version: "4.1.2" + intl: + dependency: "direct main" + description: + name: intl + sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" + url: "https://pub.dev" + source: hosted + version: "0.20.2" leak_tracker: dependency: transitive description: diff --git a/mobile/pubspec.yaml b/mobile/pubspec.yaml index d167d21..ca31f93 100644 --- a/mobile/pubspec.yaml +++ b/mobile/pubspec.yaml @@ -37,6 +37,7 @@ dependencies: provider: ^6.1.2 barcode_scan2: ^4.4.0 google_fonts: ^6.2.1 + intl: ^0.20.2 dev_dependencies: flutter_test: