From 1a0bf1ee32ae89fefe517490c4e0327c4914655b Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Fri, 10 Oct 2025 13:13:00 +0300 Subject: [PATCH] 25-10-10/1 --- lib/constant/links.dart | 3 +- lib/controller/auth/register_controller.dart | 4 +- .../home/map_passenger_controller.dart | 16 +- .../home/profile/complaint_controller.dart | 269 ++++++++-------- lib/controller/local/translations.dart | 18 ++ lib/controller/rate/rate_conroller.dart | 4 +- .../select_driver_mishwari.dart | 4 +- lib/views/home/profile/complaint_page.dart | 289 +++++++++--------- 8 files changed, 302 insertions(+), 305 deletions(-) diff --git a/lib/constant/links.dart b/lib/constant/links.dart index 5472572..b5d2591 100644 --- a/lib/constant/links.dart +++ b/lib/constant/links.dart @@ -28,7 +28,7 @@ class AppLink { // box.read(BoxName.serverChosen) ?? box.read(BoxName.basicLink); // 'https://server.sefer.click/sefer.click/sefer'; static final String server = endPoint; - static String IntaleqCairoServer = endPoint; + static String IntaleqSyriaServer = endPoint; static String IntaleqGizaServer = box.read('Giza'); static String IntaleqAlexandriaServer = box.read('Alexandria'); @@ -187,6 +187,7 @@ class AppLink { //-----------------Feed Back------------------ static String addFeedBack = "$ride/feedBack/add.php"; + static String add_solve_all = "$ride/feedBack/add_solve_all.php"; static String uploadAudio = "$ride/feedBack/upload_audio.php"; static String getFeedBack = "$ride/feedBack/get.php"; static String updateFeedBack = "$ride/feedBack/updateFeedBack.php"; diff --git a/lib/controller/auth/register_controller.dart b/lib/controller/auth/register_controller.dart index e3f9cd3..e39b077 100644 --- a/lib/controller/auth/register_controller.dart +++ b/lib/controller/auth/register_controller.dart @@ -222,7 +222,7 @@ class RegisterController extends GetxController { if (res1 != 'failure') { //Multi-server signup (moved inside the successful registration check) - if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqCairoServer) { + if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqSyriaServer) { List signUp = [ CRUD().post( link: '${AppLink.IntaleqAlexandriaServer}/auth/signup.php', @@ -297,7 +297,7 @@ class RegisterController extends GetxController { ); if (res1 != 'failure') { - if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqCairoServer) { + if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqSyriaServer) { List signUp = [ CRUD().post( link: '${AppLink.IntaleqAlexandriaServer}/auth/signup.php', diff --git a/lib/controller/home/map_passenger_controller.dart b/lib/controller/home/map_passenger_controller.dart index 6231f3c..0db38f3 100644 --- a/lib/controller/home/map_passenger_controller.dart +++ b/lib/controller/home/map_passenger_controller.dart @@ -406,7 +406,7 @@ class MapPassengerController extends GetxController { "order_id": rideId.toString(), // Convert to String "status": 'waiting' }); - if (AppLink.endPoint != AppLink.IntaleqCairoServer) { + if (AppLink.endPoint != AppLink.IntaleqSyriaServer) { CRUD().post( link: "${AppLink.endPoint}/ride/driver_order/update.php", payload: { @@ -418,7 +418,7 @@ class MapPassengerController extends GetxController { "id": rideId.toString(), // Convert to String "status": 'waiting' }); - if (AppLink.endPoint != AppLink.IntaleqCairoServer) { + if (AppLink.endPoint != AppLink.IntaleqSyriaServer) { CRUD().post( link: "${AppLink.endPoint}/ride/rides/update.php", payload: { @@ -430,7 +430,7 @@ class MapPassengerController extends GetxController { "id": rideId.toString(), // Convert to String "status": 'wait' }); - if (AppLink.endPoint != AppLink.IntaleqCairoServer) { + if (AppLink.endPoint != AppLink.IntaleqSyriaServer) { CRUD().post( link: "${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php", @@ -1899,7 +1899,7 @@ class MapPassengerController extends GetxController { try { final response = await CRUD().post( - link: "${AppLink.IntaleqCairoServer}/ride/rides/add.php", + link: "${AppLink.IntaleqSyriaServer}/ride/rides/add.php", payload: payload); if (response is String) { final parsedValue = jsonDecode(response); @@ -2566,7 +2566,7 @@ class MapPassengerController extends GetxController { } // Default case - box.write(BoxName.serverChosen, AppLink.IntaleqCairoServer); + box.write(BoxName.serverChosen, AppLink.IntaleqSyriaServer); return 'Cairo'; } @@ -3307,7 +3307,7 @@ class MapPassengerController extends GetxController { "id": rideId.toString(), // Convert to String "status": 'notApplyFromAnyDriver' }); - if (AppLink.endPoint != AppLink.IntaleqCairoServer) { + if (AppLink.endPoint != AppLink.IntaleqSyriaServer) { CRUD().post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: { "id": rideId.toString(), // Convert to String "status": 'notApplyFromAnyDriver' @@ -5667,7 +5667,7 @@ class MapPassengerController extends GetxController { // Get.snackbar('Success'.tr, 'Trip booked successfully'.tr); var id = response['message']['id'].toString(); await CRUD().post( - link: '${AppLink.IntaleqCairoServer}/ride/rides/add.php', + link: '${AppLink.IntaleqSyriaServer}/ride/rides/add.php', payload: { "start_location": '${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}', @@ -5696,7 +5696,7 @@ class MapPassengerController extends GetxController { Log.print('Unexpected response type: ${value.runtimeType}'); } }); - if (AppLink.endPoint != AppLink.IntaleqCairoServer) { + if (AppLink.endPoint != AppLink.IntaleqSyriaServer) { await CRUD().post( link: "${AppLink.endPoint}/ride/mishwari/add.php", payload: tripData); diff --git a/lib/controller/home/profile/complaint_controller.dart b/lib/controller/home/profile/complaint_controller.dart index 8418989..583bf86 100644 --- a/lib/controller/home/profile/complaint_controller.dart +++ b/lib/controller/home/profile/complaint_controller.dart @@ -15,21 +15,56 @@ import 'package:http_parser/http_parser.dart'; import 'package:mime/mime.dart'; import '../../../constant/api_key.dart'; +import '../../../env/env.dart'; import '../../../print.dart'; import '../../../views/widgets/mydialoug.dart'; +import '../../functions/encrypt_decrypt.dart'; class ComplaintController extends GetxController { bool isLoading = false; final formKey = GlobalKey(); final complaintController = TextEditingController(); - final suggestionController = TextEditingController(); + List feedBack = []; + Map? passengerReport; + Map? driverReport; + + var isUploading = false.obs; + var uploadSuccess = false.obs; + String audioLink = ''; // سيتم تخزين رابط الصوت هنا بعد الرفع + @override void onInit() { super.onInit(); getLatestRidesForPassengers(); } + // --- دالة مخصصة لعرض إشعارات Snackbar بشكل جميل --- + void _showCustomSnackbar(String title, String message, + {bool isError = false}) { + Get.snackbar( + '', // العنوان سيتم التعامل معه عبر titleText + '', // الرسالة سيتم التعامل معها عبر messageText + titleText: Text(title.tr, + style: const TextStyle( + color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16)), + messageText: Text(message.tr, + style: const TextStyle(color: Colors.white, fontSize: 14)), + backgroundColor: isError + ? AppColor.redColor.withOpacity(0.95) + : const Color.fromARGB(255, 6, 148, 79).withOpacity(0.95), + icon: Icon(isError ? Icons.error_outline : Icons.check_circle_outline, + color: Colors.white, size: 28), + borderRadius: 12, + margin: const EdgeInsets.all(15), + padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 18), + snackPosition: SnackPosition.BOTTOM, + duration: const Duration(seconds: 4), + colorText: Colors.white, + ); + } + + // --- هذه الدالة تبقى كما هي لجلب بيانات الرحلة --- getLatestRidesForPassengers() async { isLoading = true; update(); @@ -40,54 +75,24 @@ class ComplaintController extends GetxController { var d = jsonDecode(res)['message']; feedBack = d; } - isLoading = false; update(); } - void addComplaint() async { - isLoading = true; - update(); - var res = await CRUD().post(link: AppLink.addFeedBack, payload: { - 'passengerId': box.read(BoxName.passengerID).toString(), - 'feedBack': complaintController.text - }); - // var d = jsonDecode(res); - if (res != 'failure') { - Get.defaultDialog( - title: 'Success'.tr, - titleStyle: AppStyle.title, - middleText: 'Complaint data saved successfully'.tr, - middleTextStyle: AppStyle.title, - confirm: MyElevatedButton( - kolor: AppColor.greenColor, - title: 'Ok'.tr, - onPressed: () { - Get.back(); - // Get.back(); - })); - } - - isLoading = false; - update(); - } - - var isUploading = false.obs; - var uploadSuccess = false.obs; - late String audioLink = ''; + // --- تم تحديث الهيدر في هذه الدالة --- Future uploadAudioFile(File audioFile) async { try { isUploading.value = true; + update(); - // Prepare the file upload - var uri = Uri.parse('${AppLink.IntaleqCairoServer}/upload_audio.php'); + var uri = Uri.parse('${AppLink.IntaleqSyriaServer}/upload_audio.php'); var request = http.MultipartRequest('POST', uri); + String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]; - // Add the file to the request with MIME type var mimeType = lookupMimeType(audioFile.path); + // ** التعديل: تم استخدام نفس هيدر التوثيق ** request.headers.addAll({ - 'Authorization': - 'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}', + 'Authorization': 'Bearer $token', }); request.files.add( await http.MultipartFile.fromPath( @@ -97,142 +102,114 @@ class ComplaintController extends GetxController { ), ); - // Send the request var response = await request.send(); - - // Convert response to string for parsing var responseBody = await http.Response.fromStream(response); - // After the upload request if (response.statusCode == 200) { var jsonResponse = jsonDecode(responseBody.body); - if (jsonResponse['status'] == 'Audio file uploaded successfully.') { uploadSuccess.value = true; - audioLink = jsonResponse['link']; // Get the audio link + audioLink = jsonResponse['link']; // تخزين الرابط في المتغير Get.back(); - Get.snackbar('Success'.tr, 'Audio uploaded successfully.'.tr, - backgroundColor: const Color.fromARGB(255, 89, 185, 115)); + // استخدام الـ Snackbar الجديد + _showCustomSnackbar('Success', 'Audio uploaded successfully.'); } else { uploadSuccess.value = false; + _showCustomSnackbar('Error', 'Failed to upload audio file.', + isError: true); } } else { uploadSuccess.value = false; + _showCustomSnackbar('Error', 'Server error: ${response.statusCode}', + isError: true); } } catch (e) { uploadSuccess.value = false; + _showCustomSnackbar( + 'Error', 'An application error occurred during upload.', + isError: true); } finally { isUploading.value = false; + update(); } } - var customerServiceSolutions; - var passengerReport; - var driverReport; - var isloading = false; - Future geminiAudio(payload, String audioLink, String complain) async { - String prompt = ''' - Analyze the following complaint between a passenger and driver in a ride-hailing service. The complaint includes an audio link for reference. Provide two possible solutions for customer service to resolve the issue, and generate a detailed report for both the passenger and the driver. - - Complaint details: - - Passenger: $complain - - Driver: [Driver's complaint] - - Ride Information: {ride details such as start_location, end_location, date, price, status, and rating details} - - Audio Link: [$audioLink] - - Output the result in JSON format with the following structure: - { - "customerServiceSolutions": [ - "solution1", - "solution2" - ], - "passengerReport": { - "solution": "Passenger's solution" if passenger right, - "complaint": "Passenger's complaint", - "rideDetails": {detailed ride info} - }, - "driverReport": { - "complaint": "Driver's complaint", - "rideDetails": {detailed ride info} + // --- الدالة الجديدة التي تتصل بالسكريبت الجديد --- + Future submitComplaintToServer() async { + // 1. التحقق من صحة الفورم + if (!formKey.currentState!.validate() || complaintController.text.isEmpty) { + // استخدام الـ Snackbar الجديد + _showCustomSnackbar( + 'Error', 'Please describe your issue before submitting.', + isError: true); + return; } - } the response in arabic language with egypt - '''; - var requestBody = jsonEncode({ - "contents": [ - { - "parts": [ - {"text": "$payload $prompt"} - ] - } - ], - "generationConfig": { - "temperature": 1, - "topK": 64, - "topP": 0.95, - "maxOutputTokens": 8192, - "stopSequences": [] - }, - "safetySettings": [ - { - "category": "HARM_CATEGORY_HARASSMENT", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" + // 2. التحقق من وجود بيانات الرحلة + if (feedBack.isEmpty) { + // استخدام الـ Snackbar الجديد + _showCustomSnackbar( + 'Error', 'Ride information not found. Please refresh the page.', + isError: true); + return; + } + + isLoading = true; + update(); + + try { + // 3. استخراج البيانات المطلوبة + final rideId = feedBack[0]['id'].toString(); // ! تأكد أن اسم حقل ID صحيح + final complaint = complaintController.text; + String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0]; + + // 4. استدعاء سكربت PHP الجديد باستخدام http.post + final response = await http.post( + Uri.parse(AppLink.add_solve_all), + headers: {'Authorization': 'Bearer $token'}, + body: { + 'ride_id': rideId, + 'complaint_text': complaint, + 'audio_link': audioLink, }, - { - "category": "HARM_CATEGORY_HATE_SPEECH", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, - { - "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - }, - { - "category": "HARM_CATEGORY_DANGEROUS_CONTENT", - "threshold": "BLOCK_MEDIUM_AND_ABOVE" - } - ] - }); + ); - final response = await http.post( - Uri.parse( - 'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro:generateContent?key=${AK.geminiApi}'), - headers: {'Content-Type': 'application/json'}, - body: requestBody, - ); + Log.print('Server Response: ${response.body}'); - if (response.statusCode == 200) { - var responseData = jsonDecode(response.body); - var result = responseData['candidates'][0]['content']['parts'][0]['text']; - Log.print('result: ${result}'); - - // Clean up the result by removing surrounding backticks if they exist - result = result.replaceAll(RegExp(r'^```json\s*|\s*```$'), ''); - - // Attempt to decode the cleaned result as JSON - try { - var jsonResult = jsonDecode(result); - - // Access customer service solutions and reports for both passenger and driver - customerServiceSolutions = jsonResult['customerServiceSolutions']; - passengerReport = jsonResult['passengerReport']; - driverReport = jsonResult['driverReport']; - update(); - // Use the data accordingly - // For example, log the reports or display them in a UI dialog - - update(); - } catch (e) { - MyDialog().getDialog( - 'Error'.tr, - 'Unable to parse the response as JSON. Please check the format and try again.' - .tr, () { - Get.back(); - }); + if (response.statusCode != 200) { + _showCustomSnackbar( + 'Error', 'Failed to connect to the server. Please try again.', + isError: true); + return; } - } else { - Get.snackbar( - 'Error', "Request failed with status: ${response.statusCode}", - backgroundColor: AppColor.redColor); + + // 5. التعامل مع محتوى الرد من الخادم + final responseData = jsonDecode(response.body); + + if (responseData['status'] == 'success') { + passengerReport = responseData['data']['passenger_response']; + driverReport = responseData['data']['driver_response']; + update(); + + MyDialogContent().getDialog( + 'Success'.tr, Text('Your complaint has been submitted.'.tr), () { + Get.back(); + complaintController.clear(); + audioLink = ''; + formKey.currentState?.reset(); + }); + } else { + String errorMessage = + responseData['message'] ?? 'An unknown server error occurred'.tr; + _showCustomSnackbar('Submission Failed', errorMessage, isError: true); + } + } catch (e) { + Log.print("Submit Complaint Error: $e"); + _showCustomSnackbar('Error', 'An application error occurred.'.tr, + isError: true); + } finally { + isLoading = false; + update(); } } } diff --git a/lib/controller/local/translations.dart b/lib/controller/local/translations.dart index 26d7b26..99c614c 100644 --- a/lib/controller/local/translations.dart +++ b/lib/controller/local/translations.dart @@ -70,6 +70,24 @@ class MyTranslation extends Translations { "welcome to intaleq": "أهلاً بك في انطلق", "login or register subtitle": "أدخل رقم موبايلك لتسجيل الدخول أو لإنشاء حساب جديد", + 'An application error occurred.': 'حدث خطأ في التطبيق.', + 'Submission Failed': 'فشل الإرسال', + 'Your complaint has been submitted.': 'تم تقديم شكواك.', + 'Failed to connect to the server. Please try again.': + 'فشل الاتصال بالخادم. الرجاء المحاولة مرة أخرى.', + 'Ride information not found. Please refresh the page.': + 'لم يتم العثور على معلومات الرحلة. الرجاء تحديث الصفحة.', + 'Please describe your issue before submitting.': + 'يرجى وصف مشكلتك قبل الإرسال.', + 'An application error occurred during upload.': + 'حدث خطأ في التطبيق أثناء الرفع.', + 'Failed to upload audio file.': 'فشل رفع الملف الصوتي.', + 'Audio uploaded successfully.': 'تم رفع الصوت بنجاح.', + "Complaint cannot be filed for this ride. It may not have been completed or started.": + "لا يمكن تقديم شكوى لهذه الرحلة. قد لا تكون قد اكتملت أو بدأت.", + '2. Attach Recorded Audio (Optional)': + '٢. إرفاق تسجيل صوتي (اختياري)', + 'Please enter a description of the issue.': 'يرجى إدخال وصف للمشكلة.', "phone number label": "رقم الموبايل", "phone number required": "الرجاء إدخال رقم الموبايل", "send otp button": "إرسال رمز التحقق", diff --git a/lib/controller/rate/rate_conroller.dart b/lib/controller/rate/rate_conroller.dart index 79b286a..77bac9b 100644 --- a/lib/controller/rate/rate_conroller.dart +++ b/lib/controller/rate/rate_conroller.dart @@ -91,7 +91,7 @@ class RateController extends GetxController { } } await CRUD().post( - link: "${AppLink.IntaleqCairoServer}/ride/rate/addRateToDriver.php", + link: "${AppLink.IntaleqSyriaServer}/ride/rate/addRateToDriver.php", payload: { 'passenger_id': box.read(BoxName.passengerID).toString(), 'driver_id': driverId.toString(), @@ -100,7 +100,7 @@ class RateController extends GetxController { 'comment': comment.text, }); - if (AppLink.endPoint != AppLink.IntaleqCairoServer) { + if (AppLink.endPoint != AppLink.IntaleqSyriaServer) { CRUD().post( link: "${AppLink.endPoint}/ride/rate/addRateToDriver.php", payload: { diff --git a/lib/views/home/map_widget.dart/select_driver_mishwari.dart b/lib/views/home/map_widget.dart/select_driver_mishwari.dart index 0fdeea7..6f3a66b 100644 --- a/lib/views/home/map_widget.dart/select_driver_mishwari.dart +++ b/lib/views/home/map_widget.dart/select_driver_mishwari.dart @@ -47,12 +47,12 @@ class CupertinoDriverListWidget extends StatelessWidget { leading: CircleAvatar( radius: 25, backgroundImage: NetworkImage( - '${AppLink.IntaleqCairoServer}/portrate_captain_image/${driver['id']}.jpg', + '${AppLink.IntaleqSyriaServer}/portrate_captain_image/${driver['id']}.jpg', ), child: Builder( builder: (context) { return Image.network( - '${AppLink.IntaleqCairoServer}/portrate_captain_image/${driver['id']}.jpg', + '${AppLink.IntaleqSyriaServer}/portrate_captain_image/${driver['id']}.jpg', fit: BoxFit.cover, loadingBuilder: (BuildContext context, Widget child, diff --git a/lib/views/home/profile/complaint_page.dart b/lib/views/home/profile/complaint_page.dart index 452a7b1..93defb1 100644 --- a/lib/views/home/profile/complaint_page.dart +++ b/lib/views/home/profile/complaint_page.dart @@ -1,4 +1,3 @@ -import 'dart:convert'; import 'dart:io'; import 'package:flutter/material.dart'; @@ -30,155 +29,157 @@ class ComplaintPage extends StatelessWidget { body: [ GetBuilder( builder: (controller) { - if (controller.isLoading) { + if (controller.isLoading && controller.feedBack.isEmpty) { + // عرض التحميل المبدئي فقط return const MyCircularProgressIndicator(); } - return Form( - key: controller.formKey, - child: ListView( - padding: const EdgeInsets.all(16.0), - children: [ - // --- 1. بطاقة إدخال نص الشكوى --- - _buildSectionCard( - title: '1. Describe Your Issue'.tr, - child: TextFormField( - controller: controller.complaintController, - decoration: InputDecoration( - hintText: 'Enter your complaint here...'.tr, - filled: true, - fillColor: AppColor.secondaryColor.withOpacity(0.5), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide.none, - ), - contentPadding: const EdgeInsets.all(16), - ), - maxLines: 6, - style: AppStyle.subtitle, - ), - ), - - // --- 2. بطاقة إرفاق التسجيل الصوتي --- - _buildSectionCard( - title: '2. Attach Recorded Audio'.tr, - child: FutureBuilder>( - future: audioRecorderController.getRecordedFiles(), - builder: (context, snapshot) { - if (snapshot.connectionState == - ConnectionState.waiting) { - return const Center( - child: CircularProgressIndicator()); - } - if (snapshot.hasError || - !snapshot.hasData || - snapshot.data!.isEmpty) { - return Center( - child: Text('No audio files found.'.tr, - style: AppStyle.subtitle)); - } - return Column( - children: snapshot.data!.map((audioFilePath) { - final audioFile = File(audioFilePath); - final isUploaded = controller - .audioLink.isNotEmpty && - controller.audioLink - .contains(audioFilePath.split('/').last); - return ListTile( - leading: Icon( - isUploaded ? Icons.check_circle : Icons.mic, - color: isUploaded - ? AppColor.greenColor - : AppColor.primaryColor), - title: Text(audioFilePath.split('/').last, - style: AppStyle.subtitle, - overflow: TextOverflow.ellipsis), - subtitle: isUploaded - ? Text('Uploaded'.tr, - style: const TextStyle( - color: AppColor.greenColor)) - : null, - onTap: isUploaded - ? null - : () { - // --- نفس منطقك القديم --- - MyDialogContent().getDialog( - 'Confirm Attachment'.tr, - Text('Attach this audio file?'.tr), - () async { - await controller - .uploadAudioFile(audioFile); - }); - }, - ); - }).toList(), - ); - }, - ), - ), - - // --- 3. بطاقة تفاصيل الرحلة والرد --- - _buildSectionCard( - title: '3. Review Details & Response'.tr, - child: Column( - children: [ - if (controller.feedBack.isNotEmpty) ...[ - _buildDetailRow(Icons.calendar_today_outlined, - 'Date'.tr, controller.feedBack[0]['date']), - _buildDetailRow(Icons.monetization_on_outlined, - 'Price'.tr, '${controller.feedBack[0]['price']}'), - ], - const Divider(height: 24), - ListTile( - leading: const Icon(Icons.support_agent_outlined, - color: AppColor.primaryColor), - title: Text("Intaleq's Response".tr, - style: AppStyle.title), - subtitle: Text( - controller.passengerReport?['solution'] - ?.toString() ?? - 'Awaiting response...'.tr, - style: AppStyle.subtitle.copyWith(height: 1.5), + return Stack( + children: [ + Form( + key: controller.formKey, + child: ListView( + padding: const EdgeInsets.all(16.0), + children: [ + // --- 1. بطاقة إدخال نص الشكوى --- + _buildSectionCard( + title: '1. Describe Your Issue'.tr, + child: TextFormField( + controller: controller.complaintController, + decoration: InputDecoration( + hintText: 'Enter your complaint here...'.tr, + filled: true, + fillColor: AppColor.secondaryColor.withOpacity(0.5), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(12), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.all(16), ), + maxLines: 6, + style: AppStyle.subtitle, + validator: (value) { + if (value == null || value.isEmpty) { + return 'Please enter a description of the issue.' + .tr; + } + return null; + }, ), - ], - ), - ), + ), - // --- 4. زر الإرسال --- - const SizedBox(height: 24), - MyElevatedButton( - kolor: AppColor.blueColor, - title: 'Submit Complaint'.tr, - onPressed: () async { - // --- نفس منطقك القديم بالكامل --- - if (controller.formKey.currentState!.validate()) { - if (controller.audioLink.toString() == '') { - MyDialogContent().getDialog( - 'Audio file not attached'.tr, - Text( - 'The audio file is not uploaded yet.\nDo you want to submit without it?' - .tr), () async { - await controller.geminiAudio( - jsonEncode(controller.feedBack), - controller.audioLink, - controller.complaintController.text); - Get.back(); // إغلاق الدايالوج - controller.formKey.currentState!.reset(); - }); - } else { - await controller.geminiAudio( - jsonEncode(controller.feedBack), - controller.audioLink, - controller.complaintController.text); - controller.formKey.currentState!.reset(); - } - // هذه الدالة كانت مكررة في else، يجب أن تكون هنا لتنفذ في كلتا الحالتين - controller.addComplaint(); - } - }, + // --- 2. بطاقة إرفاق التسجيل الصوتي --- + _buildSectionCard( + title: '2. Attach Recorded Audio (Optional)'.tr, + child: FutureBuilder>( + future: audioRecorderController.getRecordedFiles(), + builder: (context, snapshot) { + if (snapshot.connectionState == + ConnectionState.waiting) { + return const Center( + child: CircularProgressIndicator()); + } + if (snapshot.hasError || + !snapshot.hasData || + snapshot.data!.isEmpty) { + return Center( + child: Text('No audio files found.'.tr, + style: AppStyle.subtitle)); + } + return Column( + children: snapshot.data!.map((audioFilePath) { + final audioFile = File(audioFilePath); + final isUploaded = + controller.audioLink.isNotEmpty && + controller.audioLink.contains( + audioFilePath.split('/').last); + + return ListTile( + leading: Icon( + isUploaded + ? Icons.check_circle + : Icons.mic, + color: isUploaded + ? AppColor.greenColor + : AppColor.redColor), + title: Text(audioFilePath.split('/').last, + style: AppStyle.subtitle, + overflow: TextOverflow.ellipsis), + subtitle: isUploaded + ? Text('Uploaded'.tr, + style: const TextStyle( + color: AppColor.greenColor)) + : null, + onTap: isUploaded + ? null + : () { + MyDialogContent().getDialog( + 'Confirm Attachment'.tr, + Text( + 'Attach this audio file?'.tr), + () async { + await controller + .uploadAudioFile(audioFile); + }); + }, + ); + }).toList(), + ); + }, + ), + ), + + // --- 3. بطاقة تفاصيل الرحلة والرد --- + _buildSectionCard( + title: '3. Review Details & Response'.tr, + child: Column( + children: [ + if (controller.feedBack.isNotEmpty) ...[ + _buildDetailRow(Icons.calendar_today_outlined, + 'Date'.tr, controller.feedBack[0]['date']), + _buildDetailRow( + Icons.monetization_on_outlined, + 'Price'.tr, + '${controller.feedBack[0]['price']}'), + ], + const Divider(height: 24), + ListTile( + leading: const Icon(Icons.support_agent_outlined, + color: AppColor.primaryColor), + title: Text("Intaleq's Response".tr, + style: AppStyle.title), + subtitle: Text( + // --- تعديل هنا لعرض الرد من الخادم --- + controller.passengerReport?['body'] + ?.toString() ?? + 'Awaiting response...'.tr, + style: AppStyle.subtitle.copyWith(height: 1.5), + ), + ), + ], + ), + ), + + // --- 4. زر الإرسال --- + const SizedBox(height: 24), + MyElevatedButton( + kolor: AppColor.blueColor, + title: 'Submit Complaint'.tr, + onPressed: () async { + // --- تعديل: استدعاء الدالة الجديدة فقط --- + await controller.submitComplaintToServer(); + }, + ), + const SizedBox(height: 24), // مسافة إضافية بالأسفل + ], ), - ], - ), + ), + // --- عرض مؤشر التحميل فوق كل شيء --- + if (controller.isLoading) + Container( + color: Colors.black.withOpacity(0.5), + child: const MyCircularProgressIndicator(), + ), + ], ); }, ),