Update: 2026-06-15 01:37:40

This commit is contained in:
Hamza-Ayed
2026-06-15 01:37:41 +03:00
parent f021ba5a35
commit 2321b78244
164 changed files with 1356 additions and 1560 deletions

View File

@@ -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();
}
}
}

View File

@@ -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();
}