fix: some error managment and a whole feature missing

This commit is contained in:
Alzalia 2025-08-23 15:52:51 +02:00
parent dad000a1b9
commit 6bcc3a7e88
5 changed files with 192 additions and 140 deletions

View file

@ -125,8 +125,7 @@ class AddViewModel extends ChangeNotifier {
*/
/// Retrieves the book associated with an ean through a [barcode]
Future<Result<Book>> scanBook(BarcodeCapture barcode) async {
var ean = barcode.barcodes.first.rawValue!;
Future<Result<Book>> scanBook(String ean) async {
var result = await _bookRepository.getBookByEAN(ean);
return result;
}

View file

@ -22,23 +22,20 @@ class AddPage extends StatefulWidget {
}
class _AddPageState extends State<AddPage> {
num? price;
final MobileScannerController controller = MobileScannerController(
final MobileScannerController scannerController = MobileScannerController(
formats: [BarcodeFormat.ean13],
detectionTimeoutMs: 1000,
);
@override
void dispose() {
controller.dispose();
scannerController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
// return Consumer<TabScreen>(
// builder: (context, screen, child) {
return Scaffold(
bottomNavigationBar: AppNavigationBar(startIndex: 1),
body: ListenableBuilder(
@ -78,60 +75,37 @@ class _AddPageState extends State<AddPage> {
children: [
ColoredBox(color: Colors.black),
MobileScanner(
controller: controller,
controller: scannerController,
onDetect: (barcodes) async {
if (barcodes.barcodes.isEmpty) {
return;
}
if (widget.viewModel.currentOwner == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Attention : vous devez choisir un·e propriétaire",
),
behavior: SnackBarBehavior.floating,
),
_showMissingOwnerSnackBar(
context,
scannerController,
widget.viewModel,
);
return;
}
void setPrice(num newPrice) async {
setState(() {
price = newPrice;
});
}
Result<Book> result = await widget.viewModel.scanBook(
barcodes,
_scanEan(
context,
widget.viewModel,
barcodes.barcodes.first.rawValue!,
scannerController,
);
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;
}
},
),
Center(
child: SvgPicture.asset(
'assets/scan-overlay.svg',
height: (MediaQuery.sizeOf(context).height / 5) * 2,
),
),
SafeArea(
child: SingleChildScrollView(
child: Column(
@ -155,7 +129,7 @@ class _AddPageState extends State<AddPage> {
),
onPressed: () => _ownerDialogBuilder(
context,
controller,
scannerController,
widget.viewModel,
),
),
@ -184,6 +158,7 @@ class _AddPageState extends State<AddPage> {
),
),
),
SafeArea(
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
@ -197,19 +172,17 @@ class _AddPageState extends State<AddPage> {
),
onPressed: () {
if (widget.viewModel.currentOwner == null) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Attention : vous devez choisir un·e propriétaire",
),
behavior: SnackBarBehavior.floating,
),
_showMissingOwnerSnackBar(
context,
scannerController,
widget.viewModel,
);
return;
}
_formDialogBuilder(
context,
controller,
scannerController,
widget.viewModel,
);
},
@ -231,18 +204,71 @@ class _AddPageState extends State<AddPage> {
}
}
void _scanEan(
BuildContext context,
AddViewModel viewModel,
String ean,
MobileScannerController scannerController, {
Function(BuildContext)? leaveLastPopup,
}) async {
Result<Book> result = await viewModel.scanBook(ean);
if (context.mounted) {
if (leaveLastPopup != null) {
leaveLastPopup(context);
}
switch (result) {
case Ok():
await _confirmationDialogBuilder(
context,
scannerController,
viewModel,
result.value,
);
break;
case Error():
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Erreur : ${result.error}"),
behavior: SnackBarBehavior.floating,
),
);
break;
}
}
}
void _showMissingOwnerSnackBar(
BuildContext context,
MobileScannerController scannerController,
AddViewModel viewModel,
) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Attention : vous devez choisir un·e propriétaire"),
duration: Duration(seconds: 4),
action: SnackBarAction(
label: "Choisir",
onPressed: () =>
_ownerDialogBuilder(context, scannerController, viewModel),
),
behavior: SnackBarBehavior.floating,
),
);
}
Future<void> _confirmationDialogBuilder(
BuildContext context,
Function(num) setPrice,
MobileScannerController controller,
MobileScannerController scannerController,
AddViewModel viewModel,
Book book,
) {
controller.stop();
scannerController.stop();
// Utility function to pass to downwards widgets
void exitPopup(BuildContext localContext) {
Navigator.of(localContext).pop();
controller.start();
scannerController.start();
}
return showDialog(
@ -250,7 +276,6 @@ Future<void> _confirmationDialogBuilder(
barrierDismissible: false,
builder: (context) => ConfirmationPopup(
exitPopup: exitPopup,
setPrice: setPrice,
viewModel: viewModel,
book: book,
),
@ -264,6 +289,7 @@ Future<void> _formDialogBuilder(
) {
controller.stop();
// Utility function to pass to downwards widgets
void exitPopup(BuildContext localContext) {
Navigator.of(localContext).pop();
controller.start();
@ -272,7 +298,12 @@ Future<void> _formDialogBuilder(
return showDialog(
context: context,
barrierDismissible: false,
builder: (context) => FormPopup(viewModel: viewModel, exitPopup: exitPopup),
builder: (context) => FormPopup(
viewModel: viewModel,
exitPopup: exitPopup,
scannerController: controller,
scanEan: _scanEan,
),
);
}
@ -283,7 +314,8 @@ Future<void> _ownerDialogBuilder(
) {
controller.stop();
void onPressAccept(BuildContext localContext) {
// Utility function to pass to downwards widgets
void exitPopup(BuildContext localContext) {
Navigator.of(localContext).pop();
controller.start();
}
@ -292,6 +324,6 @@ Future<void> _ownerDialogBuilder(
context: context,
barrierDismissible: false,
builder: (context) =>
OwnerPopup(viewModel: viewModel, onPressAccept: onPressAccept),
OwnerPopup(viewModel: viewModel, exitPopup: exitPopup),
);
}

View file

@ -7,13 +7,11 @@ 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;
@ -24,9 +22,11 @@ class ConfirmationPopup extends StatefulWidget {
class _ConfirmationPopupState extends State<ConfirmationPopup> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
double price = 0;
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
return AlertDialog(
title: Text("Prix"),
content: Form(
@ -72,6 +72,7 @@ class _ConfirmationPopupState extends State<ConfirmationPopup> {
],
),
),
SizedBox(height: 10),
(widget.viewModel.askPrice)
? TextFormField(
@ -87,15 +88,21 @@ class _ConfirmationPopupState extends State<ConfirmationPopup> {
validator: (value) {
if (value == null || value.isEmpty) {
return "Indiquez un prix";
} else if (num.tryParse(value) == null) {
} else if (double.tryParse(
value.replaceAll(",", "."),
) ==
null) {
return "Le prix doit être un nombre";
} else if (num.parse(value) < 0) {
} else if (double.parse(value.replaceAll(",", ".")) <
0) {
return "Le prix doit être positif ou nul";
}
return null;
},
onSaved: (newValue) {
price = double.parse(newValue!);
price = double.parse(
newValue?.replaceAll(",", ".") ?? "0",
);
},
)
: SizedBox(),
@ -105,6 +112,7 @@ class _ConfirmationPopupState extends State<ConfirmationPopup> {
),
actions: [
TextButton(
child: Text("Annuler"),
onPressed: () {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
@ -114,13 +122,15 @@ class _ConfirmationPopupState extends State<ConfirmationPopup> {
);
widget.exitPopup(context);
},
child: Text("Annuler"),
),
TextButton(
child: Text("Valider"),
onPressed: () async {
if (widget.viewModel.askPrice &&
_formKey.currentState!.validate()) {
_formKey.currentState!.save();
} else {
return;
}
var result = await widget.viewModel.sendNewBookInstance(
@ -136,49 +146,61 @@ class _ConfirmationPopupState extends State<ConfirmationPopup> {
Navigator.of(context).pop();
showDialog(
context: context,
builder: (context) => AlertDialog(
title: Text(
"ID : ${widget.viewModel.currentOwner!.firstName[0].toUpperCase()}${widget.viewModel.currentOwner!.lastName[0].toUpperCase()}${(price == 0) ? "PL" : price.toString()}",
),
content: Text(
(widget.viewModel.currentOwner!.id ==
widget.viewModel.ownerOfUser!.id)
? "Ce livre appartient à la section. Vous pouvez mettre le code, ou poser une gomette, ..."
: "Identifiant propriétaire de ce livre. Pensez à l'écrire pour retrouver lae propriétaire du livre lors de la vente ou du retour !",
),
actions: [
TextButton(
onPressed: () {
widget.exitPopup(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Enregistré: ${widget.book.title}",
),
behavior: SnackBarBehavior.floating,
),
);
},
child: Text("Ok"),
),
],
),
barrierDismissible: false,
builder: (context) =>
RegisteredBookPopup(widget: widget, price: price),
);
}
break;
case Error():
if (context.mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
"Une erreur est survenue : ${result.error}",
),
),
SnackBar(content: Text("Erreur : ${result.error}")),
);
}
}
},
child: Text("Valider"),
),
],
);
}
}
class RegisteredBookPopup extends StatelessWidget {
const RegisteredBookPopup({
super.key,
required this.widget,
required this.price,
});
final ConfirmationPopup widget;
final double price;
@override
Widget build(BuildContext context) {
return AlertDialog(
// This thing is the BookInstance's short ID
title: Text(
"ID : ${widget.viewModel.currentOwner!.firstName[0].toUpperCase()}${widget.viewModel.currentOwner!.lastName[0].toUpperCase()}${(price == 0) ? "PL" : price.toString()}",
),
content: Text(
(widget.viewModel.currentOwner!.id == widget.viewModel.ownerOfUser!.id)
? "Pensez à la gomette ! Ce livre appartient au syndicat."
: "Identifiant propriétaire de ce livre. Pensez à l'écrire pour retrouver lae propriétaire du livre lors de la vente ou du retour !",
),
actions: [
TextButton(
child: Text("Ok"),
onPressed: () {
widget.exitPopup(context);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Livre enregistré: ${widget.book.title}"),
behavior: SnackBarBehavior.floating,
duration: Duration(seconds: 2),
),
);
},
),
],
);

