Update: 2026-08-08 17:35:50
This commit is contained in:
@@ -371,9 +371,48 @@ class SiroGeminiService {
|
||||
return null;
|
||||
}
|
||||
|
||||
$data = json_decode($response, true);
|
||||
$text = $data['candidates'][0]['content']['parts'][0]['text'] ?? '';
|
||||
return trim(preg_replace('/```html|```/', '', $text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Answers a driver support inquiry acting as Siro's smart assistant.
|
||||
*/
|
||||
public function answerSupportInquiry(
|
||||
string $question,
|
||||
array $driverContext,
|
||||
string $model = 'gemini-flash-lite-latest'
|
||||
): ?string {
|
||||
if (!$this->apiKey) return null;
|
||||
|
||||
$contextJson = json_encode($driverContext, JSON_UNESCAPED_UNICODE);
|
||||
|
||||
$prompt = "
|
||||
أنت مساعد الدعم الفني الذكي لتطبيق 'سيرو' (Siro) لنقل الركاب.
|
||||
وظيفتك مساعدة السائقين (الكباتن) والإجابة على استفساراتهم باحترافية، ودقة، وبلهجة ودية ومهذبة جداً.
|
||||
|
||||
بيانات السائق الحالية (سياق مهم للإجابة على سؤاله):
|
||||
$contextJson
|
||||
|
||||
سؤال السائق:
|
||||
\"$question\"
|
||||
|
||||
المطلوب منك:
|
||||
1. الإجابة مباشرة على سؤال السائق بناءً على السياق المعطى إذا كان يخص رحلاته أو رصيده المذكور في السياق.
|
||||
2. إذا سأل عن إجراءات عامة، أجب كخبير في سيرو.
|
||||
3. اجعل الإجابة ودية وواضحة جداً وقصيرة.
|
||||
4. الرد يكون باللغة العربية حصراً.
|
||||
5. لا تذكر تفاصيل تقنية (مثل json أو database)، بل تحدث كإنسان يقدم الدعم.
|
||||
";
|
||||
|
||||
$prompt .= "\n\nالرجاء إرجاع الإجابة بصيغة JSON فقط كالتالي:\n{\n \"reply\": \"نص الإجابة هنا\"\n}";
|
||||
|
||||
$response = $this->callGemini($prompt, $model);
|
||||
if ($response && isset($response['reply'])) {
|
||||
return $response['reply'];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/../../connect.php';
|
||||
require_once __DIR__ . '/../../core/Services/SiroGeminiService.php';
|
||||
require_once __DIR__ . '/../../encrypt_decrypt.php';
|
||||
|
||||
// Variables from connect.php (JWT token)
|
||||
global $user_id, $role, $con;
|
||||
|
||||
if (!$user_id) {
|
||||
jsonError("Unauthorized");
|
||||
exit;
|
||||
}
|
||||
|
||||
$driverID = $user_id;
|
||||
$helpQuestion = filterRequest("helpQuestion");
|
||||
|
||||
if (empty($helpQuestion)) {
|
||||
jsonError("Missing parameters");
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
global $encryptionHelper;
|
||||
|
||||
// 1. Fetch driver info (Decrypting names and phone)
|
||||
$stmt = $con->prepare("SELECT `first_name`, `last_name`, `phone` FROM `driver` WHERE `id` = :id");
|
||||
$stmt->bindParam(':id', $driverID);
|
||||
$stmt->execute();
|
||||
$driverRow = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
$driverInfo = [];
|
||||
if ($driverRow) {
|
||||
$driverInfo = [
|
||||
'first_name' => $driverRow['first_name'] ? $encryptionHelper->decryptData($driverRow['first_name']) : '',
|
||||
'last_name' => $driverRow['last_name'] ? $encryptionHelper->decryptData($driverRow['last_name']) : '',
|
||||
'phone' => $driverRow['phone'] ? $encryptionHelper->decryptData($driverRow['phone']) : '',
|
||||
'user_type' => $role
|
||||
];
|
||||
}
|
||||
|
||||
// 2. Fetch wallet balance using S2S
|
||||
$walletServer = getenv('PAYMENT_SERVER_URL') ?: 'https://wallet.siromove.com';
|
||||
$walletUrl = "$walletServer/v2/main/ride/driverWallet/get_s2s_wallet_dashboard.php";
|
||||
$ch = curl_init($walletUrl);
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => http_build_query(["driverID" => $driverID]),
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_TIMEOUT => 5,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
'Content-Type: application/x-www-form-urlencoded',
|
||||
'X-S2S-Api-Key: ' . getenv('S2S_SHARED_KEY')
|
||||
]
|
||||
]);
|
||||
$s2sRes = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
$totalWallet = 0.0;
|
||||
if ($httpCode === 200 && $s2sRes) {
|
||||
$resDecoded = json_decode($s2sRes, true);
|
||||
if ($resDecoded && isset($resDecoded['status']) && $resDecoded['status'] === 'success') {
|
||||
$totalWallet = (float)($resDecoded['message']['totalWallet'] ?? 0.0);
|
||||
}
|
||||
}
|
||||
$walletInfo = ['balance' => $totalWallet];
|
||||
|
||||
// 3. Fetch recent trips (Correct table is `ride` and correct columns)
|
||||
$recentTrips = [];
|
||||
try {
|
||||
$stmt3 = $con->prepare("SELECT `id`, `date`, `time`, `status`, `price`, `paymentMethod` FROM `ride` WHERE `driver_id` = :id ORDER BY `id` DESC LIMIT 3");
|
||||
$stmt3->bindParam(':id', $driverID);
|
||||
$stmt3->execute();
|
||||
$recentTrips = $stmt3->fetchAll(PDO::FETCH_ASSOC) ?: [];
|
||||
} catch (PDOException $e) {
|
||||
// Ignored
|
||||
}
|
||||
|
||||
$contextData = [
|
||||
'driver_info' => $driverInfo,
|
||||
'wallet_balance' => $walletInfo,
|
||||
'recent_trips' => $recentTrips,
|
||||
];
|
||||
|
||||
// 4. Call Gemini
|
||||
$geminiService = new SiroGeminiService();
|
||||
$aiReply = $geminiService->answerSupportInquiry($helpQuestion, $contextData);
|
||||
|
||||
if (!$aiReply) {
|
||||
$aiReply = "مرحباً كابتن، استلمنا استفسارك ولكن المساعد الذكي غير متاح حالياً. سيقوم فريق الدعم بمراجعة رسالتك قريباً.";
|
||||
}
|
||||
|
||||
// 5. Insert into helpCenter
|
||||
$sql = "INSERT INTO `helpCenter` (`driverID`, `helpQuestion`, `replay`) VALUES (:driverID, :helpQuestion, :replay)";
|
||||
$stmt4 = $con->prepare($sql);
|
||||
$stmt4->bindParam(':driverID', $driverID);
|
||||
$stmt4->bindParam(':helpQuestion', $helpQuestion);
|
||||
$stmt4->bindParam(':replay', $aiReply);
|
||||
$stmt4->execute();
|
||||
|
||||
if ($stmt4->rowCount() > 0) {
|
||||
jsonSuccess(null, "Help question saved and answered");
|
||||
} else {
|
||||
jsonError("Failed to save help question");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
jsonError("An error occurred while processing your request.");
|
||||
}
|
||||
?>
|
||||
@@ -469,7 +469,7 @@ class AppLink {
|
||||
static String get updateTips => "$ride/tips/update.php";
|
||||
|
||||
//-----------------Help Center------------------
|
||||
static String get addhelpCenter => "$ride/helpCenter/add.php";
|
||||
static String get addhelpCenter => "$ride/helpCenter/ask_gemini.php";
|
||||
static String get gethelpCenter => "$ride/helpCenter/get.php";
|
||||
static String get getByIdhelpCenter => "$ride/helpCenter/getById.php";
|
||||
static String get updatehelpCenter => "$ride/helpCenter/update.php";
|
||||
|
||||
@@ -5,11 +5,8 @@ import 'package:get/get.dart';
|
||||
|
||||
import '../../../../constant/box_name.dart';
|
||||
import '../../../../constant/links.dart';
|
||||
import '../../../../constant/style.dart';
|
||||
import '../../../../main.dart';
|
||||
import '../../../../views/widgets/elevated_btn.dart';
|
||||
import '../../../functions/crud.dart';
|
||||
import '../../../functions/encrypt_decrypt.dart';
|
||||
|
||||
class HelpController extends GetxController {
|
||||
bool isLoading = false;
|
||||
@@ -20,6 +17,9 @@ class HelpController extends GetxController {
|
||||
String status = '';
|
||||
String qustion = '';
|
||||
late int indexQuestion = 0;
|
||||
|
||||
Map<int, String> aiReplies = {};
|
||||
|
||||
getIndex(int i, String qustion1) async {
|
||||
indexQuestion = i;
|
||||
qustion = qustion1;
|
||||
@@ -38,13 +38,15 @@ class HelpController extends GetxController {
|
||||
update();
|
||||
if (d['status'].toString() == 'success') {
|
||||
getHelpQuestion();
|
||||
// Get.snackbar('Feedback data saved successfully'.tr, '',
|
||||
// backgroundColor: AppColor.greenColor,
|
||||
// snackPosition: SnackPosition.BOTTOM);
|
||||
helpQuestionController.clear();
|
||||
}
|
||||
}
|
||||
|
||||
void getHelpQuestion() async {
|
||||
getHelpQuestionFuture();
|
||||
}
|
||||
|
||||
Future<void> getHelpQuestionFuture() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
var res = await CRUD().get(link: AppLink.gethelpCenter, payload: {
|
||||
@@ -52,27 +54,9 @@ class HelpController extends GetxController {
|
||||
});
|
||||
if (res == "failure") {
|
||||
isLoading = false;
|
||||
helpQuestionDate = {'message': []};
|
||||
update();
|
||||
Get.defaultDialog(
|
||||
title: 'There is no help Question here'.tr,
|
||||
titleStyle: AppStyle.title,
|
||||
middleText: '',
|
||||
confirm: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
MyElevatedButton(
|
||||
title: 'Add Question'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
}),
|
||||
MyElevatedButton(
|
||||
title: 'Back'.tr,
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
Get.back();
|
||||
}),
|
||||
],
|
||||
));
|
||||
return;
|
||||
}
|
||||
helpQuestionDate = jsonDecode(res);
|
||||
isLoading = false;
|
||||
@@ -89,14 +73,20 @@ class HelpController extends GetxController {
|
||||
status = 'not yet';
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
} else {
|
||||
status = 'done';
|
||||
helpQuestionRepleyDate = jsonDecode(res);
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
final storedReplies = box.read<Map<String, dynamic>>('ai_replies') ?? {};
|
||||
aiReplies = storedReplies
|
||||
.map((key, value) => MapEntry(int.parse(key), value.toString()));
|
||||
|
||||
getHelpQuestion();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
@@ -2717,4 +2717,52 @@ final Map<String, String> ar_jo = {
|
||||
"My Earnings": "أرباحي",
|
||||
"Upcoming Scheduled Rides": "الرحلات المجدولة القادمة",
|
||||
"Retry": "إعادة المحاولة",
|
||||
// Insurance
|
||||
"Cancelling stops future premiums. Outstanding dues remain payable.": "يؤدي الإلغاء إلى إيقاف الأقساط المستقبلية. تبقى المستحقات السابقة واجبة الدفع.",
|
||||
"Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.": "لم يتم إطلاق خطط التأمين في منطقتك بعد. بمجرد توفر خطة الشريك، ستظهر هنا تلقائياً.",
|
||||
"No insurance plans available yet": "لا توجد خطط تأمين متاحة بعد",
|
||||
"Outstanding": "المستحق",
|
||||
"Settled": "المدفوع",
|
||||
"Cancel policy": "إلغاء الوثيقة",
|
||||
"Subscribe": "اشتراك",
|
||||
"Not eligible yet": "غير مؤهل بعد",
|
||||
"Premium ledger": "سجل الأقساط",
|
||||
"Insurance": "التأمين",
|
||||
"Premium": "القسط",
|
||||
"Started on": "بدأ في",
|
||||
"Policy number": "رقم الوثيقة",
|
||||
"rides": "رحلة",
|
||||
"Refresh": "تحديث",
|
||||
|
||||
// Scheduled rides
|
||||
"Could not load scheduled rides": "تعذر تحميل الرحلات المجدولة",
|
||||
"Please check your connection and try again.": "يرجى التحقق من اتصالك والمحاولة مرة أخرى.",
|
||||
"You have no upcoming scheduled rides": "ليس لديك أي رحلات مجدولة قادمة",
|
||||
"Starting Point": "نقطة الانطلاق",
|
||||
"Destination": "الوجهة",
|
||||
"Currency": "دينار أردني",
|
||||
|
||||
// Invites
|
||||
"Invite": "دعوة",
|
||||
"Drivers": "سائقون",
|
||||
"Passengers": "ركاب",
|
||||
"Your Permanent Referral Code": "رمز الدعوة الدائم الخاص بك",
|
||||
"Share via WhatsApp Groups": "شارك عبر مجموعات الواتساب",
|
||||
"Enter Inviter's Code": "أدخل رمز الداعي",
|
||||
"If someone referred you, enter their code below to link accounts and receive rewards.": "إذا قام شخص ما بدعوتك، أدخل رمزه أدناه لربط الحسابات والحصول على المكافآت.",
|
||||
"Enter Code (e.g. AB1234)": "أدخل الرمز (مثال: AB1234)",
|
||||
"Link": "ربط",
|
||||
"Please enter a referral code": "يرجى إدخال رمز الدعوة",
|
||||
"Enter phone": "أدخل رقم الهاتف",
|
||||
"Send Invite": "إرسال دعوة",
|
||||
"Show Invitations": "عرض الدعوات",
|
||||
"Invitations Sent": "الدعوات المرسلة",
|
||||
"No invitation found yet!": "لم يتم العثور على أي دعوات بعد!",
|
||||
"Trip": "رحلة",
|
||||
"Choose from contact": "اختر من جهات الاتصال",
|
||||
"Invite another driver and both get a gift after he completes 100 trips!": "ادعُ سائقًا آخر واحصل كليكما على هدية بعد إكماله 100 رحلة!",
|
||||
"Share this code with passengers and earn rewards when they use it!": "شارك هذا الرمز مع الركاب واكسب مكافآت عند استخدامه!",
|
||||
|
||||
// Help Center
|
||||
"No questions asked yet.": "لم يتم طرح أي أسئلة بعد.",
|
||||
};
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
// This file is no longer used. Gemini is now handled in the PHP backend.
|
||||
@@ -7,6 +7,8 @@ import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:siro_driver/controller/auth/captin/history_captain.dart';
|
||||
import 'package:siro_driver/controller/functions/launch.dart';
|
||||
|
||||
import '../../../widgets/ui/siro_ui.dart';
|
||||
|
||||
class HistoryDetailsPage extends StatefulWidget {
|
||||
const HistoryDetailsPage({super.key});
|
||||
|
||||
@@ -32,13 +34,8 @@ class _HistoryDetailsPageState extends State<HistoryDetailsPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
appBar: AppBar(
|
||||
title: Text('Trip Details'.tr),
|
||||
backgroundColor: Colors.white,
|
||||
elevation: 1,
|
||||
),
|
||||
return SiroPage(
|
||||
title: 'Trip Details'.tr,
|
||||
body: GetBuilder<HistoryCaptainController>(
|
||||
builder: (controller) {
|
||||
if (controller.isloading) {
|
||||
@@ -256,12 +253,9 @@ class _DetailCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Card(
|
||||
elevation: 2,
|
||||
shadowColor: Colors.black.withValues(alpha: 0.05),
|
||||
margin: const EdgeInsets.only(bottom: 16.0),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
child: Padding(
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: SiroCard(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -440,7 +440,7 @@ class UserHeader extends StatelessWidget {
|
||||
),
|
||||
otherAccountsPictures: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: 0.15),
|
||||
borderRadius: BorderRadius.circular(999),
|
||||
|
||||
@@ -88,6 +88,13 @@ class HelpDetailsReplayPage extends StatelessWidget {
|
||||
replayData == null ||
|
||||
EncryptionHelper.instance.decryptData(replayData['replay']).toString() == 'not yet';
|
||||
|
||||
if (isNoReply && helpController.aiReplies.containsKey(helpController.indexQuestion)) {
|
||||
return Text(
|
||||
helpController.aiReplies[helpController.indexQuestion]!,
|
||||
style: Theme.of(context).textTheme.bodyLarge,
|
||||
);
|
||||
}
|
||||
|
||||
return Text(
|
||||
isNoReply
|
||||
? 'No Response yet.'.tr
|
||||
|
||||
@@ -28,6 +28,7 @@ class DriverInsurancePage extends StatelessWidget {
|
||||
onRefresh: c.refreshAll,
|
||||
color: SiroUi.interactive(context),
|
||||
child: ListView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: SiroUi.pageInsets,
|
||||
children:
|
||||
c.hasPolicy ? _policyView(context, c) : _plansView(context, c),
|
||||
@@ -157,8 +158,6 @@ class DriverInsurancePage extends StatelessWidget {
|
||||
message:
|
||||
'Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.'
|
||||
.tr,
|
||||
actionLabel: 'Refresh'.tr,
|
||||
onAction: c.isLoading.value ? null : () => c.refreshAll(),
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ class DriverScheduledRidesPage extends StatelessWidget {
|
||||
onRefresh: controller.fetch,
|
||||
color: SiroUi.interactive(context),
|
||||
child: ListView.builder(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
padding: SiroUi.pageInsets,
|
||||
itemCount: controller.bookings.length,
|
||||
itemBuilder: (context, index) {
|
||||
|
||||
@@ -8,6 +8,7 @@ import '../../../constant/links.dart';
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/crud.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../widgets/ui/siro_ui.dart';
|
||||
|
||||
// --- ملاحظات هامة ---
|
||||
// 1. تأكد من إضافة الرابط الجديد إلى ملف AppLink الخاص بك:
|
||||
@@ -162,8 +163,8 @@ class _EcashDriverPaymentScreenState extends State<EcashDriverPaymentScreen> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('Complete Payment'.tr)),
|
||||
return SiroPage(
|
||||
title: 'Complete Payment'.tr,
|
||||
body: WebViewWidget(controller: _controller),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../controller/functions/crud.dart';
|
||||
import '../../widgets/ui/siro_ui.dart';
|
||||
|
||||
class PaymentScreenCliq extends StatefulWidget {
|
||||
final double amount;
|
||||
@@ -89,9 +90,8 @@ class _PaymentScreenCliqState extends State<PaymentScreenCliq> with SingleTicker
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
appBar: AppBar(title: const Text("Cliq Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
|
||||
return SiroPage(
|
||||
title: "Cliq Payment",
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import '../../../controller/functions/crud.dart';
|
||||
import '../../widgets/ui/siro_ui.dart';
|
||||
|
||||
class PaymentScreenMtn extends StatefulWidget {
|
||||
final double amount;
|
||||
@@ -89,9 +90,8 @@ class _PaymentScreenMtnState extends State<PaymentScreenMtn> with SingleTickerPr
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.grey[50],
|
||||
appBar: AppBar(title: const Text("MTN Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
|
||||
return SiroPage(
|
||||
title: "MTN Payment",
|
||||
body: SafeArea(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
||||
|
||||
import '../../../constant/finance_design_system.dart';
|
||||
import '../../../controller/home/captin/behavior_controller.dart';
|
||||
import '../../widgets/ui/siro_ui.dart';
|
||||
|
||||
class BehaviorPage extends StatelessWidget {
|
||||
const BehaviorPage({super.key});
|
||||
@@ -12,23 +13,11 @@ class BehaviorPage extends StatelessWidget {
|
||||
final controller = Get.put(DriverBehaviorController());
|
||||
controller.fetchDriverBehavior();
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: FinanceDesignSystem.backgroundColor,
|
||||
appBar: AppBar(
|
||||
title: Text('Driver Behavior'.tr,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: FinanceDesignSystem.primaryDark)),
|
||||
centerTitle: true,
|
||||
backgroundColor: FinanceDesignSystem.cardColor,
|
||||
elevation: 0,
|
||||
iconTheme: IconThemeData(color: FinanceDesignSystem.primaryDark),
|
||||
),
|
||||
return SiroPage(
|
||||
title: 'Driver Behavior'.tr,
|
||||
body: Obx(() {
|
||||
if (controller.isLoading.value) {
|
||||
return Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: FinanceDesignSystem.accentBlue));
|
||||
return const SiroLoading();
|
||||
}
|
||||
|
||||
double score = controller.overallScore.value;
|
||||
|
||||
@@ -31,8 +31,8 @@ class EarningsPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
// شريط العنوان يأتي من الثيم — كان يبني ألوانه يدوياً في كل صفحة،
|
||||
// فيختلف ارتفاعه ووزن خطه من شاشة لأخرى.
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text('My Earnings'.tr)),
|
||||
return SiroPage(
|
||||
title: 'My Earnings'.tr,
|
||||
body: GetBuilder<EarningsController>(
|
||||
builder: (ctl) {
|
||||
if (ctl.isLoading && ctl.summary == null) {
|
||||
|
||||
@@ -18,9 +18,9 @@ class StatSummaryCard extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
// رأسي 12 لا 16: البطاقة داخل شبكة بارتفاع ثابت، و16 كانت تتجاوزه
|
||||
// بنحو 10px مع خط الرقم مقاس 22.
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
||||
// رأسي 8 لا 12: البطاقة داخل شبكة بارتفاع ثابت، و12 كانت تتجاوزه
|
||||
// قليلاً مع خط الرقم مقاس 22.
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: FinanceDesignSystem.cardColor,
|
||||
borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius),
|
||||
|
||||
@@ -1109,6 +1109,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.0"
|
||||
google_generative_ai:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_generative_ai
|
||||
sha256: "71f613d0247968992ad87a0eb21650a566869757442ba55a31a81be6746e0d1f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.7"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user