- 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
174 lines
6.3 KiB
Dart
174 lines
6.3 KiB
Dart
// lib/features/gamification/services/gamification_service.dart
|
|
//
|
|
// Real API service for the Contributor / Gamification module.
|
|
// Calls the Node.js gamification server at app.eventifyplus.com.
|
|
|
|
import '../../../core/api/api_client.dart';
|
|
import '../../../core/api/api_endpoints.dart';
|
|
import '../../../core/storage/token_storage.dart';
|
|
import '../models/gamification_models.dart';
|
|
|
|
class GamificationService {
|
|
final ApiClient _api = ApiClient();
|
|
|
|
/// Helper: get current user's email for API calls.
|
|
Future<String> _getUserEmail() async {
|
|
final email = await TokenStorage.getUsername();
|
|
return email ?? '';
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Dashboard (profile + submissions)
|
|
// GET /v1/gamification/dashboard?user_id={email}
|
|
// ---------------------------------------------------------------------------
|
|
Future<DashboardResponse> getDashboard() async {
|
|
final email = await _getUserEmail();
|
|
final url = '${ApiEndpoints.gamificationDashboard}?user_id=$email';
|
|
final res = await _api.post(url, requiresAuth: false);
|
|
|
|
final profileJson = res['profile'] as Map<String, dynamic>? ?? {};
|
|
final rawSubs = res['submissions'] as List? ?? [];
|
|
|
|
final submissions = rawSubs
|
|
.map((s) => SubmissionModel.fromJson(Map<String, dynamic>.from(s as Map)))
|
|
.toList();
|
|
|
|
return DashboardResponse(
|
|
profile: UserGamificationProfile.fromJson(profileJson),
|
|
submissions: submissions,
|
|
);
|
|
}
|
|
|
|
/// Convenience — returns just the profile (backward-compatible with provider).
|
|
Future<UserGamificationProfile> getProfile() async {
|
|
final dashboard = await getDashboard();
|
|
return dashboard.profile;
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Leaderboard
|
|
// GET /v1/gamification/leaderboard?period=all|month&district=X&user_id=Y&limit=50
|
|
// ---------------------------------------------------------------------------
|
|
Future<LeaderboardResponse> getLeaderboard({
|
|
required String district,
|
|
required String timePeriod,
|
|
}) async {
|
|
final email = await _getUserEmail();
|
|
|
|
// Map Flutter filter values to API params
|
|
final period = timePeriod == 'this_month' ? 'month' : 'all';
|
|
|
|
final params = <String, String>{
|
|
'period': period,
|
|
'user_id': email,
|
|
'limit': '50',
|
|
};
|
|
if (district != 'Overall Kerala') {
|
|
params['district'] = district;
|
|
}
|
|
|
|
final query = Uri(queryParameters: params).query;
|
|
final url = '${ApiEndpoints.leaderboard}?$query';
|
|
final res = await _api.post(url, requiresAuth: false);
|
|
|
|
final rawList = res['leaderboard'] as List? ?? [];
|
|
final entries = rawList
|
|
.map((e) => LeaderboardEntry.fromJson(Map<String, dynamic>.from(e as Map)))
|
|
.toList();
|
|
|
|
CurrentUserStats? currentUser;
|
|
if (res['currentUser'] != null && res['currentUser'] is Map) {
|
|
currentUser = CurrentUserStats.fromJson(
|
|
Map<String, dynamic>.from(res['currentUser'] as Map),
|
|
);
|
|
}
|
|
|
|
return LeaderboardResponse(
|
|
entries: entries,
|
|
currentUser: currentUser,
|
|
totalParticipants: (res['totalParticipants'] as num?)?.toInt() ?? entries.length,
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Shop Items
|
|
// GET /v1/shop/items
|
|
// ---------------------------------------------------------------------------
|
|
Future<List<ShopItem>> getShopItems() async {
|
|
final res = await _api.post(ApiEndpoints.shopItems, requiresAuth: false);
|
|
final rawItems = res['items'] as List? ?? [];
|
|
return rawItems
|
|
.map((e) => ShopItem.fromJson(Map<String, dynamic>.from(e as Map)))
|
|
.toList();
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Redeem Item
|
|
// POST /v1/shop/redeem body: { user_id, item_id }
|
|
// ---------------------------------------------------------------------------
|
|
Future<RedemptionRecord> redeemItem(String itemId) async {
|
|
final email = await _getUserEmail();
|
|
final res = await _api.post(
|
|
ApiEndpoints.shopRedeem,
|
|
body: {'user_id': email, 'item_id': itemId},
|
|
requiresAuth: false,
|
|
);
|
|
final voucher = res['voucher'] as Map<String, dynamic>? ?? res;
|
|
return RedemptionRecord.fromJson(Map<String, dynamic>.from(voucher));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Submit Contribution
|
|
// POST /v1/gamification/submit-event body: event data
|
|
// ---------------------------------------------------------------------------
|
|
Future<void> submitContribution(Map<String, dynamic> data) async {
|
|
final email = await _getUserEmail();
|
|
final body = <String, dynamic>{'user_id': email, ...data};
|
|
await _api.post(
|
|
ApiEndpoints.contributeSubmit,
|
|
body: body,
|
|
requiresAuth: false,
|
|
);
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Achievements
|
|
// TODO: wire to achievements API when available on Node.js server
|
|
// ---------------------------------------------------------------------------
|
|
Future<List<AchievementBadge>> getAchievements() async {
|
|
await Future.delayed(const Duration(milliseconds: 300));
|
|
return const [
|
|
AchievementBadge(
|
|
id: 'badge-01', title: 'First Submission',
|
|
description: 'Submitted your first event.',
|
|
iconName: 'edit', isUnlocked: true, progress: 1.0,
|
|
),
|
|
AchievementBadge(
|
|
id: 'badge-02', title: 'Silver Streak',
|
|
description: 'Reached Silver tier.',
|
|
iconName: 'star', isUnlocked: true, progress: 1.0,
|
|
),
|
|
AchievementBadge(
|
|
id: 'badge-03', title: 'Gold Rush',
|
|
description: 'Reach Gold tier (500 EP).',
|
|
iconName: 'emoji_events', isUnlocked: false, progress: 0.64,
|
|
),
|
|
AchievementBadge(
|
|
id: 'badge-04', title: 'Top 10',
|
|
description: 'Appear in the district leaderboard top 10.',
|
|
iconName: 'leaderboard', isUnlocked: false, progress: 0.5,
|
|
),
|
|
AchievementBadge(
|
|
id: 'badge-05', title: 'Image Pro',
|
|
description: 'Submit 10 events with 3+ images.',
|
|
iconName: 'photo_library', isUnlocked: false, progress: 0.3,
|
|
),
|
|
AchievementBadge(
|
|
id: 'badge-06', title: 'Pioneer',
|
|
description: 'One of the first 100 contributors.',
|
|
iconName: 'verified', isUnlocked: true, progress: 1.0,
|
|
),
|
|
];
|
|
}
|
|
}
|