feat: start of pending screen
This commit is contained in:
parent
019a21f00e
commit
ee9c4c3801
12 changed files with 425 additions and 67 deletions
|
|
@ -22,6 +22,17 @@ class BalRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Result<List<Bal>>> _getBalsNoCache() async {
|
||||||
|
final result = await _apiClient.getBals();
|
||||||
|
switch (result) {
|
||||||
|
case Ok():
|
||||||
|
_bals = result.value;
|
||||||
|
return Result.ok(result.value);
|
||||||
|
case Error():
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Result<Bal>> balById(int id) async {
|
Future<Result<Bal>> balById(int id) async {
|
||||||
if (_bals == null) {
|
if (_bals == null) {
|
||||||
await getBals();
|
await getBals();
|
||||||
|
|
@ -39,13 +50,20 @@ class BalRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Result<void>> addBal(String name) async {
|
Future<Result<Bal>> editBal(
|
||||||
final result = await _apiClient.addBal(name);
|
int id,
|
||||||
switch (result) {
|
String name,
|
||||||
case Ok():
|
DateTime start,
|
||||||
return result;
|
DateTime end,
|
||||||
case Error():
|
) async {
|
||||||
return result;
|
final result = await _apiClient.editBal(id, name, start, end);
|
||||||
}
|
_getBalsNoCache();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<Result<void>> addBal(String name, DateTime start, DateTime end) async {
|
||||||
|
final result = await _apiClient.addBal(name, start, end);
|
||||||
|
_getBalsNoCache();
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,38 @@ class ApiClient {
|
||||||
* =================
|
* =================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
Future<Result<Bal>> editBal(
|
||||||
|
int id,
|
||||||
|
String name,
|
||||||
|
DateTime start,
|
||||||
|
DateTime end,
|
||||||
|
) async {
|
||||||
|
final client = Client();
|
||||||
|
try {
|
||||||
|
final headers = await _getHeaders({"Content-Type": "application/json"});
|
||||||
|
final body = {
|
||||||
|
"name": name,
|
||||||
|
"start_timestamp": (start.millisecondsSinceEpoch / 1000).round(),
|
||||||
|
"end_timestamp": (end.millisecondsSinceEpoch / 1000).round(),
|
||||||
|
};
|
||||||
|
final response = await client.patch(
|
||||||
|
Uri.parse("https://$apiBasePath/bal/${id.toString()}"),
|
||||||
|
headers: headers,
|
||||||
|
body: jsonEncode(body),
|
||||||
|
);
|
||||||
|
if (response.statusCode == 200) {
|
||||||
|
final json = jsonDecode(response.body);
|
||||||
|
return Result.ok(Bal.fromJSON(json));
|
||||||
|
} else {
|
||||||
|
throw Exception("Something went wrong");
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
return Result.error(Exception(e));
|
||||||
|
} finally {
|
||||||
|
client.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<Result<Bal>> getBalById(int id) async {
|
Future<Result<Bal>> getBalById(int id) async {
|
||||||
final client = Client();
|
final client = Client();
|
||||||
try {
|
try {
|
||||||
|
|
@ -54,7 +86,7 @@ class ApiClient {
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
return Result.ok(Bal.fromJSON(json));
|
return Result.ok(Bal.fromJSON(json));
|
||||||
} else if (response.statusCode == 403) {
|
} else if (response.statusCode == 403) {
|
||||||
return Result.error(Exception("You don't own the specified bal"));
|
throw Exception("You don't own the specified bal");
|
||||||
} else {
|
} else {
|
||||||
return Result.error(
|
return Result.error(
|
||||||
Exception("No bal wirth this id exists the database"),
|
Exception("No bal wirth this id exists the database"),
|
||||||
|
|
@ -67,11 +99,15 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Result<Bal>> addBal(String name) async {
|
Future<Result<Bal>> addBal(String name, DateTime start, DateTime end) async {
|
||||||
final client = Client();
|
final client = Client();
|
||||||
try {
|
try {
|
||||||
final headers = await _getHeaders({"Content-Type": "application/json"});
|
final headers = await _getHeaders({"Content-Type": "application/json"});
|
||||||
final body = {"name": name};
|
final body = {
|
||||||
|
"name": name,
|
||||||
|
"start_timestamp": (start.millisecondsSinceEpoch / 1000).round(),
|
||||||
|
"end_timestamp": (end.millisecondsSinceEpoch / 1000).round(),
|
||||||
|
};
|
||||||
final response = await client.post(
|
final response = await client.post(
|
||||||
Uri.parse("https://$apiBasePath/bal"),
|
Uri.parse("https://$apiBasePath/bal"),
|
||||||
headers: headers,
|
headers: headers,
|
||||||
|
|
@ -81,10 +117,9 @@ class ApiClient {
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
return Result.ok(Bal.fromJSON(json));
|
return Result.ok(Bal.fromJSON(json));
|
||||||
} else {
|
} else {
|
||||||
return Result.error(Exception("Something went wrong"));
|
throw Exception("Something went wrong");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("\n\n\n\n${e.toString()}\n\n\n\n");
|
|
||||||
return Result.error(Exception(e));
|
return Result.error(Exception(e));
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
|
|
@ -101,12 +136,17 @@ class ApiClient {
|
||||||
);
|
);
|
||||||
if (response.statusCode == 200) {
|
if (response.statusCode == 200) {
|
||||||
final json = jsonDecode(response.body) as List<dynamic>;
|
final json = jsonDecode(response.body) as List<dynamic>;
|
||||||
debugPrint("\n\n\n\nRECEIVED $json\n\n\n\n");
|
debugPrint("\n\n\n\nRECEIVED : $json\n\n\n\n");
|
||||||
|
debugPrint(
|
||||||
|
"\n\n\n\nFORMATTED : ${json.map((element) => Bal.fromJSON(element)).toList()}\n\n\n\n",
|
||||||
|
);
|
||||||
|
|
||||||
return Result.ok(json.map((element) => Bal.fromJSON(element)).toList());
|
return Result.ok(json.map((element) => Bal.fromJSON(element)).toList());
|
||||||
} else {
|
} else {
|
||||||
return Result.error(Exception("Something wrong happened"));
|
throw Exception("Something wrong happened");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
debugPrint("ERROR: ${e.toString()}");
|
||||||
return Result.error(Exception(e));
|
return Result.error(Exception(e));
|
||||||
} finally {
|
} finally {
|
||||||
client.close();
|
client.close();
|
||||||
|
|
@ -127,7 +167,7 @@ class ApiClient {
|
||||||
} else if (response.statusCode == 404) {
|
} else if (response.statusCode == 404) {
|
||||||
return Result.ok(null);
|
return Result.ok(null);
|
||||||
} else {
|
} else {
|
||||||
return Result.error(Exception("Something went wrong"));
|
throw Exception("Something went wrong");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return Result.error(Exception(e));
|
return Result.error(Exception(e));
|
||||||
|
|
@ -154,7 +194,7 @@ class ApiClient {
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
return Result.ok(Book.fromJSON(json));
|
return Result.ok(Book.fromJSON(json));
|
||||||
} else {
|
} else {
|
||||||
return Result.error(Exception("The book was not found"));
|
throw Exception("The book was not found");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return Result.error(Exception("API $e"));
|
return Result.error(Exception("API $e"));
|
||||||
|
|
@ -193,9 +233,9 @@ class ApiClient {
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
return Result.ok(BookInstance.fromJSON(json));
|
return Result.ok(BookInstance.fromJSON(json));
|
||||||
} else if (response.statusCode == 403) {
|
} else if (response.statusCode == 403) {
|
||||||
return Result.error(Exception("You don't own that book instance"));
|
throw Exception("You don't own that book instance");
|
||||||
} else {
|
} else {
|
||||||
return Result.error(Exception("Something wrong happened"));
|
throw Exception("Something wrong happened");
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return Result.error(Exception(e));
|
return Result.error(Exception(e));
|
||||||
|
|
@ -225,7 +265,7 @@ class ApiClient {
|
||||||
json.map((element) => Owner.fromJSON(element)).toList(),
|
json.map((element) => Owner.fromJSON(element)).toList(),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return Result.error(Exception("Invalid request"));
|
throw Exception("Invalid request");
|
||||||
}
|
}
|
||||||
} on Exception catch (error) {
|
} on Exception catch (error) {
|
||||||
return Result.error(error);
|
return Result.error(error);
|
||||||
|
|
@ -257,7 +297,7 @@ class ApiClient {
|
||||||
final json = jsonDecode(response.body);
|
final json = jsonDecode(response.body);
|
||||||
return Result.ok(Owner.fromJSON(json));
|
return Result.ok(Owner.fromJSON(json));
|
||||||
} else {
|
} else {
|
||||||
return Result.error(Exception("Invalid request"));
|
throw Exception("Invalid request");
|
||||||
}
|
}
|
||||||
} on Exception catch (error) {
|
} on Exception catch (error) {
|
||||||
return Result.error(error);
|
return Result.error(error);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:seshat/config/dependencies.dart';
|
import 'package:seshat/config/dependencies.dart';
|
||||||
import 'package:seshat/routing/router.dart';
|
import 'package:seshat/routing/router.dart';
|
||||||
|
|
||||||
void main() {
|
void main() async {
|
||||||
Logger.root.level = Level.ALL;
|
Logger.root.level = Level.ALL;
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
|
@ -19,6 +20,12 @@ class MyApp extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
|
localizationsDelegates: [
|
||||||
|
GlobalMaterialLocalizations.delegate,
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
GlobalWidgetsLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: [const Locale("fr")],
|
||||||
routerConfig: router(context.read()),
|
routerConfig: router(context.read()),
|
||||||
theme: ThemeData.dark(),
|
theme: ThemeData.dark(),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,26 @@ class BalViewModel extends ChangeNotifier {
|
||||||
int? id;
|
int? id;
|
||||||
Bal? get bal => _bal;
|
Bal? get bal => _bal;
|
||||||
|
|
||||||
|
Future<Result<void>> editBal(
|
||||||
|
int id,
|
||||||
|
String name,
|
||||||
|
DateTime start,
|
||||||
|
DateTime end,
|
||||||
|
) async {
|
||||||
|
final result = await _balRepository.editBal(id, name, start, end);
|
||||||
|
switch (result) {
|
||||||
|
case Ok():
|
||||||
|
debugPrint("\n\n\n\nDID EDIT\n\n\n\n");
|
||||||
|
_bal = result.value;
|
||||||
|
notifyListeners();
|
||||||
|
break;
|
||||||
|
case Error():
|
||||||
|
debugPrint("\n\n\n\nERROR: ${result.error}");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* =================================
|
* =================================
|
||||||
* =====[ COMMAND AND LOADING ]=====
|
* =====[ COMMAND AND LOADING ]=====
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:seshat/domain/models/bal.dart';
|
import 'package:seshat/domain/models/bal.dart';
|
||||||
import 'package:seshat/ui/bal_page/view_model/bal_view_model.dart';
|
import 'package:seshat/ui/bal_page/view_model/bal_view_model.dart';
|
||||||
|
import 'package:seshat/ui/bal_page/widget/pending/bal_pending_screen.dart';
|
||||||
import 'package:seshat/ui/core/ui/navigation_bar.dart';
|
import 'package:seshat/ui/core/ui/navigation_bar.dart';
|
||||||
import 'package:seshat/ui/core/ui/await_loading.dart';
|
import 'package:seshat/ui/core/ui/await_loading.dart';
|
||||||
|
|
||||||
|
|
@ -16,26 +17,29 @@ class BalPage extends StatefulWidget {
|
||||||
class _BalPageState extends State<BalPage> {
|
class _BalPageState extends State<BalPage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return ListenableBuilder(
|
||||||
bottomNavigationBar: AppNavigationBar(startIndex: 0),
|
listenable: widget.viewModel,
|
||||||
body: ListenableBuilder(
|
builder: (context, child) {
|
||||||
listenable: widget.viewModel,
|
return switch (widget.viewModel.isLoaded) {
|
||||||
builder: (context, child) {
|
false => Scaffold(
|
||||||
return switch (widget.viewModel.isLoaded) {
|
bottomNavigationBar: AppNavigationBar(startIndex: 0),
|
||||||
false => AwaitLoading(),
|
body: AwaitLoading(),
|
||||||
true => switch (widget.viewModel.bal == null) {
|
),
|
||||||
true => Center(
|
true => switch (widget.viewModel.bal == null) {
|
||||||
|
true => Scaffold(
|
||||||
|
bottomNavigationBar: AppNavigationBar(startIndex: 0),
|
||||||
|
body: Center(
|
||||||
child: Text("La BAL référencée n'est pas accessible"),
|
child: Text("La BAL référencée n'est pas accessible"),
|
||||||
),
|
),
|
||||||
false => switch (widget.viewModel.bal!.state) {
|
),
|
||||||
BalState.pending => Center(child: Text("Pending")),
|
false => switch (widget.viewModel.bal!.state) {
|
||||||
BalState.ongoing => Center(child: Text("Ongoing")),
|
BalState.pending => BalPendingScreen(viewModel: widget.viewModel),
|
||||||
BalState.ended => Center(child: Text("Ending")),
|
BalState.ongoing => Center(child: Text("Ongoing")),
|
||||||
},
|
BalState.ended => Center(child: Text("Ending")),
|
||||||
},
|
},
|
||||||
};
|
},
|
||||||
},
|
};
|
||||||
),
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,191 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:seshat/domain/models/bal.dart';
|
||||||
|
import 'package:seshat/ui/bal_page/view_model/bal_view_model.dart';
|
||||||
|
import 'package:seshat/ui/core/ui/navigation_bar.dart';
|
||||||
|
|
||||||
class BalPendingScreen extends StatelessWidget {
|
class BalPendingScreen extends StatelessWidget {
|
||||||
const BalPendingScreen({super.key});
|
const BalPendingScreen({super.key, required this.viewModel});
|
||||||
|
|
||||||
|
final BalViewModel viewModel;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// TODO: implement build
|
return Scaffold(
|
||||||
throw UnimplementedError();
|
bottomNavigationBar: AppNavigationBar(startIndex: 0),
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(viewModel.bal!.name),
|
||||||
|
actions: [
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
|
builder: (context) => EditPopup(viewModel: viewModel),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.edit),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
body: Center(
|
||||||
|
child: ElevatedButton(
|
||||||
|
onPressed: () {},
|
||||||
|
child: Text("Démarrer cette BAL"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class EditPopup extends StatefulWidget {
|
||||||
|
const EditPopup({super.key, required this.viewModel});
|
||||||
|
|
||||||
|
final BalViewModel viewModel;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<EditPopup> createState() => _EditPopup();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _EditPopup extends State<EditPopup> {
|
||||||
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
|
String? name;
|
||||||
|
DateTime? start;
|
||||||
|
Future<void> _selectStart() async {
|
||||||
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: start ?? widget.viewModel.bal!.startTime,
|
||||||
|
firstDate: DateTime.now(),
|
||||||
|
lastDate: DateTime(DateTime.now().year + 2),
|
||||||
|
locale: Locale("fr", "FR"),
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
start = pickedDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? end;
|
||||||
|
Future<void> _selectEnd() async {
|
||||||
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: end ?? widget.viewModel.bal!.endTime,
|
||||||
|
firstDate: DateTime.now(),
|
||||||
|
lastDate: DateTime(DateTime.now().year + 2),
|
||||||
|
locale: Locale("fr", "FR"),
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
end = pickedDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
initializeDateFormatting();
|
||||||
|
var format = DateFormat("dd MMM yyyy", "fr");
|
||||||
|
return AlertDialog(
|
||||||
|
title: Text("Créer une BAL"),
|
||||||
|
content: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Form(
|
||||||
|
key: _formKey,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
TextFormField(
|
||||||
|
decoration: InputDecoration(
|
||||||
|
labelText: "Nom de la BAL",
|
||||||
|
border: OutlineInputBorder(),
|
||||||
|
),
|
||||||
|
initialValue: widget.viewModel.bal!.name,
|
||||||
|
validator: (value) {
|
||||||
|
if (value == null || value.isEmpty) {
|
||||||
|
return "Veuillez entrer un nom";
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
},
|
||||||
|
onSaved: (newValue) {
|
||||||
|
name = newValue;
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text("Date de début : "),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_selectStart();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
format.format(start ?? widget.viewModel.bal!.startTime),
|
||||||
|
locale: Locale("fr"),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text("Date de fin : "),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_selectEnd();
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
format.format(end ?? widget.viewModel.bal!.endTime),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text("Note: Les dates sont à titre purement indicatif."),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
actions: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
},
|
||||||
|
child: Text("Annuler"),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () async {
|
||||||
|
if (_formKey.currentState!.validate()) {
|
||||||
|
_formKey.currentState!.save();
|
||||||
|
|
||||||
|
final Bal bal = widget.viewModel.bal!;
|
||||||
|
|
||||||
|
final result = await widget.viewModel.editBal(
|
||||||
|
bal.id,
|
||||||
|
name ?? bal.name,
|
||||||
|
start ?? bal.startTime,
|
||||||
|
end ?? bal.endTime,
|
||||||
|
);
|
||||||
|
if (result is Error && context.mounted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(content: Text("Une erreur est survenue")),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (context.mounted) {
|
||||||
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
"Veuillez indiquer une date de début et de fin.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text("Valider"),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,12 @@ class _AwaitLoadingState extends State<AwaitLoading> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
t = Timer(Duration(seconds: 8), () {
|
t = Timer(Duration(seconds: 8), () {
|
||||||
setState(() {
|
if (context.mounted) {
|
||||||
text =
|
setState(() {
|
||||||
"Il semblerait qu'il y ait un problème. Vérifiez que vous êtes connecté·e à internet.";
|
text =
|
||||||
});
|
"Il semblerait qu'il y ait un problème. Vérifiez que vous êtes connecté·e à internet.";
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return Column(
|
return Column(
|
||||||
|
|
|
||||||
|
|
@ -24,8 +24,12 @@ class HomeViewModel extends ChangeNotifier {
|
||||||
Bal? _currentBal;
|
Bal? _currentBal;
|
||||||
Bal? get currentBal => _currentBal;
|
Bal? get currentBal => _currentBal;
|
||||||
|
|
||||||
Future<Result<void>> createBal(String name) async {
|
Future<Result<void>> createBal(
|
||||||
final result = await _balRepository.addBal(name);
|
String name,
|
||||||
|
DateTime start,
|
||||||
|
DateTime end,
|
||||||
|
) async {
|
||||||
|
final result = await _balRepository.addBal(name, start, end);
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case Ok():
|
case Ok():
|
||||||
final result2 = await _balRepository.getBals();
|
final result2 = await _balRepository.getBals();
|
||||||
|
|
@ -34,7 +38,6 @@ class HomeViewModel extends ChangeNotifier {
|
||||||
_bals = result2.value..sort((a, b) => a.compareTo(b));
|
_bals = result2.value..sort((a, b) => a.compareTo(b));
|
||||||
break;
|
break;
|
||||||
case Error():
|
case Error():
|
||||||
debugPrint("\n\n\n\n${result2.error.toString()}\n\n\n\n");
|
|
||||||
return result2;
|
return result2;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:seshat/ui/home_page/view_model/home_view_model.dart';
|
import 'package:seshat/ui/home_page/view_model/home_view_model.dart';
|
||||||
|
|
||||||
class CreateConfirmationPopup extends StatefulWidget {
|
class CreateConfirmationPopup extends StatefulWidget {
|
||||||
|
|
@ -14,8 +16,40 @@ class CreateConfirmationPopup extends StatefulWidget {
|
||||||
class _CreateConfirmationPopupState extends State<CreateConfirmationPopup> {
|
class _CreateConfirmationPopupState extends State<CreateConfirmationPopup> {
|
||||||
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
|
||||||
String? name;
|
String? name;
|
||||||
|
DateTime? start;
|
||||||
|
Future<void> _selectStart() async {
|
||||||
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: start ?? DateTime.now(),
|
||||||
|
firstDate: DateTime.now(),
|
||||||
|
lastDate: DateTime(DateTime.now().year + 2),
|
||||||
|
locale: Locale("fr"),
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
start = pickedDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime? end;
|
||||||
|
Future<void> _selectEnd() async {
|
||||||
|
final DateTime? pickedDate = await showDatePicker(
|
||||||
|
context: context,
|
||||||
|
initialDate: end ?? DateTime.now(),
|
||||||
|
firstDate: DateTime.now(),
|
||||||
|
lastDate: DateTime(DateTime.now().year + 2),
|
||||||
|
locale: Locale("fr"),
|
||||||
|
);
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
end = pickedDate;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
initializeDateFormatting();
|
||||||
|
var format = DateFormat("dd MMM yyyy", "fr");
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: Text("Créer une BAL"),
|
title: Text("Créer une BAL"),
|
||||||
content: Column(
|
content: Column(
|
||||||
|
|
@ -41,6 +75,29 @@ class _CreateConfirmationPopupState extends State<CreateConfirmationPopup> {
|
||||||
name = newValue;
|
name = newValue;
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text("Date de début : "),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_selectStart();
|
||||||
|
},
|
||||||
|
child: Text(format.format(start ?? DateTime.now())),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text("Date de fin : "),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () {
|
||||||
|
_selectEnd();
|
||||||
|
},
|
||||||
|
child: Text(format.format(end ?? DateTime.now())),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Text("Note: Les dates sont à titre purement indicatif."),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -55,12 +112,23 @@ class _CreateConfirmationPopupState extends State<CreateConfirmationPopup> {
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
if (_formKey.currentState!.validate()) {
|
if (_formKey.currentState!.validate() &&
|
||||||
|
start != null &&
|
||||||
|
end != null) {
|
||||||
_formKey.currentState!.save();
|
_formKey.currentState!.save();
|
||||||
await widget.viewModel.createBal(name!);
|
await widget.viewModel.createBal(name!, start!, end!);
|
||||||
}
|
|
||||||
if (context.mounted) {
|
if (context.mounted) {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Text(
|
||||||
|
"Veuillez indiquer une date de début et de fin.",
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: Text("Valider"),
|
child: Text("Valider"),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
import 'package:intl/date_symbol_data_local.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
import 'package:seshat/domain/models/bal.dart';
|
import 'package:seshat/domain/models/bal.dart';
|
||||||
import 'package:seshat/ui/core/ui/navigation_bar.dart';
|
import 'package:seshat/ui/core/ui/navigation_bar.dart';
|
||||||
import 'package:seshat/ui/home_page/view_model/home_view_model.dart';
|
import 'package:seshat/ui/home_page/view_model/home_view_model.dart';
|
||||||
|
|
@ -18,6 +20,8 @@ class HomePage extends StatefulWidget {
|
||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
initializeDateFormatting();
|
||||||
|
var format = DateFormat("dd MMM yyyy", "fr");
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
bottomNavigationBar: AppNavigationBar(startIndex: 0),
|
bottomNavigationBar: AppNavigationBar(startIndex: 0),
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
|
|
@ -55,30 +59,24 @@ class _HomePageState extends State<HomePage> {
|
||||||
title: Text(bal.name),
|
title: Text(bal.name),
|
||||||
subtitle: switch (bal.state) {
|
subtitle: switch (bal.state) {
|
||||||
BalState.pending => Text(
|
BalState.pending => Text(
|
||||||
"À venir · Débute le ${bal.startTime.toString()}",
|
"À venir · Débute le ${format.format(bal.startTime)}",
|
||||||
),
|
),
|
||||||
BalState.ongoing => Text("En cours"),
|
BalState.ongoing => Text("En cours"),
|
||||||
BalState.ended => Text("Terminée"),
|
BalState.ended => Text("Terminée"),
|
||||||
},
|
},
|
||||||
trailing: switch (bal.state) {
|
trailing: switch (bal.state) {
|
||||||
BalState.pending => IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
_moveToBal(context, bal.id);
|
|
||||||
},
|
|
||||||
icon: Icon(Icons.edit),
|
|
||||||
),
|
|
||||||
BalState.ongoing => IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
_moveToBal(context, bal.id);
|
|
||||||
},
|
|
||||||
icon: Icon(Icons.arrow_forward),
|
|
||||||
),
|
|
||||||
BalState.ended => IconButton(
|
BalState.ended => IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
_moveToBal(context, bal.id);
|
_moveToBal(context, bal.id);
|
||||||
},
|
},
|
||||||
icon: Icon(Icons.analytics),
|
icon: Icon(Icons.analytics),
|
||||||
),
|
),
|
||||||
|
_ => IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
_moveToBal(context, bal.id);
|
||||||
|
},
|
||||||
|
icon: Icon(Icons.arrow_forward),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|
@ -113,6 +111,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
|
barrierDismissible: false,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return CreateConfirmationPopup(
|
return CreateConfirmationPopup(
|
||||||
viewModel: widget.viewModel,
|
viewModel: widget.viewModel,
|
||||||
|
|
|
||||||
13
pubspec.lock
13
pubspec.lock
|
|
@ -94,6 +94,11 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_secure_storage:
|
flutter_secure_storage:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
|
@ -184,6 +189,14 @@ packages:
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "4.1.2"
|
version: "4.1.2"
|
||||||
|
intl:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: intl
|
||||||
|
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.20.2"
|
||||||
js:
|
js:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,9 @@ environment:
|
||||||
dependencies:
|
dependencies:
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
|
|
||||||
# The following adds the Cupertino Icons font to your application.
|
# The following adds the Cupertino Icons font to your application.
|
||||||
# Use with the CupertinoIcons class for iOS style icons.
|
# Use with the CupertinoIcons class for iOS style icons.
|
||||||
|
|
@ -44,6 +47,7 @@ dependencies:
|
||||||
nested: ^1.0.0
|
nested: ^1.0.0
|
||||||
flutter_secure_storage: ^9.2.4
|
flutter_secure_storage: ^9.2.4
|
||||||
rxdart: ^0.28.0
|
rxdart: ^0.28.0
|
||||||
|
intl: ^0.20.2
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|
|
||||||
Reference in a new issue