diff --git a/devtools_options.yaml b/devtools_options.yaml new file mode 100644 index 0000000..fa0b357 --- /dev/null +++ b/devtools_options.yaml @@ -0,0 +1,3 @@ +description: This file stores settings for Dart & Flutter DevTools. +documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states +extensions: diff --git a/lib/domain/models/book.dart b/lib/domain/models/book.dart new file mode 100644 index 0000000..60a0a53 --- /dev/null +++ b/lib/domain/models/book.dart @@ -0,0 +1,15 @@ +class Book { + Book({ + required this.author, + required this.ean, + required this.id, + required this.priceNew, + required this.title, + }); + + String author; + String title; + String ean; + int id; + String priceNew; +} diff --git a/lib/domain/models/book_instance.dart b/lib/domain/models/book_instance.dart new file mode 100644 index 0000000..3c3bde7 --- /dev/null +++ b/lib/domain/models/book_instance.dart @@ -0,0 +1,28 @@ +class BookInstance { + BookInstance({ + required this.balId, + required this.bookId, + required this.id, + required this.ownerId, + required this.price, + required this.status, + this.soldPrice, + }); + + int balId; + int bookId; + int id; + int ownerId; + double price; + double? soldPrice; + bool status; + + factory BookInstance.fromJSON(Map json) => BookInstance( + balId: json["balId"], + bookId: json["bookId"], + id: json["id"], + ownerId: json["ownerId"], + price: json["price"], + status: json["status"], + ); +} diff --git a/lib/domain/models/owner.dart b/lib/domain/models/owner.dart index 9e2b00d..a4feb85 100644 --- a/lib/domain/models/owner.dart +++ b/lib/domain/models/owner.dart @@ -1,6 +1,4 @@ -import 'package:flutter/material.dart'; - -class Owner extends ChangeNotifier { +class Owner { Owner({ required this.firstName, required this.lastName, diff --git a/lib/main.dart b/lib/main.dart index 9eb782b..2eed0bf 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,11 +1,7 @@ import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; -import 'package:provider/provider.dart'; -import 'package:seshat/config/dependencies.dart'; import 'package:seshat/routing/router.dart'; -// TODO: In router, make it so that the navbar is integrated in everyscreen that needs it -> consistancy (should be at least, hope so) so it stops jumping. Then, make it so that the pages are children of scaffold. That's all ? - void main() { Logger.root.level = Level.ALL; diff --git a/lib/ui/add_page/view_model/add_view_model.dart b/lib/ui/add_page/view_model/add_view_model.dart index 6a96452..44d0549 100644 --- a/lib/ui/add_page/view_model/add_view_model.dart +++ b/lib/ui/add_page/view_model/add_view_model.dart @@ -1,7 +1,11 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:logging/logging.dart'; +import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:seshat/domain/models/book.dart'; +import 'package:seshat/domain/models/book_instance.dart'; import 'package:seshat/domain/models/owner.dart'; +import 'package:seshat/utils/result.dart'; class AddViewModel extends ChangeNotifier { AddViewModel(); @@ -15,44 +19,44 @@ class AddViewModel extends ChangeNotifier { notifyListeners(); } - List? _owners = [ - Owner( - firstName: "Jean", - lastName: "Henri", - contact: "contact@gmail.com", - id: 1, - ), - Owner( - firstName: "Jeanette", - lastName: "Henriette", - contact: "contact@gmail.com", - id: 2, - ), - Owner( - firstName: "Jacques", - lastName: "Gerard", - contact: "contact@gmail.com", - id: 3, - ), - Owner( - firstName: "Jacquelines", - lastName: "Geraldine", - contact: "contact@gmail.com", - id: 4, - ), - Owner( - firstName: "Louis", - lastName: "Valentin", - contact: "contact@gmail.com", - id: 5, - ), - Owner( - firstName: "Louise", - lastName: "Valentine", - contact: "contact@gmail.com", - id: 6, - ), - ]; + List _owners = []; + // Owner( + // firstName: "Jean", + // lastName: "Henri", + // contact: "contact@gmail.com", + // id: 1, + // ), + // Owner( + // firstName: "Jeanette", + // lastName: "Henriette", + // contact: "contact@gmail.com", + // id: 2, + // ), + // Owner( + // firstName: "Jacques", + // lastName: "Gerard", + // contact: "contact@gmail.com", + // id: 3, + // ), + // Owner( + // firstName: "Jacquelines", + // lastName: "Geraldine", + // contact: "contact@gmail.com", + // id: 4, + // ), + // Owner( + // firstName: "Louis", + // lastName: "Valentin", + // contact: "contact@gmail.com", + // id: 5, + // ), + // Owner( + // firstName: "Louise", + // lastName: "Valentine", + // contact: "contact@gmail.com", + // id: 6, + // ), + // ]; List? get owners => _owners; Owner addOwner(String firstName, String lastName, String contact) { @@ -61,7 +65,7 @@ class AddViewModel extends ChangeNotifier { firstName: firstName, lastName: lastName, contact: contact, - id: _owners!.last.id + 1, + id: _owners.last.id + 1, ), ); notifyListeners(); @@ -79,4 +83,18 @@ class AddViewModel extends ChangeNotifier { _askPrice = newValue; notifyListeners(); } + + Future> scanBook(BarcodeCapture barcode) async { + return Result.ok( + Book( + author: "Patrick K. Dewdney", + ean: barcode.barcodes.first.rawValue!, + id: 56, + priceNew: "50 EUR", + title: "Les chiens et la charrue", + ), + ); + } + + // Result sendBook() {}; } diff --git a/lib/ui/add_page/widgets/add_page.dart b/lib/ui/add_page/widgets/add_page.dart index c009ab4..4d9aae1 100644 --- a/lib/ui/add_page/widgets/add_page.dart +++ b/lib/ui/add_page/widgets/add_page.dart @@ -1,11 +1,13 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; import 'package:mobile_scanner/mobile_scanner.dart'; +import 'package:seshat/domain/models/book.dart'; import 'package:seshat/ui/add_page/view_model/add_view_model.dart'; import 'package:seshat/ui/add_page/widgets/form_popup.dart'; import 'package:seshat/ui/add_page/widgets/owner_popup.dart'; -import 'package:seshat/ui/add_page/widgets/price_popup.dart'; +import 'package:seshat/ui/add_page/widgets/confirmation_popup.dart'; import 'package:seshat/ui/core/ui/navigation_bar.dart'; +import 'package:seshat/utils/result.dart'; class AddPage extends StatefulWidget { const AddPage({super.key, required this.viewModel}); @@ -36,27 +38,33 @@ class _AddPageState extends State { MobileScanner( controller: controller, onDetect: (barcodes) async { - void setPrice(num newPrice) { + void setPrice(num newPrice) async { setState(() { price = newPrice; }); } - if (widget.viewModel.askPrice) { - await _priceDialogBuilder(context, setPrice, controller); - } else { - setPrice(0); - } + Result result = await widget.viewModel.scanBook(barcodes); - ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text( - "Envoyé : ${barcodes.barcodes.first.rawValue} pour $price€", - ), - behavior: SnackBarBehavior.floating, - ), - ); - debugPrint(price.toString()); + switch (result) { + case Ok(): + await _confirmationDialogBuilder( + context, + setPrice, + controller, + widget.viewModel, + result.value, + ); + break; + case Error(): + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("Erreur : ${result.error}"), + behavior: SnackBarBehavior.floating, + ), + ); + break; + } }, ), SafeArea( @@ -128,10 +136,12 @@ class _AddPageState extends State { } } -Future _priceDialogBuilder( +Future _confirmationDialogBuilder( BuildContext context, Function(num) setPrice, MobileScannerController controller, + AddViewModel viewModel, + Book book, ) { controller.stop(); @@ -143,7 +153,12 @@ Future _priceDialogBuilder( return showDialog( context: context, barrierDismissible: false, - builder: (context) => PricePopup(exitPopup: exitPopup, setPrice: setPrice), + builder: (context) => ConfirmationPopup( + exitPopup: exitPopup, + setPrice: setPrice, + viewModel: viewModel, + book: book, + ), ); } diff --git a/lib/ui/add_page/widgets/confirmation_popup.dart b/lib/ui/add_page/widgets/confirmation_popup.dart new file mode 100644 index 0000000..18bd5d9 --- /dev/null +++ b/lib/ui/add_page/widgets/confirmation_popup.dart @@ -0,0 +1,145 @@ +import 'package:flutter/material.dart'; +import 'package:seshat/domain/models/book.dart'; +import 'package:seshat/ui/add_page/view_model/add_view_model.dart'; + +class ConfirmationPopup extends StatefulWidget { + const ConfirmationPopup({ + super.key, + required this.exitPopup, + required this.setPrice, + required this.viewModel, + required this.book, + }); + + final Function(BuildContext) exitPopup; + final Function(num) setPrice; + final AddViewModel viewModel; + final Book book; + + @override + State createState() => _ConfirmationPopupState(); +} + +class _ConfirmationPopupState extends State { + final GlobalKey _formKey = GlobalKey(); + num price = 0; + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + return AlertDialog( + title: Text("Prix"), + content: Form( + key: _formKey, + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + RichText( + text: TextSpan( + style: theme.textTheme.bodyMedium, + children: [ + TextSpan( + text: "Titre : ", + style: TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan(text: widget.book.title), + ], + ), + ), + RichText( + text: TextSpan( + style: theme.textTheme.bodyMedium, + children: [ + TextSpan( + text: "Auteur·ice : ", + style: TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan(text: widget.book.author), + ], + ), + ), + RichText( + text: TextSpan( + style: theme.textTheme.bodyMedium, + children: [ + TextSpan( + text: "Prix à neuf : ", + style: TextStyle(fontWeight: FontWeight.bold), + ), + TextSpan(text: widget.book.priceNew), + ], + ), + ), + SizedBox(height: 10), + (widget.viewModel.askPrice) + ? TextFormField( + decoration: InputDecoration( + labelText: "Prix", + border: OutlineInputBorder(), + suffixText: "€", + ), + keyboardType: TextInputType.number, + validator: (value) { + if (value == null || value.isEmpty) { + return "Indiquez un prix"; + } else if (num.tryParse(value) == null) { + return "Le prix doit être un nombre"; + } + return null; + }, + onSaved: (newValue) { + price = num.parse(newValue!); + }, + ) + : SizedBox(), + ], + ), + ), + actions: [ + TextButton( + onPressed: () { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text("Le livre n'a pas été enregistré"), + behavior: SnackBarBehavior.floating, + ), + ); + widget.exitPopup(context); + }, + child: Text("Annuler"), + ), + TextButton( + onPressed: () { + switch (widget.viewModel.askPrice) { + case true: + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "\"${widget.book.title}\" ($price) a bien été enregistré", + ), + behavior: SnackBarBehavior.floating, + ), + ); + widget.exitPopup(context); + } + break; + case false: + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "\"${widget.book.title}\" (PL) a bien été enregistré", + ), + behavior: SnackBarBehavior.floating, + ), + ); + widget.exitPopup(context); + } + }, + child: Text("Valider"), + ), + ], + ); + } +} diff --git a/lib/ui/add_page/widgets/owner_popup.dart b/lib/ui/add_page/widgets/owner_popup.dart index b85f5e8..90a2074 100644 --- a/lib/ui/add_page/widgets/owner_popup.dart +++ b/lib/ui/add_page/widgets/owner_popup.dart @@ -41,7 +41,7 @@ class _OwnerPopupState extends State { ), ), SizedBox(height: 5), - (showNewOwner) + (showNewOwner || widget.viewModel.owners!.isEmpty) ? SizedBox() : DropdownMenu( enableFilter: true, diff --git a/lib/ui/add_page/widgets/price_popup.dart b/lib/ui/add_page/widgets/price_popup.dart deleted file mode 100644 index bec698f..0000000 --- a/lib/ui/add_page/widgets/price_popup.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'package:flutter/material.dart'; - -class PricePopup extends StatefulWidget { - const PricePopup({ - super.key, - required this.exitPopup, - required this.setPrice, - }); - - final Function(BuildContext) exitPopup; - final Function(num) setPrice; - - @override - State createState() => _PricePopupState(); -} - -class _PricePopupState extends State { - final GlobalKey _formKey = GlobalKey(); - num? price; - @override - Widget build(BuildContext context) { - return AlertDialog( - title: Text("Prix"), - content: Form( - key: _formKey, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - TextFormField( - decoration: InputDecoration( - labelText: "Prix", - border: OutlineInputBorder(), - suffixText: "€", - ), - keyboardType: TextInputType.number, - validator: (value) { - if (value == null || value.isEmpty) { - return "Indiquez un prix"; - } else if (num.tryParse(value) == null) { - return "Le prix doit être un nombre"; - } - return null; - }, - onSaved: (newValue) { - price = num.parse(newValue!); - }, - ), - ], - ), - ), - actions: [ - TextButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - _formKey.currentState!.save(); - widget.setPrice(price!); - widget.exitPopup(context); - } - }, - child: Text("Valider"), - ), - ], - ); - } -}