release: bump version to 1.4(p) (versionCode 14)
- Update versionCode 12 → 14, versionName 1.3(p) → 1.4(p) - Update pubspec.yaml version to 1.4.0+14 - Add CHANGELOG.md with full version history - Update README.md: version badge + changelog section - Desktop Contribute Dashboard rebuilt to match web version - Contributor Dashboard title, 3-tab nav (Contribute/Leaderboard/Achievements) - Two-column submit form, tier milestone progress bar - Desktop leaderboard with podium, filters, rank table (green points) - Desktop achievements 3-column badge grid - Inline Reward Shop with RP balance - Gamification feature module (EP, RP, leaderboard, achievements, shop) - Profile screen redesigned to match web app layout with animations - Home screen bottom sheet date filter chips - Updated API endpoints, login/event detail screens, theme colors - Added Gilroy font suite, responsive layout improvements Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
180
lib/features/gamification/services/gamification_service.dart
Normal file
180
lib/features/gamification/services/gamification_service.dart
Normal file
@@ -0,0 +1,180 @@
|
||||
// lib/features/gamification/services/gamification_service.dart
|
||||
//
|
||||
// Stub service using the real API contract from TechDocs v2.
|
||||
// All methods currently return mock data.
|
||||
// TODO: replace each mock block with a real ApiClient call once
|
||||
// the backend endpoints are live on uat.eventifyplus.com.
|
||||
|
||||
import 'dart:math';
|
||||
import '../models/gamification_models.dart';
|
||||
|
||||
class GamificationService {
|
||||
// ---------------------------------------------------------------------------
|
||||
// User Gamification Profile
|
||||
// TODO: replace with ApiClient.get(ApiEndpoints.gamificationProfile)
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<UserGamificationProfile> getProfile() async {
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
return const UserGamificationProfile(
|
||||
userId: 'mock-user-001',
|
||||
lifetimeEp: 320,
|
||||
currentEp: 70,
|
||||
currentRp: 45,
|
||||
tier: ContributorTier.SILVER,
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Leaderboard
|
||||
// district: 'Overall Kerala' | 'Thiruvananthapuram' | 'Kollam' | ...
|
||||
// timePeriod: 'all_time' | 'this_month'
|
||||
// TODO: replace with ApiClient.get(ApiEndpoints.leaderboard, params: {'district': district, 'period': timePeriod})
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<List<LeaderboardEntry>> getLeaderboard({
|
||||
required String district,
|
||||
required String timePeriod,
|
||||
}) async {
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
// Realistic mock names per district
|
||||
final names = [
|
||||
'Annette Black', 'Jerome Bell', 'Theresa Webb', 'Courtney Henry',
|
||||
'Cameron Williamson', 'Dianne Russell', 'Wade Warren', 'Albert Flores',
|
||||
'Kristin Watson', 'Guy Hawkins',
|
||||
];
|
||||
|
||||
final rng = Random(district.hashCode ^ timePeriod.hashCode);
|
||||
final baseEp = timePeriod == 'this_month' ? 800 : 4500;
|
||||
|
||||
final entries = List.generate(10, (i) {
|
||||
final ep = baseEp - (i * (timePeriod == 'this_month' ? 55 : 280)) + rng.nextInt(30);
|
||||
return LeaderboardEntry(
|
||||
rank: i + 1,
|
||||
username: names[i],
|
||||
lifetimeEp: ep,
|
||||
tier: tierFromEp(ep),
|
||||
eventsCount: 149 - i * 12,
|
||||
isCurrentUser: i == 7, // mock: current user is rank 8
|
||||
);
|
||||
});
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Redeem Shop Items
|
||||
// TODO: replace with ApiClient.get(ApiEndpoints.shopItems)
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<List<ShopItem>> getShopItems() async {
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
return const [
|
||||
ShopItem(
|
||||
id: 'item-001',
|
||||
name: 'Amazon ₹500 Voucher',
|
||||
description: 'Redeem for any purchase on Amazon India.',
|
||||
rpCost: 50,
|
||||
stockQuantity: 20,
|
||||
),
|
||||
ShopItem(
|
||||
id: 'item-002',
|
||||
name: 'Swiggy ₹200 Voucher',
|
||||
description: 'Free food delivery credit on Swiggy.',
|
||||
rpCost: 20,
|
||||
stockQuantity: 35,
|
||||
),
|
||||
ShopItem(
|
||||
id: 'item-003',
|
||||
name: 'Eventify Pro — 1 Month',
|
||||
description: 'Premium access to Eventify.Plus features.',
|
||||
rpCost: 30,
|
||||
stockQuantity: 100,
|
||||
),
|
||||
ShopItem(
|
||||
id: 'item-004',
|
||||
name: 'Zomato ₹150 Voucher',
|
||||
description: 'Discount on your next Zomato order.',
|
||||
rpCost: 15,
|
||||
stockQuantity: 50,
|
||||
),
|
||||
ShopItem(
|
||||
id: 'item-005',
|
||||
name: 'BookMyShow ₹300 Voucher',
|
||||
description: 'Movie & event ticket credit on BookMyShow.',
|
||||
rpCost: 30,
|
||||
stockQuantity: 15,
|
||||
),
|
||||
ShopItem(
|
||||
id: 'item-006',
|
||||
name: 'Exclusive Badge',
|
||||
description: 'Rare "Pioneer" badge for your profile.',
|
||||
rpCost: 5,
|
||||
stockQuantity: 0, // out of stock
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Redeem an item
|
||||
// TODO: replace with ApiClient.post(ApiEndpoints.shopRedeem, body: {'item_id': itemId})
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<RedemptionRecord> redeemItem(String itemId) async {
|
||||
await Future.delayed(const Duration(milliseconds: 600));
|
||||
// Generate a fake voucher code
|
||||
final code = 'EVT-${itemId.toUpperCase().replaceAll('-', '').substring(0, 4)}-${Random().nextInt(9000) + 1000}';
|
||||
return RedemptionRecord(
|
||||
id: 'redemption-${DateTime.now().millisecondsSinceEpoch}',
|
||||
itemId: itemId,
|
||||
rpSpent: 0, // provider will look up cost
|
||||
voucherCode: code,
|
||||
timestamp: DateTime.now(),
|
||||
);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Submit Contribution
|
||||
// TODO: replace with ApiClient.post(ApiEndpoints.contributeSubmit, body: data)
|
||||
// ---------------------------------------------------------------------------
|
||||
Future<void> submitContribution(Map<String, dynamic> data) async {
|
||||
await Future.delayed(const Duration(milliseconds: 800));
|
||||
// Mock always succeeds
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Achievements
|
||||
// ---------------------------------------------------------------------------
|
||||
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,
|
||||
),
|
||||
];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user