feat: added authentification and redirection

This commit is contained in:
Alzalia 2025-08-08 01:03:48 +02:00
parent 1c9c5ce5fe
commit ef641d4023
24 changed files with 731 additions and 173 deletions

View file

@ -1,12 +1,19 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:mobile_scanner/mobile_scanner.dart';
import 'package:seshat/data/repositories/owner_repository.dart';
import 'package:seshat/domain/models/book.dart';
import 'package:seshat/domain/models/owner.dart';
import 'package:seshat/utils/command.dart';
import 'package:seshat/utils/result.dart';
class AddViewModel extends ChangeNotifier {
AddViewModel();
AddViewModel({required OwnerRepository ownerRepository})
: _ownerRepository = ownerRepository {
load = Command0(_load)..execute();
}
final OwnerRepository _ownerRepository;
/*
* ====================
@ -21,37 +28,37 @@ class AddViewModel extends ChangeNotifier {
notifyListeners();
}
final List<Owner> _owners = [];
List<Owner> _owners = [];
List<Owner>? get owners => _owners;
Owner addOwner(String firstName, String lastName, String contact) {
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,
lastName: lastName,
contact: contact,
id: 0,
Future<Result<Owner>> addOwner(
String firstName,
String lastName,
String contact,
) async {
final result = await _ownerRepository.postOwner(
firstName,
lastName,
contact,
);
switch (result) {
case Ok():
final secondResult = await _ownerRepository.getOwners();
switch (secondResult) {
case Ok():
_owners = secondResult.value;
_currentOwner = result.value;
notifyListeners();
return Result.ok(result.value);
case Error():
return Result.error(secondResult.error);
}
case Error():
return Result.error(result.error);
}
}
/*
@ -87,8 +94,38 @@ class AddViewModel extends ChangeNotifier {
);
}
/// Sens an api request with
/// Sends an api request with
// Result<BookInstance> newBookInstance() {
// };
/*
* =================================
* =====[ COMMAND AND LOADING ]=====
* =================================
*/
late final Command0 load;
bool isLoaded = false;
Future<Result<void>> _load() async {
return await _loadOwners();
}
Future<Result<void>> _loadOwners() async {
final result = await _ownerRepository.getOwners();
switch (result) {
case Ok():
_owners = result.value;
isLoaded = true;
case Error():
debugPrint("Oupsie daysie, ${result.error}");
}
notifyListeners();
_ownerRepository.liveOwners().listen((Owner owner) {
_owners.add(owner);
notifyListeners();
});
return result;
}
}

View file

@ -32,130 +32,137 @@ class _AddPageState extends State<AddPage> {
// builder: (context, screen, child) {
return Scaffold(
bottomNavigationBar: AppNavigationBar(startIndex: 1),
body: Stack(
children: [
ColoredBox(color: Colors.black),
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;
});
}
Result<Book> result = await widget.viewModel.scanBook(barcodes);
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(
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.askPrice)
? "Demander à chaque fois"
: "Prix libre toujours",
),
onPressed: () {
setState(() {
widget.viewModel.askPrice =
!widget.viewModel.askPrice;
});
},
),
),
],
body: ListenableBuilder(
listenable: widget.viewModel,
builder: (context, child) => switch (widget.viewModel.isLoaded) {
false => CircularProgressIndicator(),
true => Stack(
children: [
ColoredBox(color: Colors.black),
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,
),
),
),
SizedBox(height: 100),
SvgPicture.asset('assets/scan-overlay.svg'),
],
);
return;
}
void setPrice(num newPrice) async {
setState(() {
price = newPrice;
});
}
Result<Book> result = await widget.viewModel.scanBook(
barcodes,
);
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(
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"),
SafeArea(
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: [
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.askPrice)
? "Demander à chaque fois"
: "Prix libre toujours",
),
onPressed: () {
setState(() {
widget.viewModel.askPrice =
!widget.viewModel.askPrice;
});
},
),
),
],
),
),
),
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"),
),
),
],
),
),
],
),
],
},
),
);
// },

View file

@ -141,11 +141,14 @@ class _OwnerPopupState extends State<OwnerPopup> {
),
SizedBox(height: 10),
ElevatedButton(
onPressed: () {
onPressed: () async {
if (_formKey.currentState!.validate()) {
_formKey.currentState!.save();
widget.viewModel.currentOwner = widget.viewModel
.addOwner(firstName!, lastName!, contact!);
await widget.viewModel.addOwner(
firstName!,
lastName!,
contact!,
);
setState(() {
showNewOwner = false;
});

View file

@ -0,0 +1,24 @@
import 'package:flutter/material.dart';
import 'package:seshat/data/repositories/auth_repository.dart';
import 'package:seshat/utils/command.dart';
import 'package:seshat/utils/result.dart';
class LoginViewModel extends ChangeNotifier {
LoginViewModel({required AuthRepository authRepository})
: _authRepository = authRepository {
login = Command1<void, (String username, String password)>(_login);
}
final AuthRepository _authRepository;
late Command1 login;
Future<Result<void>> _login((String, String) credentials) async {
final (username, password) = credentials;
final result = await _authRepository.login(username, password);
if (result is Error<void>) {
debugPrint("Hehe no");
}
return result;
}
}

View file

@ -0,0 +1,92 @@
import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';
import 'package:seshat/routing/routes.dart';
import 'package:seshat/ui/auth/viewmodel/login_view_model.dart';
class LoginPage extends StatefulWidget {
const LoginPage({super.key, required this.viewModel});
final LoginViewModel viewModel;
@override
State<LoginPage> createState() => _LoginPageState();
}
class _LoginPageState extends State<LoginPage> {
final TextEditingController _username = TextEditingController(
text: "ueauvergne",
);
final TextEditingController _password = TextEditingController(
text: "ueauvergne",
);
@override
void initState() {
super.initState();
widget.viewModel.login.addListener(_onResult);
}
@override
void didUpdateWidget(covariant LoginPage oldWidget) {
super.didUpdateWidget(oldWidget);
oldWidget.viewModel.removeListener(_onResult);
widget.viewModel.login.addListener(_onResult);
}
@override
void dispose() {
widget.viewModel.login.removeListener(_onResult);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
TextField(controller: _username),
TextField(controller: _password),
ListenableBuilder(
listenable: widget.viewModel.login,
builder: (context, child) {
return FilledButton(
onPressed: () {
widget.viewModel.login.execute((
_username.value.text,
_password.value.text,
));
},
child: Text("Connexion"),
);
},
),
],
),
);
}
void _onResult() {
if (widget.viewModel.login.completed) {
widget.viewModel.login.clearResult();
context.go(Routes.add);
}
if (widget.viewModel.login.error) {
widget.viewModel.login.clearResult();
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text("Une erreur est survenue lors de la connexion."),
action: SnackBarAction(
label: "Réessayer",
onPressed: () => widget.viewModel.login.execute((
_username.value.text,
_password.value.text,
)),
),
),
);
}
}
}