diff --git a/lib/backend/objectbox/entities/product/product_entity.dart b/lib/backend/objectbox/entities/product/product_entity.dart index c13a29e..3e85c5a 100644 --- a/lib/backend/objectbox/entities/product/product_entity.dart +++ b/lib/backend/objectbox/entities/product/product_entity.dart @@ -4,7 +4,7 @@ import 'package:objectbox/objectbox.dart'; /// Modèle de base de données ObjectBox @Entity() class ProductEntity { - @Id() + @Id(assignable: true) int id; String? code; diff --git a/lib/backend/objectbox/objectbox-model.json b/lib/backend/objectbox/objectbox-model.json index 0cdd6b2..e65b9e2 100644 --- a/lib/backend/objectbox/objectbox-model.json +++ b/lib/backend/objectbox/objectbox-model.json @@ -12,7 +12,7 @@ "id": "1:1853465479129290672", "name": "id", "type": 6, - "flags": 1 + "flags": 129 }, { "id": "2:4521897043130066476", diff --git a/lib/backend/objectbox/objectbox.g.dart b/lib/backend/objectbox/objectbox.g.dart index be94e25..19611ef 100644 --- a/lib/backend/objectbox/objectbox.g.dart +++ b/lib/backend/objectbox/objectbox.g.dart @@ -29,7 +29,7 @@ final _entities = [ id: const obx_int.IdUid(1, 1853465479129290672), name: 'id', type: 6, - flags: 1, + flags: 129, ), obx_int.ModelProperty( id: const obx_int.IdUid(2, 4521897043130066476), diff --git a/lib/backend/objectbox/objectbox_manager.dart b/lib/backend/objectbox/objectbox_manager.dart index 36f626e..c01c6d7 100644 --- a/lib/backend/objectbox/objectbox_manager.dart +++ b/lib/backend/objectbox/objectbox_manager.dart @@ -3,6 +3,7 @@ import 'package:path/path.dart' as p; import 'package:path_provider/path_provider.dart'; import 'package:barcode_scanner/backend/objectbox/objectbox.g.dart'; +export 'package:barcode_scanner/backend/objectbox/objectbox.g.dart'; late ObjectboxManager objectboxManager; diff --git a/lib/backend/schema/product/product_struct.dart b/lib/backend/schema/product/product_struct.dart index 1796ef4..901233e 100644 --- a/lib/backend/schema/product/product_struct.dart +++ b/lib/backend/schema/product/product_struct.dart @@ -1,3 +1,4 @@ +import 'package:barcode_scanner/backend/objectbox/entities/product/product_entity.dart'; import 'package:freezed_annotation/freezed_annotation.dart'; part 'product_struct.freezed.dart'; @@ -18,3 +19,14 @@ abstract class ProductStruct with _$ProductStruct { factory ProductStruct.fromJson(Map json) => _$ProductStructFromJson(json); } + +extension ProductStructExt on ProductStruct { + ProductEntity get toEntity => ProductEntity( + id: id, + name: name, + description: description, + price: price, + quantity: quantity, + image: image, + ); +} diff --git a/lib/components/product_scanned_component.dart b/lib/components/product_scanned_component.dart index 1a9aa71..3e8641c 100644 --- a/lib/components/product_scanned_component.dart +++ b/lib/components/product_scanned_component.dart @@ -73,7 +73,7 @@ class _ProductScannedComponentState extends State { alignment: Alignment.topCenter, child: Image.network( widget.productStruct.image ?? '', - height: MediaQuery.sizeOf(context).height * .2, + height: MediaQuery.sizeOf(context).height * .12, ), ), SizedBox(height: 15), @@ -164,7 +164,9 @@ class _ProductScannedComponentState extends State { }, child: Text( 'Voir détails', - style: AppTheme.of(context).bodyMedium, + style: AppTheme.of(context).bodyMedium.override( + color: AppTheme.of(context).white, + ), ), ), ), diff --git a/lib/pages/home_page/home_page_model.dart b/lib/pages/home_page/home_page_model.dart index fb314ea..6746384 100644 --- a/lib/pages/home_page/home_page_model.dart +++ b/lib/pages/home_page/home_page_model.dart @@ -29,7 +29,7 @@ class HomePageModel extends StateNotifier { Future getUserConnected() async { state = state.copyWith(loading: true); final user = await UserStruct(id: '1').getFromLocalStorage(); - state = state.copyWith(user: user); + state = state.copyWith(user: user, loading: false); } } diff --git a/lib/pages/product_list_page/product_list_page.dart b/lib/pages/product_list_page/product_list_page.dart index 4173c16..ff32bb4 100644 --- a/lib/pages/product_list_page/product_list_page.dart +++ b/lib/pages/product_list_page/product_list_page.dart @@ -1,5 +1,9 @@ +import 'package:barcode_scanner/components/loading_progress_component.dart'; +import 'package:barcode_scanner/pages/product_list_page/product_list_page_model.dart'; +import 'package:barcode_scanner/router/go_secure_router_builder.dart'; import 'package:barcode_scanner/themes/app_theme.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; class ProductListPage extends ConsumerStatefulWidget { @@ -11,8 +15,17 @@ class ProductListPage extends ConsumerStatefulWidget { } class _ProductListPageState extends ConsumerState { + @override + void initState() { + super.initState(); + SchedulerBinding.instance.addPostFrameCallback((_) { + ref.read(productListPageModelProvider.notifier).getProductsLocal(); + }); + } + @override Widget build(BuildContext context) { + final state = ref.watch(productListPageModelProvider); return Scaffold( backgroundColor: AppTheme.of(context).primaryBackground, appBar: AppBar( @@ -33,6 +46,55 @@ class _ProductListPageState extends ConsumerState { backgroundColor: AppTheme.of(context).primaryBackground, elevation: 0, ), + body: state.loading + ? Center(child: LoadingProgressComponent()) + : Builder( + builder: (context) { + if (state.products.isEmpty) { + return Center(child: Text('Aucun donnée trouvée')); + } else { + return ListView.builder( + padding: EdgeInsets.symmetric(horizontal: 16, vertical: 20), + itemCount: state.products.length, + itemBuilder: (context, index) { + final product = state.products[index]; + return ListTile( + onTap: () { + ProductFormRoute().push(context); + }, + leading: Container( + height: 60, + width: 60, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(60), + image: DecorationImage( + image: NetworkImage(product.image ?? ''), + ), + ), + ), + title: Text( + product.name ?? '', + style: AppTheme.of(context).titleMedium, + ), + subtitle: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "Code: ${product.id}", + style: AppTheme.of(context).bodyMedium, + ), + Text( + "Quatité: ${product.quantity ?? ''}", + style: AppTheme.of(context).bodyMedium, + ), + ], + ), + ); + }, + ); + } + }, + ), ); } } diff --git a/lib/pages/product_list_page/product_list_page_model.dart b/lib/pages/product_list_page/product_list_page_model.dart new file mode 100644 index 0000000..7099634 --- /dev/null +++ b/lib/pages/product_list_page/product_list_page_model.dart @@ -0,0 +1,35 @@ +import 'package:barcode_scanner/backend/objectbox/entities/product/product_entity.dart'; +import 'package:barcode_scanner/backend/objectbox/objectbox_manager.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:freezed_annotation/freezed_annotation.dart'; + +part 'product_list_page_model.freezed.dart'; + +/// The provider for the AuthViewModel, using Riverpod's StateNotifierProvider +/// with autoDispose to manage the lifecycle of the view model. +final productListPageModelProvider = + StateNotifierProvider((ref) { + return ProductListPageModel(); + }); + +class ProductListPageModel extends StateNotifier { + /// Constructor initializes the TaskRepository using the provider reference. + ProductListPageModel() : super(const ProductListPageState()); + + Future getProductsLocal() async { + Box productStore = objectboxManager.store + .box(); + state = state.copyWith(loading: true); + final products = await productStore.getAllAsync(); + state = state.copyWith(products: products, loading: false); + } +} + +@freezed +abstract class ProductListPageState with _$ProductListPageState { + const factory ProductListPageState({ + @Default([]) List products, + @Default(false) bool loading, + }) = _ProductListPageState; +} diff --git a/lib/pages/product_list_page/product_list_page_model.freezed.dart b/lib/pages/product_list_page/product_list_page_model.freezed.dart new file mode 100644 index 0000000..7c3660e --- /dev/null +++ b/lib/pages/product_list_page/product_list_page_model.freezed.dart @@ -0,0 +1,163 @@ +// dart format width=80 +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark + +part of 'product_list_page_model.dart'; + +// ************************************************************************** +// FreezedGenerator +// ************************************************************************** + +// dart format off +T _$identity(T value) => value; +/// @nodoc +mixin _$ProductListPageState implements DiagnosticableTreeMixin { + + List get products; bool get loading; +/// Create a copy of ProductListPageState +/// with the given fields replaced by the non-null parameter values. +@JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +$ProductListPageStateCopyWith get copyWith => _$ProductListPageStateCopyWithImpl(this as ProductListPageState, _$identity); + + +@override +void debugFillProperties(DiagnosticPropertiesBuilder properties) { + properties + ..add(DiagnosticsProperty('type', 'ProductListPageState')) + ..add(DiagnosticsProperty('products', products))..add(DiagnosticsProperty('loading', loading)); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is ProductListPageState&&const DeepCollectionEquality().equals(other.products, products)&&(identical(other.loading, loading) || other.loading == loading)); +} + + +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(products),loading); + +@override +String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { + return 'ProductListPageState(products: $products, loading: $loading)'; +} + + +} + +/// @nodoc +abstract mixin class $ProductListPageStateCopyWith<$Res> { + factory $ProductListPageStateCopyWith(ProductListPageState value, $Res Function(ProductListPageState) _then) = _$ProductListPageStateCopyWithImpl; +@useResult +$Res call({ + List products, bool loading +}); + + + + +} +/// @nodoc +class _$ProductListPageStateCopyWithImpl<$Res> + implements $ProductListPageStateCopyWith<$Res> { + _$ProductListPageStateCopyWithImpl(this._self, this._then); + + final ProductListPageState _self; + final $Res Function(ProductListPageState) _then; + +/// Create a copy of ProductListPageState +/// with the given fields replaced by the non-null parameter values. +@pragma('vm:prefer-inline') @override $Res call({Object? products = null,Object? loading = null,}) { + return _then(_self.copyWith( +products: null == products ? _self.products : products // ignore: cast_nullable_to_non_nullable +as List,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + +} + + +/// @nodoc + + +class _ProductListPageState with DiagnosticableTreeMixin implements ProductListPageState { + const _ProductListPageState({final List products = const [], this.loading = false}): _products = products; + + + final List _products; +@override@JsonKey() List get products { + if (_products is EqualUnmodifiableListView) return _products; + // ignore: implicit_dynamic_type + return EqualUnmodifiableListView(_products); +} + +@override@JsonKey() final bool loading; + +/// Create a copy of ProductListPageState +/// with the given fields replaced by the non-null parameter values. +@override @JsonKey(includeFromJson: false, includeToJson: false) +@pragma('vm:prefer-inline') +_$ProductListPageStateCopyWith<_ProductListPageState> get copyWith => __$ProductListPageStateCopyWithImpl<_ProductListPageState>(this, _$identity); + + +@override +void debugFillProperties(DiagnosticPropertiesBuilder properties) { + properties + ..add(DiagnosticsProperty('type', 'ProductListPageState')) + ..add(DiagnosticsProperty('products', products))..add(DiagnosticsProperty('loading', loading)); +} + +@override +bool operator ==(Object other) { + return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProductListPageState&&const DeepCollectionEquality().equals(other._products, _products)&&(identical(other.loading, loading) || other.loading == loading)); +} + + +@override +int get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_products),loading); + +@override +String toString({ DiagnosticLevel minLevel = DiagnosticLevel.info }) { + return 'ProductListPageState(products: $products, loading: $loading)'; +} + + +} + +/// @nodoc +abstract mixin class _$ProductListPageStateCopyWith<$Res> implements $ProductListPageStateCopyWith<$Res> { + factory _$ProductListPageStateCopyWith(_ProductListPageState value, $Res Function(_ProductListPageState) _then) = __$ProductListPageStateCopyWithImpl; +@override @useResult +$Res call({ + List products, bool loading +}); + + + + +} +/// @nodoc +class __$ProductListPageStateCopyWithImpl<$Res> + implements _$ProductListPageStateCopyWith<$Res> { + __$ProductListPageStateCopyWithImpl(this._self, this._then); + + final _ProductListPageState _self; + final $Res Function(_ProductListPageState) _then; + +/// Create a copy of ProductListPageState +/// with the given fields replaced by the non-null parameter values. +@override @pragma('vm:prefer-inline') $Res call({Object? products = null,Object? loading = null,}) { + return _then(_ProductListPageState( +products: null == products ? _self._products : products // ignore: cast_nullable_to_non_nullable +as List,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable +as bool, + )); +} + + +} + +// dart format on diff --git a/lib/pages/scanner_page/scanner_page.dart b/lib/pages/scanner_page/scanner_page.dart index 287c69b..171c521 100644 --- a/lib/pages/scanner_page/scanner_page.dart +++ b/lib/pages/scanner_page/scanner_page.dart @@ -1,7 +1,10 @@ import 'dart:async'; import 'package:barcode_scanner/backend/api/api_calls.dart'; +import 'package:barcode_scanner/backend/objectbox/entities/product/product_entity.dart'; +import 'package:barcode_scanner/backend/objectbox/objectbox_manager.dart'; import 'package:barcode_scanner/backend/schema/product/product_struct.dart'; +import 'package:barcode_scanner/components/loading_progress_component.dart'; import 'package:barcode_scanner/components/product_scanned_component.dart'; import 'package:barcode_scanner/router/go_secure_router_builder.dart'; import 'package:barcode_scanner/themes/app_theme.dart'; @@ -107,11 +110,14 @@ class _ScannerPageState extends ConsumerState debugPrint('Image : ${product["image_url"]}'); final productStruct = ProductStruct( id: int.parse(product["id"]), - name: product["product_name"], image: product["image_thumb_url"], - description: product["categories"], + name: product["generic_name"], + description: product["product_name"], quantity: product["quantity"], ); + Box productStore = objectboxManager.store + .box(); + productStore.put(productStruct.toEntity); //show dialog await showDialog( barrierDismissible: false, @@ -179,10 +185,13 @@ class _ScannerPageState extends ConsumerState final productStruct = ProductStruct( id: int.parse(product["id"]), image: product["image_thumb_url"], - name: product["product_name"], - description: product["categories"], + name: product["generic_name"], + description: product["product_name"], quantity: product["quantity"], ); + Box productStore = objectboxManager.store + .box(); + productStore.put(productStruct.toEntity); //show dialog await showDialog( barrierDismissible: false, @@ -234,6 +243,7 @@ class _ScannerPageState extends ConsumerState @override Widget build(BuildContext context) { return Scaffold( + backgroundColor: AppTheme.of(context).primaryBackground, appBar: AppBar( backgroundColor: AppTheme.of(context).primaryBackground, automaticallyImplyLeading: false, @@ -263,7 +273,7 @@ class _ScannerPageState extends ConsumerState elevation: 0.0, ), body: loading - ? Center(child: CircularProgressIndicator()) + ? Center(child: LoadingProgressComponent()) : SizedBox( width: double.infinity, height: double.infinity,