Update: 2026-08-08 17:35:50
This commit is contained in:
@@ -371,9 +371,48 @@ class SiroGeminiService {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = json_decode($response, true);
|
|
||||||
$text = $data['candidates'][0]['content']['parts'][0]['text'] ?? '';
|
$text = $data['candidates'][0]['content']['parts'][0]['text'] ?? '';
|
||||||
return trim(preg_replace('/```html|```/', '', $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;
|
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";
|
static String get updateTips => "$ride/tips/update.php";
|
||||||
|
|
||||||
//-----------------Help Center------------------
|
//-----------------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 gethelpCenter => "$ride/helpCenter/get.php";
|
||||||
static String get getByIdhelpCenter => "$ride/helpCenter/getById.php";
|
static String get getByIdhelpCenter => "$ride/helpCenter/getById.php";
|
||||||
static String get updatehelpCenter => "$ride/helpCenter/update.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/box_name.dart';
|
||||||
import '../../../../constant/links.dart';
|
import '../../../../constant/links.dart';
|
||||||
import '../../../../constant/style.dart';
|
|
||||||
import '../../../../main.dart';
|
import '../../../../main.dart';
|
||||||
import '../../../../views/widgets/elevated_btn.dart';
|
|
||||||
import '../../../functions/crud.dart';
|
import '../../../functions/crud.dart';
|
||||||
import '../../../functions/encrypt_decrypt.dart';
|
|
||||||
|
|
||||||
class HelpController extends GetxController {
|
class HelpController extends GetxController {
|
||||||
bool isLoading = false;
|
bool isLoading = false;
|
||||||
@@ -20,6 +17,9 @@ class HelpController extends GetxController {
|
|||||||
String status = '';
|
String status = '';
|
||||||
String qustion = '';
|
String qustion = '';
|
||||||
late int indexQuestion = 0;
|
late int indexQuestion = 0;
|
||||||
|
|
||||||
|
Map<int, String> aiReplies = {};
|
||||||
|
|
||||||
getIndex(int i, String qustion1) async {
|
getIndex(int i, String qustion1) async {
|
||||||
indexQuestion = i;
|
indexQuestion = i;
|
||||||
qustion = qustion1;
|
qustion = qustion1;
|
||||||
@@ -38,13 +38,15 @@ class HelpController extends GetxController {
|
|||||||
update();
|
update();
|
||||||
if (d['status'].toString() == 'success') {
|
if (d['status'].toString() == 'success') {
|
||||||
getHelpQuestion();
|
getHelpQuestion();
|
||||||
// Get.snackbar('Feedback data saved successfully'.tr, '',
|
helpQuestionController.clear();
|
||||||
// backgroundColor: AppColor.greenColor,
|
|
||||||
// snackPosition: SnackPosition.BOTTOM);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void getHelpQuestion() async {
|
void getHelpQuestion() async {
|
||||||
|
getHelpQuestionFuture();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> getHelpQuestionFuture() async {
|
||||||
isLoading = true;
|
isLoading = true;
|
||||||
update();
|
update();
|
||||||
var res = await CRUD().get(link: AppLink.gethelpCenter, payload: {
|
var res = await CRUD().get(link: AppLink.gethelpCenter, payload: {
|
||||||
@@ -52,27 +54,9 @@ class HelpController extends GetxController {
|
|||||||
});
|
});
|
||||||
if (res == "failure") {
|
if (res == "failure") {
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
|
helpQuestionDate = {'message': []};
|
||||||
update();
|
update();
|
||||||
Get.defaultDialog(
|
return;
|
||||||
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();
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
helpQuestionDate = jsonDecode(res);
|
helpQuestionDate = jsonDecode(res);
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
@@ -89,14 +73,20 @@ class HelpController extends GetxController {
|
|||||||
status = 'not yet';
|
status = 'not yet';
|
||||||
isLoading = false;
|
isLoading = false;
|
||||||
update();
|
update();
|
||||||
|
} else {
|
||||||
|
status = 'done';
|
||||||
|
helpQuestionRepleyDate = jsonDecode(res);
|
||||||
|
isLoading = false;
|
||||||
|
update();
|
||||||
}
|
}
|
||||||
helpQuestionRepleyDate = jsonDecode(res);
|
|
||||||
isLoading = false;
|
|
||||||
update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
|
final storedReplies = box.read<Map<String, dynamic>>('ai_replies') ?? {};
|
||||||
|
aiReplies = storedReplies
|
||||||
|
.map((key, value) => MapEntry(int.parse(key), value.toString()));
|
||||||
|
|
||||||
getHelpQuestion();
|
getHelpQuestion();
|
||||||
super.onInit();
|
super.onInit();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2717,4 +2717,52 @@ final Map<String, String> ar_jo = {
|
|||||||
"My Earnings": "أرباحي",
|
"My Earnings": "أرباحي",
|
||||||
"Upcoming Scheduled Rides": "الرحلات المجدولة القادمة",
|
"Upcoming Scheduled Rides": "الرحلات المجدولة القادمة",
|
||||||
"Retry": "إعادة المحاولة",
|
"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/auth/captin/history_captain.dart';
|
||||||
import 'package:siro_driver/controller/functions/launch.dart';
|
import 'package:siro_driver/controller/functions/launch.dart';
|
||||||
|
|
||||||
|
import '../../../widgets/ui/siro_ui.dart';
|
||||||
|
|
||||||
class HistoryDetailsPage extends StatefulWidget {
|
class HistoryDetailsPage extends StatefulWidget {
|
||||||
const HistoryDetailsPage({super.key});
|
const HistoryDetailsPage({super.key});
|
||||||
|
|
||||||
@@ -32,13 +34,8 @@ class _HistoryDetailsPageState extends State<HistoryDetailsPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return SiroPage(
|
||||||
backgroundColor: Colors.grey[50],
|
title: 'Trip Details'.tr,
|
||||||
appBar: AppBar(
|
|
||||||
title: Text('Trip Details'.tr),
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
elevation: 1,
|
|
||||||
),
|
|
||||||
body: GetBuilder<HistoryCaptainController>(
|
body: GetBuilder<HistoryCaptainController>(
|
||||||
builder: (controller) {
|
builder: (controller) {
|
||||||
if (controller.isloading) {
|
if (controller.isloading) {
|
||||||
@@ -256,12 +253,9 @@ class _DetailCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Card(
|
return Padding(
|
||||||
elevation: 2,
|
padding: const EdgeInsets.only(bottom: 16.0),
|
||||||
shadowColor: Colors.black.withValues(alpha: 0.05),
|
child: SiroCard(
|
||||||
margin: const EdgeInsets.only(bottom: 16.0),
|
|
||||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
|||||||
@@ -440,7 +440,7 @@ class UserHeader extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
otherAccountsPictures: [
|
otherAccountsPictures: [
|
||||||
Container(
|
Container(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 5),
|
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 4),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.white.withValues(alpha: 0.15),
|
color: Colors.white.withValues(alpha: 0.15),
|
||||||
borderRadius: BorderRadius.circular(999),
|
borderRadius: BorderRadius.circular(999),
|
||||||
|
|||||||
@@ -88,6 +88,13 @@ class HelpDetailsReplayPage extends StatelessWidget {
|
|||||||
replayData == null ||
|
replayData == null ||
|
||||||
EncryptionHelper.instance.decryptData(replayData['replay']).toString() == 'not yet';
|
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(
|
return Text(
|
||||||
isNoReply
|
isNoReply
|
||||||
? 'No Response yet.'.tr
|
? 'No Response yet.'.tr
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ class DriverInsurancePage extends StatelessWidget {
|
|||||||
onRefresh: c.refreshAll,
|
onRefresh: c.refreshAll,
|
||||||
color: SiroUi.interactive(context),
|
color: SiroUi.interactive(context),
|
||||||
child: ListView(
|
child: ListView(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
padding: SiroUi.pageInsets,
|
padding: SiroUi.pageInsets,
|
||||||
children:
|
children:
|
||||||
c.hasPolicy ? _policyView(context, c) : _plansView(context, c),
|
c.hasPolicy ? _policyView(context, c) : _plansView(context, c),
|
||||||
@@ -157,8 +158,6 @@ class DriverInsurancePage extends StatelessWidget {
|
|||||||
message:
|
message:
|
||||||
'Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.'
|
'Insurance plans have not been launched in your area yet. Once a partner plan is available it will appear here automatically.'
|
||||||
.tr,
|
.tr,
|
||||||
actionLabel: 'Refresh'.tr,
|
|
||||||
onAction: c.isLoading.value ? null : () => c.refreshAll(),
|
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ class DriverScheduledRidesPage extends StatelessWidget {
|
|||||||
onRefresh: controller.fetch,
|
onRefresh: controller.fetch,
|
||||||
color: SiroUi.interactive(context),
|
color: SiroUi.interactive(context),
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
padding: SiroUi.pageInsets,
|
padding: SiroUi.pageInsets,
|
||||||
itemCount: controller.bookings.length,
|
itemCount: controller.bookings.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import '../../../constant/links.dart';
|
|||||||
import '../../../constant/style.dart';
|
import '../../../constant/style.dart';
|
||||||
import '../../../controller/functions/crud.dart';
|
import '../../../controller/functions/crud.dart';
|
||||||
import '../../../main.dart';
|
import '../../../main.dart';
|
||||||
|
import '../../widgets/ui/siro_ui.dart';
|
||||||
|
|
||||||
// --- ملاحظات هامة ---
|
// --- ملاحظات هامة ---
|
||||||
// 1. تأكد من إضافة الرابط الجديد إلى ملف AppLink الخاص بك:
|
// 1. تأكد من إضافة الرابط الجديد إلى ملف AppLink الخاص بك:
|
||||||
@@ -162,8 +163,8 @@ class _EcashDriverPaymentScreenState extends State<EcashDriverPaymentScreen> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return SiroPage(
|
||||||
appBar: AppBar(title: Text('Complete Payment'.tr)),
|
title: 'Complete Payment'.tr,
|
||||||
body: WebViewWidget(controller: _controller),
|
body: WebViewWidget(controller: _controller),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import '../../../constant/links.dart';
|
import '../../../constant/links.dart';
|
||||||
import '../../../controller/functions/crud.dart';
|
import '../../../controller/functions/crud.dart';
|
||||||
|
import '../../widgets/ui/siro_ui.dart';
|
||||||
|
|
||||||
class PaymentScreenCliq extends StatefulWidget {
|
class PaymentScreenCliq extends StatefulWidget {
|
||||||
final double amount;
|
final double amount;
|
||||||
@@ -89,9 +90,8 @@ class _PaymentScreenCliqState extends State<PaymentScreenCliq> with SingleTicker
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return SiroPage(
|
||||||
backgroundColor: Colors.grey[50],
|
title: "Cliq Payment",
|
||||||
appBar: AppBar(title: const Text("Cliq Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
|
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import 'package:get/get.dart';
|
|||||||
import 'package:intl/intl.dart';
|
import 'package:intl/intl.dart';
|
||||||
import '../../../constant/links.dart';
|
import '../../../constant/links.dart';
|
||||||
import '../../../controller/functions/crud.dart';
|
import '../../../controller/functions/crud.dart';
|
||||||
|
import '../../widgets/ui/siro_ui.dart';
|
||||||
|
|
||||||
class PaymentScreenMtn extends StatefulWidget {
|
class PaymentScreenMtn extends StatefulWidget {
|
||||||
final double amount;
|
final double amount;
|
||||||
@@ -89,9 +90,8 @@ class _PaymentScreenMtnState extends State<PaymentScreenMtn> with SingleTickerPr
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Scaffold(
|
return SiroPage(
|
||||||
backgroundColor: Colors.grey[50],
|
title: "MTN Payment",
|
||||||
appBar: AppBar(title: const Text("MTN Payment"), centerTitle: true, backgroundColor: Colors.white, foregroundColor: Colors.black),
|
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:get/get.dart';
|
|||||||
|
|
||||||
import '../../../constant/finance_design_system.dart';
|
import '../../../constant/finance_design_system.dart';
|
||||||
import '../../../controller/home/captin/behavior_controller.dart';
|
import '../../../controller/home/captin/behavior_controller.dart';
|
||||||
|
import '../../widgets/ui/siro_ui.dart';
|
||||||
|
|
||||||
class BehaviorPage extends StatelessWidget {
|
class BehaviorPage extends StatelessWidget {
|
||||||
const BehaviorPage({super.key});
|
const BehaviorPage({super.key});
|
||||||
@@ -12,23 +13,11 @@ class BehaviorPage extends StatelessWidget {
|
|||||||
final controller = Get.put(DriverBehaviorController());
|
final controller = Get.put(DriverBehaviorController());
|
||||||
controller.fetchDriverBehavior();
|
controller.fetchDriverBehavior();
|
||||||
|
|
||||||
return Scaffold(
|
return SiroPage(
|
||||||
backgroundColor: FinanceDesignSystem.backgroundColor,
|
title: 'Driver Behavior'.tr,
|
||||||
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),
|
|
||||||
),
|
|
||||||
body: Obx(() {
|
body: Obx(() {
|
||||||
if (controller.isLoading.value) {
|
if (controller.isLoading.value) {
|
||||||
return Center(
|
return const SiroLoading();
|
||||||
child: CircularProgressIndicator(
|
|
||||||
color: FinanceDesignSystem.accentBlue));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
double score = controller.overallScore.value;
|
double score = controller.overallScore.value;
|
||||||
|
|||||||
@@ -31,8 +31,8 @@ class EarningsPage extends StatelessWidget {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
// شريط العنوان يأتي من الثيم — كان يبني ألوانه يدوياً في كل صفحة،
|
// شريط العنوان يأتي من الثيم — كان يبني ألوانه يدوياً في كل صفحة،
|
||||||
// فيختلف ارتفاعه ووزن خطه من شاشة لأخرى.
|
// فيختلف ارتفاعه ووزن خطه من شاشة لأخرى.
|
||||||
return Scaffold(
|
return SiroPage(
|
||||||
appBar: AppBar(title: Text('My Earnings'.tr)),
|
title: 'My Earnings'.tr,
|
||||||
body: GetBuilder<EarningsController>(
|
body: GetBuilder<EarningsController>(
|
||||||
builder: (ctl) {
|
builder: (ctl) {
|
||||||
if (ctl.isLoading && ctl.summary == null) {
|
if (ctl.isLoading && ctl.summary == null) {
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ class StatSummaryCard extends StatelessWidget {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
// رأسي 12 لا 16: البطاقة داخل شبكة بارتفاع ثابت، و16 كانت تتجاوزه
|
// رأسي 8 لا 12: البطاقة داخل شبكة بارتفاع ثابت، و12 كانت تتجاوزه
|
||||||
// بنحو 10px مع خط الرقم مقاس 22.
|
// قليلاً مع خط الرقم مقاس 22.
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: FinanceDesignSystem.cardColor,
|
color: FinanceDesignSystem.cardColor,
|
||||||
borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius),
|
borderRadius: BorderRadius.circular(FinanceDesignSystem.cardRadius),
|
||||||
|
|||||||
@@ -1109,6 +1109,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "8.1.0"
|
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:
|
graphs:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
Reference in New Issue
Block a user