This commit is contained in:
Phuoc Nguyen
2025-12-09 16:57:58 +07:00
parent 597c6a0e57
commit 4cfe000172
11 changed files with 89 additions and 2 deletions

View File

@@ -87,6 +87,7 @@ class _LoginPageState extends ConsumerState<LoginPage> {
..when(
data: (user) {
if (user != null && mounted) {
// Analytics (logLogin & setUserId) are handled in auth_provider
// Navigate to home on success
context.goHome();
}

View File

@@ -26,6 +26,7 @@ import 'package:worker/features/auth/presentation/providers/customer_groups_prov
import 'package:worker/features/auth/presentation/providers/session_provider.dart';
import 'package:worker/features/auth/presentation/widgets/phone_input_field.dart';
import 'package:worker/features/auth/presentation/widgets/file_upload_card.dart';
import 'package:worker/core/services/analytics_service.dart';
/// Registration Page
///
@@ -345,6 +346,9 @@ class _RegisterPageState extends ConsumerState<RegisterPage> {
);
if (mounted) {
// Log sign up analytics event
AnalyticsService.logSignUp(method: 'phone');
// Show success message
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(

View File

@@ -14,6 +14,7 @@ import 'package:worker/core/services/frappe_auth_service.dart';
import 'package:worker/features/auth/data/datasources/auth_local_datasource.dart';
import 'package:worker/features/auth/data/datasources/auth_remote_datasource.dart';
import 'package:worker/features/auth/domain/entities/user.dart';
import 'package:worker/core/services/analytics_service.dart';
part 'auth_provider.g.dart';
@@ -182,6 +183,11 @@ class Auth extends _$Auth {
// Save rememberMe preference
await _localDataSource.saveRememberMe(rememberMe);
// Set user ID for analytics tracking
await AnalyticsService.setUserId(phoneNumber);
// Log login event
await AnalyticsService.logLogin(method: 'phone');
// Create and return User entity
final now = DateTime.now();
return User(
@@ -218,6 +224,9 @@ class Auth extends _$Auth {
state = await AsyncValue.guard(() async {
final frappeService = await _frappeAuthService;
// Clear user ID from analytics
await AnalyticsService.setUserId(null);
// Clear saved session
await _localDataSource.clearSession();
await frappeService.clearSession();

View File

@@ -272,7 +272,7 @@ final class AuthProvider extends $AsyncNotifierProvider<Auth, User?> {
Auth create() => Auth();
}
String _$authHash() => r'd851980cad7a624f00eba69e19d8a4fee22008e7';
String _$authHash() => r'f1d856a9a8fc461da111699e3c7ca2af1f2ce7ca';
/// Authentication Provider
///

View File

@@ -17,6 +17,8 @@ import 'package:worker/core/theme/typography.dart';
import 'package:worker/features/cart/presentation/providers/cart_provider.dart';
import 'package:worker/features/cart/presentation/providers/cart_state.dart';
import 'package:worker/features/cart/presentation/widgets/cart_item_widget.dart';
import 'package:worker/core/services/analytics_service.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
/// Cart Page
///
@@ -52,6 +54,19 @@ class _CartPageState extends ConsumerState<CartPage> {
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,
items: cartState.items.map((item) => AnalyticsEventItem(
itemId: item.product.productId,
itemName: item.product.name,
price: item.product.basePrice,
quantity: item.quantity.toInt(),
)).toList(),
);
}
return PopScope(
// Intercept back navigation to sync pending updates
onPopInvokedWithResult: (didPop, result) async {
@@ -442,6 +457,17 @@ class _CartPageState extends ConsumerState<CartPage> {
),
ElevatedButton(
onPressed: () {
// Log remove from cart analytics for selected items
for (final item in cartState.items) {
if (cartState.selectedItems[item.product.productId] == true) {
AnalyticsService.logRemoveFromCart(
productId: item.product.productId,
productName: item.product.name,
price: item.product.basePrice,
quantity: item.quantity.toInt(),
);
}
}
ref.read(cartProvider.notifier).deleteSelected();
context.pop();
ScaffoldMessenger.of(context).showSnackBar(

View File

@@ -29,6 +29,8 @@ import 'package:worker/features/cart/presentation/widgets/payment_method_section
import 'package:worker/features/cart/presentation/widgets/price_negotiation_section.dart';
import 'package:worker/features/orders/presentation/providers/order_status_provider.dart';
import 'package:worker/features/orders/presentation/providers/payment_terms_provider.dart';
import 'package:worker/core/services/analytics_service.dart';
import 'package:firebase_analytics/firebase_analytics.dart';
/// Checkout Page
///
@@ -104,6 +106,22 @@ class CheckoutPage extends HookConsumerWidget {
final total = subtotal - memberDiscount + shipping;
// Log begin checkout analytics event
if (cartItemsData.isNotEmpty) {
AnalyticsService.logBeginCheckout(
value: total,
items: cartItemsData.map((itemData) {
final cartItem = itemData as CartItemData;
return AnalyticsEventItem(
itemId: cartItem.product.productId,
itemName: cartItem.product.name,
price: cartItem.product.basePrice,
quantity: cartItem.quantity.toInt(),
);
}).toList(),
);
}
return Scaffold(
backgroundColor: colorScheme.surfaceContainerLowest,
appBar: AppBar(

View File

@@ -15,6 +15,7 @@ import 'package:worker/features/loyalty/presentation/providers/gifts_provider.da
import 'package:worker/features/loyalty/presentation/providers/loyalty_points_provider.dart';
import 'package:worker/features/loyalty/presentation/widgets/points_balance_card.dart';
import 'package:worker/features/loyalty/presentation/widgets/reward_card.dart';
import 'package:worker/core/services/analytics_service.dart';
/// Rewards Page
///
@@ -441,6 +442,12 @@ class RewardsPage extends ConsumerWidget {
WidgetRef ref,
GiftCatalog gift,
) {
// Log spend points analytics event
AnalyticsService.logSpendPoints(
points: gift.pointsCost,
itemName: gift.name,
);
// Deduct points
ref.read(loyaltyPointsProvider.notifier).deductPoints(gift.pointsCost);

View File

@@ -15,6 +15,7 @@ import 'package:intl/intl.dart';
import 'package:worker/core/constants/ui_constants.dart';
import 'package:worker/core/router/app_router.dart';
import 'package:worker/core/theme/colors.dart';
import 'package:worker/core/services/analytics_service.dart';
/// Order Success Page
class OrderSuccessPage extends StatelessWidget {
@@ -37,6 +38,15 @@ class OrderSuccessPage extends StatelessWidget {
final now = DateTime.now();
final dateFormat = DateFormat('dd/MM/yyyy HH:mm');
// Log purchase analytics event (only for actual purchases, not negotiations)
if (!isNegotiation && total != null) {
AnalyticsService.logPurchase(
orderId: orderNumber,
value: total!,
items: [], // Items not available in this page
);
}
return Scaffold(
backgroundColor: colorScheme.surface,
body: SafeArea(

View File

@@ -239,6 +239,15 @@ class _ProductDetailPageState extends ConsumerState<ProductDetailPage> {
),
body: productAsync.when(
data: (product) {
// Log view item analytics event
AnalyticsService.logViewItem(
productId: product.productId,
productName: product.name,
price: product.basePrice,
brand: product.itemGroupName,
category: product.itemGroupName,
);
return Column(
children: [
// Scrollable content

View File

@@ -6,6 +6,7 @@ library;
import 'dart:async';
import 'package:riverpod_annotation/riverpod_annotation.dart';
import 'package:worker/core/services/analytics_service.dart';
part 'search_query_provider.g.dart';
@@ -63,6 +64,8 @@ class SearchQuery extends _$SearchQuery {
// Only update if query still meets requirements after delay
if (trimmedQuery.length >= 2) {
state = trimmedQuery;
// Log search analytics event
AnalyticsService.logSearch(searchTerm: trimmedQuery);
}
});
}

View File

@@ -94,7 +94,7 @@ final class SearchQueryProvider extends $NotifierProvider<SearchQuery, String> {
}
}
String _$searchQueryHash() => r'3a4178c8c220a1016d20887d7bd97cd157f777f8';
String _$searchQueryHash() => r'62ee3245dca6a43fb276bee72ba6e6d16238e69b';
/// Search Query Provider
///