200 lines
6.9 KiB
Dart
200 lines
6.9 KiB
Dart
// food_service.dart — طبقة الاتصال بـ backend/food (جهة الراكب)
|
|
import 'dart:convert';
|
|
import '../functions/crud.dart';
|
|
import '../../constant/links.dart';
|
|
import 'food_models.dart';
|
|
|
|
class FoodApiResult<T> {
|
|
final bool success;
|
|
final T? data;
|
|
final String message;
|
|
final int? code;
|
|
FoodApiResult(this.success, this.data, this.message, {this.code});
|
|
}
|
|
|
|
class FoodService {
|
|
static String get _base => '${AppLink.server}/food';
|
|
|
|
static Future<FoodApiResult<List<FoodMerchant>>> browseMerchants({
|
|
required String city,
|
|
String? category,
|
|
}) async {
|
|
final payload = <String, dynamic>{'city': city};
|
|
if (category != null && category.isNotEmpty) payload['category'] = category;
|
|
|
|
final res = await CRUD().post(link: '$_base/merchant/browse.php', payload: payload);
|
|
|
|
if (res is Map && res['status'] == 'success') {
|
|
final msg = res['message'];
|
|
final list = (msg is Map && msg['merchants'] is List)
|
|
? (msg['merchants'] as List)
|
|
.map((m) => FoodMerchant.fromJson(Map<String, dynamic>.from(m)))
|
|
.toList()
|
|
: <FoodMerchant>[];
|
|
return FoodApiResult(true, list, 'ok');
|
|
}
|
|
return FoodApiResult(false, null, _errMsg(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<List<FoodMerchant>>> searchMerchants({
|
|
required String city,
|
|
required String query,
|
|
}) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/merchant/search.php',
|
|
payload: {'city': city, 'q': query},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') {
|
|
final msg = res['message'];
|
|
final list = (msg is Map && msg['merchants'] is List)
|
|
? (msg['merchants'] as List)
|
|
.map((m) => FoodMerchant.fromJson(Map<String, dynamic>.from(m)))
|
|
.toList()
|
|
: <FoodMerchant>[];
|
|
return FoodApiResult(true, list, 'ok');
|
|
}
|
|
return FoodApiResult(false, null, _errMsg(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<Map<String, dynamic>>> merchantDetails(int merchantId) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/merchant/details.php',
|
|
payload: {'merchant_id': merchantId.toString()},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') {
|
|
final msg = res['message'];
|
|
if (msg is Map) {
|
|
final merchant = FoodMerchant.fromJson(Map<String, dynamic>.from(msg['merchant']));
|
|
final categories = (msg['categories'] is List)
|
|
? (msg['categories'] as List)
|
|
.map((c) => FoodMenuCategory.fromJson(Map<String, dynamic>.from(c)))
|
|
.toList()
|
|
: <FoodMenuCategory>[];
|
|
return FoodApiResult(true, {'merchant': merchant, 'categories': categories}, 'ok');
|
|
}
|
|
}
|
|
return FoodApiResult(false, null, _errMsg(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<FoodQuote>> getQuote({
|
|
required int merchantId,
|
|
required List<FoodCartLine> lines,
|
|
}) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/cart/quote.php',
|
|
payload: {
|
|
'merchant_id': merchantId.toString(),
|
|
'items': jsonEncode(lines.map((l) => l.toQuotePayload()).toList()),
|
|
},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') {
|
|
final msg = res['message'];
|
|
if (msg is Map) return FoodApiResult(true, FoodQuote.fromJson(Map<String, dynamic>.from(msg)), 'ok');
|
|
}
|
|
return FoodApiResult(false, null, _errMsg(res), code: _errCode(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<int>> createOrder({
|
|
required String quoteToken,
|
|
required String clientOrderUuid,
|
|
required String deliveryAddress,
|
|
required double deliveryLat,
|
|
required double deliveryLng,
|
|
required String paymentMethod, // wallet | cash
|
|
String? customerNote,
|
|
}) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/order/create.php',
|
|
payload: {
|
|
'quote_token': quoteToken,
|
|
'client_order_uuid': clientOrderUuid,
|
|
'delivery_address': deliveryAddress,
|
|
'delivery_lat': deliveryLat.toString(),
|
|
'delivery_lng': deliveryLng.toString(),
|
|
'payment_method': paymentMethod,
|
|
if (customerNote != null && customerNote.isNotEmpty) 'customer_note': customerNote,
|
|
},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') {
|
|
final msg = res['message'];
|
|
if (msg is Map && msg['order_id'] != null) {
|
|
return FoodApiResult(true, int.tryParse(msg['order_id'].toString()) ?? 0, 'ok');
|
|
}
|
|
}
|
|
return FoodApiResult(false, null, _errMsg(res), code: _errCode(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<FoodOrder>> getOrderStatus(int orderId) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/order/status.php',
|
|
payload: {'order_id': orderId.toString()},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') {
|
|
final msg = res['message'];
|
|
if (msg is Map && msg['order'] is Map) {
|
|
return FoodApiResult(true, FoodOrder.fromJson(Map<String, dynamic>.from(msg['order'])), 'ok');
|
|
}
|
|
}
|
|
return FoodApiResult(false, null, _errMsg(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<void>> cancelOrder(int orderId, {String? reason}) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/order/cancel.php',
|
|
payload: {'order_id': orderId.toString(), if (reason != null) 'reason': reason},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') return FoodApiResult(true, null, 'ok');
|
|
return FoodApiResult(false, null, _errMsg(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<void>> rateOrder(int orderId, int rating, {String? comment}) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/order/rate.php',
|
|
payload: {
|
|
'order_id': orderId.toString(),
|
|
'rating': rating.toString(),
|
|
if (comment != null && comment.isNotEmpty) 'comment': comment,
|
|
},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') return FoodApiResult(true, null, 'ok');
|
|
return FoodApiResult(false, null, _errMsg(res));
|
|
}
|
|
|
|
static Future<FoodApiResult<List<FoodOrder>>> getHistory({int page = 1}) async {
|
|
final res = await CRUD().post(
|
|
link: '$_base/order/history.php',
|
|
payload: {'page': page.toString()},
|
|
);
|
|
|
|
if (res is Map && res['status'] == 'success') {
|
|
final msg = res['message'];
|
|
final list = (msg is Map && msg['orders'] is List)
|
|
? (msg['orders'] as List)
|
|
.map((o) => FoodOrder.fromJson(Map<String, dynamic>.from(o)))
|
|
.toList()
|
|
: <FoodOrder>[];
|
|
return FoodApiResult(true, list, 'ok');
|
|
}
|
|
return FoodApiResult(false, null, _errMsg(res));
|
|
}
|
|
|
|
static int? _errCode(dynamic res) {
|
|
if (res is Map && res['code'] != null) return int.tryParse(res['code'].toString());
|
|
return null;
|
|
}
|
|
|
|
static String _errMsg(dynamic res) {
|
|
if (res == 'no_internet') return 'تحقق من اتصالك بالإنترنت';
|
|
if (res == 'token_expired') return 'انتهت الجلسة، حاول مجدداً';
|
|
if (res is Map && res['message'] is String) return res['message'];
|
|
return 'حدث خطأ، حاول مجدداً';
|
|
}
|
|
}
|