From f8f1849d9d981b5f92e04093a886e732ab04fd83 Mon Sep 17 00:00:00 2001 From: Alzalia Date: Tue, 5 Aug 2025 15:50:37 +0200 Subject: [PATCH] fix: overflow + owner managment error --- .../add_page/view_model/add_view_model.dart | 93 ++++--- lib/ui/add_page/widgets/add_page.dart | 123 +++++---- .../add_page/widgets/confirmation_popup.dart | 126 ++++----- lib/ui/add_page/widgets/owner_popup.dart | 248 +++++++++--------- 4 files changed, 309 insertions(+), 281 deletions(-) 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 44d0549..1769a55 100644 --- a/lib/ui/add_page/view_model/add_view_model.dart +++ b/lib/ui/add_page/view_model/add_view_model.dart @@ -3,7 +3,6 @@ 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'; @@ -12,6 +11,12 @@ class AddViewModel extends ChangeNotifier { final _log = Logger("AddViewModel"); + /* + * ==================== + * =====[ OWNERS ]===== + * ==================== +*/ + Owner? _currentOwner; Owner? get currentOwner => _currentOwner; set currentOwner(Owner? owner) { @@ -19,55 +24,30 @@ 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, - // ), - // ]; + final List _owners = []; List? get owners => _owners; + Owner addOwner(String firstName, String lastName, String contact) { - _owners!.add( - Owner( - firstName: firstName, - lastName: lastName, - contact: contact, - id: _owners.last.id + 1, - ), - ); + if (_owners.isEmpty) { + _owners.add( + Owner( + firstName: firstName, + lastName: lastName, + contact: contact, + id: 1, + ), + ); + } else { + _owners.add( + Owner( + firstName: firstName, + lastName: lastName, + contact: contact, + id: _owners.last.id + 1, + ), + ); + } notifyListeners(); return Owner( firstName: firstName, @@ -77,6 +57,12 @@ class AddViewModel extends ChangeNotifier { ); } + /* + * =================== + * =====[ PRICE ]===== + * =================== +*/ + bool _askPrice = true; bool get askPrice => _askPrice; set askPrice(bool newValue) { @@ -84,6 +70,14 @@ class AddViewModel extends ChangeNotifier { notifyListeners(); } + /* + * ================================= + * =====[ BOOKS AND INSTANCES ]===== + * ================================= +*/ + + /// Sends an api request with a [bacorde], then gets the [Book] that was + /// either created or retrieved. Sens the [Book] back wrapped in a [Result]. Future> scanBook(BarcodeCapture barcode) async { return Result.ok( Book( @@ -96,5 +90,8 @@ class AddViewModel extends ChangeNotifier { ); } - // Result sendBook() {}; + /// Sens an api request with + // Result newBookInstance() { + + // }; } diff --git a/lib/ui/add_page/widgets/add_page.dart b/lib/ui/add_page/widgets/add_page.dart index 4d9aae1..a4b95bd 100644 --- a/lib/ui/add_page/widgets/add_page.dart +++ b/lib/ui/add_page/widgets/add_page.dart @@ -38,6 +38,18 @@ class _AddPageState extends State { MobileScanner( controller: controller, onDetect: (barcodes) async { + if (widget.viewModel.currentOwner == null) { + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + "Attention : vous devez choisir un·e propriétaire", + ), + behavior: SnackBarBehavior.floating, + ), + ); + return; + } + void setPrice(num newPrice) async { setState(() { price = newPrice; @@ -68,62 +80,77 @@ class _AddPageState extends State { }, ), SafeArea( - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Center( - child: Card( - margin: EdgeInsets.symmetric(horizontal: 50), - child: Column( - children: [ - ListenableBuilder( - listenable: widget.viewModel, - builder: (context, child) => ListTile( - leading: Icon(Icons.person), + child: SingleChildScrollView( + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: Card( + margin: EdgeInsets.symmetric(horizontal: 50), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + ListenableBuilder( + listenable: widget.viewModel, + builder: (context, child) => ListTile( + leading: Icon(Icons.person), + title: TextButton( + child: Text( + (widget.viewModel.currentOwner == null) + ? "Aucun" + : "${widget.viewModel.currentOwner!.firstName} ${widget.viewModel.currentOwner!.lastName}", + ), + onPressed: () => _ownerDialogBuilder( + context, + controller, + widget.viewModel, + ), + ), + ), + ), + ListTile( + leading: Icon(Icons.attach_money), title: TextButton( child: Text( - (widget.viewModel.currentOwner == null) - ? "Aucun" - : "${widget.viewModel.currentOwner!.firstName} ${widget.viewModel.currentOwner!.lastName}", - ), - onPressed: () => _ownerDialogBuilder( - context, - controller, - widget.viewModel, + (widget.viewModel.askPrice) + ? "Demander à chaque fois" + : "Prix libre toujours", ), + onPressed: () { + setState(() { + widget.viewModel.askPrice = + !widget.viewModel.askPrice; + }); + }, ), ), - ), - ListTile( - leading: Icon(Icons.attach_money), - title: TextButton( - child: Text( - (widget.viewModel.askPrice) - ? "Demander à chaque fois" - : "Prix libre toujours", - ), - onPressed: () { - setState(() { - widget.viewModel.askPrice = - !widget.viewModel.askPrice; - }); - }, - ), - ), - ], + ], + ), ), ), - ), - Expanded(child: SizedBox()), - SvgPicture.asset('assets/scan-overlay.svg'), - Expanded(child: SizedBox()), - TextButton( - style: ButtonStyle( - backgroundColor: WidgetStatePropertyAll(theme.cardColor), + SizedBox(height: 100), + SvgPicture.asset('assets/scan-overlay.svg'), + ], + ), + ), + ), + SafeArea( + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Center( + child: TextButton( + style: ButtonStyle( + backgroundColor: WidgetStatePropertyAll(theme.cardColor), + ), + onPressed: () => _formDialogBuilder( + context, + controller, + widget.viewModel, + ), + child: Text("Enregistrer manuellement"), ), - onPressed: () => - _formDialogBuilder(context, controller, widget.viewModel), - child: Text("Enregistrer manuellement"), ), ], ), diff --git a/lib/ui/add_page/widgets/confirmation_popup.dart b/lib/ui/add_page/widgets/confirmation_popup.dart index 18bd5d9..ad8f757 100644 --- a/lib/ui/add_page/widgets/confirmation_popup.dart +++ b/lib/ui/add_page/widgets/confirmation_popup.dart @@ -30,69 +30,71 @@ class _ConfirmationPopupState extends State { 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: "€", + child: SingleChildScrollView( + 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), ), - 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(), - ], + 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: [ diff --git a/lib/ui/add_page/widgets/owner_popup.dart b/lib/ui/add_page/widgets/owner_popup.dart index 90a2074..96f4b32 100644 --- a/lib/ui/add_page/widgets/owner_popup.dart +++ b/lib/ui/add_page/widgets/owner_popup.dart @@ -29,133 +29,135 @@ class _OwnerPopupState extends State { listenable: widget.viewModel, builder: (context, child) => AlertDialog( title: Center(child: Text("Propriétaire du livre")), - content: Column( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.min, - children: [ - Center( - child: Text( - (widget.viewModel.currentOwner == null) - ? "Choix actuel : aucun" - : "Choix actuel : ${widget.viewModel.currentOwner!.firstName} ${widget.viewModel.currentOwner!.lastName}", + content: SingleChildScrollView( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.min, + children: [ + Center( + child: Text( + (widget.viewModel.currentOwner == null) + ? "Choix actuel : aucun" + : "Choix actuel : ${widget.viewModel.currentOwner!.firstName} ${widget.viewModel.currentOwner!.lastName}", + ), ), - ), - SizedBox(height: 5), - (showNewOwner || widget.viewModel.owners!.isEmpty) - ? SizedBox() - : DropdownMenu( - enableFilter: true, - label: Text("Rechercher un·e propriétaire"), - dropdownMenuEntries: [ - for (var owner in widget.viewModel.owners!) - DropdownMenuEntry( - value: owner, - label: "${owner.firstName} ${owner.lastName}", - style: ButtonStyle( - backgroundColor: - (widget.viewModel.currentOwner == owner) - ? WidgetStatePropertyAll( - theme.highlightColor, - ) - : WidgetStatePropertyAll( - theme.canvasColor, - ), + SizedBox(height: 5), + (showNewOwner || widget.viewModel.owners!.isEmpty) + ? SizedBox() + : DropdownMenu( + enableFilter: true, + label: Text("Rechercher un·e propriétaire"), + dropdownMenuEntries: [ + for (var owner in widget.viewModel.owners!) + DropdownMenuEntry( + value: owner, + label: "${owner.firstName} ${owner.lastName}", + style: ButtonStyle( + backgroundColor: + (widget.viewModel.currentOwner == owner) + ? WidgetStatePropertyAll( + theme.highlightColor, + ) + : WidgetStatePropertyAll( + theme.canvasColor, + ), + ), ), - ), - ], - initialSelection: widget.viewModel.currentOwner, - onSelected: (Owner? owner) { - widget.viewModel.currentOwner = owner; - }, - ), - SizedBox(height: 20), - TextButton( - onPressed: () { - setState(() { - showNewOwner = !showNewOwner; - }); - }, - child: Text( - (showNewOwner) ? "Annuler" : "Ajouter un propriétaire", - ), - ), - (!showNewOwner) - ? SizedBox() - : Form( - key: _formKey, - child: Column( - children: [ - TextFormField( - decoration: InputDecoration( - labelText: "Nom", - border: OutlineInputBorder(), - ), - onSaved: (newValue) { - setState(() { - lastName = newValue; - }); - }, - validator: (value) { - if (value == null || value.isEmpty) { - return "Indiquez un nom"; - } - return null; - }, - ), - SizedBox(height: 10), - TextFormField( - decoration: InputDecoration( - labelText: "Prénom", - border: OutlineInputBorder(), - ), - onSaved: (newValue) { - setState(() { - firstName = newValue; - }); - }, - validator: (value) { - if (value == null || value.isEmpty) { - return "Indiquez un prénom"; - } - return null; - }, - ), - SizedBox(height: 10), - TextFormField( - decoration: InputDecoration( - labelText: "Contact", - border: OutlineInputBorder(), - ), - onSaved: (newValue) { - setState(() { - contact = newValue; - }); - }, - validator: (value) { - if (value == null || value.isEmpty) { - return "Indiquez un moyen de contact"; - } - return null; - }, - ), - SizedBox(height: 10), - ElevatedButton( - onPressed: () { - if (_formKey.currentState!.validate()) { - _formKey.currentState!.save(); - widget.viewModel.currentOwner = widget.viewModel - .addOwner(firstName!, lastName!, contact!); - setState(() { - showNewOwner = false; - }); - } - }, - child: Text("Créer"), - ), ], + initialSelection: widget.viewModel.currentOwner, + onSelected: (Owner? owner) { + widget.viewModel.currentOwner = owner; + }, ), - ), - ], + SizedBox(height: 20), + TextButton( + onPressed: () { + setState(() { + showNewOwner = !showNewOwner; + }); + }, + child: Text( + (showNewOwner) ? "Annuler" : "Ajouter un propriétaire", + ), + ), + (!showNewOwner) + ? SizedBox() + : Form( + key: _formKey, + child: Column( + children: [ + TextFormField( + decoration: InputDecoration( + labelText: "Nom", + border: OutlineInputBorder(), + ), + onSaved: (newValue) { + setState(() { + lastName = newValue; + }); + }, + validator: (value) { + if (value == null || value.isEmpty) { + return "Indiquez un nom"; + } + return null; + }, + ), + SizedBox(height: 10), + TextFormField( + decoration: InputDecoration( + labelText: "Prénom", + border: OutlineInputBorder(), + ), + onSaved: (newValue) { + setState(() { + firstName = newValue; + }); + }, + validator: (value) { + if (value == null || value.isEmpty) { + return "Indiquez un prénom"; + } + return null; + }, + ), + SizedBox(height: 10), + TextFormField( + decoration: InputDecoration( + labelText: "Contact", + border: OutlineInputBorder(), + ), + onSaved: (newValue) { + setState(() { + contact = newValue; + }); + }, + validator: (value) { + if (value == null || value.isEmpty) { + return "Indiquez un moyen de contact"; + } + return null; + }, + ), + SizedBox(height: 10), + ElevatedButton( + onPressed: () { + if (_formKey.currentState!.validate()) { + _formKey.currentState!.save(); + widget.viewModel.currentOwner = widget.viewModel + .addOwner(firstName!, lastName!, contact!); + setState(() { + showNewOwner = false; + }); + } + }, + child: Text("Créer"), + ), + ], + ), + ), + ], + ), ), actions: [