diff --git a/lib/src/app.dart b/lib/src/app.dart index 18300508eac9af68dfca443a3761e712713495d3..618b1bb91fc8993d70614f6c1b07e06ae2d3a67a 100644 --- a/lib/src/app.dart +++ b/lib/src/app.dart @@ -1,5 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/widgets.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:mobile_apps/src/repositories/keranjang_sqlite_repo.dart'; import 'package:mobile_apps/src/services/auth_service.dart'; import 'package:provider/provider.dart'; import 'package:screenshot/screenshot.dart'; @@ -12,20 +14,23 @@ class App extends StatelessWidget { Widget build(BuildContext context) { var screenshotController = ScreenshotController(); - return MultiProvider( - providers: [ - // Add service that can be accessed globally here - Provider<AuthService>(create: (_) => AuthService()), - Provider.value(value: screenshotController), - ], - child: Screenshot( - controller: screenshotController, - child: MaterialApp( - theme: AppTheme.light(), - themeMode: ThemeMode.light, - darkTheme: AppTheme.dark(), - onGenerateRoute: AppRoute.router.generator, - initialRoute: AppRoute.home, + return RepositoryProvider( + create: (context) => const KeranjangDbRepo(), + child: MultiProvider( + providers: [ + // Add service that can be accessed globally here + Provider<AuthService>(create: (_) => AuthService()), + Provider.value(value: screenshotController), + ], + child: Screenshot( + controller: screenshotController, + child: MaterialApp( + theme: AppTheme.light(), + themeMode: ThemeMode.light, + darkTheme: AppTheme.dark(), + onGenerateRoute: AppRoute.router.generator, + initialRoute: AppRoute.home, + ), ), ), ); diff --git a/lib/src/interfaces/interface_keranjang_repository.dart b/lib/src/interfaces/interface_keranjang_repository.dart new file mode 100644 index 0000000000000000000000000000000000000000..c66ade95348d2465894e1cdde483cd9f0a5b04be --- /dev/null +++ b/lib/src/interfaces/interface_keranjang_repository.dart @@ -0,0 +1,25 @@ +import 'package:flutter/material.dart'; +import 'package:mobile_apps/src/model_db/keranjang_item.dart'; +import 'package:mobile_apps/src/model_db/keranjang_supplier.dart'; + +abstract class KeranjangRepo { + const KeranjangRepo(); + Future<List<KeranjangItem>> fetchKeranjangItemFromSupplier( + {@required String idSupplier}); + Future<List<KeranjangSupplier>> fetchKeranjangSupplier(); + Future<int> insertSupplier(String idSupplier); + Future<bool> isSupplierAdded(String idSupplier); + Future<int> insertItemFromSupplier( + {@required String idSupplier, + @required String idItem, + @required int jumlah}); + Future<int> updateItemFromSupplier( + {@required String idSupplier, + @required String idItem, + @required int jumlah}); + Future<void> updateItemBatch( + {@required String idSupplier, @required List<Map<String, int>> items}); + Future<int> deleteItem( + {@required String idSupplier, @required List<String> idItems}); + Future<int> deleteSupplier({@required String idSupplier}); +} diff --git a/lib/src/model_db/keranjang_item.dart b/lib/src/model_db/keranjang_item.dart new file mode 100644 index 0000000000000000000000000000000000000000..81e9e271109c2c0276104a41d9bd53af50e3bb56 --- /dev/null +++ b/lib/src/model_db/keranjang_item.dart @@ -0,0 +1,26 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; + +part 'keranjang_item.g.dart'; + +@JsonSerializable(nullable: false) +class KeranjangItem extends Equatable { + final String id; + + final int jumlah; + + @JsonKey(name: 'id_supplier') + final String idSupplier; + + const KeranjangItem({@required this.id, @required this.idSupplier, + @required this.jumlah}); + + factory KeranjangItem.fromJson(Map<String, dynamic> json) => + _$KeranjangItemFromJson(json); + + Map<String, dynamic> toJson() => _$KeranjangItemToJson(this); + + @override + List<Object> get props => [idSupplier, id, jumlah]; +} \ No newline at end of file diff --git a/lib/src/model_db/keranjang_item.g.dart b/lib/src/model_db/keranjang_item.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..6c72c4170939671aaf678db523041a8a474c3984 --- /dev/null +++ b/lib/src/model_db/keranjang_item.g.dart @@ -0,0 +1,22 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'keranjang_item.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +KeranjangItem _$KeranjangItemFromJson(Map<String, dynamic> json) { + return KeranjangItem( + id: json['id'] as String, + idSupplier: json['id_supplier'] as String, + jumlah: json['jumlah'] as int, + ); +} + +Map<String, dynamic> _$KeranjangItemToJson(KeranjangItem instance) => + <String, dynamic>{ + 'id': instance.id, + 'jumlah': instance.jumlah, + 'id_supplier': instance.idSupplier, + }; diff --git a/lib/src/model_db/keranjang_supplier.dart b/lib/src/model_db/keranjang_supplier.dart new file mode 100644 index 0000000000000000000000000000000000000000..66e26936bf379818af8e4236dc1d697adcb1f871 --- /dev/null +++ b/lib/src/model_db/keranjang_supplier.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +part 'keranjang_supplier.g.dart'; + +@JsonSerializable(nullable: false) +class KeranjangSupplier { + final String id; + + const KeranjangSupplier({@required this.id}); + + factory KeranjangSupplier.fromJson(Map<String, dynamic> json) => + _$KeranjangSupplierFromJson(json); + + Map<String, dynamic> toJson() => _$KeranjangSupplierToJson(this); +} \ No newline at end of file diff --git a/lib/src/model_db/keranjang_supplier.g.dart b/lib/src/model_db/keranjang_supplier.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..830c0dcf76d8ff51096c857f00d55f3426bfb5f1 --- /dev/null +++ b/lib/src/model_db/keranjang_supplier.g.dart @@ -0,0 +1,18 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'keranjang_supplier.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +KeranjangSupplier _$KeranjangSupplierFromJson(Map<String, dynamic> json) { + return KeranjangSupplier( + id: json['id'] as String, + ); +} + +Map<String, dynamic> _$KeranjangSupplierToJson(KeranjangSupplier instance) => + <String, dynamic>{ + 'id': instance.id, + }; diff --git a/lib/src/models/keranjang_item_firestore.dart b/lib/src/models/keranjang_item_firestore.dart new file mode 100644 index 0000000000000000000000000000000000000000..24634b9d7e00352eeb9acfadffccec5c9fa89d87 --- /dev/null +++ b/lib/src/models/keranjang_item_firestore.dart @@ -0,0 +1,17 @@ + +import 'package:flutter/material.dart'; +import 'package:json_annotation/json_annotation.dart'; +import 'package:mobile_apps/src/models/product_supplier.dart'; +part 'keranjang_item_firestore.g.dart'; +@JsonSerializable(explicitToJson: true, nullable: false) +class KeranjangItemFirestore { + final ProductSupplier productSupplier; + final int jumlah; + + KeranjangItemFirestore({@required this.productSupplier, @required this.jumlah}); + + factory KeranjangItemFirestore.fromJson(Map<String, dynamic> json) => + _$KeranjangItemFirestoreFromJson(json); + + Map<String, dynamic> toJson() => _$KeranjangItemFirestoreToJson(this); +} \ No newline at end of file diff --git a/lib/src/models/keranjang_item_firestore.g.dart b/lib/src/models/keranjang_item_firestore.g.dart new file mode 100644 index 0000000000000000000000000000000000000000..34d9c7dacd5a472f2bd1eef8d5cf83da528338a3 --- /dev/null +++ b/lib/src/models/keranjang_item_firestore.g.dart @@ -0,0 +1,23 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'keranjang_item_firestore.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +KeranjangItemFirestore _$KeranjangItemFirestoreFromJson( + Map<String, dynamic> json) { + return KeranjangItemFirestore( + productSupplier: ProductSupplier.fromJson( + json['productSupplier'] as Map<String, dynamic>), + jumlah: json['jumlah'] as int, + ); +} + +Map<String, dynamic> _$KeranjangItemFirestoreToJson( + KeranjangItemFirestore instance) => + <String, dynamic>{ + 'productSupplier': instance.productSupplier.toJson(), + 'jumlah': instance.jumlah, + }; diff --git a/lib/src/models/product_supplier.dart b/lib/src/models/product_supplier.dart index 034facc26aba48f9bb999e1756b5db98df0c4f82..1332f2e90dc07ea7ebb476aed0b7f4485399b99a 100644 --- a/lib/src/models/product_supplier.dart +++ b/lib/src/models/product_supplier.dart @@ -1,10 +1,11 @@ +import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:mobile_apps/src/common/CustomSerializer.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; part 'product_supplier.g.dart'; @JsonSerializable() -class ProductSupplier { +class ProductSupplier extends Equatable { @JsonKey(name: 'item', fromJson: CustomSerializer.docReferenceToPath) final String idItem; final String admin; @@ -17,10 +18,22 @@ class ProductSupplier { @JsonKey(fromJson: CustomSerializer.timeStampToDateTime) final DateTime timestamp; - ProductSupplier({this.id, this.idItem, this.admin, this.spesifikasi, this.stok, this.timestamp, this.manfaat, this.harga}); + ProductSupplier( + {this.id, + this.idItem, + this.admin, + this.spesifikasi, + this.stok, + this.timestamp, + this.manfaat, + this.harga}); factory ProductSupplier.fromJson(Map<String, dynamic> json) => _$ProductSupplierFromJson(json); Map<String, dynamic> toJson() => _$ProductSupplierToJson(this); -} \ No newline at end of file + + @override + List<Object> get props => + [idItem, admin, spesifikasi, stok, manfaat, id, harga]; +} diff --git a/lib/src/models/supplier.dart b/lib/src/models/supplier.dart index c403f129a924405c6fe325b3b996b113e4526a34..9fecb013e7ff599d37ba49f62f624b149c45667f 100644 --- a/lib/src/models/supplier.dart +++ b/lib/src/models/supplier.dart @@ -1,3 +1,4 @@ +import 'package:equatable/equatable.dart'; import 'package:json_annotation/json_annotation.dart'; import 'package:mobile_apps/src/common/CustomSerializer.dart'; import 'package:cloud_firestore/cloud_firestore.dart'; @@ -5,7 +6,7 @@ import 'package:cloud_firestore/cloud_firestore.dart'; part 'supplier.g.dart'; @JsonSerializable() -class Supplier { +class Supplier extends Equatable { final String id; final String alamat; final String nama; @@ -31,4 +32,7 @@ class Supplier { _$SupplierFromJson(json); Map<String, dynamic> toJson() => _$SupplierToJson(this); + + @override + List<Object> get props => [nama]; } diff --git a/lib/src/repositories/keranjang_sqlite_repo.dart b/lib/src/repositories/keranjang_sqlite_repo.dart new file mode 100644 index 0000000000000000000000000000000000000000..68aef062fc552b7afc9fcbfc80344171f12fc82e --- /dev/null +++ b/lib/src/repositories/keranjang_sqlite_repo.dart @@ -0,0 +1,39 @@ +import 'dart:io'; + +import 'package:mobile_apps/src/interfaces/interface_keranjang_repository.dart'; +import 'package:sqflite/sqflite.dart'; + +class KeranjangDbRepo implements KeranjangRepo { + static Database _database; + + static const supplierTable = 'SupplierTable'; + static const idSupplierCol = 'id'; + static const itemTable = 'ItemTable'; + static const idItemCol = 'id'; + static const jumlahItemCol = 'jumlah'; + + const KeranjangDbRepo(); + Future<Database> get database async { + _database ??= await initializeDatabase(); + return _database; + } + + Future<Database> initializeDatabase() async { + // Get the directory path for both Android and iOS to store database. + final directory = await getDatabasesPath(); + final path = '${directory}Keranjang.db'; + try { + await Directory(directory).create(recursive: true); + } catch (_) {} + // Open/create the database at a given path + var keranjangDb = await openDatabase(path, version: 1, onCreate: _onCreate); + return keranjangDb; + } + + void _onCreate(Database db, int version) async { + // Database is created, create the table + await db.execute( + 'CREATE TABLE ? (? TEXT PRIMARY KEY)', [supplierTable, idSupplierCol]); + await db.execute('CREATE TABLE ? (? TEXT PRIMARY KEY, ? INTEGER)', [itemTable, idItemCol]); + } +} \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/KeranjangScreen.dart b/lib/src/screens/keranjang_screen/KeranjangScreen.dart new file mode 100644 index 0000000000000000000000000000000000000000..eb20be9e8fc8a46c9536027ae12600f16c271a1c --- /dev/null +++ b/lib/src/screens/keranjang_screen/KeranjangScreen.dart @@ -0,0 +1,218 @@ +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:intl/intl.dart'; +import 'package:mobile_apps/src/common/constants.dart'; +import 'package:mobile_apps/src/components/appbar/sigapappbar.dart'; +import 'package:mobile_apps/src/components/cached_image/cached_image_network.dart'; +import 'package:mobile_apps/src/interfaces/interface_keranjang_repository.dart'; +import 'package:mobile_apps/src/models/keranjang_item_firestore.dart'; +import 'package:mobile_apps/src/models/supplier.dart'; +import 'package:mobile_apps/src/repositories/item_repository.dart'; +import 'package:mobile_apps/src/repositories/product_supplier_repository.dart'; +import 'package:mobile_apps/src/repositories/supplier_repository.dart'; +import 'package:mobile_apps/src/screens/keranjang_screen/keranjang_item_bloc/bloc.dart'; +import 'package:mobile_apps/src/screens/keranjang_screen/keranjang_supplier/bloc.dart'; +import 'package:mobile_apps/src/screens/keranjang_screen/product_supplier_bloc/bloc.dart'; +import 'package:mobile_apps/src/screens/keranjang_screen/supplier_bloc/supplier_bloc.dart'; + +class KeranjangScreen extends StatelessWidget { + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: SigapAppBar( + title: 'Keranjang Belanja', + ), + body: BlocProvider<KeranjangSupplierBloc>( + create: (context) => KeranjangSupplierBloc( + keranjangRepo: RepositoryProvider.of<KeranjangRepo>(context), + supplierBloc: SupplierBloc(SupplierRepository())) + ..add(const FetchKeranjangSupplier()), + child: Padding( + padding: const EdgeInsets.symmetric( + vertical: MarginConstants.verticalFromScreen, + horizontal: MarginConstants.horizontalFromScreen), + child: Column( + children: <Widget>[ + Text( + 'Rekap Produk', + style: TextStyle(fontWeight: FontWeight.w800, fontSize: 18), + ), + RepositoryProvider<ProductSupplierRepository>( + create: (context) => ProductSupplierRepository(), + child: const _BlocKeranjang()), + ], + ), + ), + ), + ); + } +} + +class _BlocKeranjang extends StatelessWidget { + const _BlocKeranjang({Key key}) : super(key: key); + @override + Widget build(BuildContext context) { + return BlocBuilder<KeranjangSupplierBloc, KeranjangSupplierState>( + builder: (context, state) { + if (state is KeranjangSupplierLoaded) { + return ListView.builder( + itemCount: state.supplier.length, + physics: BouncingScrollPhysics(), + itemBuilder: (BuildContext context, int index) { + return BlocProvider<ProductSupplierBloc>( + create: (context) => ProductSupplierBloc( + RepositoryProvider.of<ProductSupplierRepository>(context)), + child: _BodyKeranjangSupplier( + keranjangSupplier: state.supplier[index], + ), + ); + }); + } + return null; + }); + } +} + +class _BodyKeranjangSupplier extends StatelessWidget { + final Supplier keranjangSupplier; + + const _BodyKeranjangSupplier({Key key, this.keranjangSupplier}) + : super(key: key); + @override + Widget build(BuildContext context) { + return BlocProvider<KeranjangItemBloc>( + create: (context) => KeranjangItemBloc( + keranjangRepo: RepositoryProvider.of<KeranjangRepo>(context), + productSupplierBloc: BlocProvider.of<ProductSupplierBloc>(context)), + child: Column( + mainAxisAlignment: MainAxisAlignment.spaceAround, + crossAxisAlignment: CrossAxisAlignment.start, + children: <Widget>[ + Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: <Widget>[ + Row( + mainAxisAlignment: MainAxisAlignment.start, + children: <Widget>[ + Icon(Icons.work), + Text( + keranjangSupplier.nama, + style: TextStyle(fontWeight: FontWeight.w600, fontSize: 16), + ) + ], + ), + Text( + 'Lihat Supplier', + style: TextStyle(color: ColorConstants.redPrimary), + ) + ], + ), + Builder( + builder: (BuildContext context) { + return BlocBuilder<KeranjangItemBloc, KeranjangItemState>( + builder: (context, state) { + if (state is FetchItemBySupplierSuccess) { + _BodyItem( + keranjangItemFirestores: state.items, + total: state.total, + ); + } + return null; + }); + }, + ) + ], + ), + ); + } +} + +class _BodyItem extends StatelessWidget { + final List<KeranjangItemFirestore> keranjangItemFirestores; + final double total; + + const _BodyItem( + {Key key, @required this.keranjangItemFirestores, @required this.total}) + : super(key: key); + @override + Widget build(BuildContext context) { + return Column( + children: <Widget>[ + _BodyItemList( + keranjangItemFirestores: keranjangItemFirestores, + ), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: <Widget>[ + Text( + 'Subtotal', + style: TextStyle(fontWeight: FontWeight.w600), + ), + Text( + NumberFormat.currency(symbol: 'Rp', decimalDigits: 0) + .format(total), + style: TextStyle(fontWeight: FontWeight.w600), + ) + ], + ) + ], + ); + } +} + +class _BodyItemList extends StatelessWidget { + final List<KeranjangItemFirestore> keranjangItemFirestores; + + const _BodyItemList({Key key, @required this.keranjangItemFirestores}) + : super(key: key); + @override + Widget build(BuildContext context) { + return ListView.builder( + itemCount: keranjangItemFirestores.length, + itemBuilder: (BuildContext context, int index) { + return _BodyItemTiles( + keranjangItemFirestore: keranjangItemFirestores[index], + ); + }); + } +} + +class _BodyItemTiles extends StatelessWidget { + final KeranjangItemFirestore keranjangItemFirestore; + + const _BodyItemTiles({Key key, this.keranjangItemFirestore}) + : super(key: key); + @override + Widget build(BuildContext context) { + final itemRepo = ItemRepository(); + return Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: <Widget>[ + StreamBuilder<DocumentSnapshot>( + stream: itemRepo + .itemDetail(keranjangItemFirestore.productSupplier.idItem) + .snapshots(), + builder: (context, snapshot) { + final item = itemRepo.item(snapshot); + return Row( + children: <Widget>[ + CustomCachedNetworkImage(imageUrl: item.url), + Column( + children: <Widget>[ + Text(item.nama), + Text( + NumberFormat.currency(symbol: '', decimalDigits: 0) + .format( + keranjangItemFirestore.productSupplier.harga), + ) + ], + ) + ], + ); + }) + ], + ); + } +} diff --git a/lib/src/screens/keranjang_screen/keranjang_item_bloc/bloc.dart b/lib/src/screens/keranjang_screen/keranjang_item_bloc/bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..95ea8632c3e7335cddcea913a0ddc57f65540625 --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_item_bloc/bloc.dart @@ -0,0 +1,3 @@ +export 'keranjang_item_bloc.dart'; +export 'keranjang_item_event.dart'; +export 'keranjang_item_state.dart'; \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_bloc.dart b/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..b5f8066a054dc9014bd84bea38014853ee315a55 --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_bloc.dart @@ -0,0 +1,115 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:mobile_apps/src/interfaces/interface_keranjang_repository.dart'; +import 'package:mobile_apps/src/models/keranjang_item_firestore.dart'; +import 'package:mobile_apps/src/screens/keranjang_screen/product_supplier_bloc/bloc.dart'; +import './bloc.dart'; + +class KeranjangItemBloc extends Bloc<KeranjangItemEvent, KeranjangItemState> { + final KeranjangRepo keranjangRepo; + final ProductSupplierBloc productSupplierBloc; + StreamSubscription productSupplierSubs; + + KeranjangItemBloc( + {@required this.keranjangRepo, @required this.productSupplierBloc}) + : assert(keranjangRepo != null), + assert(productSupplierBloc != null) { + productSupplierSubs = + productSupplierBloc.listen((ProductSupplierState state) { + if (state is ProductSupplierLoaded) { + UpdateItemFromFirestore(state.productSupplier, state.supplierId); + } + }); + } + @override + KeranjangItemState get initialState => const InitialKeranjangItemState(); + + @override + Stream<KeranjangItemState> mapEventToState( + KeranjangItemEvent event, + ) async* { + yield const KeranjangItemLoading(); + try { + if (event is FetchKeranjangItem) { + final items = await keranjangRepo.fetchKeranjangItemFromSupplier( + idSupplier: event.idSupplier); + productSupplierBloc.add(FetchProductSupplier(event.idSupplier, items)); + } else if (event is InsertItem) { + if (!await keranjangRepo.isSupplierAdded(event.idSupplier)) { + await keranjangRepo.insertSupplier(event.idSupplier); + } + await keranjangRepo.insertItemFromSupplier( + idSupplier: event.idSupplier, + idItem: event.idItem, + jumlah: event.jumlah); + yield const InsertItemSuccess(); + } else if (event is UpdateItem) { + await keranjangRepo.updateItemFromSupplier( + idSupplier: event.idSupplier, + idItem: event.idItem, + jumlah: event.jumlah); + yield const UpdateItemSuccess(); + final items = await keranjangRepo.fetchKeranjangItemFromSupplier( + idSupplier: event.idSupplier); + productSupplierBloc.add(FetchProductSupplier(event.idSupplier, items)); + } else if (event is DeleteItem) { + await keranjangRepo + .deleteItem(idSupplier: event.idSupplier, idItems: [event.idItem]); + yield DeleteItemSuccess(idSupplier: event.idSupplier); + } else if (event is UpdateItemFromFirestore) { + //Find deleted item + final items = await keranjangRepo.fetchKeranjangItemFromSupplier( + idSupplier: event.supplierId); + final itemSet = {...items.map((e) => e.id)}; + final itemFireSet = {...event.productSupplier.map((e) => e.id)}; + final deletedItem = itemSet.difference(itemFireSet); + if (deletedItem.isNotEmpty) { + await keranjangRepo.deleteItem( + idSupplier: event.supplierId, idItems: deletedItem.toList()); + yield DeleteItemSuccess(idSupplier: event.supplierId); + } + + //Find newest stok + final zeroStok = <String>[]; + final stockSmallerThanKeranjang = <Map<String, int>>[]; + final itemMap = {for (var e in items) e.id: e}; + for (var i = 0; i < event.productSupplier.length; i++) { + if (event.productSupplier[i].stok < + itemMap[event.productSupplier[i].id].jumlah) { + if (event.productSupplier[i].stok == 0) { + zeroStok.add(event.productSupplier[i].id); + } else { + stockSmallerThanKeranjang.add( + {event.productSupplier[i].id: event.productSupplier[i].stok}); + } + } + } + if (zeroStok.isNotEmpty) { + await keranjangRepo.deleteItem( + idSupplier: event.supplierId, idItems: zeroStok); + } + if (stockSmallerThanKeranjang.isNotEmpty) { + await keranjangRepo.updateItemBatch( + idSupplier: event.supplierId, items: stockSmallerThanKeranjang); + } + + final result = <KeranjangItemFirestore>[ + ...event.productSupplier.where((element) => element.stok != 0).map( + (e) => KeranjangItemFirestore( + productSupplier: e, + jumlah: itemMap[e.id].jumlah > e.stok + ? e.stok + : itemMap[e.id].jumlah)) + ]; + final total = result.fold( + 0, + (previousValue, element) => + previousValue + element.jumlah * element.productSupplier.harga); + yield FetchItemBySupplierSuccess(result, total); + } + } catch (e) { + yield KeranjangItemError(e.toString()); + } + } +} diff --git a/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_event.dart b/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_event.dart new file mode 100644 index 0000000000000000000000000000000000000000..2edaa83c1cb7cf232ade4a2da7c15b012e5e1309 --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_event.dart @@ -0,0 +1,63 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:mobile_apps/src/models/product_supplier.dart'; + +abstract class KeranjangItemEvent extends Equatable { + const KeranjangItemEvent(); +} +class FetchKeranjangItem extends KeranjangItemEvent { + + final String idSupplier; + const FetchKeranjangItem(this.idSupplier); + + @override + List<Object> get props => [idSupplier]; +} + +class InsertItem extends KeranjangItemEvent { + final String idItem; + final String idSupplier; + final int jumlah; + InsertItem( + {@required this.idItem, @required this.idSupplier, @required this.jumlah}) + : assert(idSupplier != null), + assert(idItem != null), + assert(jumlah != null); + + @override + List<Object> get props => [idItem, idSupplier, jumlah]; +} + +class UpdateItem extends KeranjangItemEvent { + final String idItem; + final String idSupplier; + final int jumlah; + UpdateItem( + {@required this.idItem, @required this.idSupplier, @required this.jumlah}) + : assert(idSupplier != null), + assert(idItem != null), + assert(jumlah != null); + + @override + List<Object> get props => [idItem, idSupplier, jumlah]; +} + +class DeleteItem extends KeranjangItemEvent { + final String idItem; + final String idSupplier; + + DeleteItem(this.idItem, this.idSupplier); + + @override + List<Object> get props => [idItem, idSupplier]; + +} + +class UpdateItemFromFirestore extends KeranjangItemEvent { + final List<ProductSupplier> productSupplier; + final String supplierId; + UpdateItemFromFirestore(this.productSupplier, this.supplierId); + + @override + List<Object> get props => [productSupplier]; +} diff --git a/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_state.dart b/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..6d785777a4ab57ae73421125e596fb62ebc59e44 --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_item_bloc/keranjang_item_state.dart @@ -0,0 +1,57 @@ +import 'package:equatable/equatable.dart'; +import 'package:flutter/material.dart'; +import 'package:mobile_apps/src/models/keranjang_item_firestore.dart'; + +abstract class KeranjangItemState extends Equatable { + const KeranjangItemState(); +} + +class InitialKeranjangItemState extends KeranjangItemState { + const InitialKeranjangItemState(); + @override + List<Object> get props => []; +} + +class InsertItemSuccess extends KeranjangItemState { + const InsertItemSuccess(); + @override + List<Object> get props => []; +} + +class UpdateItemSuccess extends KeranjangItemState { + const UpdateItemSuccess(); + @override + List<Object> get props => []; +} + +class DeleteItemSuccess extends KeranjangItemState { + final String idSupplier; + const DeleteItemSuccess({@required this.idSupplier}) + : assert(idSupplier != null); + @override + List<Object> get props => []; +} + +class FetchItemBySupplierSuccess extends KeranjangItemState { + final List<KeranjangItemFirestore> items; + final double total; + FetchItemBySupplierSuccess(this.items, this.total); + + @override + List<Object> get props => [items]; +} + +class KeranjangItemLoading extends KeranjangItemState { + const KeranjangItemLoading(); + + @override + List<Object> get props => []; +} + +class KeranjangItemError extends KeranjangItemState { + final String error; + + const KeranjangItemError(this.error); + @override + List<Object> get props => []; +} diff --git a/lib/src/screens/keranjang_screen/keranjang_supplier/bloc.dart b/lib/src/screens/keranjang_screen/keranjang_supplier/bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..e09f5b1dd6c792921e6725e6ed9cf504b3ff3a76 --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_supplier/bloc.dart @@ -0,0 +1,3 @@ +export 'keranjang_supplier_bloc.dart'; +export 'keranjang_supplier_event.dart'; +export 'keranjang_supplier_state.dart'; \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_bloc.dart b/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..10a76a988146583ca23402f4e90d53282b282296 --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_bloc.dart @@ -0,0 +1,55 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:flutter/material.dart'; +import 'package:mobile_apps/src/interfaces/interface_keranjang_repository.dart'; +import 'package:mobile_apps/src/screens/keranjang_screen/supplier_bloc/bloc.dart'; +import './bloc.dart'; + +class KeranjangSupplierBloc + extends Bloc<KeranjangSupplierEvent, KeranjangSupplierState> { + final KeranjangRepo keranjangRepo; + StreamSubscription keranjangItemSubs; + final SupplierBloc supplierBloc; + StreamSubscription supplierSubs; + + KeranjangSupplierBloc( + {@required this.keranjangRepo, @required this.supplierBloc}) + : assert(keranjangRepo != null), + assert(supplierBloc != null) { + supplierSubs = supplierBloc.listen((SupplierState state) { + if (state is SupplierLoaded) { + add(UpdateSupplier(state.supplier)); + } + }); + } + @override + KeranjangSupplierState get initialState => + const InitialKeranjangSupplierState(); + + @override + Stream<KeranjangSupplierState> mapEventToState( + KeranjangSupplierEvent event, + ) async* { + try { + if (event is FetchKeranjangSupplier) { + final suppliers = await keranjangRepo.fetchKeranjangSupplier(); + supplierBloc.add(FetchSuppliers([...suppliers.map((e) => e.id)])); + } else if (event is DeletetSupplier) { + final suppliers = await keranjangRepo.fetchKeranjangSupplier(); + supplierBloc.add(FetchSuppliers([...suppliers.map((e) => e.id)])); + yield const DeleteKeranjangSupplierSuccess(); + } else if (event is UpdateSupplier) { + yield KeranjangSupplierLoaded(event.suppliers); + } + } catch (e) { + yield KeranjangSupplierError(e.toString()); + } + } + + @override + Future<void> close() { + keranjangItemSubs?.cancel(); + supplierBloc.close(); + return super.close(); + } +} diff --git a/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_event.dart b/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_event.dart new file mode 100644 index 0000000000000000000000000000000000000000..1265fde81bc8202e98b2bb5bc4f07fb9f1a38223 --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_event.dart @@ -0,0 +1,31 @@ +import 'package:equatable/equatable.dart'; +import 'package:mobile_apps/src/models/supplier.dart'; + +abstract class KeranjangSupplierEvent extends Equatable { + const KeranjangSupplierEvent(); +} +class FetchKeranjangSupplier extends KeranjangSupplierEvent { + + const FetchKeranjangSupplier(); + + @override + List<Object> get props => []; +} + +class DeletetSupplier extends KeranjangSupplierEvent { + final String id; + + const DeletetSupplier(this.id); + + @override + List<Object> get props => []; +} + +class UpdateSupplier extends KeranjangSupplierEvent { + final List<Supplier> suppliers; + + UpdateSupplier(this.suppliers); + + @override + List<Object> get props => [suppliers]; +} diff --git a/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_state.dart b/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..1568d30cc14cfc02b4f4a5c328f1de997d8bbcad --- /dev/null +++ b/lib/src/screens/keranjang_screen/keranjang_supplier/keranjang_supplier_state.dart @@ -0,0 +1,34 @@ +import 'package:equatable/equatable.dart'; +import 'package:mobile_apps/src/models/supplier.dart'; + +abstract class KeranjangSupplierState extends Equatable { + const KeranjangSupplierState(); +} + +class InitialKeranjangSupplierState extends KeranjangSupplierState { + const InitialKeranjangSupplierState(); + @override + List<Object> get props => []; +} +class KeranjangSupplierLoaded extends KeranjangSupplierState { + final List<Supplier> supplier; + + KeranjangSupplierLoaded(this.supplier); + + @override + List<Object> get props => [supplier]; +} + +class KeranjangSupplierError extends KeranjangSupplierState { + final String error; + const KeranjangSupplierError(this.error); + @override + List<Object> get props => [error]; +} + +class DeleteKeranjangSupplierSuccess extends KeranjangSupplierState { + const DeleteKeranjangSupplierSuccess(); + @override + List<Object> get props => throw []; + +} \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/product_supplier_bloc/bloc.dart b/lib/src/screens/keranjang_screen/product_supplier_bloc/bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..b76eb2ee7b2474de4927cad1f7b133c77dca4c45 --- /dev/null +++ b/lib/src/screens/keranjang_screen/product_supplier_bloc/bloc.dart @@ -0,0 +1,3 @@ +export 'product_supplier_bloc.dart'; +export 'product_supplier_event.dart'; +export 'product_supplier_state.dart'; \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_bloc.dart b/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..f70bef6dad0945dbcdd37d9e9015e7253d52af6d --- /dev/null +++ b/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_bloc.dart @@ -0,0 +1,55 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:mobile_apps/src/models/product_supplier.dart'; +import 'package:mobile_apps/src/repositories/product_supplier_repository.dart'; +import './bloc.dart'; + +class ProductSupplierBloc + extends Bloc<ProductSupplierEvent, ProductSupplierState> { + final ProductSupplierRepository productSupplierRepository; + StreamSubscription _productSupplierSubscription; + ProductSupplierBloc(this.productSupplierRepository); + + @override + ProductSupplierState get initialState => ProductSupplierLoading(); + + @override + Stream<ProductSupplierState> mapEventToState( + ProductSupplierEvent event, + ) async* { + if (event is FetchProductSupplier) { + yield* _mapFetchProductSupplierToState(event); + } else if (event is ProductSupplierUpdated) { + yield* _mapProductSupplierUpdated(event); + } + } + + Stream<ProductSupplierState> _mapFetchProductSupplierToState( + FetchProductSupplier event) async* { + await _productSupplierSubscription?.cancel(); + _productSupplierSubscription = productSupplierRepository + .allProductsFromSupplier(idSupplier: event.idSupplier) + .where(FieldPath.documentId, + whereIn: [...event.idItems.map((e) => e.id)]) + .snapshots() + .map((snapshot) { + return snapshot.documents + .map((e) => ProductSupplier.fromJson(e.data)) + .toList(); + }) + .listen((products) => + add(ProductSupplierUpdated(products, event.idSupplier))); + } + + Stream<ProductSupplierState> _mapProductSupplierUpdated( + ProductSupplierUpdated event) async* { + yield ProductSupplierLoaded(event.supplierId, event.products); + } + + @override + Future<void> close() { + _productSupplierSubscription?.cancel(); + return super.close(); + } +} diff --git a/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_event.dart b/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_event.dart new file mode 100644 index 0000000000000000000000000000000000000000..a8a81415c893f3593dea48dd9c7f329a65905375 --- /dev/null +++ b/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_event.dart @@ -0,0 +1,27 @@ +import 'package:equatable/equatable.dart'; +import 'package:mobile_apps/src/model_db/keranjang_item.dart'; +import 'package:mobile_apps/src/models/product_supplier.dart'; + +abstract class ProductSupplierEvent extends Equatable { + const ProductSupplierEvent(); +} + +class FetchProductSupplier extends ProductSupplierEvent { + final String idSupplier; + final List<KeranjangItem> idItems; + + FetchProductSupplier(this.idSupplier, this.idItems); + + @override + List<Object> get props => [idSupplier, idItems]; +} + +class ProductSupplierUpdated extends ProductSupplierEvent { + final List<ProductSupplier> products; + final String supplierId; + ProductSupplierUpdated(this.products, this.supplierId); + + @override + List<Object> get props => [products]; + +} \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_state.dart b/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..0a4d13ef83c1eed52618d98d053b34de5c53091b --- /dev/null +++ b/lib/src/screens/keranjang_screen/product_supplier_bloc/product_supplier_state.dart @@ -0,0 +1,27 @@ +import 'package:equatable/equatable.dart'; +import 'package:mobile_apps/src/models/product_supplier.dart'; + +abstract class ProductSupplierState extends Equatable { + const ProductSupplierState(); +} + +class ProductSupplierLoading extends ProductSupplierState { + @override + List<Object> get props => []; +} + +class ProductSupplierLoaded extends ProductSupplierState { + final List<ProductSupplier> productSupplier; + final String supplierId; + + ProductSupplierLoaded(this.supplierId, [this.productSupplier = const []]); + + @override + List<Object> get props => [productSupplier]; +} + +class ProductNotLoaded extends ProductSupplierState { + @override + List<Object> get props => []; + +} \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/supplier_bloc/bloc.dart b/lib/src/screens/keranjang_screen/supplier_bloc/bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..039007e2b047721ef71dc139d877e3278d189eac --- /dev/null +++ b/lib/src/screens/keranjang_screen/supplier_bloc/bloc.dart @@ -0,0 +1,3 @@ +export 'supplier_bloc.dart'; +export 'supplier_event.dart'; +export 'supplier_state.dart'; \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/supplier_bloc/supplier_bloc.dart b/lib/src/screens/keranjang_screen/supplier_bloc/supplier_bloc.dart new file mode 100644 index 0000000000000000000000000000000000000000..9716a7c95a7944588391d353e6d2d2c51aaa8bd0 --- /dev/null +++ b/lib/src/screens/keranjang_screen/supplier_bloc/supplier_bloc.dart @@ -0,0 +1,46 @@ +import 'dart:async'; +import 'package:bloc/bloc.dart'; +import 'package:cloud_firestore/cloud_firestore.dart'; +import 'package:mobile_apps/src/models/supplier.dart'; +import 'package:mobile_apps/src/repositories/supplier_repository.dart'; +import './bloc.dart'; + +class SupplierBloc extends Bloc<SupplierEvent, SupplierState> { + final SupplierRepository supplierRepository; + StreamSubscription _suppliersSubscription; + SupplierBloc(this.supplierRepository); + @override + SupplierState get initialState => InitialSupplierState(); + + @override + Stream<SupplierState> mapEventToState( + SupplierEvent event, + ) async* { + if (event is FetchSuppliers) { + yield* _mapFetchSupplierToState(event); + } else if (event is ProductSupplierUpdated) { + yield* _mapSupplierUpdated(event); + } + } + + Stream<SupplierState> _mapFetchSupplierToState(FetchSuppliers event) async* { + await _suppliersSubscription?.cancel(); + _suppliersSubscription = supplierRepository.supplierCollections + .where(FieldPath.documentId, whereIn: event.idSuppliers) + .snapshots() + .map((snapshot) { + return snapshot.documents.map((e) => Supplier.fromJson(e.data)).toList(); + }).listen((products) => add(ProductSupplierUpdated(products))); + } + + Stream<SupplierState> _mapSupplierUpdated( + ProductSupplierUpdated event) async* { + yield SupplierLoaded(event.suppliers); + } + + @override + Future<void> close() { + _suppliersSubscription?.cancel(); + return super.close(); + } +} diff --git a/lib/src/screens/keranjang_screen/supplier_bloc/supplier_event.dart b/lib/src/screens/keranjang_screen/supplier_bloc/supplier_event.dart new file mode 100644 index 0000000000000000000000000000000000000000..5daa9733e01bf58ed7dc61c9e8edefb2a7423f76 --- /dev/null +++ b/lib/src/screens/keranjang_screen/supplier_bloc/supplier_event.dart @@ -0,0 +1,25 @@ +import 'package:equatable/equatable.dart'; +import 'package:mobile_apps/src/models/supplier.dart'; + +abstract class SupplierEvent extends Equatable { + const SupplierEvent(); +} + +class FetchSuppliers extends SupplierEvent { + final List<String> idSuppliers; + + FetchSuppliers(this.idSuppliers); + + @override + List<Object> get props => [idSuppliers]; +} + +class ProductSupplierUpdated extends SupplierEvent { + final List<Supplier> suppliers; + + ProductSupplierUpdated(this.suppliers); + + @override + List<Object> get props => [suppliers]; + +} \ No newline at end of file diff --git a/lib/src/screens/keranjang_screen/supplier_bloc/supplier_state.dart b/lib/src/screens/keranjang_screen/supplier_bloc/supplier_state.dart new file mode 100644 index 0000000000000000000000000000000000000000..abcaa614706bc6274798b66a77bbbeb624ae40ce --- /dev/null +++ b/lib/src/screens/keranjang_screen/supplier_bloc/supplier_state.dart @@ -0,0 +1,30 @@ +import 'package:equatable/equatable.dart'; +import 'package:mobile_apps/src/models/supplier.dart'; + +abstract class SupplierState extends Equatable { + const SupplierState(); +} + +class InitialSupplierState extends SupplierState { + @override + List<Object> get props => []; +} +class SupplierLoading extends SupplierState { + @override + List<Object> get props => []; +} + +class SupplierLoaded extends SupplierState { + final List<Supplier> supplier; + + SupplierLoaded([this.supplier = const []]); + + @override + List<Object> get props => [supplier]; +} + +class SupplierNotLoaded extends SupplierState { + @override + List<Object> get props => []; + +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 2be2573bbcdd3ec73844f3609dafb9be054a2e7b..048e2b6de718aa38ddef9fdf73893de7b9354a8b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -37,6 +37,9 @@ dependencies: url_launcher: ^5.4.0 universal_html: ^1.1.20 material_design_icons_flutter: 4.0.5045 + localstorage: ^3.0.0 + sqflite: ^1.3.0 + flutter_bloc: ^3.2.0 # Dart utils equatable: ^1.1.1