View file

@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:seshat/ui/add_page/view_model/add_view_model.dart';
class FormPopup extends StatelessWidget {
@ -6,10 +7,14 @@ class FormPopup extends StatelessWidget {
super.key,
required this.viewModel,
required this.exitPopup,
required this.scannerController,
required this.scanEan,
});
final AddViewModel viewModel;
final Function(BuildContext) exitPopup;
final MobileScannerController scannerController;
final Function scanEan;
@override
Widget build(BuildContext context) {
@ -31,6 +36,8 @@ class FormPopup extends StatelessWidget {
return _ManualEANPopup(
exitPopup: exitPopup,
viewModel: viewModel,
scannerController: scannerController,
scanEan: scanEan,
);
},
);
@ -51,6 +58,7 @@ class FormPopup extends StatelessWidget {
),
),
),
Card(
clipBehavior: Clip.hardEdge,
child: InkWell(
@ -93,11 +101,24 @@ class FormPopup extends StatelessWidget {
}
}
/*
* ======================
* ====< MANUAL EAN >====
* ======================
*/
class _ManualEANPopup extends StatefulWidget {
const _ManualEANPopup({required this.exitPopup, required this.viewModel});
const _ManualEANPopup({
required this.exitPopup,
required this.viewModel,
required this.scannerController,
required this.scanEan,
});
final Function(BuildContext) exitPopup;
final AddViewModel viewModel;
final MobileScannerController scannerController;
final Function scanEan;
@override
State<_ManualEANPopup> createState() => _ManualEANPopupState();
@ -106,11 +127,11 @@ class _ManualEANPopup extends StatefulWidget {
class _ManualEANPopupState extends State<_ManualEANPopup> {
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
String? ean;
num? price;
@override
Widget build(BuildContext context) {
return AlertDialog(
title: Text("Recherche par EAN"),
title: Text("Entrée manuelle par EAN"),
content: Form(
key: _formKey,
child: Column(
@ -128,61 +149,39 @@ class _ManualEANPopupState extends State<_ManualEANPopup> {
validator: (value) {
if (value == null ||
value.length != 13 ||
int.tryParse(value) == null) {
int.tryParse(value) == null ||
int.parse(value) < 0) {
return "L'entrée n'est pas un code EAN-13 valide";
}
return null;
},
),
SizedBox(height: 10),
ListenableBuilder(
listenable: widget.viewModel,
builder: (context, child) {
return (widget.viewModel.askPrice)
? TextFormField(
decoration: InputDecoration(
labelText: "Prix",
border: OutlineInputBorder(),
suffixText: "",
),
keyboardType: TextInputType.numberWithOptions(
decimal: true,
),
validator: (value) {
if (value == null || value.isEmpty) {
return "Indiquez un prix";
} else if (num.tryParse(value) == null) {
return "Le prix doit être un nombre";
} else if (num.parse(value) < 0) {
return "Le prix doit être positif ou nul";
}
return null;
},
onSaved: (newValue) {
price = num.parse(newValue!);
},
)
: SizedBox();
},
),
],
),
),
actions: [
TextButton(
child: Text("Annuler"),
onPressed: () {
widget.exitPopup(context);
},
child: Text("Annuler"),
),
TextButton(
child: Text("Valider"),
onPressed: () {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
widget.exitPopup(context);
widget.scanEan(
context,
widget.viewModel,
ean!,
widget.scannerController,
leaveLastPopup: (context) {
Navigator.of(context).pop();
},
);
}
},
child: Text("Valider"),
),
],
);

View file

@ -6,11 +6,11 @@ class OwnerPopup extends StatefulWidget {
const OwnerPopup({
super.key,
required this.viewModel,
required this.onPressAccept,
required this.exitPopup,
});
final AddViewModel viewModel;
final Function(BuildContext) onPressAccept;
final Function(BuildContext) exitPopup;
@override
State<OwnerPopup> createState() => _OwnerPopupState();
@ -166,7 +166,7 @@ class _OwnerPopupState extends State<OwnerPopup> {
});
}
}
widget.onPressAccept(context);
widget.exitPopup(context);
},
child: Text(
(!showNewOwner && searchController.text == "")