feat: Phase 1 critical gaps — gamification API, Razorpay checkout, Google OAuth, notifications

- Fix gamification endpoints to use Node.js server (app.eventifyplus.com)
- Replace 6 mock gamification methods with real API calls (dashboard, leaderboard, shop, redeem, submit)
- Add booking models, service, payment service (Razorpay), checkout provider
- Add 3-step CheckoutScreen with Razorpay native modal integration
- Add Google OAuth login (Flutter + Django backend)
- Add full notifications system (Django model + 3 endpoints + Flutter UI)
- Register CheckoutProvider, NotificationProvider in main.dart MultiProvider
- Wire notification bell in HomeScreen app bar
- Add razorpay_flutter ^1.3.7 and google_sign_in ^6.2.2 packages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-04 15:46:53 +05:30
parent 847577c09d
commit 29e326b8fc
24 changed files with 1663 additions and 164 deletions

View File

@@ -59,6 +59,19 @@ class AuthProvider extends ChangeNotifier {
}
}
/// Google OAuth login.
Future<void> googleLogin() async {
_loading = true;
notifyListeners();
try {
final user = await _authService.googleLogin();
_user = user;
} finally {
_loading = false;
notifyListeners();
}
}
Future<void> logout() async {
await _authService.logout();
_user = null;

View File

@@ -1,10 +1,12 @@
// lib/features/auth/services/auth_service.dart
import 'package:flutter/foundation.dart' show kDebugMode, debugPrint;
import 'package:shared_preferences/shared_preferences.dart';
import 'package:google_sign_in/google_sign_in.dart';
import '../../../core/api/api_client.dart';
import '../../../core/api/api_endpoints.dart';
import '../../../core/auth/auth_guard.dart';
import '../../../core/storage/token_storage.dart';
import '../../../core/analytics/posthog_service.dart';
import '../models/user_model.dart';
class AuthService {
@@ -58,6 +60,12 @@ class AuthService {
// Save phone if provided (optional)
if (res['phone_number'] != null) await prefs.setString('phone_number', res['phone_number'].toString());
PostHogService.instance.identify(savedEmail, properties: {
'username': displayCandidate,
'login_method': 'email',
});
PostHogService.instance.capture('user_logged_in');
return UserModel.fromJson(res);
} catch (e) {
if (kDebugMode) debugPrint('AuthService.login error: $e');
@@ -130,6 +138,54 @@ class AuthService {
}
}
/// GOOGLE OAUTH LOGIN → returns UserModel
Future<UserModel> googleLogin() async {
try {
final googleSignIn = GoogleSignIn(scopes: ['email']);
final account = await googleSignIn.signIn();
if (account == null) throw Exception('Google sign-in cancelled');
final auth = await account.authentication;
final idToken = auth.idToken;
if (idToken == null) throw Exception('Failed to get Google ID token');
final res = await _api.post(
ApiEndpoints.googleLogin,
body: {'id_token': idToken},
requiresAuth: false,
);
final token = res['token'];
if (token == null) throw Exception('Token missing from response');
final serverEmail = (res['email'] as String?) ?? account.email;
final displayName = (res['username'] as String?) ?? account.displayName ?? serverEmail;
AuthGuard.setGuest(false);
await TokenStorage.saveToken(token.toString(), serverEmail);
final prefs = await SharedPreferences.getInstance();
await prefs.setString('current_email', serverEmail);
await prefs.setString('email', serverEmail);
final perKey = 'display_name_$serverEmail';
if ((prefs.getString(perKey) ?? '').isEmpty) {
await prefs.setString(perKey, displayName);
}
if (res['phone_number'] != null) await prefs.setString('phone_number', res['phone_number'].toString());
PostHogService.instance.identify(serverEmail, properties: {
'username': displayName,
'login_method': 'google',
});
PostHogService.instance.capture('user_logged_in');
return UserModel.fromJson(res);
} catch (e) {
if (kDebugMode) debugPrint('AuthService.googleLogin error: $e');
rethrow;
}
}
/// Logout clear auth token and current_email (keep per-account display_name entries so they persist)
Future<void> logout() async {
try {
@@ -141,6 +197,8 @@ class AuthService {
// Also remove canonical 'email' pointing to current user
await prefs.remove('email');
// Do not delete display_name_<email> entries — they are per-account and should remain on device.
PostHogService.instance.capture('user_logged_out');
PostHogService.instance.reset();
} catch (e) {
if (kDebugMode) debugPrint('AuthService.logout warning: $e');
}