25-10-10/1
This commit is contained in:
@@ -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";
|
||||||
|
|||||||
@@ -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',
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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": "إرسال رمز التحقق",
|
||||||
|
|||||||
@@ -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: {
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
),
|
||||||
|
],
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|||||||
Reference in New Issue
Block a user