diff --git a/lib/core/container/export_vmod.dart b/lib/core/container/export_vmod.dart index c0c4a15..64d9fa1 100644 --- a/lib/core/container/export_vmod.dart +++ b/lib/core/container/export_vmod.dart @@ -17,3 +17,5 @@ export 'package:rijig_mobile/features/home/service/about_service.dart'; export 'package:rijig_mobile/globaldata/article/article_repository.dart'; export 'package:rijig_mobile/globaldata/article/article_service.dart'; export 'package:rijig_mobile/globaldata/article/article_vmod.dart'; +export 'package:rijig_mobile/features/cart/presentation/viewmodel/cartitem_vmod.dart'; +export 'package:rijig_mobile/features/cart/repositories/cartitem_repo.dart'; diff --git a/lib/core/container/injection_container.dart b/lib/core/container/injection_container.dart index 68bf8c0..50b6ee3 100644 --- a/lib/core/container/injection_container.dart +++ b/lib/core/container/injection_container.dart @@ -13,4 +13,5 @@ void init() { sl.registerFactory(() => AboutViewModel(AboutService(AboutRepository()))); sl.registerFactory(() => AboutDetailViewModel(AboutService(AboutRepository()))); sl.registerFactory(() => ArticleViewModel(ArticleService(ArticleRepository()))); + sl.registerFactory(() => CartViewModel(CartRepository())); } diff --git a/lib/core/utils/exportimportview.dart b/lib/core/utils/exportimportview.dart index 357e0be..6f10e45 100644 --- a/lib/core/utils/exportimportview.dart +++ b/lib/core/utils/exportimportview.dart @@ -1,7 +1,7 @@ export 'package:go_router/go_router.dart'; export 'package:rijig_mobile/core/utils/navigation.dart'; export 'package:rijig_mobile/features/activity/presentation/screen/activity_screen.dart'; -export 'package:rijig_mobile/features/cart/presentation/cart_screen.dart'; +export 'package:rijig_mobile/features/cart/presentation/screens/cart_screen.dart'; export 'package:rijig_mobile/features/home/presentation/screen/home_screen.dart'; export 'package:rijig_mobile/features/profil/presentation/screen/profil_screen.dart'; export 'package:rijig_mobile/features/requestpick/presentation/screen/requestpickup_screen.dart'; diff --git a/lib/core/utils/guide.dart b/lib/core/utils/guide.dart index 464c283..33eee63 100644 --- a/lib/core/utils/guide.dart +++ b/lib/core/utils/guide.dart @@ -40,9 +40,9 @@ class Tulisan { ); } - static TextStyle subheading({Color? color}) { + static TextStyle subheading({Color? color, double? fontsize}) { return GoogleFonts.spaceGrotesk( - fontSize: 18.sp, + fontSize: fontsize?.sp ?? 18.sp, fontWeight: FontWeight.w500, color: color ?? blackNavyColor, ); diff --git a/lib/core/utils/navigation.dart b/lib/core/utils/navigation.dart index 2518e9e..0753a41 100644 --- a/lib/core/utils/navigation.dart +++ b/lib/core/utils/navigation.dart @@ -3,7 +3,7 @@ import 'package:iconsax_flutter/iconsax_flutter.dart'; import 'package:rijig_mobile/core/utils/guide.dart'; import 'package:rijig_mobile/core/router.dart'; import 'package:rijig_mobile/features/activity/presentation/screen/activity_screen.dart'; -import 'package:rijig_mobile/features/cart/presentation/cart_screen.dart'; +import 'package:rijig_mobile/features/cart/presentation/screens/cart_screen.dart'; import 'package:rijig_mobile/features/home/presentation/screen/home_screen.dart'; import 'package:rijig_mobile/features/profil/presentation/screen/profil_screen.dart'; import 'package:shared_preferences/shared_preferences.dart'; diff --git a/lib/features/cart/model/cartitem_model.dart b/lib/features/cart/model/cartitem_model.dart new file mode 100644 index 0000000..774f753 --- /dev/null +++ b/lib/features/cart/model/cartitem_model.dart @@ -0,0 +1,89 @@ +import 'dart:convert'; + +class CartItem { + final String trashId; + final double amount; + + CartItem({required this.trashId, required this.amount}); + + Map toJson() => {'trashid': trashId, 'amount': amount}; + + factory CartItem.fromJson(Map json) { + return CartItem( + trashId: json['trashid'], + amount: (json['amount'] as num).toDouble(), + ); + } + + static String encodeList(List items) => + jsonEncode(items.map((e) => e.toJson()).toList()); + + static List decodeList(String source) => + (jsonDecode(source) as List) + .map((e) => CartItem.fromJson(e)) + .toList(); +} + +class CartItemResponse { + final String trashId; + final String trashIcon; + final String trashName; + final double amount; + final double estimatedSubTotalPrice; + + CartItemResponse({ + required this.trashId, + required this.trashIcon, + required this.trashName, + required this.amount, + required this.estimatedSubTotalPrice, + }); + + factory CartItemResponse.fromJson(Map json) { + return CartItemResponse( + trashId: json['trashid'], + trashIcon: json['trashicon'] ?? '', + trashName: json['trashname'] ?? '', + amount: (json['amount'] as num).toDouble(), + estimatedSubTotalPrice: + (json['estimated_subtotalprice'] as num).toDouble(), + ); + } +} + +class CartResponse { + final String id; + final String userId; + final double totalAmount; + final double estimatedTotalPrice; + final String createdAt; + final String updatedAt; + final List cartItems; + + CartResponse({ + required this.id, + required this.userId, + required this.totalAmount, + required this.estimatedTotalPrice, + required this.createdAt, + required this.updatedAt, + required this.cartItems, + }); + + factory CartResponse.fromJson(Map json) { + var items = + (json['cartitems'] as List) + .map((e) => CartItemResponse.fromJson(e)) + .toList(); + + return CartResponse( + id: json['id'], + userId: json['userid'], + totalAmount: (json['totalamount'] as num).toDouble(), + estimatedTotalPrice: (json['estimated_totalprice'] as num).toDouble(), + createdAt: json['createdAt'], + updatedAt: json['updatedAt'], + cartItems: items, + ); + } +} diff --git a/lib/features/cart/presentation/cart_screen.dart b/lib/features/cart/presentation/cart_screen.dart deleted file mode 100644 index 7366c17..0000000 --- a/lib/features/cart/presentation/cart_screen.dart +++ /dev/null @@ -1,236 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:gap/gap.dart'; -import 'package:rijig_mobile/core/utils/guide.dart'; -import 'package:rijig_mobile/widget/buttoncard.dart'; -import 'package:rijig_mobile/widget/counter_dialog.dart'; - -class CartScreen extends StatefulWidget { - const CartScreen({super.key}); - - @override - State createState() => _CartScreenState(); -} - -class _CartScreenState extends State { - double totalWeight = 1.0; - double pricePerKg = 700; - - void _openEditAmountDialog(String itemName) { - showDialog( - context: context, - builder: (BuildContext context) { - return EditAmountDialog( - initialAmount: totalWeight, - itemName: itemName, - pricePerKg: pricePerKg, - onSave: (newAmount) { - setState(() { - totalWeight = newAmount; - }); - }, - ); - }, - ); - } - - void _increment() { - setState(() { - totalWeight += 0.25; - }); - } - - void _decrement() { - if (totalWeight > 0.25) { - setState(() { - totalWeight -= 0.25; - }); - } - } - - String formatAmount(double value) { - String formattedValue = value.toStringAsFixed(2); - - if (formattedValue.endsWith('.00')) { - return formattedValue.split('.').first; - } - - return formattedValue; - } - - @override - Widget build(BuildContext context) { - return Scaffold( - backgroundColor: whiteColor, - appBar: AppBar( - title: Text( - 'Keranjang Sampah', - style: Tulisan.subheading(color: blackNavyColor), - ), - ), - body: SafeArea( - child: SingleChildScrollView( - child: Padding( - padding: PaddingCustom().paddingHorizontal(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - padding: EdgeInsets.all(20), - width: double.infinity, - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.grey.withValues(alpha: 0.1), - spreadRadius: 1, - blurRadius: 8, - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Lokasi Penjemputan', - style: TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - ), - ), - SizedBox(height: 10), - Text( - 'Lokasi harus di Jember', - style: TextStyle(fontSize: 14), - ), - - Gap(15), - CardButtonOne( - textButton: "Pilih Alamat", - fontSized: 16.sp, - color: primaryColor, - colorText: whiteColor, - borderRadius: 9, - horizontal: double.infinity, - vertical: 40, - onTap: () { - debugPrint("pilih alamat tapped"); - }, - ), - ], - ), - ), - Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.grey.withValues(alpha: 0.1), - spreadRadius: 1, - blurRadius: 8, - ), - ], - ), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Jenis Sampah', - style: TextStyle(fontWeight: FontWeight.bold), - ), - SizedBox(height: 10), - Row( - children: [ - Icon(Icons.delete, color: Colors.blue), - SizedBox(width: 8), - Text('Kertas Campur'), - Spacer(), - GestureDetector( - onTap: () { - _openEditAmountDialog('Kertas Campur'); - }, - child: Row( - children: [ - Text("${formatAmount(totalWeight)} kg"), - Icon(Icons.edit, color: Colors.blue), - ], - ), - ), - ], - ), - SizedBox(height: 10), - Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - IconButton( - onPressed: _decrement, - icon: Icon(Icons.remove), - color: Colors.red, - ), - SizedBox(width: 10), - IconButton( - onPressed: _increment, - icon: Icon(Icons.add), - color: Colors.green, - ), - ], - ), - ], - ), - ), - SizedBox(height: 20), - - Container( - padding: EdgeInsets.all(20), - decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.grey.withValues(alpha: 0.1), - spreadRadius: 1, - blurRadius: 8, - ), - ], - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - 'Estimasi Total Berat', - style: TextStyle(fontWeight: FontWeight.bold), - ), - Text( - "${formatAmount(totalWeight)} kg", - style: TextStyle(fontSize: 18), - ), - ], - ), - ), - SizedBox(height: 20), - - Center( - child: CardButtonOne( - textButton: "Lanjutkan", - fontSized: 16.sp, - color: primaryColor, - colorText: whiteColor, - borderRadius: 9, - horizontal: double.infinity, - vertical: 60, - onTap: () { - debugPrint("lanjutkan tapped"); - }, - ), - ), - ], - ), - ), - ), - ), - ); - } -} diff --git a/lib/features/cart/presentation/screens/cart_screen.dart b/lib/features/cart/presentation/screens/cart_screen.dart new file mode 100644 index 0000000..95da468 --- /dev/null +++ b/lib/features/cart/presentation/screens/cart_screen.dart @@ -0,0 +1,460 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gap/gap.dart'; +import 'package:iconsax_flutter/iconsax_flutter.dart'; +import 'package:provider/provider.dart'; +import 'package:rijig_mobile/core/utils/guide.dart'; +import 'package:rijig_mobile/features/cart/model/cartitem_model.dart'; +import 'package:rijig_mobile/features/cart/presentation/viewmodel/cartitem_vmod.dart'; +import 'package:rijig_mobile/widget/buttoncard.dart'; +import 'package:rijig_mobile/widget/skeletonize.dart'; + +class CartScreen extends StatefulWidget { + const CartScreen({super.key}); + + @override + State createState() => _CartScreenState(); +} + +class _CartScreenState extends State with WidgetsBindingObserver { + CartResponse? cart; + bool isLoading = true; + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + fetchCart(); + } + + @override + void dispose() { + WidgetsBinding.instance.removeObserver(this); + super.dispose(); + } + + @override + void didChangeAppLifecycleState(AppLifecycleState state) { + if (state == AppLifecycleState.resumed) { + debugPrint("App resumed, flushing cart..."); + final vmod = Provider.of(context, listen: false); + vmod.flushCartToServer(); + } + } + + Future fetchCart() async { + final vmod = Provider.of(context, listen: false); + final result = await vmod.fetchCartFromServer(); + setState(() { + cart = result; + isLoading = false; + }); + } + + double get totalAmount => cart?.totalAmount ?? 0; + double get totalPrice => cart?.estimatedTotalPrice ?? 0; + + String formatAmount(double value) { + String formattedValue = value.toStringAsFixed(2); + return formattedValue.endsWith('.00') + ? formattedValue.split('.').first + : formattedValue; + } + + void _showEditDialog(String trashId, double currentAmount, int index) { + double editedAmount = currentAmount; + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Edit Jumlah'), + content: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + IconButton( + icon: const Icon(Icons.remove), + onPressed: () { + setState(() { + if (editedAmount > 0.25) editedAmount -= 0.25; + }); + }, + ), + Text('${formatAmount(editedAmount)} kg'), + IconButton( + icon: const Icon(Icons.add), + onPressed: () { + setState(() { + editedAmount += 0.25; + }); + }, + ), + ], + ), + actions: [ + TextButton( + onPressed: () => Navigator.of(context).pop(), + child: const Text('Batal'), + ), + TextButton( + onPressed: () { + final vmod = Provider.of(context, listen: false); + vmod.addOrUpdateItem( + CartItem(trashId: trashId, amount: editedAmount), + ); + + final item = cart!.cartItems[index]; + setState(() { + cart!.cartItems[index] = CartItemResponse( + trashIcon: item.trashIcon, + trashName: item.trashName, + amount: editedAmount, + estimatedSubTotalPrice: + editedAmount * + (item.estimatedSubTotalPrice / item.amount), + trashId: item.trashId, + ); + }); + + Navigator.of(context).pop(); + }, + child: const Text('Simpan'), + ), + ], + ); + }, + ); + } + + void recalculateCartSummary() { + double totalWeight = 0; + double totalPrice = 0; + + for (final item in cart!.cartItems) { + totalWeight += item.amount; + totalPrice += item.estimatedSubTotalPrice; + } + + setState(() { + cart = CartResponse( + id: cart!.id, + userId: cart!.userId, + totalAmount: totalWeight, + estimatedTotalPrice: totalPrice, + createdAt: cart!.createdAt, + updatedAt: cart!.updatedAt, + cartItems: cart!.cartItems, + ); + }); + } + + @override + Widget build(BuildContext context) { + final String? baseUrl = dotenv.env["BASE_URL"]; + + return Scaffold( + backgroundColor: whiteColor, + appBar: AppBar( + title: Text("Keranjang Item", style: Tulisan.subheading()), + backgroundColor: whiteColor, + centerTitle: true, + ), + body: SafeArea( + child: + isLoading + ? ListView.builder( + shrinkWrap: true, + itemCount: 3, + itemBuilder: (context, index) { + return SkeletonCard(); + }, + ) + : Padding( + padding: PaddingCustom().paddingOnly( + left: 10, + right: 10, + bottom: 40, + top: 10, + ), + child: Column( + children: [ + const Gap(20), + Expanded( + child: ListView.builder( + itemCount: cart?.cartItems.length ?? 0, + itemBuilder: (context, index) { + final item = cart!.cartItems[index]; + final perKgPrice = + item.estimatedSubTotalPrice / item.amount; + final currentAmount = item.amount; + + return Container( + margin: const EdgeInsets.only(bottom: 20), + padding: PaddingCustom().paddingAll(20), + decoration: BoxDecoration( + color: whiteColor, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: greyColor.withValues(alpha: 0.1), + spreadRadius: 1, + blurRadius: 8, + ), + ], + ), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Image.network( + "$baseUrl${item.trashIcon}", + width: 50, + height: 50, + ), + const Gap(10), + Expanded( + child: Column( + crossAxisAlignment: + CrossAxisAlignment.start, + children: [ + Text( + item.trashName, + style: Tulisan.customText(), + ), + Text( + "Rp${perKgPrice.toStringAsFixed(0)} / kg", + style: Tulisan.body(fontsize: 12), + ), + ], + ), + ), + IconButton( + onPressed: () { + final vmod = + Provider.of( + context, + listen: false, + ); + vmod.removeItem(item.trashId); + + setState(() { + cart!.cartItems.removeAt(index); + }); + + recalculateCartSummary(); + }, + + icon: Icon( + Iconsax.trash, + color: redColor, + ), + ), + ], + ), + const Gap(10), + Row( + children: [ + Text( + "berat", + style: Tulisan.body(fontsize: 12), + ), + const Gap(12), + Container( + padding: const EdgeInsets.symmetric( + horizontal: 8, + vertical: 4, + ), + decoration: BoxDecoration( + border: Border.all( + color: greyAbsolutColor, + ), + borderRadius: BorderRadius.circular( + 6, + ), + ), + child: Row( + children: [ + GestureDetector( + onTap: () { + final newAmount = + (currentAmount - 0.25) + .clamp( + 0.25, + double.infinity, + ); + Provider.of( + context, + listen: false, + ).addOrUpdateItem( + CartItem( + trashId: item.trashId, + amount: newAmount, + ), + ); + + setState(() { + cart!.cartItems[index] = + CartItemResponse( + trashIcon: + item.trashIcon, + trashName: + item.trashName, + amount: newAmount, + estimatedSubTotalPrice: + newAmount * + (item.estimatedSubTotalPrice / + item.amount), + trashId: item.trashId, + ); + }); + + recalculateCartSummary(); + }, + child: const Icon( + Icons.remove, + size: 20, + ), + ), + const Gap(8), + GestureDetector( + onTap: + () => _showEditDialog( + item.trashId, + currentAmount, + index, + ), + child: Text( + formatAmount(currentAmount), + ), + ), + const Gap(8), + GestureDetector( + onTap: () { + final newAmount = + currentAmount + 0.25; + Provider.of( + context, + listen: false, + ).addOrUpdateItem( + CartItem( + trashId: item.trashId, + amount: newAmount, + ), + ); + + setState(() { + cart!.cartItems[index] = + CartItemResponse( + trashIcon: + item.trashIcon, + trashName: + item.trashName, + amount: newAmount, + estimatedSubTotalPrice: + newAmount * + (item.estimatedSubTotalPrice / + item.amount), + trashId: item.trashId, + ); + }); + + recalculateCartSummary(); + }, + child: const Icon( + Icons.add, + size: 20, + ), + ), + ], + ), + ), + ], + ), + ], + ), + ); + }, + ), + ), + Container( + padding: const EdgeInsets.all(20), + decoration: BoxDecoration( + color: whiteColor, + borderRadius: BorderRadius.circular(10), + boxShadow: [ + BoxShadow( + color: greyAbsolutColor.withValues(alpha: 0.1), + spreadRadius: 1, + blurRadius: 8, + ), + ], + ), + child: Column( + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Cart Total", + style: Tulisan.subheading(fontsize: 14), + ), + Text( + "${formatAmount(totalAmount)} kg", + style: Tulisan.body(fontsize: 14), + ), + ], + ), + const Gap(10), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Text( + "Harga Total", + style: Tulisan.subheading(fontsize: 14), + ), + Text( + "Rp${totalPrice.toStringAsFixed(0)}", + style: Tulisan.body(fontsize: 14), + ), + ], + ), + ], + ), + ), + const Gap(20), + SizedBox( + width: double.infinity, + child: CardButtonOne( + textButton: "Request Pickup", + fontSized: 16.sp, + color: primaryColor, + colorText: whiteColor, + borderRadius: 9, + horizontal: double.infinity, + vertical: 50, + onTap: () async { + final vmod = Provider.of( + context, + listen: false, + ); + await vmod.flushCartToServer(); + + ScaffoldMessenger.of(context).showSnackBar( + const SnackBar( + content: Text( + "Keranjang berhasil dikirim ke server!", + ), + ), + ); + }, + ), + ), + const Gap(20), + ], + ), + ), + ), + ); + } +} diff --git a/lib/features/cart/presentation/viewmodel/cartitem_vmod.dart b/lib/features/cart/presentation/viewmodel/cartitem_vmod.dart new file mode 100644 index 0000000..0af5923 --- /dev/null +++ b/lib/features/cart/presentation/viewmodel/cartitem_vmod.dart @@ -0,0 +1,87 @@ +import 'package:flutter/material.dart'; +import 'package:rijig_mobile/features/cart/model/cartitem_model.dart'; +import 'package:rijig_mobile/features/cart/repositories/cartitem_repo.dart'; + +class CartViewModel extends ChangeNotifier { + final CartRepository _repository; + + CartViewModel(this._repository); + + List _cartItems = []; + + List get cartItems => _cartItems; + + bool _isLoading = false; + bool get isLoading => _isLoading; + + Future loadLocalCart() async { + _isLoading = true; + notifyListeners(); + + _cartItems = await _repository.getLocalCart(); + + _isLoading = false; + notifyListeners(); + } + + void addOrUpdateItem(CartItem item) { + final index = _cartItems.indexWhere((e) => e.trashId == item.trashId); + if (index != -1) { + _cartItems[index] = item; + } else { + _cartItems.add(item); + } + _repository.saveLocalCart(_cartItems); + notifyListeners(); + } + + void removeItem(String trashId) { + _cartItems.removeWhere((e) => e.trashId == trashId); + _repository.saveLocalCart(_cartItems); + notifyListeners(); + } + + Future clearLocalCart() async { + _cartItems.clear(); + await _repository.clearLocalCart(); + notifyListeners(); + } + + Future flushCartToServer() async { + if (_cartItems.isEmpty) return; + + _isLoading = true; + notifyListeners(); + + await _repository.flushCartToServer(); + await clearLocalCart(); + + _isLoading = false; + notifyListeners(); + } + + Future fetchCartFromServer() async { + try { + return await _repository.getCartFromServer(); + } catch (e) { + debugPrint("Error fetching cart: $e"); + return null; + } + } + + Future commitCart() async { + await _repository.commitCart(); + } + + Future refreshTTL() async { + await _repository.refreshCartTTL(); + } + + Future deleteItemFromServer(String trashId) async { + await _repository.deleteCartItemFromServer(trashId); + } + + Future clearCartFromServer() async { + await _repository.clearServerCart(); + } +} diff --git a/lib/features/cart/repositories/cartitem_repo.dart b/lib/features/cart/repositories/cartitem_repo.dart new file mode 100644 index 0000000..640e184 --- /dev/null +++ b/lib/features/cart/repositories/cartitem_repo.dart @@ -0,0 +1,55 @@ +import 'package:rijig_mobile/features/cart/model/cartitem_model.dart'; +import 'package:rijig_mobile/features/cart/service/cartitem_service.dart'; +import 'package:shared_preferences/shared_preferences.dart'; + +class CartRepository { + final CartService _cartService = CartService(); + final String _localCartKey = 'local_cart'; + + Future> getLocalCart() async { + final prefs = await SharedPreferences.getInstance(); + final raw = prefs.getString(_localCartKey); + if (raw == null || raw.isEmpty) return []; + + return CartItem.decodeList(raw); + } + + Future saveLocalCart(List items) async { + final prefs = await SharedPreferences.getInstance(); + final encoded = CartItem.encodeList(items); + await prefs.setString(_localCartKey, encoded); + } + + Future clearLocalCart() async { + final prefs = await SharedPreferences.getInstance(); + await prefs.remove(_localCartKey); + } + + Future flushCartToServer() async { + final items = await getLocalCart(); + if (items.isEmpty) return; + + await _cartService.postCart(items); + await clearLocalCart(); + } + + Future getCartFromServer() async { + return await _cartService.getCart(); + } + + Future commitCart() async { + await _cartService.commitCart(); + } + + Future clearServerCart() async { + await _cartService.clearCart(); + } + + Future deleteCartItemFromServer(String trashId) async { + await _cartService.deleteCartItem(trashId); + } + + Future refreshCartTTL() async { + await _cartService.refreshCartTTL(); + } +} diff --git a/lib/features/cart/service/cartitem_service.dart b/lib/features/cart/service/cartitem_service.dart new file mode 100644 index 0000000..09c462f --- /dev/null +++ b/lib/features/cart/service/cartitem_service.dart @@ -0,0 +1,33 @@ +import 'package:rijig_mobile/core/api/api_services.dart'; +import 'package:rijig_mobile/features/cart/model/cartitem_model.dart'; + +class CartService { + final Https _https = Https(); + + Future postCart(List items) async { + final body = {"items": items.map((e) => e.toJson()).toList()}; + + await _https.post("/cart", body: body); + } + + Future getCart() async { + final response = await _https.get("/cart"); + return CartResponse.fromJson(response['data']); + } + + Future deleteCartItem(String trashId) async { + await _https.delete("/cart/$trashId"); + } + + Future clearCart() async { + await _https.delete("/cart"); + } + + Future refreshCartTTL() async { + await _https.put("/cart/refresh"); + } + + Future commitCart() async { + await _https.post("/cart/commit"); + } +} diff --git a/lib/features/home/repositories/about_repository.dart b/lib/features/home/repositories/about_repository.dart index bfdf208..681b442 100644 --- a/lib/features/home/repositories/about_repository.dart +++ b/lib/features/home/repositories/about_repository.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:rijig_mobile/core/api/api_services.dart'; import 'package:rijig_mobile/features/home/model/about_model.dart'; @@ -7,14 +6,14 @@ class AboutRepository { Future> getAboutList() async { final response = await _https.get('/about'); - debugPrint("response about: $response"); + // debugPrint("response about: $response"); final List data = response['data'] ?? []; return data.map((e) => AboutModel.fromJson(e)).toList(); } Future> getAboutDetail(String id) async { final response = await _https.get('/about/$id'); - debugPrint("response about detail: $response"); + // debugPrint("response about detail: $response"); final List aboutDetail = response['data']['about_detail'] ?? []; return aboutDetail.map((e) => AboutDetailModel.fromJson(e)).toList(); } diff --git a/lib/globaldata/article/article_repository.dart b/lib/globaldata/article/article_repository.dart index 7983705..40a264e 100644 --- a/lib/globaldata/article/article_repository.dart +++ b/lib/globaldata/article/article_repository.dart @@ -1,4 +1,3 @@ -import 'package:flutter/material.dart'; import 'package:rijig_mobile/core/api/api_services.dart'; import 'package:rijig_mobile/globaldata/article/article_model.dart'; @@ -7,7 +6,7 @@ class ArticleRepository { Future> fetchArticles() async { final response = await _https.get('/article-rijik/view-article'); - debugPrint("reponse article: $response"); + // debugPrint("reponse article: $response"); final List data = response['data']; return data.map((json) => ArticleModel.fromJson(json)).toList(); } diff --git a/lib/main.dart b/lib/main.dart index be1dfb2..96ee897 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,6 +34,7 @@ class MyApp extends StatelessWidget { ChangeNotifierProvider(create: (_) => sl()), ChangeNotifierProvider(create: (_) => sl()), ChangeNotifierProvider(create: (_) => sl()), + ChangeNotifierProvider(create: (_) => sl()), ], child: ScreenUtilInit( designSize: const Size(375, 812),