fix: flow on scan + owners managment
This commit is contained in:
parent
70146055df
commit
86094b5d76
10 changed files with 283 additions and 130 deletions
3
devtools_options.yaml
Normal file
3
devtools_options.yaml
Normal file
|
|
@ -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:
|
||||
15
lib/domain/models/book.dart
Normal file
15
lib/domain/models/book.dart
Normal file
|
|
@ -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;
|
||||
}
|
||||
28
lib/domain/models/book_instance.dart
Normal file
28
lib/domain/models/book_instance.dart
Normal file
|
|
@ -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<String, dynamic> json) => BookInstance(
|
||||
balId: json["balId"],
|
||||
bookId: json["bookId"],
|
||||
id: json["id"],
|
||||
ownerId: json["ownerId"],
|
||||
price: json["price"],
|
||||
status: json["status"],
|
||||
);
|
||||
}
|
||||
|
|
@ -1,6 +1,4 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
class Owner extends ChangeNotifier {
|
||||
class Owner {
|
||||
Owner({
|
||||
required this.firstName,
|
||||
required this.lastName,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Owner>? _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<Owner> _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<Owner>? 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<Result<Book>> 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<BookInstance> sendBook() {};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<AddPage> {
|
|||
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<Book> 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<AddPage> {
|
|||
}
|
||||
}
|
||||
|
||||
Future<void> _priceDialogBuilder(
|
||||
Future<void> _confirmationDialogBuilder(
|
||||
BuildContext context,
|
||||
Function(num) setPrice,
|
||||
MobileScannerController controller,
|
||||
AddViewModel viewModel,
|
||||
Book book,
|
||||
) {
|
||||
controller.stop();
|
||||
|
||||
|
|
@ -143,7 +153,12 @@ Future<void> _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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
145
lib/ui/add_page/widgets/confirmation_popup.dart
Normal file
145
lib/ui/add_page/widgets/confirmation_popup.dart
Normal file
|
|
@ -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<ConfirmationPopup> createState() => _ConfirmationPopupState();
|
||||
}
|
||||
|
||||
class _ConfirmationPopupState extends State<ConfirmationPopup> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
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"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -41,7 +41,7 @@ class _OwnerPopupState extends State<OwnerPopup> {
|
|||
),
|
||||
),
|
||||
SizedBox(height: 5),
|
||||
(showNewOwner)
|
||||
(showNewOwner || widget.viewModel.owners!.isEmpty)
|
||||
? SizedBox()
|
||||
: DropdownMenu<Owner>(
|
||||
enableFilter: true,
|
||||
|
|
|
|||
|
|
@ -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<PricePopup> createState() => _PricePopupState();
|
||||
}
|
||||
|
||||
class _PricePopupState extends State<PricePopup> {
|
||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||
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"),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in a new issue