fix cart, fix log cart
This commit is contained in:
@@ -79,7 +79,8 @@ class SentryService {
|
||||
..beforeSend = (event, hint) {
|
||||
// Filter out certain errors if needed
|
||||
// Return null to drop the event
|
||||
return event;
|
||||
// return event;
|
||||
return null;
|
||||
};
|
||||
},
|
||||
appRunner: appRunner,
|
||||
|
||||
@@ -36,6 +36,7 @@ class CartPage extends ConsumerStatefulWidget {
|
||||
|
||||
class _CartPageState extends ConsumerState<CartPage> {
|
||||
bool _isSyncing = false;
|
||||
bool _hasLoggedViewCart = false;
|
||||
|
||||
// Cart is initialized once in home_page.dart at app startup
|
||||
// Provider has keepAlive: true, so no need to reload here
|
||||
@@ -44,20 +45,12 @@ class _CartPageState extends ConsumerState<CartPage> {
|
||||
// and in checkout button handler for checkout flow.
|
||||
// No dispose() method needed - using ref.read() in dispose() is unsafe.
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final cartState = ref.watch(cartProvider);
|
||||
void _logViewCartOnce(CartState cartState) {
|
||||
if (_hasLoggedViewCart || cartState.isEmpty) return;
|
||||
_hasLoggedViewCart = true;
|
||||
|
||||
|
||||
|
||||
final itemCount = cartState.itemCount;
|
||||
final hasSelection = cartState.selectedCount > 0;
|
||||
|
||||
// Log view cart analytics event when cart has items
|
||||
if (cartState.isNotEmpty) {
|
||||
AnalyticsService.logViewCart(
|
||||
cartValue: cartState.selectedTotal,
|
||||
cartValue: cartState.totalPrice,
|
||||
items: cartState.items.map((item) => AnalyticsEventItem(
|
||||
itemId: item.product.productId,
|
||||
itemName: item.product.name,
|
||||
@@ -67,6 +60,17 @@ class _CartPageState extends ConsumerState<CartPage> {
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorScheme = Theme.of(context).colorScheme;
|
||||
final cartState = ref.watch(cartProvider);
|
||||
|
||||
// Log view cart analytics event only once when page opens
|
||||
_logViewCartOnce(cartState);
|
||||
|
||||
final itemCount = cartState.itemCount;
|
||||
final hasSelection = cartState.selectedCount > 0;
|
||||
|
||||
return PopScope(
|
||||
// Intercept back navigation to sync pending updates
|
||||
onPopInvokedWithResult: (didPop, result) async {
|
||||
@@ -346,6 +350,7 @@ class _CartPageState extends ConsumerState<CartPage> {
|
||||
|
||||
/// Build error banner (shown at top when there's an error but cart has items)
|
||||
Widget _buildErrorBanner(String errorMessage) {
|
||||
print(errorMessage);
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:go_router/go_router.dart';
|
||||
import 'package:worker/core/utils/extensions.dart';
|
||||
import 'package:worker/core/widgets/loading_indicator.dart';
|
||||
import 'package:worker/core/theme/typography.dart';
|
||||
@@ -113,8 +113,13 @@ class _CartItemWidgetState extends ConsumerState<CartItemWidget> {
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
// Product Image (bigger: 100x100)
|
||||
ClipRRect(
|
||||
// Product Image (bigger: 100x100) - tap to navigate to product detail
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
// Navigate to product detail with product ID in path
|
||||
context.push('/products/${widget.item.product.productId}');
|
||||
},
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
child: CachedNetworkImage(
|
||||
imageUrl: widget.item.product.thumbnail.isNotEmpty
|
||||
@@ -145,6 +150,7 @@ class _CartItemWidgetState extends ConsumerState<CartItemWidget> {
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(width: 12),
|
||||
|
||||
@@ -193,14 +199,15 @@ class _CartItemWidgetState extends ConsumerState<CartItemWidget> {
|
||||
|
||||
const SizedBox(width: 8),
|
||||
|
||||
// Quantity TextField
|
||||
// Quantity TextField - uses text keyboard for Done button on iOS/Android
|
||||
SizedBox(
|
||||
width: 50,
|
||||
height: 32,
|
||||
child: TextField(
|
||||
controller: _quantityController,
|
||||
focusNode: _quantityFocusNode,
|
||||
keyboardType: TextInputType.number,
|
||||
keyboardType: TextInputType.text,
|
||||
textInputAction: TextInputAction.done,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppTypography.titleMedium.copyWith(
|
||||
fontWeight: FontWeight.w600,
|
||||
|
||||
@@ -138,7 +138,15 @@ class ProductsRemoteDataSource {
|
||||
throw Exception('Product not found: $itemCode');
|
||||
}
|
||||
|
||||
return ProductModel.fromJson(message as Map<String, dynamic>);
|
||||
// Handle API error response: {success: false, message: "Item not found"}
|
||||
if (message is Map<String, dynamic>) {
|
||||
if (message['success'] == false) {
|
||||
throw Exception(message['message'] ?? 'Product not found: $itemCode');
|
||||
}
|
||||
return ProductModel.fromJson(message);
|
||||
}
|
||||
|
||||
throw Exception('Invalid response format for product: $itemCode');
|
||||
} on DioException catch (e) {
|
||||
if (e.response?.statusCode == 404) {
|
||||
throw Exception('Product not found: $itemCode');
|
||||
|
||||
Reference in New Issue
Block a user