25-10-10/1

This commit is contained in:
Hamza-Ayed
2025-10-10 13:13:00 +03:00
parent 6e1215ef66
commit 1a0bf1ee32
8 changed files with 302 additions and 305 deletions

View File

@@ -28,7 +28,7 @@ class AppLink {
// box.read(BoxName.serverChosen) ?? box.read(BoxName.basicLink); // box.read(BoxName.serverChosen) ?? box.read(BoxName.basicLink);
// 'https://server.sefer.click/sefer.click/sefer'; // 'https://server.sefer.click/sefer.click/sefer';
static final String server = endPoint; static final String server = endPoint;
static String IntaleqCairoServer = endPoint; static String IntaleqSyriaServer = endPoint;
static String IntaleqGizaServer = box.read('Giza'); static String IntaleqGizaServer = box.read('Giza');
static String IntaleqAlexandriaServer = box.read('Alexandria'); static String IntaleqAlexandriaServer = box.read('Alexandria');
@@ -187,6 +187,7 @@ class AppLink {
//-----------------Feed Back------------------ //-----------------Feed Back------------------
static String addFeedBack = "$ride/feedBack/add.php"; 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 uploadAudio = "$ride/feedBack/upload_audio.php";
static String getFeedBack = "$ride/feedBack/get.php"; static String getFeedBack = "$ride/feedBack/get.php";
static String updateFeedBack = "$ride/feedBack/updateFeedBack.php"; static String updateFeedBack = "$ride/feedBack/updateFeedBack.php";

View File

@@ -222,7 +222,7 @@ class RegisterController extends GetxController {
if (res1 != 'failure') { if (res1 != 'failure') {
//Multi-server signup (moved inside the successful registration check) //Multi-server signup (moved inside the successful registration check)
if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqCairoServer) { if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqSyriaServer) {
List<Future> signUp = [ List<Future> signUp = [
CRUD().post( CRUD().post(
link: '${AppLink.IntaleqAlexandriaServer}/auth/signup.php', link: '${AppLink.IntaleqAlexandriaServer}/auth/signup.php',
@@ -297,7 +297,7 @@ class RegisterController extends GetxController {
); );
if (res1 != 'failure') { if (res1 != 'failure') {
if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqCairoServer) { if (AppLink.IntaleqAlexandriaServer != AppLink.IntaleqSyriaServer) {
List<Future> signUp = [ List<Future> signUp = [
CRUD().post( CRUD().post(
link: '${AppLink.IntaleqAlexandriaServer}/auth/signup.php', link: '${AppLink.IntaleqAlexandriaServer}/auth/signup.php',

View File

@@ -406,7 +406,7 @@ class MapPassengerController extends GetxController {
"order_id": rideId.toString(), // Convert to String "order_id": rideId.toString(), // Convert to String
"status": 'waiting' "status": 'waiting'
}); });
if (AppLink.endPoint != AppLink.IntaleqCairoServer) { if (AppLink.endPoint != AppLink.IntaleqSyriaServer) {
CRUD().post( CRUD().post(
link: "${AppLink.endPoint}/ride/driver_order/update.php", link: "${AppLink.endPoint}/ride/driver_order/update.php",
payload: { payload: {
@@ -418,7 +418,7 @@ class MapPassengerController extends GetxController {
"id": rideId.toString(), // Convert to String "id": rideId.toString(), // Convert to String
"status": 'waiting' "status": 'waiting'
}); });
if (AppLink.endPoint != AppLink.IntaleqCairoServer) { if (AppLink.endPoint != AppLink.IntaleqSyriaServer) {
CRUD().post( CRUD().post(
link: "${AppLink.endPoint}/ride/rides/update.php", link: "${AppLink.endPoint}/ride/rides/update.php",
payload: { payload: {
@@ -430,7 +430,7 @@ class MapPassengerController extends GetxController {
"id": rideId.toString(), // Convert to String "id": rideId.toString(), // Convert to String
"status": 'wait' "status": 'wait'
}); });
if (AppLink.endPoint != AppLink.IntaleqCairoServer) { if (AppLink.endPoint != AppLink.IntaleqSyriaServer) {
CRUD().post( CRUD().post(
link: link:
"${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php", "${AppLink.endPoint}/ride/notificationCaptain/updateWaitingTrip.php",
@@ -1899,7 +1899,7 @@ class MapPassengerController extends GetxController {
try { try {
final response = await CRUD().post( final response = await CRUD().post(
link: "${AppLink.IntaleqCairoServer}/ride/rides/add.php", link: "${AppLink.IntaleqSyriaServer}/ride/rides/add.php",
payload: payload); payload: payload);
if (response is String) { if (response is String) {
final parsedValue = jsonDecode(response); final parsedValue = jsonDecode(response);
@@ -2566,7 +2566,7 @@ class MapPassengerController extends GetxController {
} }
// Default case // Default case
box.write(BoxName.serverChosen, AppLink.IntaleqCairoServer); box.write(BoxName.serverChosen, AppLink.IntaleqSyriaServer);
return 'Cairo'; return 'Cairo';
} }
@@ -3307,7 +3307,7 @@ class MapPassengerController extends GetxController {
"id": rideId.toString(), // Convert to String "id": rideId.toString(), // Convert to String
"status": 'notApplyFromAnyDriver' "status": 'notApplyFromAnyDriver'
}); });
if (AppLink.endPoint != AppLink.IntaleqCairoServer) { if (AppLink.endPoint != AppLink.IntaleqSyriaServer) {
CRUD().post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: { CRUD().post(link: "${AppLink.endPoint}/ride/rides/update.php", payload: {
"id": rideId.toString(), // Convert to String "id": rideId.toString(), // Convert to String
"status": 'notApplyFromAnyDriver' "status": 'notApplyFromAnyDriver'
@@ -5667,7 +5667,7 @@ class MapPassengerController extends GetxController {
// Get.snackbar('Success'.tr, 'Trip booked successfully'.tr); // Get.snackbar('Success'.tr, 'Trip booked successfully'.tr);
var id = response['message']['id'].toString(); var id = response['message']['id'].toString();
await CRUD().post( await CRUD().post(
link: '${AppLink.IntaleqCairoServer}/ride/rides/add.php', link: '${AppLink.IntaleqSyriaServer}/ride/rides/add.php',
payload: { payload: {
"start_location": "start_location":
'${data[0]["start_location"]['lat']},${data[0]["start_location"]['lng']}', '${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}'); Log.print('Unexpected response type: ${value.runtimeType}');
} }
}); });
if (AppLink.endPoint != AppLink.IntaleqCairoServer) { if (AppLink.endPoint != AppLink.IntaleqSyriaServer) {
await CRUD().post( await CRUD().post(
link: "${AppLink.endPoint}/ride/mishwari/add.php", link: "${AppLink.endPoint}/ride/mishwari/add.php",
payload: tripData); payload: tripData);

View File

@@ -15,21 +15,56 @@ import 'package:http_parser/http_parser.dart';
import 'package:mime/mime.dart'; import 'package:mime/mime.dart';
import '../../../constant/api_key.dart'; import '../../../constant/api_key.dart';
import '../../../env/env.dart';
import '../../../print.dart'; import '../../../print.dart';
import '../../../views/widgets/mydialoug.dart'; import '../../../views/widgets/mydialoug.dart';
import '../../functions/encrypt_decrypt.dart';
class ComplaintController extends GetxController { class ComplaintController extends GetxController {
bool isLoading = false; bool isLoading = false;
final formKey = GlobalKey<FormState>(); final formKey = GlobalKey<FormState>();
final complaintController = TextEditingController(); final complaintController = TextEditingController();
final suggestionController = TextEditingController();
List feedBack = []; List feedBack = [];
Map<String, dynamic>? passengerReport;
Map<String, dynamic>? driverReport;
var isUploading = false.obs;
var uploadSuccess = false.obs;
String audioLink = ''; // سيتم تخزين رابط الصوت هنا بعد الرفع
@override @override
void onInit() { void onInit() {
super.onInit(); super.onInit();
getLatestRidesForPassengers(); 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 { getLatestRidesForPassengers() async {
isLoading = true; isLoading = true;
update(); update();
@@ -40,54 +75,24 @@ class ComplaintController extends GetxController {
var d = jsonDecode(res)['message']; var d = jsonDecode(res)['message'];
feedBack = d; feedBack = d;
} }
isLoading = false; isLoading = false;
update(); 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<void> uploadAudioFile(File audioFile) async { Future<void> uploadAudioFile(File audioFile) async {
try { try {
isUploading.value = true; isUploading.value = true;
update();
// Prepare the file upload var uri = Uri.parse('${AppLink.IntaleqSyriaServer}/upload_audio.php');
var uri = Uri.parse('${AppLink.IntaleqCairoServer}/upload_audio.php');
var request = http.MultipartRequest('POST', uri); 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); var mimeType = lookupMimeType(audioFile.path);
// ** التعديل: تم استخدام نفس هيدر التوثيق **
request.headers.addAll({ request.headers.addAll({
'Authorization': 'Authorization': 'Bearer $token',
'Basic ${base64Encode(utf8.encode(AK.basicAuthCredentials.toString()))}',
}); });
request.files.add( request.files.add(
await http.MultipartFile.fromPath( await http.MultipartFile.fromPath(
@@ -97,142 +102,114 @@ class ComplaintController extends GetxController {
), ),
); );
// Send the request
var response = await request.send(); var response = await request.send();
// Convert response to string for parsing
var responseBody = await http.Response.fromStream(response); var responseBody = await http.Response.fromStream(response);
// After the upload request
if (response.statusCode == 200) { if (response.statusCode == 200) {
var jsonResponse = jsonDecode(responseBody.body); var jsonResponse = jsonDecode(responseBody.body);
if (jsonResponse['status'] == 'Audio file uploaded successfully.') { if (jsonResponse['status'] == 'Audio file uploaded successfully.') {
uploadSuccess.value = true; uploadSuccess.value = true;
audioLink = jsonResponse['link']; // Get the audio link audioLink = jsonResponse['link']; // تخزين الرابط في المتغير
Get.back(); Get.back();
Get.snackbar('Success'.tr, 'Audio uploaded successfully.'.tr, // استخدام الـ Snackbar الجديد
backgroundColor: const Color.fromARGB(255, 89, 185, 115)); _showCustomSnackbar('Success', 'Audio uploaded successfully.');
} else { } else {
uploadSuccess.value = false; uploadSuccess.value = false;
_showCustomSnackbar('Error', 'Failed to upload audio file.',
isError: true);
} }
} else { } else {
uploadSuccess.value = false; uploadSuccess.value = false;
_showCustomSnackbar('Error', 'Server error: ${response.statusCode}',
isError: true);
} }
} catch (e) { } catch (e) {
uploadSuccess.value = false; uploadSuccess.value = false;
_showCustomSnackbar(
'Error', 'An application error occurred during upload.',
isError: true);
} finally { } finally {
isUploading.value = false; isUploading.value = false;
update();
} }
} }
var customerServiceSolutions; // --- الدالة الجديدة التي تتصل بالسكريبت الجديد ---
var passengerReport; Future<void> submitComplaintToServer() async {
var driverReport; // 1. التحقق من صحة الفورم
var isloading = false; if (!formKey.currentState!.validate() || complaintController.text.isEmpty) {
Future<void> geminiAudio(payload, String audioLink, String complain) async { // استخدام الـ Snackbar الجديد
String prompt = ''' _showCustomSnackbar(
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. 'Error', 'Please describe your issue before submitting.',
isError: true);
Complaint details: return;
- 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}
} }
} the response in arabic language with egypt
''';
var requestBody = jsonEncode({ // 2. التحقق من وجود بيانات الرحلة
"contents": [ if (feedBack.isEmpty) {
{ // استخدام الـ Snackbar الجديد
"parts": [ _showCustomSnackbar(
{"text": "$payload $prompt"} 'Error', 'Ride information not found. Please refresh the page.',
] isError: true);
} return;
], }
"generationConfig": {
"temperature": 1, isLoading = true;
"topK": 64, update();
"topP": 0.95,
"maxOutputTokens": 8192, try {
"stopSequences": [] // 3. استخراج البيانات المطلوبة
}, final rideId = feedBack[0]['id'].toString(); // ! تأكد أن اسم حقل ID صحيح
"safetySettings": [ final complaint = complaintController.text;
{ String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
"category": "HARM_CATEGORY_HARASSMENT",
"threshold": "BLOCK_MEDIUM_AND_ABOVE" // 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( Log.print('Server Response: ${response.body}');
Uri.parse(
'https://generativelanguage.googleapis.com/v1beta/models/gemini-1.0-pro:generateContent?key=${AK.geminiApi}'),
headers: {'Content-Type': 'application/json'},
body: requestBody,
);
if (response.statusCode == 200) { if (response.statusCode != 200) {
var responseData = jsonDecode(response.body); _showCustomSnackbar(
var result = responseData['candidates'][0]['content']['parts'][0]['text']; 'Error', 'Failed to connect to the server. Please try again.',
Log.print('result: ${result}'); isError: true);
return;
// 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();
});
} }
} else {
Get.snackbar( // 5. التعامل مع محتوى الرد من الخادم
'Error', "Request failed with status: ${response.statusCode}", final responseData = jsonDecode(response.body);
backgroundColor: AppColor.redColor);
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();
} }
} }
} }

View File

@@ -70,6 +70,24 @@ class MyTranslation extends Translations {
"welcome to intaleq": "أهلاً بك في انطلق", "welcome to intaleq": "أهلاً بك في انطلق",
"login or register subtitle": "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 label": "رقم الموبايل",
"phone number required": "الرجاء إدخال رقم الموبايل", "phone number required": "الرجاء إدخال رقم الموبايل",
"send otp button": "إرسال رمز التحقق", "send otp button": "إرسال رمز التحقق",

View File

@@ -91,7 +91,7 @@ class RateController extends GetxController {
} }
} }
await CRUD().post( await CRUD().post(
link: "${AppLink.IntaleqCairoServer}/ride/rate/addRateToDriver.php", link: "${AppLink.IntaleqSyriaServer}/ride/rate/addRateToDriver.php",
payload: { payload: {
'passenger_id': box.read(BoxName.passengerID).toString(), 'passenger_id': box.read(BoxName.passengerID).toString(),
'driver_id': driverId.toString(), 'driver_id': driverId.toString(),
@@ -100,7 +100,7 @@ class RateController extends GetxController {
'comment': comment.text, 'comment': comment.text,
}); });
if (AppLink.endPoint != AppLink.IntaleqCairoServer) { if (AppLink.endPoint != AppLink.IntaleqSyriaServer) {
CRUD().post( CRUD().post(
link: "${AppLink.endPoint}/ride/rate/addRateToDriver.php", link: "${AppLink.endPoint}/ride/rate/addRateToDriver.php",
payload: { payload: {

View File

@@ -47,12 +47,12 @@ class CupertinoDriverListWidget extends StatelessWidget {
leading: CircleAvatar( leading: CircleAvatar(
radius: 25, radius: 25,
backgroundImage: NetworkImage( backgroundImage: NetworkImage(
'${AppLink.IntaleqCairoServer}/portrate_captain_image/${driver['id']}.jpg', '${AppLink.IntaleqSyriaServer}/portrate_captain_image/${driver['id']}.jpg',
), ),
child: Builder( child: Builder(
builder: (context) { builder: (context) {
return Image.network( return Image.network(
'${AppLink.IntaleqCairoServer}/portrate_captain_image/${driver['id']}.jpg', '${AppLink.IntaleqSyriaServer}/portrate_captain_image/${driver['id']}.jpg',
fit: BoxFit.cover, fit: BoxFit.cover,
loadingBuilder: (BuildContext context, loadingBuilder: (BuildContext context,
Widget child, Widget child,

View File

@@ -1,4 +1,3 @@
import 'dart:convert';
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
@@ -30,155 +29,157 @@ class ComplaintPage extends StatelessWidget {
body: [ body: [
GetBuilder<ComplaintController>( GetBuilder<ComplaintController>(
builder: (controller) { builder: (controller) {
if (controller.isLoading) { if (controller.isLoading && controller.feedBack.isEmpty) {
// عرض التحميل المبدئي فقط
return const MyCircularProgressIndicator(); return const MyCircularProgressIndicator();
} }
return Form( return Stack(
key: controller.formKey, children: [
child: ListView( Form(
padding: const EdgeInsets.all(16.0), key: controller.formKey,
children: [ child: ListView(
// --- 1. بطاقة إدخال نص الشكوى --- padding: const EdgeInsets.all(16.0),
_buildSectionCard( children: [
title: '1. Describe Your Issue'.tr, // --- 1. بطاقة إدخال نص الشكوى ---
child: TextFormField( _buildSectionCard(
controller: controller.complaintController, title: '1. Describe Your Issue'.tr,
decoration: InputDecoration( child: TextFormField(
hintText: 'Enter your complaint here...'.tr, controller: controller.complaintController,
filled: true, decoration: InputDecoration(
fillColor: AppColor.secondaryColor.withOpacity(0.5), hintText: 'Enter your complaint here...'.tr,
border: OutlineInputBorder( filled: true,
borderRadius: BorderRadius.circular(12), fillColor: AppColor.secondaryColor.withOpacity(0.5),
borderSide: BorderSide.none, border: OutlineInputBorder(
), borderRadius: BorderRadius.circular(12),
contentPadding: const EdgeInsets.all(16), borderSide: BorderSide.none,
), ),
maxLines: 6, contentPadding: const EdgeInsets.all(16),
style: AppStyle.subtitle,
),
),
// --- 2. بطاقة إرفاق التسجيل الصوتي ---
_buildSectionCard(
title: '2. Attach Recorded Audio'.tr,
child: FutureBuilder<List<String>>(
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),
), ),
maxLines: 6,
style: AppStyle.subtitle,
validator: (value) {
if (value == null || value.isEmpty) {
return 'Please enter a description of the issue.'
.tr;
}
return null;
},
), ),
], ),
),
),
// --- 4. زر الإرسال --- // --- 2. بطاقة إرفاق التسجيل الصوتي ---
const SizedBox(height: 24), _buildSectionCard(
MyElevatedButton( title: '2. Attach Recorded Audio (Optional)'.tr,
kolor: AppColor.blueColor, child: FutureBuilder<List<String>>(
title: 'Submit Complaint'.tr, future: audioRecorderController.getRecordedFiles(),
onPressed: () async { builder: (context, snapshot) {
// --- نفس منطقك القديم بالكامل --- if (snapshot.connectionState ==
if (controller.formKey.currentState!.validate()) { ConnectionState.waiting) {
if (controller.audioLink.toString() == '') { return const Center(
MyDialogContent().getDialog( child: CircularProgressIndicator());
'Audio file not attached'.tr, }
Text( if (snapshot.hasError ||
'The audio file is not uploaded yet.\nDo you want to submit without it?' !snapshot.hasData ||
.tr), () async { snapshot.data!.isEmpty) {
await controller.geminiAudio( return Center(
jsonEncode(controller.feedBack), child: Text('No audio files found.'.tr,
controller.audioLink, style: AppStyle.subtitle));
controller.complaintController.text); }
Get.back(); // إغلاق الدايالوج return Column(
controller.formKey.currentState!.reset(); children: snapshot.data!.map((audioFilePath) {
}); final audioFile = File(audioFilePath);
} else { final isUploaded =
await controller.geminiAudio( controller.audioLink.isNotEmpty &&
jsonEncode(controller.feedBack), controller.audioLink.contains(
controller.audioLink, audioFilePath.split('/').last);
controller.complaintController.text);
controller.formKey.currentState!.reset(); return ListTile(
} leading: Icon(
// هذه الدالة كانت مكررة في else، يجب أن تكون هنا لتنفذ في كلتا الحالتين isUploaded
controller.addComplaint(); ? 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(),
),
],
); );
}, },
), ),