Update: 2026-06-15 01:37:40
This commit is contained in:
@@ -47,9 +47,6 @@ class Challenge {
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════
|
||||
// Controller
|
||||
// ════════════════════════════════════════════
|
||||
|
||||
class ChallengesController extends GetxController {
|
||||
bool isLoading = false;
|
||||
List<Challenge> dailyChallenges = [];
|
||||
@@ -66,6 +63,47 @@ class ChallengesController extends GetxController {
|
||||
void _generateChallenges() {
|
||||
final now = DateTime.now();
|
||||
final isWeekend = now.weekday == 5 || now.weekday == 6; // الجمعة والسبت
|
||||
String country = box.read(BoxName.countryCode) ?? 'Jordan';
|
||||
|
||||
int dailyEarningsTarget;
|
||||
int dailyEarningsReward;
|
||||
int weeklyEarningsTarget;
|
||||
int weeklyEarningsReward;
|
||||
|
||||
String dailyEarningsDescAr;
|
||||
String dailyEarningsDescEn;
|
||||
String weeklyEarningsDescAr;
|
||||
String weeklyEarningsDescEn;
|
||||
|
||||
if (country == 'Syria') {
|
||||
dailyEarningsTarget = 150000;
|
||||
dailyEarningsReward = 100; // 100 pts -> 10,000 SYP
|
||||
weeklyEarningsTarget = 1000000;
|
||||
weeklyEarningsReward = 500; // 500 pts -> 50,000 SYP
|
||||
dailyEarningsDescAr = 'اربح 150,000 ل.س اليوم';
|
||||
dailyEarningsDescEn = 'Earn 150,000 SYP today';
|
||||
weeklyEarningsDescAr = 'اربح 1,000,000 ل.س هذا الأسبوع';
|
||||
weeklyEarningsDescEn = 'Earn 1,000,000 SYP this week';
|
||||
} else if (country == 'Egypt') {
|
||||
dailyEarningsTarget = 300;
|
||||
dailyEarningsReward = 30; // 30 pts -> 30 EGP
|
||||
weeklyEarningsTarget = 2000;
|
||||
weeklyEarningsReward = 200; // 200 pts -> 200 EGP
|
||||
dailyEarningsDescAr = 'اربح 300 ج.م اليوم';
|
||||
dailyEarningsDescEn = 'Earn 300 EGP today';
|
||||
weeklyEarningsDescAr = 'اربح 2000 ج.م هذا الأسبوع';
|
||||
weeklyEarningsDescEn = 'Earn 2000 EGP this week';
|
||||
} else {
|
||||
// Jordan / default
|
||||
dailyEarningsTarget = 30;
|
||||
dailyEarningsReward = 60; // 60 pts -> 3 JOD (60 * 0.05 = 3 JOD)
|
||||
weeklyEarningsTarget = 200;
|
||||
weeklyEarningsReward = 400; // 400 pts -> 20 JOD (400 * 0.05 = 20 JOD)
|
||||
dailyEarningsDescAr = 'اربح 30 د.أ اليوم';
|
||||
dailyEarningsDescEn = 'Earn 30 JOD today';
|
||||
weeklyEarningsDescAr = 'اربح 200 د.أ هذا الأسبوع';
|
||||
weeklyEarningsDescEn = 'Earn 200 JOD this week';
|
||||
}
|
||||
|
||||
dailyChallenges = [
|
||||
Challenge(
|
||||
@@ -77,7 +115,7 @@ class ChallengesController extends GetxController {
|
||||
icon: Icons.local_taxi_rounded,
|
||||
color: const Color(0xFF2196F3),
|
||||
target: 5,
|
||||
reward: 50,
|
||||
reward: country == 'Syria' ? 50 : (country == 'Egypt' ? 50 : 100),
|
||||
type: 'daily',
|
||||
metric: 'trips',
|
||||
),
|
||||
@@ -90,7 +128,7 @@ class ChallengesController extends GetxController {
|
||||
icon: Icons.directions_car_rounded,
|
||||
color: const Color(0xFFFF9800),
|
||||
target: 10,
|
||||
reward: 150,
|
||||
reward: country == 'Syria' ? 150 : (country == 'Egypt' ? 150 : 300),
|
||||
type: 'daily',
|
||||
metric: 'trips',
|
||||
),
|
||||
@@ -98,12 +136,12 @@ class ChallengesController extends GetxController {
|
||||
id: 'daily_earnings',
|
||||
titleEn: 'Money Maker',
|
||||
titleAr: 'صانع المال',
|
||||
descriptionEn: 'Earn 3000 ${CurrencyHelper.currency} today',
|
||||
descriptionAr: 'اربح 3000 ل.س اليوم',
|
||||
descriptionEn: dailyEarningsDescEn,
|
||||
descriptionAr: dailyEarningsDescAr,
|
||||
icon: Icons.monetization_on_rounded,
|
||||
color: const Color(0xFF4CAF50),
|
||||
target: 3000,
|
||||
reward: 100,
|
||||
target: dailyEarningsTarget,
|
||||
reward: dailyEarningsReward,
|
||||
type: 'daily',
|
||||
metric: 'earnings',
|
||||
),
|
||||
@@ -117,7 +155,7 @@ class ChallengesController extends GetxController {
|
||||
icon: Icons.celebration_rounded,
|
||||
color: const Color(0xFFE91E63),
|
||||
target: 8,
|
||||
reward: 200,
|
||||
reward: country == 'Syria' ? 200 : (country == 'Egypt' ? 200 : 400),
|
||||
type: 'daily',
|
||||
metric: 'trips',
|
||||
),
|
||||
@@ -133,7 +171,7 @@ class ChallengesController extends GetxController {
|
||||
icon: Icons.emoji_events_rounded,
|
||||
color: const Color(0xFFFFD700),
|
||||
target: 30,
|
||||
reward: 300,
|
||||
reward: country == 'Syria' ? 300 : (country == 'Egypt' ? 300 : 600),
|
||||
type: 'weekly',
|
||||
metric: 'trips',
|
||||
),
|
||||
@@ -141,12 +179,12 @@ class ChallengesController extends GetxController {
|
||||
id: 'weekly_earnings',
|
||||
titleEn: 'Big Earner',
|
||||
titleAr: 'الربح الكبير',
|
||||
descriptionEn: 'Earn 20,000 ${CurrencyHelper.currency} this week',
|
||||
descriptionAr: 'اربح 20,000 ل.س هذا الأسبوع',
|
||||
descriptionEn: weeklyEarningsDescEn,
|
||||
descriptionAr: weeklyEarningsDescAr,
|
||||
icon: Icons.account_balance_wallet_rounded,
|
||||
color: const Color(0xFF9C27B0),
|
||||
target: 20000,
|
||||
reward: 500,
|
||||
target: weeklyEarningsTarget,
|
||||
reward: weeklyEarningsReward,
|
||||
type: 'weekly',
|
||||
metric: 'earnings',
|
||||
),
|
||||
@@ -159,10 +197,10 @@ class ChallengesController extends GetxController {
|
||||
icon: Icons.timer_rounded,
|
||||
color: const Color(0xFF00BCD4),
|
||||
target: 20,
|
||||
reward: 400,
|
||||
reward: country == 'Syria' ? 400 : (country == 'Egypt' ? 400 : 800),
|
||||
type: 'weekly',
|
||||
metric: 'hours',
|
||||
),
|
||||
)
|
||||
];
|
||||
}
|
||||
|
||||
@@ -203,8 +241,12 @@ class ChallengesController extends GetxController {
|
||||
|
||||
if (todayRes != null && todayRes != 'failure') {
|
||||
var data = jsonDecode(todayRes);
|
||||
todayEarnings = double.tryParse(data['message']?[0]?['todayAmount']?.toString() ?? '0') ?? 0;
|
||||
todayTrips = int.tryParse(data['message']?[0]?['todayCount']?.toString() ?? '0') ?? 0;
|
||||
todayEarnings = double.tryParse(
|
||||
data['message']?[0]?['todayAmount']?.toString() ?? '0') ??
|
||||
0;
|
||||
todayTrips = int.tryParse(
|
||||
data['message']?[0]?['todayCount']?.toString() ?? '0') ??
|
||||
0;
|
||||
}
|
||||
|
||||
// تحديث التحديات اليومية
|
||||
@@ -229,7 +271,9 @@ class ChallengesController extends GetxController {
|
||||
if (weeklyEarningsRes != null && weeklyEarningsRes != 'failure') {
|
||||
var data = jsonDecode(weeklyEarningsRes);
|
||||
if (data['message'] is List && data['message'].isNotEmpty) {
|
||||
weeklyEarnings = double.tryParse(data['message'][0]['totalAmount']?.toString() ?? '0') ?? 0;
|
||||
weeklyEarnings = double.tryParse(
|
||||
data['message'][0]['totalAmount']?.toString() ?? '0') ??
|
||||
0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +291,8 @@ class ChallengesController extends GetxController {
|
||||
if (data['message'] is List) {
|
||||
for (var day in data['message']) {
|
||||
weeklyTrips += int.tryParse(day['trips']?.toString() ?? '0') ?? 0;
|
||||
weeklyHours += double.tryParse(day['hours']?.toString() ?? '0') ?? 0;
|
||||
weeklyHours +=
|
||||
double.tryParse(day['hours']?.toString() ?? '0') ?? 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,8 +318,18 @@ class ChallengesController extends GetxController {
|
||||
update();
|
||||
}
|
||||
|
||||
final Set<String> _claimingChallenges = {};
|
||||
|
||||
bool isClaiming(String challengeId) =>
|
||||
_claimingChallenges.contains(challengeId);
|
||||
|
||||
Future<void> claimReward(Challenge challenge) async {
|
||||
if (!challenge.isCompleted || challenge.isClaimed) return;
|
||||
if (!challenge.isCompleted ||
|
||||
challenge.isClaimed ||
|
||||
_claimingChallenges.contains(challenge.id)) return;
|
||||
|
||||
_claimingChallenges.add(challenge.id);
|
||||
update();
|
||||
|
||||
try {
|
||||
var res = await CRUD().post(
|
||||
@@ -290,10 +345,12 @@ class ChallengesController extends GetxController {
|
||||
challenge.isClaimed = true;
|
||||
box.write('challenge_${challenge.id}_claimed', true);
|
||||
debugPrint('🎉 Claimed ${challenge.reward} points for ${challenge.id}');
|
||||
update();
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ [Challenges] Claim error: $e');
|
||||
} finally {
|
||||
_claimingChallenges.remove(challenge.id);
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -191,6 +191,27 @@ class GamificationController extends GetxController {
|
||||
dailyGoal = (box.read('dailyGoal') ?? 0).toDouble();
|
||||
totalTrips = box.read('gamification_totalTrips') ?? 0;
|
||||
consecutiveDays = box.read('gamification_consecutiveDays') ?? 0;
|
||||
averageRating = (box.read('gamification_averageRating') ?? 5.0).toDouble();
|
||||
totalPoints = box.read('gamification_totalPoints') ?? 0;
|
||||
totalReferrals = box.read('gamification_totalReferrals') ?? 0;
|
||||
dailyEarnings = (box.read('gamification_dailyEarnings') ?? 0.0).toDouble();
|
||||
behaviorScore = (box.read('gamification_behaviorScore') ?? 100.0).toDouble();
|
||||
hardBrakes = box.read('gamification_hardBrakes') ?? 0;
|
||||
maxSpeed = (box.read('gamification_maxSpeed') ?? 0.0).toDouble();
|
||||
}
|
||||
|
||||
// ═══════ حفظ البيانات المحلية (التخزين المؤقت) ═══════
|
||||
void _saveLocalData() {
|
||||
box.write('gamification_totalTrips', totalTrips);
|
||||
box.write('gamification_consecutiveDays', consecutiveDays);
|
||||
box.write('gamification_averageRating', averageRating);
|
||||
box.write('gamification_totalPoints', totalPoints);
|
||||
box.write('gamification_totalReferrals', totalReferrals);
|
||||
box.write('gamification_dailyEarnings', dailyEarnings);
|
||||
box.write('gamification_behaviorScore', behaviorScore);
|
||||
box.write('gamification_hardBrakes', hardBrakes);
|
||||
box.write('gamification_maxSpeed', maxSpeed);
|
||||
box.write('gamification_last_fetch', DateTime.now().toIso8601String());
|
||||
}
|
||||
|
||||
// ═══════ حفظ الهدف اليومي ═══════
|
||||
@@ -330,85 +351,45 @@ class GamificationController extends GetxController {
|
||||
}
|
||||
|
||||
// ═══════ جلب البيانات من السيرفر ═══════
|
||||
Future<void> fetchGamificationData() async {
|
||||
Future<void> fetchGamificationData({bool force = false}) async {
|
||||
// التحقق من التخزين المؤقت (6 ساعات)
|
||||
String? lastFetchStr = box.read('gamification_last_fetch');
|
||||
if (!force && lastFetchStr != null) {
|
||||
try {
|
||||
DateTime lastFetch = DateTime.parse(lastFetchStr);
|
||||
if (DateTime.now().difference(lastFetch).inHours < 6) {
|
||||
debugPrint('ℹ️ [Gamification] Loading cached data (last fetch: $lastFetchStr)');
|
||||
_loadLocalData();
|
||||
_calculateLevel();
|
||||
_updateAchievementProgress();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('⚠️ [Gamification] Failed to parse last fetch time: $e');
|
||||
}
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
try {
|
||||
// 1. جلب عدد الرحلات الكلي
|
||||
var tripRes = await CRUD().get(
|
||||
link: AppLink.getTripCountByCaptain,
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getGamificationDashboard,
|
||||
payload: {'driver_id': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
if (tripRes != null && tripRes != 'failure') {
|
||||
var data = jsonDecode(tripRes);
|
||||
totalTrips =
|
||||
int.tryParse(data['message']?[0]?['count']?.toString() ?? '0') ?? 0;
|
||||
box.write('gamification_totalTrips', totalTrips);
|
||||
}
|
||||
|
||||
// 2. جلب التقييم
|
||||
var rateRes = await CRUD().get(
|
||||
link: AppLink.getDriverRate,
|
||||
payload: {'driver_id': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
if (rateRes != null && rateRes != 'failure') {
|
||||
var data = jsonDecode(rateRes);
|
||||
averageRating =
|
||||
double.tryParse(data['message']?[0]?['rating']?.toString() ?? '5') ??
|
||||
5.0;
|
||||
}
|
||||
|
||||
// 3. جلب النقاط (الرصيد)
|
||||
var pointsRes = await CRUD().getWallet(
|
||||
link: AppLink.getDriverPaymentPoints,
|
||||
payload: {'driverID': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
if (pointsRes != null && pointsRes != 'failure') {
|
||||
var data = jsonDecode(pointsRes);
|
||||
totalPoints = double.tryParse(
|
||||
data['message']?[0]?['total_amount']?.toString() ?? '0')
|
||||
?.abs()
|
||||
.toInt() ??
|
||||
0;
|
||||
}
|
||||
|
||||
// 4. جلب عدد الدعوات
|
||||
var invRes = await CRUD().get(
|
||||
link: AppLink.getInviteDriver,
|
||||
payload: {'driver_id': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
if (invRes != null && invRes != 'failure') {
|
||||
var data = jsonDecode(invRes);
|
||||
if (data['message'] is List) {
|
||||
totalReferrals = (data['message'] as List).length;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. جلب أرباح اليوم
|
||||
var todayRes = await CRUD().getWallet(
|
||||
link: AppLink.getDriverPaymentToday,
|
||||
payload: {'driverID': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
if (todayRes != null && todayRes != 'failure') {
|
||||
var data = jsonDecode(todayRes);
|
||||
dailyEarnings = double.tryParse(
|
||||
data['message']?[0]?['todayAmount']?.toString() ?? '0') ??
|
||||
0;
|
||||
}
|
||||
|
||||
// 6. جلب تقييم سلوك القيادة
|
||||
var behaviorRes = await CRUD().get(
|
||||
link: AppLink.getDriverBehavior,
|
||||
payload: {'driver_id': box.read(BoxName.driverID).toString()},
|
||||
);
|
||||
if (behaviorRes != null && behaviorRes != 'failure') {
|
||||
var data = jsonDecode(behaviorRes);
|
||||
if (data['message'] is List && data['message'].isNotEmpty) {
|
||||
var behavior = data['message'][0];
|
||||
behaviorScore = double.tryParse(behavior['avg_score']?.toString() ?? '100') ?? 100.0;
|
||||
hardBrakes = int.tryParse(behavior['total_hard_brakes']?.toString() ?? '0') ?? 0;
|
||||
maxSpeed = double.tryParse(behavior['max_speed']?.toString() ?? '0') ?? 0.0;
|
||||
if (res != null && res != 'failure') {
|
||||
var data = jsonDecode(res);
|
||||
if (data['status'] == 'success' && data['message'] != null) {
|
||||
var details = data['message'];
|
||||
totalTrips = int.tryParse(details['totalTrips']?.toString() ?? '0') ?? 0;
|
||||
averageRating = double.tryParse(details['averageRating']?.toString() ?? '5.0') ?? 5.0;
|
||||
totalReferrals = int.tryParse(details['totalReferrals']?.toString() ?? '0') ?? 0;
|
||||
dailyEarnings = double.tryParse(details['todayEarnings']?.toString() ?? '0.0') ?? 0.0;
|
||||
behaviorScore = double.tryParse(details['behaviorScore']?.toString() ?? '100.0') ?? 100.0;
|
||||
hardBrakes = int.tryParse(details['hardBrakes']?.toString() ?? '0') ?? 0;
|
||||
maxSpeed = double.tryParse(details['maxSpeed']?.toString() ?? '0.0') ?? 0.0;
|
||||
totalPoints = int.tryParse(details['totalPoints']?.toString() ?? '0') ?? 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -420,6 +401,7 @@ class GamificationController extends GetxController {
|
||||
|
||||
_calculateLevel();
|
||||
_updateAchievementProgress();
|
||||
_saveLocalData();
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user